diff --git a/.gitignore b/.gitignore index 54ee8b6b5a10..204c463e7457 100644 --- a/.gitignore +++ b/.gitignore @@ -62,6 +62,7 @@ libconftest.dylib* *.o *.o-* *.patch +!depends/patches/*/*.patch *.a *.pb.cc *.pb.h diff --git a/.travis.yml b/.travis.yml index 5a81f2049473..3a57c00eb2a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,21 @@ # 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 os: linux language: generic +addons: + apt: + packages: + # Use more recent docker version + - docker-ce + # Can be removed if Travis ever upgrades to Bionic + - realpath + services: - docker @@ -20,9 +23,7 @@ cache: apt: true ccache: true directories: - - depends/built - - depends/sdk-sources - - $HOME/.ccache + - $HOME/cache env: global: @@ -30,78 +31,50 @@ env: - secure: "J0T+zrRBBzBnxoioCD378cRSJUF8Vms32QMmrENVVfuJl03oBQv0QxSatLEiD4PtRPrN00mJ8/k/HdqFQ0SN1yg6a00duOZ6C9jk6t/fExBO1LNhp3Z7lcaMUrNEyRN6sgfVeznDVTGj9jBQU2HNlwHk0UD9BNp/k+Bjhrtw7Njp9JTLZKBgxrXk0WZmfk0d75Q+9DajA1j2tSfWVpwzv6HMGfqIH1wsVEakBY+tnaE6EO3IdzdWHhf/iV+Tx0tL0Qz/6mejgSuoGLTJLmeTrceTTmsDlN3B95y+XfcYDs6EP7gpzN8Ffg/mLV8xJ2Myj7+b1o+smMJZE5tLuPIeAu0YsfkPNa/tNLotOlDxF72per0kXyNYbRf+JrpxiOu9pLCtdtd40V2SsiNhpQqHVy+b+wkkqo2syTHT+9UNkqEi3WFm6TqFuOeQNtDm6R0hRl/Talw/ZEeKs68z1Cf6VyBrZG5LqtOurtWlKwXCe/Tr1i2g4BEAcbywphtCCcfgMK9YSGQnjKkGk6k9gVEa2bneYLZz1RSh2OpXnIsIJOX7X0hNd5xtQDLgbpSUlKwVcEriWeGe12GCszKNX9gdEo2bHeq4xAJ6BE+ot745z9dc+nC2BJ6Su+I5L6zBg4m2FWn0TWhPvz/6JR26t6ls/E7m+hkwJTCHnIUiFmmzpEw=" # DOCKER_HUB_PASSWORD - secure: "RLzlMhfLqwSBrZqJOVOd61suXBn+HtUR3vOZfuFYF/Qmjjj5TE41+rObZmzc54hP/ZL+OH6blnibpvfDXlX+eN38ivFQfuxkJIGL68SJsEwNBRwW39Yw6Hl5RdI41MLCH7ByN15wifLp1JKBilHJ7XGMOUjI7P0yl7JjX8GBXUCtJbVLRugo80/yn+XQ1NdnlpbpYHNjMEQFWSODPa3pSK5McWvyQjDZDgS+IkdrZmIYJPMa7bmKH5I/edUPSmXQT905FgEwq9u8XR0SyBopli21EK9l6GkXIIvmDTYz5vT26Apvi2B4Aoazlklg+KNRUJuLGerpt6kbnU0gMSUChVkFfFhOk6GRSN3a/AUfD2FOudvMhet2QvlPHx+GYdEUr5XVo5HW42pHsqfD6eDtHd8VLTsHP0q4C8V85fNMv21lkkehy2ry8fx/RRy6x4O2wg2mua+79UkGKXp75gMKzWEcth34PCFCOu37l2F8R/ANnrQ52K/8vIQ88TtU2OpYX89fHjLojBxu+WKEBGZH2LRPsZBOUHeeO5C/xKDrhZU24ORnMW8wg66Qg5GIX1KI4a8yp73Mpues5hzpJ6wkMuRrQt40ymKndLCjv8KSd+5BfP6Or/KIrzDNYdZaasjk7JNi6rcZmm9d3fTAo+Ja/mjpUCIOo3SX14luzVCJIig=" - - 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_BUILD=false 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 + dash - - 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/dash" 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" -# dashd - - 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_DASH_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" -# dashd release build (for docker) - - HOST=x86_64-unknown-linux-gnu PPA="ppa:bitcoin/bitcoin" PACKAGES="bc python3-zmq" DEP_OPTS="NO_QT=1 NO_UPNP=1" GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports" PYZMQ=true DOCKER_BUILD=true + - BUILD_TARGET=arm-linux + - BUILD_TARGET=win32 + - BUILD_TARGET=win64 + - BUILD_TARGET=linux32 + - BUILD_TARGET=linux64 + - BUILD_TARGET=linux64_nowallet + - BUILD_TARGET=linux64_release DOCKER_BUILD=true + - BUILD_TARGET=mac + +before_cache: + # Save builder image + - docker save dash-builder-$BUILD_TARGET-$TRAVIS_JOB_NUMBER $(docker history -q dash-builder-$BUILD_TARGET-$TRAVIS_JOB_NUMBER | grep -v \) | gzip -2 > $HOME/cache/docker/dash-builder-$BUILD_TARGET.tar.gz -before_install: - - git clone https://github.com/dashpay/dash_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") 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 dash_hash && python3 setup.py install --user && cd .. + # 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 + - 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/dash-builder-$BUILD_TARGET.tar.gz ]; then zcat $HOST_CACHE_DIR/docker/dash-builder-$BUILD_TARGET.tar.gz | docker load || true; fi + - docker build --pull -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: - - unset CC; unset CXX - - unset DISPLAY # 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);' - #- 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: - 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 dashcore-$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/dashcore-$HOST ./docker/build-docker.sh; fi + # Our scripts try to be Travis agnostic + - $DOCKER_RUN_IN_BUILDER ./ci/build_depends.sh; + # Gracefully stop build without running into timeouts (which won't update caches) when building depends took too long + # Next build should fix this situation as it will start with a populated depends cache + - if [ $SECONDS -gt 1200 ]; then export DEPENDS_TIMEOUT="true"; false; fi # The "false" here ensures that the build is marked as failed even though the whole script returns 0 + - test "$DEPENDS_TIMEOUT" != "true" && $DOCKER_RUN_IN_BUILDER ./ci/build_src.sh + - test "$DEPENDS_TIMEOUT" != "true" && $DOCKER_RUN_IN_BUILDER ./ci/test_unittests.sh + - test "$DEPENDS_TIMEOUT" != "true" && $DOCKER_RUN_IN_BUILDER ./ci/test_integrationtests.sh -parallel=3 + - test "$DEPENDS_TIMEOUT" != "true" && if [ "$DOCKER_BUILD" = "true" ]; then BUILD_DIR=build-ci/dashcore-$BUILD_TARGET ./docker/build-docker.sh; fi after_script: - echo $TRAVIS_COMMIT_RANGE - echo $TRAVIS_COMMIT_LOG diff --git a/CMakeLists.txt b/CMakeLists.txt index 99cbed2b494a..8ba410964e32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,14 +8,30 @@ cmake_minimum_required(VERSION 3.7) project(dash) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 14) include_directories( src + src/qt/forms src/leveldb/include src/univalue/include ) +if(UNIX AND NOT APPLE) + set(DEPENDS_PREFIX depends/x86_64-pc-linux-gnu) +elseif(APPLE) + set(DEPENDS_PREFIX depends/x86_64-apple-darwin11) +elseif(WIN32) + set(DEPENDS_PREFIX depends/x86_64-w64-mingw32) +endif() + +message(STATUS "DEPENDS_PREFIX: ${DEPENDS_PREFIX}") + +if(DEFINED DEPENDS_PREFIX) + include_directories(${DEPENDS_PREFIX}/include) + include_directories(${DEPENDS_PREFIX}/include/QtWidgets) +endif() + add_definitions( -DENABLE_WALLET=1 ) @@ -58,6 +74,8 @@ file(GLOB SOURCE_FILES src/*.h src/evo/*.h src/evo/*.cpp + src/llmq/*.h + src/llmq/*.cpp src/rpc/*.cpp src/rpc/*.h ) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dab20e9beba3..078e0ec756b1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -174,7 +174,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/Jenkinsfile b/Jenkinsfile new file mode 100644 index 000000000000..a27dae758be4 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,100 @@ +// 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 Dash 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 + + def env = [ + "BUILD_TARGET=${target}", + "PULL_REQUEST=false", + "JOB_NUMBER=${BUILD_NUMBER}", + ] + withEnv(env) { + def builderImageName="dash-builder-${target}" + + def builderImage + stage("${target}/builder-image") { + builderImage = docker.build("${builderImageName}", "--build-arg BUILD_TARGET=${target} ci -f ci/Dockerfile.builder") + } + + builderImage.inside("-t") { + // copy source into fixed path + // we must build under the same path everytime as otherwise caches won't work properly + sh "cp -ra ${pwd}/. /dash-src/" + + // restore cache + def hasCache = false + try { + copyArtifacts(projectName: "dashpay-dash/${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 dashpay-dash/${BRANCH_NAME}" + } else { + try { + copyArtifacts(projectName: 'dashpay-dash/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 dashpay-dash/develop" + } + } + + if (hasCache) { + sh "cd /dash-src && tar xzf ${pwd}/ci-cache-${target}.tar.gz" + } else { + sh "mkdir -p /dash-src/ci-cache-${target}" + } + + stage("${target}/depends") { + sh 'cd /dash-src && ./ci/build_depends.sh' + } + stage("${target}/build") { + sh 'cd /dash-src && ./ci/build_src.sh' + } + stage("${target}/test") { + sh 'cd /dash-src && ./ci/test_unittests.sh' + } + stage("${target}/test") { + sh 'cd /dash-src && ./ci/test_integrationtests.sh' + } + + // archive cache and copy it into the jenkins workspace + sh "cd /dash-src && tar czfv ci-cache-${target}.tar.gz ci-cache-${target} && cp ci-cache-${target}.tar.gz ${pwd}/" + } + + // upload cache + 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..793d4880718c --- /dev/null +++ b/Jenkinsfile.gitian @@ -0,0 +1,129 @@ +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/dashpay/dash.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('dash') { + checkout scm + gitianDescriptor = readYaml file: "contrib/gitian-descriptors/gitian-${target}.yml" + } + dir('gitian-builder') { + git url: 'https://github.com/dashpay/gitian-builder.git' + } + sh "mkdir -p dashcore-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: "dashpay-dash-gitian-nightly/${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 dashpay-dash-gitian-nightly/develop" + } + } + } + + def gitianImage + stage("${target}/builder-image") { + dir('dash') { + gitianImage = docker.build("dash-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 ../dash/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 dash=${commit} --url dash=${repositoryUrl} ../dash/contrib/gitian-descriptors/gitian-${target}.yml + RET=\$? + # give the above tail calls enough time to print everything on failure + sleep 2s + exit \$RET + """ + } finally { + // make sure it doesn't run forever + sh "docker rm -fv gitian-target || true" + } + sh "mv build/out/dashcore-* ../dashcore-binaries/" + sh "mv build/out/src/dashcore-* ../dashcore-binaries/" + } + archiveArtifacts artifacts: 'dashcore-binaries/*', fingerprint: true + } + + // TODO remove this in a few days (only needed to prune the old compressed file from Jenkins caches) + sh "cd gitian-builder/cache && find -name ccache.tar.gz | xargs rm -f" + 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 3b9b31ef062b..9445a4768655 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Dash Core staging tree 0.12.3 +Dash Core staging tree 0.13.0 =============================== `master:` [![Build Status](https://travis-ci.org/dashpay/dash.svg?branch=master)](https://travis-ci.org/dashpay/dash) `develop:` [![Build Status](https://travis-ci.org/dashpay/dash.svg?branch=develop)](https://travis-ci.org/dashpay/dash/branches) diff --git a/ci/Dockerfile.builder b/ci/Dockerfile.builder new file mode 100644 index 000000000000..250fac8ab7ac --- /dev/null +++ b/ci/Dockerfile.builder @@ -0,0 +1,59 @@ +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) +RUN apt-get update && apt-get install -y \ + git \ + g++ \ + autotools-dev libtool m4 automake autoconf pkg-config \ + zlib1g-dev \ + libssl1.0-dev \ + curl \ + ccache \ + bsdmainutils \ + python3-pip python3-dev \ + cmake \ + && rm -rf /var/lib/apt/lists + +# Python stuff +RUN pip3 install pyzmq # really needed? + +# dash_hash +RUN git clone https://github.com/dashpay/dash_hash +RUN cd dash_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} dash +RUN useradd -u ${USER_ID} -g dash -s /bin/bash -m -d /dash dash + +# 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 && rm -rf /var/lib/apt/lists; 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 /dash-src && \ + mkdir -p /cache/ccache && \ + mkdir /cache/depends && \ + mkdir /cache/sdk-sources && \ + chown $USER_ID:$GROUP_ID /dash-src && \ + chown $USER_ID:$GROUP_ID /cache && \ + chown $USER_ID:$GROUP_ID /cache -R +WORKDIR /dash-src + +USER dash diff --git a/ci/Dockerfile.gitian-builder b/ci/Dockerfile.gitian-builder new file mode 100644 index 000000000000..79bd2a381b45 --- /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} dash +RUN useradd -u ${USER_ID} -g dash -s /bin/bash -m -d /dash dash + +WORKDIR /dash +USER dash \ No newline at end of file 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..2a659e7efc8a --- /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 dashcore-$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..15e918bc53e4 --- /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="dash-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=true +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=true +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=true +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_DASH_DEBUG" + export PYZMQ=true + export RUN_TESTS=true +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..7b3573961919 --- /dev/null +++ b/ci/test_integrationtests.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# This script is executed inside the builder image + +set -e + +PASS_ARGS="$@" + +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/dashcore-$BUILD_TARGET + +./qa/pull-tester/rpc-tests.py --coverage $PASS_ARGS diff --git a/ci/test_unittests.sh b/ci/test_unittests.sh new file mode 100755 index 000000000000..f5af7e39637e --- /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/dashcore-$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_dash.exe +else + make $MAKEJOBS check VERBOSE=1 +fi diff --git a/configure.ac b/configure.ac index fe7382891283..8313a7bda58b 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,10 @@ 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, 3) -define(_CLIENT_VERSION_BUILD, 2) -define(_CLIENT_VERSION_IS_RELEASE, true) +define(_CLIENT_VERSION_MINOR, 13) +define(_CLIENT_VERSION_REVISION, 0) +define(_CLIENT_VERSION_BUILD, 0) +define(_CLIENT_VERSION_IS_RELEASE, false) define(_COPYRIGHT_YEAR, 2018) define(_COPYRIGHT_HOLDERS,[The %s developers]) define(_COPYRIGHT_HOLDERS_SUBSTITUTION,[[Dash Core]]) @@ -54,8 +54,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 diff --git a/contrib/README.md b/contrib/README.md index df8c11bdc64b..19d04640f4ab 100644 --- a/contrib/README.md +++ b/contrib/README.md @@ -34,7 +34,7 @@ PGP keys used for signing Bitcoin Core [Gitian release](/doc/release-process.md) ### [MacDeploy](/contrib/macdeploy) ### Scripts and notes for Mac builds. -### [Gitian-build](/contrib/gitian-build.sh) ### +### [Gitian-build](/contrib/gitian-build.py) ### Script for running full Gitian builds. Test and Verify Tools 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 100755 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/dash-qt.pro b/contrib/dash-qt.pro index 57a29cdea769..cde8c56930a8 100644 --- a/contrib/dash-qt.pro +++ b/contrib/dash-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/devtools/check-doc.py b/contrib/devtools/check-doc.py index fa1e137587a6..464e2a8a46b6 100755 --- 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 100755 --- 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 ce4cca8e5136..e8375a78bebd 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''' @@ -27,7 +27,7 @@ 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/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']).rstrip('\n') +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(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("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 75c60faf7438..67a7dffe4c0c 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 100755 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 deleted file mode 100755 index 15392c17778f..000000000000 --- a/contrib/gitian-build.sh +++ /dev/null @@ -1,413 +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. - -# What to do -sign=false -verify=false -build=false -setupenv=false - -# Systems to build -linux=true -windows=true -osx=true - -# Other Basic variables -SIGNER= -VERSION= -commit=false -url=https://github.com/dashpay/dash -proc=2 -mem=2000 -lxc=true -docker=false -osslTarUrl=http://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz -osslPatchUrl=https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch -scriptName=$(basename -- "$0") -signProg="gpg --detach-sign" -commitFiles=true - -# Help Message -read -d '' usage <<- EOF -Usage: $scriptName [-c|u|v|b|s|B|o|h|j|m|] signer version - -Run this script from the directory containing the dash, gitian-builder, gitian.sigs, and dash-detached-sigs. - -Arguments: -signer GPG signer to sign each build assert file -version Version number, commit, or branch to build. If building a commit or branch, the -c option must be specified - -Options: --c|--commit Indicate that the version argument is for a commit or branch --u|--url Specify the URL of the repository. Default is https://github.com/dashpay/dash --v|--verify Verify the gitian build --b|--build Do a gitian build --s|--sign Make signed binaries for Windows and Mac OSX --B|--buildsign Build both signed and unsigned binaries --o|--os Specify which Operating Systems the build is for. Default is lwx. l for linux, w for windows, x for osx --j Number of processes to use. Default 2 --m Memory to allocate in MiB. Default 2000 ---kvm Use KVM ---lxc Use LXC ---docker Use Docker ---setup Setup the gitian building environment. Uses KVM. If you want to use lxc, use the --lxc option. If you want to use Docker, use --docker. Only works on Debian-based systems (Ubuntu, Debian) ---detach-sign Create the assert file for detached signing. Will not commit anything. ---no-commit Do not commit anything to git --h|--help Print this help message -EOF - -# Get options and arguments -while :; do - case $1 in - # Verify - -v|--verify) - verify=true - ;; - # Build - -b|--build) - build=true - ;; - # Sign binaries - -s|--sign) - sign=true - ;; - # Build then Sign - -B|--buildsign) - sign=true - build=true - ;; - # PGP Signer - -S|--signer) - if [ -n "$2" ] - then - SIGNER="$2" - shift - else - echo 'Error: "--signer" requires a non-empty argument.' - exit 1 - fi - ;; - # Operating Systems - -o|--os) - if [ -n "$2" ] - then - linux=false - windows=false - osx=false - if [[ "$2" = *"l"* ]] - then - linux=true - fi - if [[ "$2" = *"w"* ]] - then - windows=true - fi - if [[ "$2" = *"x"* ]] - then - osx=true - fi - shift - else - echo 'Error: "--os" requires an argument containing an l (for linux), w (for windows), or x (for Mac OSX)\n' - exit 1 - fi - ;; - # Help message - -h|--help) - echo "$usage" - exit 0 - ;; - # Commit or branch - -c|--commit) - commit=true - ;; - # Number of Processes - -j) - if [ -n "$2" ] - then - proc=$2 - shift - else - echo 'Error: "-j" requires an argument' - exit 1 - fi - ;; - # Memory to allocate - -m) - if [ -n "$2" ] - then - mem=$2 - shift - else - echo 'Error: "-m" requires an argument' - exit 1 - fi - ;; - # URL - -u) - if [ -n "$2" ] - then - url=$2 - shift - else - echo 'Error: "-u" requires an argument' - exit 1 - fi - ;; - # kvm - --lxc) - lxc=true - docker=false - ;; - # kvm - --kvm) - lxc=false - docker=false - ;; - # docker - --docker) - lxc=false - docker=true - ;; - # Detach sign - --detach-sign) - signProg="true" - commitFiles=false - ;; - # Commit files - --no-commit) - commitFiles=false - ;; - # Setup - --setup) - setup=true - ;; - *) # Default case: If no more options then break out of the loop. - break - esac - shift -done - -# Set up LXC -if [[ $lxc = true ]] -then - export USE_LXC=1 - export LXC_BRIDGE=lxcbr0 - sudo ifconfig lxcbr0 up 10.0.3.2 -elif [[ $docker = true ]] -then - export USE_DOCKER=1 -fi - -# Check for OSX SDK -if [[ ! -e "gitian-builder/inputs/MacOSX10.11.sdk.tar.gz" && $osx == true ]] -then - echo "Cannot build for OSX, SDK does not exist. Will build for other OSes" - osx=false -fi - -# Get signer -if [[ -n"$1" ]] -then - SIGNER="$1" - shift -fi - -# Get version -if [[ -n "$1" ]] -then - VERSION=$1 - COMMIT=$VERSION - shift -fi - -# Check that a signer is specified -if [[ "$SIGNER" == "" ]] -then - echo "$scriptName: Missing signer." - echo "Try $scriptName --help for more information" - exit 1 -fi - -# Check that a version is specified -if [[ $VERSION == "" ]] -then - echo "$scriptName: Missing version." - echo "Try $scriptName --help for more information" - exit 1 -fi - -# Add a "v" if no -c -if [[ $commit = false ]] -then - COMMIT="v${VERSION}" -fi -echo ${COMMIT} - -# Setup build environment -if [[ $setup = true ]] -then - sudo apt-get install ruby apache2 git apt-cacher-ng python-vm-builder qemu-kvm qemu-utils - git clone https://github.com/dashpay/gitian.sigs.git - git clone https://github.com/dashpay/dash-detached-sigs.git - git clone https://github.com/devrandom/gitian-builder.git - pushd ./gitian-builder - if [[ -n "$USE_LXC" ]] - then - sudo apt-get install lxc - bin/make-base-vm --suite trusty --arch amd64 --lxc - elif [[ -n "$USE_DOCKER" ]] - then - sudo apt-get install docker-ce - bin/make-base-vm --suite trusty --arch amd64 --docker - else - bin/make-base-vm --suite trusty --arch amd64 - fi - popd -fi - -# Set up build -pushd ./dash -git fetch -git checkout ${COMMIT} -popd - -# Build -if [[ $build = true ]] -then - # Make output folder - mkdir -p ./dashcore-binaries/${VERSION} - - # Build Dependencies - echo "" - echo "Building Dependencies" - echo "" - pushd ./gitian-builder - mkdir -p inputs - wget -N -P inputs $osslPatchUrl - wget -N -P inputs $osslTarUrl - make -C ../dash/depends download SOURCES_PATH=`pwd`/cache/common - - # Linux - if [[ $linux = true ]] - then - echo "" - echo "Compiling ${VERSION} Linux" - echo "" - ./bin/gbuild -j ${proc} -m ${mem} --commit dash=${COMMIT} --url dash=${url} ../dash/contrib/gitian-descriptors/gitian-linux.yml - ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-linux --destination ../gitian.sigs/ ../dash/contrib/gitian-descriptors/gitian-linux.yml - mv build/out/dashcore-*.tar.gz build/out/src/dashcore-*.tar.gz ../dashcore-binaries/${VERSION} - fi - # Windows - if [[ $windows = true ]] - then - echo "" - echo "Compiling ${VERSION} Windows" - echo "" - ./bin/gbuild -j ${proc} -m ${mem} --commit dash=${COMMIT} --url dash=${url} ../dash/contrib/gitian-descriptors/gitian-win.yml - ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-win-unsigned --destination ../gitian.sigs/ ../dash/contrib/gitian-descriptors/gitian-win.yml - mv build/out/dashcore-*-win-unsigned.tar.gz inputs/dashcore-win-unsigned.tar.gz - mv build/out/dashcore-*.zip build/out/dashcore-*.exe ../dashcore-binaries/${VERSION} - fi - # Mac OSX - if [[ $osx = true ]] - then - echo "" - echo "Compiling ${VERSION} Mac OSX" - echo "" - ./bin/gbuild -j ${proc} -m ${mem} --commit dash=${COMMIT} --url dash=${url} ../dash/contrib/gitian-descriptors/gitian-osx.yml - ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-osx-unsigned --destination ../gitian.sigs/ ../dash/contrib/gitian-descriptors/gitian-osx.yml - mv build/out/dashcore-*-osx-unsigned.tar.gz inputs/dashcore-osx-unsigned.tar.gz - mv build/out/dashcore-*.tar.gz build/out/dashcore-*.dmg ../dashcore-binaries/${VERSION} - fi - popd - - if [[ $commitFiles = true ]] - then - # Commit to gitian.sigs repo - echo "" - echo "Committing ${VERSION} Unsigned Sigs" - echo "" - pushd gitian.sigs - git add ${VERSION}-linux/"${SIGNER}" - git add ${VERSION}-win-unsigned/"${SIGNER}" - git add ${VERSION}-osx-unsigned/"${SIGNER}" - git commit -a -m "Add ${VERSION} unsigned sigs for ${SIGNER}" - popd - fi -fi - -# Verify the build -if [[ $verify = true ]] -then - # Linux - pushd ./gitian-builder - echo "" - echo "Verifying v${VERSION} Linux" - echo "" - ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-linux ../dash/contrib/gitian-descriptors/gitian-linux.yml - # Windows - echo "" - echo "Verifying v${VERSION} Windows" - echo "" - ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-win-unsigned ../dash/contrib/gitian-descriptors/gitian-win.yml - # Mac OSX - echo "" - echo "Verifying v${VERSION} Mac OSX" - echo "" - ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-unsigned ../dash/contrib/gitian-descriptors/gitian-osx.yml - # Signed Windows - echo "" - echo "Verifying v${VERSION} Signed Windows" - echo "" - ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-signed ../dash/contrib/gitian-descriptors/gitian-osx-signer.yml - # Signed Mac OSX - echo "" - echo "Verifying v${VERSION} Signed Mac OSX" - echo "" - ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-signed ../dash/contrib/gitian-descriptors/gitian-osx-signer.yml - popd -fi - -# Sign binaries -if [[ $sign = true ]] -then - - pushd ./gitian-builder - # Sign Windows - if [[ $windows = true ]] - then - echo "" - echo "Signing ${VERSION} Windows" - echo "" - ./bin/gbuild -i --commit signature=${COMMIT} ../dash/contrib/gitian-descriptors/gitian-win-signer.yml - ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-win-signed --destination ../gitian.sigs/ ../dash/contrib/gitian-descriptors/gitian-win-signer.yml - mv build/out/dashcore-*win64-setup.exe ../dashcore-binaries/${VERSION} - mv build/out/dashcore-*win32-setup.exe ../dashcore-binaries/${VERSION} - fi - # Sign Mac OSX - if [[ $osx = true ]] - then - echo "" - echo "Signing ${VERSION} Mac OSX" - echo "" - ./bin/gbuild -i --commit signature=${COMMIT} ../dash/contrib/gitian-descriptors/gitian-osx-signer.yml - ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-osx-signed --destination ../gitian.sigs/ ../dash/contrib/gitian-descriptors/gitian-osx-signer.yml - mv build/out/dashcore-osx-signed.dmg ../dashcore-binaries/${VERSION}/dashcore-${VERSION}-osx.dmg - fi - popd - - if [[ $commitFiles = true ]] - then - # Commit Sigs - pushd gitian.sigs - echo "" - echo "Committing ${VERSION} Signed Sigs" - echo "" - git add ${VERSION}-win-signed/"${SIGNER}" - git add ${VERSION}-osx-signed/"${SIGNER}" - git commit -a -m "Add ${VERSION} signed binary sigs for ${SIGNER}" - popd - fi -fi diff --git a/contrib/gitian-descriptors/README.md b/contrib/gitian-descriptors/README.md index 1660cd3db0ff..7b4993b6694e 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 2c26d4fd60d4..39523dfc31d6 100755 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -1,32 +1,35 @@ --- -name: "dash-linux-0.12" +name: "dash-linux-0.13" 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/dashpay/dash.git" "dir": "dash" @@ -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 } @@ -99,6 +126,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 @@ -111,6 +139,7 @@ script: | \$REAL \$@ EOF chmod +x ${WRAP_DIR}/${prog} + touch -d "${REFERENCE_DATETIME}" ${WRAP_DIR}/${prog} done cd dash @@ -154,7 +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}" + 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 @@ -182,3 +211,11 @@ script: | done 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 8fb6beeda8e2..214c5425ff63 100644 --- a/contrib/gitian-descriptors/gitian-osx-signer.yml +++ b/contrib/gitian-descriptors/gitian-osx-signer.yml @@ -1,7 +1,7 @@ --- name: "dash-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 9abc4776fbbd..5c17c5b4660e 100644 --- a/contrib/gitian-descriptors/gitian-osx.yml +++ b/contrib/gitian-descriptors/gitian-osx.yml @@ -1,15 +1,15 @@ --- -name: "dash-osx-0.12" +name: "dash-osx-0.13" 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/dashpay/dash.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 } @@ -124,7 +143,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} @@ -156,3 +175,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 a7119da378fd..000000000000 --- a/contrib/gitian-descriptors/gitian-rpi2.yml +++ /dev/null @@ -1,112 +0,0 @@ ---- -name: "dash-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/dashpay/dash.git" - "dir": "dash" -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 dash - 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 dashcore-*.tar.gz` - DISTNAME=`echo ${SOURCEDIST} | sed 's/.tar.*//'` - # Correct tar file order - mkdir -p temp - pushd temp - tar xf ../$SOURCEDIST - find dashcore-* | 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 16750642cb11..feb94ff920c8 100644 --- a/contrib/gitian-descriptors/gitian-win-signer.yml +++ b/contrib/gitian-descriptors/gitian-win-signer.yml @@ -1,7 +1,7 @@ --- name: "dash-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 68aa01440880..6d1b673a3736 100755 --- a/contrib/gitian-descriptors/gitian-win.yml +++ b/contrib/gitian-descriptors/gitian-win.yml @@ -1,18 +1,19 @@ --- -name: "dash-win-0.12" +name: "dash-win-0.13" 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/dashpay/dash.git" "dir": "dash" @@ -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 } @@ -141,7 +174,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}" + 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 @@ -166,3 +199,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 f0bdbd85debc..8b70cf7c684e 100755 --- a/contrib/macdeploy/custom_dsstore.py +++ b/contrib/macdeploy/custom_dsstore.py @@ -1,8 +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 71d43d8f4750..7a22d99bbc6c 100755 --- a/contrib/macdeploy/macdeployqtplus +++ b/contrib/macdeploy/macdeployqtplus @@ -1,5 +1,4 @@ -#!/usr/bin/env python -from __future__ import division, print_function, unicode_literals +#!/usr/bin/env python3 # # Copyright (C) 2011 Patrick "p2k" Schneider # @@ -203,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: @@ -211,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. @@ -716,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: @@ -749,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: @@ -791,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) @@ -867,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: diff --git a/contrib/seeds/makeseeds.py b/contrib/seeds/makeseeds.py index 0f43154e874e..c4b279618d43 100755 --- a/contrib/seeds/makeseeds.py +++ b/contrib/seeds/makeseeds.py @@ -10,7 +10,7 @@ MAX_SEEDS_PER_ASN=4 -MIN_PROTOCOL_VERSION = 70208 +MIN_PROTOCOL_VERSION = 70210 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 4a0e89c5865d..8881999e662c 100644 --- a/contrib/seeds/nodes_main.txt +++ b/contrib/seeds/nodes_main.txt @@ -1,288 +1,275 @@ -5.2.66.144:9999 -5.132.159.6:9999 -5.132.159.8:9999 -5.132.159.9:9999 -5.132.159.10:9999 +5.9.99.80:9999 +5.132.191.109:9999 +5.132.191.211:9999 +5.132.191.213:9999 +5.132.191.216:9999 5.134.115.37:9999 -5.189.129.41:9999 +5.189.171.64:9999 +5.200.23.33:9999 +5.200.53.96:9999 18.221.96.149:9999 +23.175.0.111:9999 23.175.0.112:9999 23.175.0.113:9999 23.175.0.114:9999 -23.175.0.115:9999 23.182.128.21:9999 -23.182.128.22:9999 23.182.128.23:9999 23.182.128.24:9999 +23.182.128.25:9999 23.227.160.76:9999 -31.13.217.52:9999 -31.13.217.53:9999 -31.13.217.54:9999 -31.13.217.55:9999 -31.14.134.237:9999 +23.227.163.23:9999 +23.227.163.52:9999 +27.102.114.61:9999 31.31.73.82:9999 +34.196.96.99:9999 34.199.130.69:9999 -34.206.0.63:9999 34.225.101.97:9999 +35.167.192.165:9999 35.196.242.227:9999 +35.197.187.1:9999 +37.97.227.21:9999 37.120.161.145:9999 37.157.199.210:9999 -37.200.122.223:9999 +37.221.192.220:9999 37.221.194.42:9999 -37.221.194.220:9999 -37.221.194.222:9999 -38.127.169.147:9999 +37.221.194.43:9999 +40.113.6.44:9999 43.229.77.46:9999 -45.32.192.69:9999 -45.58.48.17:9999 +45.32.206.185:9999 45.76.64.6:9999 -45.76.128.61:9999 -47.74.23.207:9999 +46.28.109.195:9999 +46.37.158.177:9999 +46.163.166.57:9999 +47.52.229.72:9999 +47.75.49.228:9999 47.90.14.156:9999 -47.90.53.103:9999 -47.91.217.49:9999 -47.100.25.118:9999 -50.3.74.81:9999 -50.3.74.82:9999 -50.3.74.84:9999 -50.3.74.85:9999 -50.99.0.243:9999 -52.14.192.211:9999 -52.44.128.25:9999 -52.78.165.91:9999 +47.98.66.94:9999 +47.104.96.207:9999 +47.244.102.81:9999 +51.15.42.143:9999 +51.38.105.36:9999 +51.38.128.251:9999 +51.255.32.213:9999 52.79.112.22:9999 -52.80.55.223:9999 -52.170.102.253:9999 52.187.135.187:9999 +52.235.36.187:9999 52.242.37.8:9999 -52.242.39.96:9999 +54.70.231.195:9999 +54.172.145.162:9999 62.138.3.214:9999 62.138.8.7:9999 -62.210.5.246:9999 -62.210.161.248:9999 -64.19.211.41:9999 +62.138.8.103:9999 +62.146.13.50:9999 +63.142.253.85:9999 +64.34.218.48:9999 64.140.157.114:9999 -64.140.158.96:9999 -64.210.41.226:9999 +64.140.159.226:9999 66.23.242.86:9999 66.23.242.89:9999 66.23.242.90:9999 66.23.242.91:9999 -66.172.11.249:9999 -66.172.12.244:9999 +66.172.11.85:9999 66.172.27.70:9999 -66.172.33.108:9999 -69.12.67.171:9999 -69.12.67.193:9999 -69.12.67.197:9999 -69.27.173.165:9999 -69.27.173.166:9999 -69.30.193.154:9999 -69.90.186.9:9999 -69.90.186.10:9999 -69.90.186.13:9999 -72.18.213.107:9999 -76.74.178.221:9999 -77.73.69.190:9999 +69.51.13.90:9999 +69.51.13.101:9999 +69.51.20.70:9999 +69.51.20.71:9999 +74.207.228.180:9999 +75.127.4.248:9999 +76.74.219.148:9999 77.81.226.146:9999 -78.46.234.241:9999 +78.41.207.70:9999 78.83.19.0:9999 78.133.169.70:9999 80.120.27.150:9999 -80.211.197.235:9999 -81.169.139.149:9999 -81.169.188.88:9999 81.169.205.220:9999 81.169.229.84:9999 81.171.2.245:9999 -81.229.119.216:9999 82.118.227.52:9999 +82.165.29.103:9999 82.211.21.128:9999 82.211.21.131:9999 -82.211.21.173:9999 -82.211.34.103:9999 -82.239.79.88:9999 -83.1.99.1:9999 -83.1.99.6:9999 -85.25.194.204:9999 +82.211.21.136:9999 +82.211.21.139:9999 +84.234.96.82:9999 +84.234.96.86:9999 +84.234.96.87:9999 85.25.199.215:9999 85.184.254.29:9999 85.184.254.181:9999 +85.217.170.206:9999 +85.217.171.243:9999 +85.255.1.65:9999 85.255.4.212:9999 -86.105.235.103:9999 87.106.63.52:9999 87.117.253.55:9999 -88.119.179.178:9999 -88.119.179.179:9999 -88.166.200.153:9999 89.36.220.241:9999 -89.38.144.71:9999 +89.40.0.14:9999 89.40.114.69:9999 -89.45.67.33:9999 -89.163.134.227:9999 -90.187.5.61:9999 -91.123.195.83:9999 -91.123.201.94:9999 +89.238.181.74:9999 91.219.239.82:9999 91.219.239.83:9999 +92.63.57.28:9999 +92.63.57.120:9999 93.90.205.80:9999 +93.104.213.208:9999 93.158.216.153:9999 -93.185.103.70:9999 -93.185.103.71:9999 -94.130.104.169:9999 +94.156.174.166:9999 +94.176.239.62:9999 +94.177.170.223:9999 94.177.224.225:9999 +94.177.225.115:9999 94.177.225.210:9999 94.177.232.33:9999 +94.177.250.61:9999 95.43.139.162:9999 +95.181.226.47:9999 +95.183.50.97:9999 95.183.51.98:9999 95.183.51.141:9999 -95.183.53.56:9999 -95.183.53.96:9999 -95.215.45.225:9999 +95.183.53.134:9999 +95.216.11.57:9999 +95.216.147.47:9999 96.126.96.164:9999 -101.132.25.188:9999 -103.11.64.94:9999 -104.128.226.70:9999 -104.128.226.72:9999 -104.238.192.146:9999 +101.37.96.0:9999 +101.37.96.96:9999 +104.196.169.81:9999 +104.223.6.164:9999 107.6.140.35:9999 107.6.140.55:9999 -107.6.175.206:9999 +107.6.175.150:9999 107.155.118.137:9999 +107.191.101.212:9999 +108.61.224.22:9999 108.61.224.33:9999 110.10.25.76:9999 -116.62.237.190:9999 -118.31.38.232:9999 -120.132.27.39:9999 +115.159.86.118:9999 +122.106.55.214:9999 122.114.173.218:9999 123.193.64.166:9999 -125.89.62.36:9999 -125.89.62.37:9999 130.185.251.69:9999 130.185.251.113:9999 133.130.102.22:9999 -139.162.11.82:9999 -141.255.184.191:9999 -142.44.229.61:9999 -142.54.162.179:9999 -149.56.128.199:9999 -154.127.57.66:9999 +139.99.193.23:9999 +148.66.50.42:9999 +151.236.10.109:9999 154.127.57.240:9999 +154.127.57.254:9999 154.127.59.27:9999 154.127.60.8:9999 -155.254.32.139:9999 +159.69.155.94:9999 +159.89.32.12:9999 159.100.19.166:9999 159.100.19.167:9999 -162.221.195.17:9999 -162.221.195.243:9999 -162.250.145.107:9999 +159.203.20.131:9999 163.44.167.144:9999 163.44.167.237:9999 -163.44.171.51:9999 -163.172.128.187:9999 -163.172.129.13:9999 -164.132.42.162:9999 -167.88.15.98:9999 -168.235.96.202:9999 +163.44.168.191:9999 +167.88.15.97:9999 168.235.96.203:9999 168.235.96.205:9999 -168.235.99.45:9999 +168.235.99.47:9999 170.75.162.60:9999 170.75.162.219:9999 170.75.163.108:9999 +172.81.177.42:9999 172.86.121.148:9999 172.104.64.13:9999 -172.110.6.167:9999 +172.110.5.98:9999 172.110.6.169:9999 173.212.241.50:9999 -176.31.145.50:9999 -178.62.128.50:9999 +176.122.20.207:9999 178.62.160.29:9999 -178.62.192.42:9999 178.209.50.30:9999 178.238.42.7:9999 -178.254.23.160:9999 -178.254.32.48:9999 -185.2.82.137:9999 +185.22.174.37:9999 185.26.126.250:9999 185.28.101.91:9999 185.28.101.133:9999 185.35.64.217:9999 185.35.67.117:9999 185.43.210.125:9999 +185.58.194.113:9999 185.58.224.234:9999 185.64.104.221:9999 185.64.104.222:9999 185.64.104.223:9999 -185.82.22.228:9999 -185.106.121.163:9999 +185.106.122.46:9999 185.119.85.30:9999 +185.133.37.105:9999 +185.139.237.154:9999 +185.141.26.106:9999 185.142.212.144:9999 -185.156.178.74:9999 185.156.178.125:9999 185.156.178.216:9999 -185.156.178.221:9999 +185.156.178.218:9999 +185.156.178.222:9999 +185.165.168.21:9999 185.165.168.22:9999 185.165.168.23:9999 185.165.168.25:9999 -185.165.168.27:9999 -185.166.239.48:9999 -185.166.239.162:9999 -185.166.239.187:9999 -185.166.239.191:9999 185.168.8.144:9999 +185.183.97.131:9999 185.185.40.13:9999 +185.203.117.229:9999 +185.212.44.250:9999 +185.213.37.1:9999 +185.213.37.6:9999 185.217.1.99:9999 -188.40.62.22:9999 -188.40.93.205:9999 -188.166.74.173:9999 -188.213.171.30:9999 -188.227.16.16:9999 -188.227.16.83:9999 +185.217.1.100:9999 +185.243.112.48:9999 +185.243.112.80:9999 +185.243.112.187:9999 +185.243.112.221:9999 +185.253.189.18:9999 +185.253.189.66:9999 +185.253.189.70:9999 +185.253.189.80:9999 +188.166.69.88:9999 +188.227.74.171:9999 188.227.74.193:9999 -188.227.74.196:9999 +190.4.184.180:9999 190.10.8.250:9999 192.81.130.228:9999 +192.161.182.104:9999 +192.161.182.108:9999 +192.161.182.109:9999 +192.227.143.41:9999 192.227.228.196:9999 -192.227.228.197:9999 -192.243.214.90:9999 192.250.230.17:9999 +193.29.187.85:9999 193.234.224.72:9999 193.234.224.100:9999 +193.234.224.103:9999 193.234.224.145:9999 -193.234.224.152:9999 +194.99.20.100:9999 +195.154.105.109:9999 +195.154.105.125:9999 198.20.101.116:9999 198.23.128.75:9999 -198.23.226.41:9999 +198.53.74.14:9999 198.61.190.142:9999 -198.204.251.58:9999 199.201.110.195:9999 200.122.128.172:9999 202.71.98.123:9999 202.71.98.124:9999 202.71.98.125:9999 202.71.98.126:9999 -204.12.240.98:9999 +204.16.243.106:9999 204.16.245.98:9999 -204.27.58.212:9999 -209.177.87.148:9999 209.177.88.87:9999 -211.99.224.163:9999 -211.99.224.167:9999 -211.99.224.174:9999 -211.99.224.202:9999 +209.177.91.203:9999 +212.24.96.16:9999 212.24.96.26:9999 -212.24.96.120:9999 -212.24.96.159:9999 -212.24.96.178:9999 -212.227.136.235:9999 +212.47.235.253:9999 212.227.201.47:9999 212.237.37.240:9999 -213.136.80.137:9999 +213.64.197.95:9999 +213.136.80.93:9999 213.136.80.159:9999 -213.169.73.74:9999 -217.13.101.94:9999 -217.13.107.26:9999 +216.107.217.62:9999 +216.189.145.161:9999 +216.189.147.178:9999 217.61.2.53:9999 -217.61.3.193:9999 -219.2.130.110:9999 +221.156.133.110:9999 +222.231.59.30:9999 diff --git a/contrib/seeds/nodes_test.txt b/contrib/seeds/nodes_test.txt index 3e17335c830f..06e4b94633aa 100644 --- a/contrib/seeds/nodes_test.txt +++ b/contrib/seeds/nodes_test.txt @@ -1,15 +1,35 @@ -34.237.167.248:19999 -34.238.93.224:19999 -43.229.77.46:19999 -45.32.104.95:19999 -45.32.215.31:19999 -45.32.221.230:19999 -45.32.237.76:19999 -52.7.46.113:19999 -52.201.23.129:19999 -138.68.20.142:19999 -165.227.8.80:19999 -217.182.229.128:19999 -217.182.229.132:19999 -217.182.229.134:19999 -217.182.229.136:19999 +18.202.52.170:20008 +18.202.52.170:20004 +18.202.52.170:20000 +18.202.52.170:20012 +18.202.52.170:20016 +18.202.52.170:20020 +34.255.15.20:20007 +34.255.15.20:20003 +34.255.15.20:20015 +34.255.15.20:20011 +34.255.15.20:20019 +34.255.15.20:20023 +52.50.208.53:20009 +52.50.208.53:20005 +52.50.208.53:20001 +52.50.208.53:20013 +52.50.208.53:20017 +52.50.208.53:20021 +63.33.238.85:20010 +63.33.238.85:20006 +63.33.238.85:20002 +63.33.238.85:20014 +63.33.238.85:20018 +63.33.238.85:20022 +145.239.235.16:19999 +145.239.235.17:19999 +145.239.235.18:19999 +145.239.235.19:19999 +145.239.235.20:19999 +145.239.235.21:19999 +145.239.235.22:19999 +145.239.235.23:19999 +145.239.235.24:19999 +145.239.235.25:19999 +178.62.203.249:19999 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/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..85eb21472e1c --- /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=$(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..3be669dc47cb 100644 --- a/depends/packages/native_ccache.mk +++ b/depends/packages/native_ccache.mk @@ -1,8 +1,8 @@ package=native_ccache -$(package)_version=3.3.3 +$(package)_version=3.4.2 $(package)_download_path=https://samba.org/ftp/ccache $(package)_file_name=ccache-$($(package)_version).tar.bz2 -$(package)_sha256_hash=2985bc5e32ebe38d2958d508eb54ddcad39eed909489c0c2988035214597ca54 +$(package)_sha256_hash=3aa5587793d4c790bd22999bfc678250c029307b3afb9d16f6f4a49c67b78fb3 define $(package)_set_vars $(package)_config_opts= 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 d5354c6bf650..e6936b9f1e03 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 @@ -8,7 +8,10 @@ $(package)_dependencies=openssl zlib $(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 fix_qt_pkgconfig.patch +$(package)_patches=mac-qmake.conf mingw-uuidof.patch pidlist_absolute.patch fix-xcb-include-order.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,7 +142,10 @@ 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 && \ echo "!host_build: QMAKE_CFLAGS += $($(package)_cflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ echo "!host_build: QMAKE_CXXFLAGS += $($(package)_cxxflags) $($(package)_cppflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ echo "!host_build: QMAKE_LFLAGS += $($(package)_ldflags)" >> qtbase/mkspecs/common/gcc-base.conf && \ diff --git a/depends/patches/qt/fix-cocoahelpers-macos.patch b/depends/patches/qt/fix-cocoahelpers-macos.patch new file mode 100644 index 000000000000..1b43a9eff8fe --- /dev/null +++ b/depends/patches/qt/fix-cocoahelpers-macos.patch @@ -0,0 +1,70 @@ +From 0707260a4f8e64dfadf1df5f935e74cabb7c7d27 Mon Sep 17 00:00:00 2001 +From: Jake Petroules +Date: Sun, 1 Oct 2017 21:48:17 -0700 +Subject: [PATCH] Fix build error with macOS 10.13 SDK +MIME-Version: 1.0 +Content-Type: text/plain; charset=utf8 +Content-Transfer-Encoding: 8bit + +Several of these variables/macros are no longer defined. We didn't +validate the preconditions on iOS, tvOS, or watchOS, so no +need to bother validating them on macOS either. Nor did we check the +OSStatus result on any platform anyways. + +Task-number: QTBUG-63401 +Change-Id: Ife64dff767cf6d3f4b839fc53ec486181c176bf3 +(cherry-picked from 861544583511d4e6f7745d2339b26ff1cd44132b) +Reviewed-by: Timur Pocheptsov +Reviewed-by: Tor Arne Vestbø +--- + src/plugins/platforms/cocoa/qcocoahelpers.h | 2 +- + src/plugins/platforms/cocoa/qcocoahelpers.mm | 13 +------------ + 2 files changed, 2 insertions(+), 13 deletions(-) + +diff --git old/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.h new/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.h +index bbb3793..74371d5 100644 +--- old/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.h ++++ new/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.h +@@ -80,7 +80,7 @@ QColor qt_mac_toQColor(CGColorRef color); + // Creates a mutable shape, it's the caller's responsibility to release. + HIMutableShapeRef qt_mac_QRegionToHIMutableShape(const QRegion ®ion); + +-OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage); ++void qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage); + + NSDragOperation qt_mac_mapDropAction(Qt::DropAction action); + NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions); +diff --git old/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.mm new/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.mm +index cd73148..3f8429e 100644 +--- old/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.mm ++++ new/qtbase/src/plugins/platforms/cocoa/qcocoahelpers.mm +@@ -544,15 +544,8 @@ NSRect qt_mac_flipRect(const QRect &rect) + return NSMakeRect(rect.x(), flippedY, rect.width(), rect.height()); + } + +-OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage) ++void qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage) + { +- // Verbatim copy if HIViewDrawCGImage (as shown on Carbon-Dev) +- OSStatus err = noErr; +- +- require_action(inContext != NULL, InvalidContext, err = paramErr); +- require_action(inBounds != NULL, InvalidBounds, err = paramErr); +- require_action(inImage != NULL, InvalidImage, err = paramErr); +- + CGContextSaveGState( inContext ); + CGContextTranslateCTM (inContext, 0, inBounds->origin.y + CGRectGetMaxY(*inBounds)); + CGContextScaleCTM(inContext, 1, -1); +@@ -560,10 +553,6 @@ OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGIm + CGContextDrawImage(inContext, *inBounds, inImage); + + CGContextRestoreGState(inContext); +-InvalidImage: +-InvalidBounds: +-InvalidContext: +- return err; + } + + Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum) +-- +2.7.4 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/depends/patches/qt/qfixed-coretext.patch b/depends/patches/qt/qfixed-coretext.patch new file mode 100644 index 000000000000..c5ad7d063237 --- /dev/null +++ b/depends/patches/qt/qfixed-coretext.patch @@ -0,0 +1,34 @@ +From dbdd5f0ffbce52c8b789ed09f1aa3f1da6c02e23 Mon Sep 17 00:00:00 2001 +From: Gabriel de Dietrich +Date: Fri, 30 Mar 2018 11:58:16 -0700 +Subject: [PATCH] QCoreTextFontEngine: Fix build with Xcode 9.3 + +Apple LLVM version 9.1.0 (clang-902.0.39.1) + +Error message: + +.../qfontengine_coretext.mm:827:20: error: qualified reference to + 'QFixed' is a constructor name rather than a type in this context + return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont))); + +Change-Id: Iebe26b3b087a16b10664208fc8851cbddb47f043 +Reviewed-by: Konstantin Ritt +--- + src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git old/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm new/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm +index 25ff69d877d..98b753eff96 100644 +--- old/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm ++++ new/qtbase/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm +@@ -824,7 +824,7 @@ void QCoreTextFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, gl + + QFixed QCoreTextFontEngine::emSquareSize() const + { +- return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont))); ++ return QFixed(int(CTFontGetUnitsPerEm(ctfont))); + } + + QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const +-- +2.16.3 diff --git a/doc/Doxyfile b/doc/Doxyfile index 3dc283de4429..c55bc2f66d69 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -41,7 +41,7 @@ PROJECT_NAME = "Dash Core" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 0.12.3.2 +PROJECT_NUMBER = 0.13.0.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/doc/README.md b/doc/README.md index 59b619ca9f3e..5081c6dceebc 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,4 +1,4 @@ -Dash Core 0.12.1 +Dash Core 0.13.0 ===================== This is the official reference wallet for Dash digital currency and comprises the backbone of the Dash peer-to-peer network. You can [download Dash Core](https://www.dash.org/downloads/) or [build it yourself](#building) using the guides below. @@ -24,7 +24,7 @@ Drag Dash-Qt to your applications folder, and then run Dash-Qt. ### Need Help? -* See the [Dash documentation](https://dashpay.atlassian.net/wiki/display/DOC) +* See the [Dash documentation](https://docs.dash.org) for help and more information. * Ask for help on [Dash Nation Discord](http://dashchat.org) * Ask for help on the [Dash Forum](https://dash.org/forum) diff --git a/doc/README_windows.txt b/doc/README_windows.txt index 1a347d4be28e..32b150c60e72 100644 --- a/doc/README_windows.txt +++ b/doc/README_windows.txt @@ -1,4 +1,4 @@ -Dash Core 0.12.1 +Dash Core 0.13.0 ===================== Intro @@ -18,6 +18,6 @@ However, it downloads and stores the entire history of Dash transactions; depending on the speed of your computer and network connection, the synchronization process can take anywhere from a few hours to a day or more. -See the dash wiki at: - https://dashpay.atlassian.net/wiki/ +See the Dash documentation at: + https://docs.dash.org for more help and information. diff --git a/doc/build-cross.md b/doc/build-cross.md new file mode 100644 index 000000000000..ce6a05f24dad --- /dev/null +++ b/doc/build-cross.md @@ -0,0 +1,95 @@ +Cross-compiliation of Dash Core +=============================== + +Dash 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 Dash. + +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 Dash 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 Dash 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 Dash 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..d1ae09a1de2a --- /dev/null +++ b/doc/build-generic.md @@ -0,0 +1,81 @@ +GENERIC BUILD NOTES +==================== +Some notes on how to build Dash 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 Dash 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 Dash 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 +--------------------- +Dash inherited the `depends` folder from Bitcoin, which contains all dependencies required to build Dash. These +dependencies must be built before Dash 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 Dash 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 Dash. + +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 Dash Core +--------------------- + +```bash +$ ./autogen.sh +$ ./configure --prefix `pwd`/depends/ +$ make +$ make install # optional +``` + +Please replace `` with your local system's `host-platform-triplet`. The following triplets are usually valid: +- `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 Dash 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 faeb3a021bb3..000000000000 --- a/doc/build-openbsd.md +++ /dev/null @@ -1,176 +0,0 @@ -OpenBSD build guide -====================== -(updated for OpenBSD 6.0) - -This guide describes how to build dashd 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 Dash 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_dash:/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 Dash Core, manually: - -``` -# Pick some path to install boost to, here we create a directory within the dash 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 dash -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 dash 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 Dash 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 87a5096b3a37..a0e595bdf5ed 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 Dash Core ------------------------- - -1. Clone the Dash Core source code and cd into `dash` - - git clone https://github.com/dashpay/dash - cd dash - -2. Build Dash Core: - - Configure and build the headless dash 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 fa0c7239e2ba..8cce23b8b376 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -4,204 +4,39 @@ Some notes on how to build Dash Core in Unix. (for OpenBSD specific instructions, see [build-openbsd.md](build-openbsd.md)) -Note ---------------------- -Always use absolute paths to configure and compile Dash Core and the dependencies, -for example, when specifying the path of the dependency: +Base build dependencies +----------------------- +Building the dependencies and Dash 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 dash-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 Dash 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 Dash 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 Dash-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 dash-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 dashd" 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 -DASH_ROOT=$(pwd) - -# Pick some path to install BDB to, here we create a directory within the dash directory -BDB_PREFIX="${DASH_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 Dash Core to use our own-built instance of BDB -cd $DASH_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 -------- @@ -211,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: @@ -257,7 +92,7 @@ Disable-wallet mode When the intention is to run only a P2P node without a wallet, Dash 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. @@ -270,78 +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/dashpay/dash.git - cd dash/ - ./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 Dash 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 - 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) +(TODO, this is untested, please report if it works and if changes to this documentation are needed) -Clang is installed by default as `cc` compiler, this makes it easier to get -started than on [OpenBSD](build-openbsd.md). Installing dependencies: +Building on FreeBSD is basically the same as on Linux based systems, with the difference that you have to use `gmake` +instead of `make`. - pkg install autoconf automake libtool pkgconf - pkg install boost-libs openssl libevent - pkg install gmake - -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 Dash 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 a877625ca932..2434d939a0d8 100644 --- a/doc/build-windows.md +++ b/doc/build-windows.md @@ -47,51 +47,7 @@ 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 a021fd00a724..be67692e2a47 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -189,8 +189,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 60ea38464d44..b121091025cc 100644 --- a/doc/gitian-building.md +++ b/doc/gitian-building.md @@ -270,14 +270,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 ``` @@ -326,7 +326,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. @@ -344,7 +344,7 @@ 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 Dash Core repository under 'Fetch and create inputs' to install sources which require @@ -378,7 +378,7 @@ Output from `gbuild` will look something like Resolving deltas: 100% (41590/41590), done. From https://github.com/dashpay/dash ... (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 @@ -427,14 +427,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}}' ../dash/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 d85cbf1393cb..bea11735c098 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 Dash 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 Dash 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/dash-cli.1 b/doc/man/dash-cli.1 index 88fe0b0d1dff..54708eb09ac0 100644 --- a/doc/man/dash-cli.1 +++ b/doc/man/dash-cli.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH DASH-CLI "1" "May 2018" "dash-cli v0.12.3.0" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.8. +.TH DASH-CLI "1" "November 2018" "dash-cli v0.13.0.0" "User Commands" .SH NAME -dash-cli \- manual page for dash-cli v0.12.3.0 +dash-cli \- manual page for dash-cli v0.13.0.0 .SH DESCRIPTION -Dash Core RPC client version v0.12.3.0 +Dash Core RPC client version v0.13.0.0 .SS "Usage:" .TP dash\-cli [options] [params] @@ -75,8 +75,8 @@ 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 Dash Core developers -Copyright (C) 2009-2017 The Bitcoin Core developers +Copyright (C) 2014-2018 The Dash Core developers +Copyright (C) 2009-2018 The Bitcoin Core developers Please contribute if you find Dash Core useful. Visit for further information about the software. diff --git a/doc/man/dash-qt.1 b/doc/man/dash-qt.1 index 2ad3d1c67548..31475512e305 100644 --- a/doc/man/dash-qt.1 +++ b/doc/man/dash-qt.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH DASH-QT "1" "May 2018" "dash-qt v0.12.3.0" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.8. +.TH DASH-QT "1" "November 2018" "dash-qt v0.13.0.0" "User Commands" .SH NAME -dash-qt \- manual page for dash-qt v0.12.3.0 +dash-qt \- manual page for dash-qt v0.13.0.0 .SH DESCRIPTION -Dash Core version v0.12.3.0 (64\-bit) +Dash Core version v0.13.0.0 (64\-bit) Usage: .IP dash\-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: -0000000000000044d888c60cf2a3646f511a66fc35f4abc5f20377a7d658263b, +0000000000000024e26c7df7e46d673724d223cf4ca2b2adc21297cc095600f4, testnet: -0000000003aa53e24b6e60ef97642e4193611f2bcb75ea1fa8105f0b5ffd5242) +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 DASH) 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 devnet. 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 devnet. 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,8 @@ Show splash screen on startup (default: 1) .IP Reset all settings changed in the GUI .SH COPYRIGHT -Copyright (C) 2014-2017 The Dash Core developers -Copyright (C) 2009-2017 The Bitcoin Core developers +Copyright (C) 2014-2018 The Dash Core developers +Copyright (C) 2009-2018 The Bitcoin Core developers Please contribute if you find Dash Core useful. Visit for further information about the software. diff --git a/doc/man/dash-tx.1 b/doc/man/dash-tx.1 index 2ad58f5b94ea..cedce40a0455 100644 --- a/doc/man/dash-tx.1 +++ b/doc/man/dash-tx.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH DASH-TX "1" "May 2018" "dash-tx v0.12.3.0" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.8. +.TH DASH-TX "1" "November 2018" "dash-tx v0.13.0.0" "User Commands" .SH NAME -dash-tx \- manual page for dash-tx v0.12.3.0 +dash-tx \- manual page for dash-tx v0.13.0.0 .SH DESCRIPTION -Dash Core dash\-tx utility version v0.12.3.0 +Dash Core dash\-tx utility version v0.13.0.0 .SS "Usage:" .TP dash\-tx [options] [commands] @@ -108,8 +108,8 @@ set=NAME:JSON\-STRING .IP Set register NAME to given JSON\-STRING .SH COPYRIGHT -Copyright (C) 2014-2017 The Dash Core developers -Copyright (C) 2009-2017 The Bitcoin Core developers +Copyright (C) 2014-2018 The Dash Core developers +Copyright (C) 2009-2018 The Bitcoin Core developers Please contribute if you find Dash Core useful. Visit for further information about the software. diff --git a/doc/man/dashd.1 b/doc/man/dashd.1 index 430f4803a91f..40f655669080 100644 --- a/doc/man/dashd.1 +++ b/doc/man/dashd.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH DASHD "1" "May 2018" "dashd v0.12.3.0" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.8. +.TH DASHD "1" "November 2018" "dashd v0.13.0.0" "User Commands" .SH NAME -dashd \- manual page for dashd v0.12.3.0 +dashd \- manual page for dashd v0.13.0.0 .SH DESCRIPTION -Dash Core Daemon version v0.12.3.0 +Dash Core Daemon version v0.13.0.0 .SS "Usage:" .TP dashd [options] @@ -37,9 +37,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: -0000000000000044d888c60cf2a3646f511a66fc35f4abc5f20377a7d658263b, +0000000000000024e26c7df7e46d673724d223cf4ca2b2adc21297cc095600f4, testnet: -0000000003aa53e24b6e60ef97642e4193611f2bcb75ea1fa8105f0b5ffd5242) +000000000118fa2f031efedf874adbe7fe6b023cdf9478e8e2400c2d505df30d) .HP \fB\-conf=\fR .IP @@ -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 DASH) 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 @@ -503,6 +521,12 @@ InstantSend, Governance) (0\-1, default: 0) .IP Override spork address. Only useful for regtest and devnet. 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 devnet. Using this on mainnet or testnet will ban +you. .PP Masternode options: .HP @@ -534,10 +558,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 @@ -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,8 @@ 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 Dash Core developers -Copyright (C) 2009-2017 The Bitcoin Core developers +Copyright (C) 2014-2018 The Dash Core developers +Copyright (C) 2009-2018 The Bitcoin Core developers Please contribute if you find Dash Core useful. Visit for further information about the software. diff --git a/doc/release-notes.md b/doc/release-notes.md index a44a8789e2e0..ddda21de6e4f 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,12 +1,11 @@ -Dash Core version 0.12.3.2 +Dash Core version 0.13.0.0 ========================== Release is now available from: -This is a new minor version release, bringing various bugfixes and other -improvements. +This is a new major version release, bringing new features, various bugfixes and other improvements. Please report bugs using the issue tracker at github: @@ -22,69 +21,642 @@ 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/Dash-Qt (on Mac) or -dashd/dash-qt (on Linux). +dashd/dash-qt (on Linux). If you upgrade after DIP0003 activation you will +have to reindex (start with -reindex-chainstate or -reindex) to make sure +your wallet has all the new data synced. Downgrade warning ----------------- -### Downgrade to a version < 0.12.2.2 - -Because release 0.12.2.2 included the [per-UTXO fix](release-notes/dash/release-notes-0.12.2.2.md#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. - -### Downgrade to 0.12.2.2/3, 0.12.3.1 - -Downgrading to these versions does not require any additional actions, should be -fully compatible. +### Downgrade to a version < 0.13.0.0 +Downgrading to a version smaller than 0.13 is only supported as long as DIP2/DIP3 +has not been activated. Activation will happen when enough miners signal compatibility +through a BIP9 (bit 3) deployment. Notable changes =============== -Improve initial sync --------------------- - -Some users had problems getting their nodes synced. The issue occured due to nodes trying to -get additional data from each available peer but not being able to process this data fast enough. -This was recognized as a stalled sync process and thus the process was reset. To address the issue -we limited sync process to 3 peers max now and the issue should no longer appear as long as there -are at least 4 connections. - -Testnet/Devnet fixes +DIP0002 - Special Transactions +------------------------------ +Currently, new features and consensus mechanisms have to be implemented on top of the restrictions +imposed by the simple nature of transactions. Since classical transactions can only carry inputs +and outputs, they are most useful for financial transactions (i.e. transfers of quantities of Dash +between addresses on the distributed ledger). These inputs and outputs carry scripts and signatures +which are used to authorize and validate the transaction. + +To implement new on-chain features and consensus mechanisms which do not fit into this concept of +financial transactions, it is often necessary to misuse the inputs/outputs and their corresponding +scripts to add additional data and meaning to a transaction. For example, new opcodes would have +to be introduced to mark a transaction as something special and add a payload. In other cases, +OP_RETURN has been misused to store data on-chain. + +The introduction of special transactions will require the whole Dash ecosystem to perform a one-time +mandatory update of all the software and libraries involved. Software and libraries will have to be +changed so that they can differentiate between classical transactions and special transactions. +Deserialization of a classical transaction remains unchanged. Deserialization of a special transaction +requires the software/library to at least implement skipping and ignoring the extra_payload field. +Validation and processing of the inputs and outputs of a special transaction must remain identical to +classical transactions. + +Read more: https://github.com/dashpay/dips/blob/master/dip-0002.md + +DIP0003 - Deterministic Masternode Lists +---------------------------------------- +This DIP provides on-chain consensus for masternode lists that in turn allow for deterministic quorum +derivation and service scoring of masternode rewards. + +In the previous system, each node maintained its own individual masternode list. Masternodes gained +entry to that masternode list after the owner created a 1000 Dash UTXO and the masternode broadcast +a "masternode broadcast/announcement" P2P message. This in turn set the masternode to a PRE_ENABLED +state in the list maintained by each node. Masternodes then regularly broadcasted ping messages to +keep the masternode in ENABLED state. + +The previous system was maintained with consensus mechanisms that predated Satoshi Nakamoto’s solution +to the Byzantine Generals Problem. This meant that each node needed to maintain their own individual +masternode list with P2P messages and not a blockchain based solution. Due to the nature of the P2P +system, there was no guarantee that nodes would come to the same conclusion on what the masternode +list ought to look like. Discrepancies might, for example, occur due to a different order of message +reception or if messages had not been received at all. This posed some risks in regard to consensus +and limited the possible uses of quorums by the system. + +Additionally, the previous system required a complicated and failure prone "masternode sync" after +the initial startup of the node. After the blockchain was synced, the node would request the current +masternode list, the reward payment votes, and then verify the received list. This process tended to +take an unnecessarily long amount of time and sometimes resulted in failure. + +In the new system, the masternode list is derived entirely from information found on-chain. New +masternodes are added by new special transactions called Provider Registration Transactions +(abbreviated as ProRegTx). They are only removed by spending the collateral. A ProRegTx is a special +transaction which includes either a 1000-Dash collateral payment or a reference to it, along with +other payload information (DIP0002). + +The new system is going to be activated via combination of a BIP9-like deployment (bit 3) and new spork +(`SPORK_15_DETERMINISTIC_MNS_ENABLED`). + +Read more: https://github.com/dashpay/dips/blob/master/dip-0003.md +Upgrade instructions: https://docs.dash.org/DIP3-masternode-upgrade + +DIP0004 - Simplified Verification of Deterministic Masternode Lists +------------------------------------------------------------------- +A verifiable and correct masternode list is foundational to many Dash features, including verification +of an InstantSend transaction, mixing in PrivateSend and many features of Evolution. The deterministic +masternode lists introduced by DIP0003 enable full derivation and verification of a masternode list via +on-chain data. This, however, requires the full chain to be available to construct or verify this list. +A SPV client does not have the full chain and thus would have to rely on a list provided by one or more +nodes in the network. This provided list must be verifiable by the SPV client without needing the full +chain. This DIP proposes additions to the block’s coinbase transaction and new P2P messages to get and +update a masternode list with additional proof data. + +Read more: https://github.com/dashpay/dips/blob/master/dip-0004.md + +PrivateSend +----------- +With further refactoring of PrivateSend code it became possible to implement mixing in few parallel +mixing sessions at once from one single wallet. You can set number of mixing sessions via +`privatesendsessions` cmd-line option or dash.conf. You can pick any number of sessions between 1 and 10, +default is 4 which should be good enough for most users. For this feature to work you should also make +sure that `privatesendmultisession` is set to `1` via cmd-line or `Enable PrivateSend multi-session` is enabled +in GUI. + +Introducing parallel mixing sessions should speed mixing up which makes it's reasonable to add a new +mixing denom (0.00100001 DASH) now while keeping all the old ones too. It also makes sense to allow more +mixing rounds now, so the new default number of rounds is 4 and the maximum number of rounds is 16 now. + +You can also adjust rounds and amount via `setprivatesendrounds` and `setprivatesendamount` RPC commands +which override corresponding cmd-line params (`privatesendrounds` and `privatesendamount` respectively). + +InstantSend +----------- +With further improvements of networking code it's now possible to handle more load, so we are changing +InstantSend to be always-on for so called "simple txes" - transactions with 4 or less inputs. Such +transactions will be automatically locked even if they only pay minimal fee. According to stats, this +means that up to 90% of currently observed transactions will became automatically locked via InstantSend +with no additional cost to end users or any additional effort from wallet developers or other service +providers. + +This feature is going to be activated via combination of a BIP9-like deployment (we are reusing bit 3) +and new spork (`SPORK_16_INSTANTSEND_AUTOLOCKS`). + +Historically, InstantSend transactions were shown in GUI and RPC with more confirmations than regular ones, +which caused quite a bit of confusion. This will no longer be the case, instead we are going to show real +blockchain confirmations only and a separate indicator to show if transaction was locked via InstantSend +or not. For GUI it's color highlight and a new column, for RPC commands - `instantlock` field and `addlocked` +param. + +One of the issues with InstantSend adoption by SPV wallets (besides lack of Deterministic Masternode List) +was inability to filter all InstantSend messages the same way transactions are filtered. This should be +fixed now and SPV wallets should only get lock votes for transactions they are interested in. + +Another popular request was to preserve information about InstantSend locks between wallet restarts, which +is now implemented. This data is stored in a new cache file `instantsend.dat`. You can safely remove it, +if you don't need information about recent transaction locks for some reason (NOTE: make sure it's not one +of your wallets!). + +We also added new ZMQ notifications for double-spend attempts which try to override transactions locked +via InstantSend - `zmqpubrawinstantsenddoublespend` and `zmqpubhashinstantsenddoublespend`. + +Sporks +------ +There are a couple of new sporks introduced in this version `SPORK_15_DETERMINISTIC_MNS_ENABLED` (block +based) and `SPORK_16_INSTANTSEND_AUTOLOCKS` (timestamp based). There is aslo `SPORK_17_QUORUM_DKG_ENABLED` +(timestamp based) which is going to be used on testnet only for now. + +Spork data is stored in a new cache file (`sporks.dat`) now. + +Governance +---------- +Introduction of Deterministic Masternodes requires replacing of the old masternode private key which was used +both for operating a MN and for voting on proposals with a set of separate keys, preferably fresh new ones. +This means that votes casted for proposals by Masternode Owners via the old system will no longer be valid +after DIP0003 activation and must be re-casted using the new voting key. + +Also, you can now get notifications about governance objects or votes via new ZMQ notifications: +`zmqpubhashgovernancevote`, `zmqpubhashgovernanceobject`, `zmqpubrawgovernancevote` and +`zmqpubhashgovernanceobject`. + +GUI changes +----------- +Masternodes tab has a new section dedicated to DIP0003 registered masternodes now. After DIP0003 activation +this will be the only section shown here, the two old sections for non-deterministic masternodes will no +longer be available. + +There are changes in the way InstantSend transactions are displayed, see `InstantSend` section above. + +Some other (mostly minor) issues were also fixed, see `GUI` part of `0.13.0.0 Change log` section below for +detailed list of fixes. + +RPC changes +----------- +There are a few changes in existing RPC interfaces in this release: +- `gobject prepare` accepts an UTXO reference to spend; +- `masternode status` has DIP0003 related info now; +- `previousbits` and `coinbase_payload` fields were added in `getblocktemplate`; +- `getblocktemplate` now returns an array for masternode payments instead of a single object (miners and mining pools have to upgrade their software to support multiple masternode payees); +- `getblockchaininfo` shows BIP9 deployment progress; +- `help command subCommand` should give detailed help for subcommands e.g. `help protx list`; +- `compressed` option in `masternode genkey`; +- `dumpwallet` shows info about dumped wallet and warns user about security issues; +- `instantlock` field added in output of `getrawmempool`, `getmempoolancestors`, `getmempooldescendants`, `getmempoolentry`, +`getrawtransaction`, `decoderawtransaction`, `gettransaction`, `listtransactions`, `listsinceblock`; +- `addlocked` param added to `getreceivedbyaddress`, `getreceivedbyaccount`, `getbalance`, `sendfrom`, `sendmany`, +`listreceivedbyaddress`, `listreceivedbyaccount`, `listaccounts`. + +There are also new RPC commands: +- `protx` (`list`, `info`, `diff`, `register`, `register_fund`, `register_prepare`, +`register_submit`, `update_service`, `update_registrar`, `revoke`); +- `bls generate`; +- `setprivatesendrounds`; +- `setprivatesendamount`. + +See `help command` in rpc for more info. + +Command-line options -------------------- -Turned out that a low-diff rule for slow blocks backported from Bitcoin works a bit too aggressive for -a blockchain which uses a dynamic per-block difficulty adjustment algorithm (DGW). While blocks are still -produced at a more or less constant rate on average, the rate however is way too high. +New cmd-line options: +- `masternodeblsprivkey`; +- `minsporkkeys`; +- `privatesendsessions`; +- `zmqpubrawinstantsenddoublespend`; +- `zmqpubhashinstantsenddoublespend`; +- `zmqpubhashgovernancevote`; +- `zmqpubhashgovernanceobject`; +- `zmqpubrawgovernancevote`; +- `zmqpubhashgovernanceobject`. -We also lifted multiple ports restriction on devnet and also incuded other fixes which should improve -connectivity on devnets which are using nodes with multiple different ports. +Some of them are Devnet only: +- `budgetparams`; +- `minimumdifficultyblocks`; +- `highsubsidyblocks`; +- `highsubsidyfactor`. +Few cmd-line options are no longer supported: +- `instantsenddepth`; +- `mempoolreplacement`. -0.12.3.2 Change log -=================== - -See detailed [change log](https://github.com/dashpay/dash/compare/v0.12.3.1...dashpay:v0.12.3.2) below. +See `Help -> Command-line options` in Qt wallet or `dashd --help` for more info. -### Network: -- [`2474d9cb8`](https://github.com/dashpay/dash/commit/2474d9cb8) Sync mn list and mnw list from 3 peers max (#2169) -- [`2c303cdb1`](https://github.com/dashpay/dash/commit/2c303cdb1) A few devnet related fixes (#2168) +Lots of refactoring and bug fixes +--------------------------------- -### Mining: -- [`2ba0c7760`](https://github.com/dashpay/dash/commit/2ba0c7760) Add tests for special rules for slow blocks on devnet/testnet (#2176) -- [`b9a83d2ae`](https://github.com/dashpay/dash/commit/b9a83d2ae) Allow mining min diff for very slow (2h+) blocks (#2175) -- [`050cabdf5`](https://github.com/dashpay/dash/commit/050cabdf5) Adjust diff for slow testnet/devnet blocks a bit smoother (#2161) +A lot of refactoring, code cleanups and other small fixes were done in this release. -### GUI: -- [`7b9919d18`](https://github.com/dashpay/dash/commit/7b9919d18) Fix issues with selections on Masternode tab (#2170) -- [`c4698d5f3`](https://github.com/dashpay/dash/commit/c4698d5f3) Make PS Buttons not react to spacebar (#2154) - -### Other: -- [`f833e2ed6`](https://github.com/dashpay/dash/commit/f833e2ed6) Bump to 0.12.3.2 (#2173) +0.13.0.0 Change log +=================== +See detailed [set of changes](https://github.com/dashpay/dash/compare/v0.12.3.4...dashpay:v0.13.0.0). + +### Network +- [`03a6865d9`](https://github.com/dashpay/dash/commit/03a6865d9) Enforce correct port on mainnet for DIP3 MNs (#2576) +- [`3f26ed78c`](https://github.com/dashpay/dash/commit/3f26ed78c) Backport network checks missing in CActiveDeterministicMasternodeManager::Init() (#2572) +- [`7c7500864`](https://github.com/dashpay/dash/commit/7c7500864) Also stop asking other peers for a TX when ProcessTxLockRequest fails (#2529) +- [`19a6f718d`](https://github.com/dashpay/dash/commit/19a6f718d) Don't respond with getdata for legacy inv types when spork15 is active (#2528) +- [`22dcec71a`](https://github.com/dashpay/dash/commit/22dcec71a) Punish nodes which keep requesting and then rejecting blocks (#2518) +- [`a18ca49a2`](https://github.com/dashpay/dash/commit/a18ca49a2) Disconnect peers with version < 70212 after DIP3 activation via BIP9 (#2497) +- [`a57e9dea7`](https://github.com/dashpay/dash/commit/a57e9dea7) Fix filtering of the lock votes for SPV nodes. (#2468) +- [`c6cf4d9a4`](https://github.com/dashpay/dash/commit/c6cf4d9a4) Relay txes through MN network faster than through regular nodes (#2397) +- [`e66c4e184`](https://github.com/dashpay/dash/commit/e66c4e184) Don't revert to INV based block announcements when the previous block is the devnet genesis block (#2388) +- [`b5142ee2c`](https://github.com/dashpay/dash/commit/b5142ee2c) Implement RemoveAskFor to indicate that we're not interested in an item anymore (#2384) +- [`53e12b7b4`](https://github.com/dashpay/dash/commit/53e12b7b4) Don't bail out from ProcessMasternodeConnections in regtest (#2368) +- [`31759a44d`](https://github.com/dashpay/dash/commit/31759a44d) Fix tx inv throughput (#2300) +- [`9d90b4fa4`](https://github.com/dashpay/dash/commit/9d90b4fa4) Honor filterInventoryKnown for non-tx/non-block items (#2292) +- [`6764dafec`](https://github.com/dashpay/dash/commit/6764dafec) Skip initial masternode list sync if spork 15 is active +- [`fced9a4b8`](https://github.com/dashpay/dash/commit/fced9a4b8) Ban peers that send us MNLISTDIFF messages +- [`d3ac86206`](https://github.com/dashpay/dash/commit/d3ac86206) Implement GETMNLISTDIFF and MNLISTDIFF P2P messages +- [`40eee1775`](https://github.com/dashpay/dash/commit/40eee1775) Fix sync in regtest (again) (#2241) +- [`c4ee2c89e`](https://github.com/dashpay/dash/commit/c4ee2c89e) Fix mnsync in regtest (#2202) + +### Mining +- [`f96563462`](https://github.com/dashpay/dash/commit/f96563462) Fix check for nTemporaryTestnetForkDIP3Height (#2508) +- [`80656038f`](https://github.com/dashpay/dash/commit/80656038f) Bump nTemporaryTestnetForkHeight to 274000 (#2498) +- [`1c25356ff`](https://github.com/dashpay/dash/commit/1c25356ff) Allow to use low difficulty and higher block rewards for devnet (#2369) +- [`3cc4ac137`](https://github.com/dashpay/dash/commit/3cc4ac137) Fix crash bug with duplicate inputs within a transaction (#2302) +- [`e6b699bc2`](https://github.com/dashpay/dash/commit/e6b699bc2) Enforce MN and superblock payments in same block +- [`c7f75afdd`](https://github.com/dashpay/dash/commit/c7f75afdd) Fix nulldummy tests by creating correct DIP4 coinbase transactions +- [`bcc071957`](https://github.com/dashpay/dash/commit/bcc071957) Calculate and enforce DIP4 masternodes merkle root in CbTx +- [`0a086898f`](https://github.com/dashpay/dash/commit/0a086898f) Implement and enforce CbTx with correct block height and deprecate BIP34 + +### RPC +- [`0dce846d5`](https://github.com/dashpay/dash/commit/0dce846d5) Add an option to use specific address as a source of funds in protx rpc commands (otherwise use payoutAddress/operatorPayoutAddress) (#2581) +- [`e71ea29e6`](https://github.com/dashpay/dash/commit/e71ea29e6) Add ownerAddr and votingAddr to CDeterministicMNState::ToJson (#2571) +- [`999a51907`](https://github.com/dashpay/dash/commit/999a51907) Fix optional revocation reason parameter for "protx revoke" and a few help strings (#2568) +- [`c08926146`](https://github.com/dashpay/dash/commit/c08926146) Unify "protx list" options (#2559) +- [`e9f7142ed`](https://github.com/dashpay/dash/commit/e9f7142ed) Bump PROTOCOL_VERSION and DMN_PROTO_VERSION to 70213 (#2557) +- [`818f0f464`](https://github.com/dashpay/dash/commit/818f0f464) Allow consuming specific UTXO in gobject prepare command (#2482) +- [`1270b7122`](https://github.com/dashpay/dash/commit/1270b7122) Use a verbosity instead of two verbose parameters (#2506) +- [`f6f6d075d`](https://github.com/dashpay/dash/commit/f6f6d075d) Still support "protx list" and "protx diff" when wallet is disabled at compile time (#2511) +- [`5a3f64310`](https://github.com/dashpay/dash/commit/5a3f64310) Deserialize CFinalCommitmentTxPayload instead of CFinalCommitment in TxToJSON (#2510) +- [`5da4c9728`](https://github.com/dashpay/dash/commit/5da4c9728) Use "registered" as default for "protx list" (#2513) +- [`fc6d651c4`](https://github.com/dashpay/dash/commit/fc6d651c4) Fix crashes in "protx" RPCs when wallet is disabled (#2509) +- [`ba49a4a16`](https://github.com/dashpay/dash/commit/ba49a4a16) Trivial: protx fund_register RPC help corrections (#2502) +- [`45421b1a3`](https://github.com/dashpay/dash/commit/45421b1a3) Add IS parameter for gobject prepare (#2452) +- [`2ba1ff521`](https://github.com/dashpay/dash/commit/2ba1ff521) Use ParseFixedPoint instead of ParseDoubleV in "protx register" commands (#2458) +- [`e049f9c1e`](https://github.com/dashpay/dash/commit/e049f9c1e) fix protx register rpc help (#2461) +- [`eb2103760`](https://github.com/dashpay/dash/commit/eb2103760) trivail, clarifies help text for protx register (#2462) +- [`76e93c7d7`](https://github.com/dashpay/dash/commit/76e93c7d7) Corrections to incorrect syntax in RPC help (#2466) +- [`3c1f44c3a`](https://github.com/dashpay/dash/commit/3c1f44c3a) Make sure protx_update_registrar adds enough funds for the fees +- [`d130f25ac`](https://github.com/dashpay/dash/commit/d130f25ac) Fix check for number of params to protx_update_service (#2443) +- [`adf9c87e2`](https://github.com/dashpay/dash/commit/adf9c87e2) Fix protx/bls rpc help (#2438) +- [`579c83e88`](https://github.com/dashpay/dash/commit/579c83e88) Add coinbase_payload to getblocktemplate help (#2437) +- [`3685c85e7`](https://github.com/dashpay/dash/commit/3685c85e7) Show BIP9 progress in getblockchaininfo (#2435) +- [`da3e3db4d`](https://github.com/dashpay/dash/commit/da3e3db4d) Fix sub-command help for masternode, gobject and protx rpcs (#2425) +- [`adad3fcfe`](https://github.com/dashpay/dash/commit/adad3fcfe) RPC: protx help corrections (#2422) +- [`1d56dffda`](https://github.com/dashpay/dash/commit/1d56dffda) Unify help display logic for various "complex" rpc commands (#2415) +- [`02442673d`](https://github.com/dashpay/dash/commit/02442673d) Trivial: Correct protx diff RPC help (#2410) +- [`1f56600c4`](https://github.com/dashpay/dash/commit/1f56600c4) Trivial: Protx operator reward clarification (#2407) +- [`7011fec1b`](https://github.com/dashpay/dash/commit/7011fec1b) RPC: Add help details for the bls RPC (#2403) +- [`50f133ad0`](https://github.com/dashpay/dash/commit/50f133ad0) Add merkle tree and coinbase transaction to the `protx diff` rpc command (#2392) +- [`25b6dae9e`](https://github.com/dashpay/dash/commit/25b6dae9e) Code style and RPC help string cleanups for DIP2/DIP3 (#2379) +- [`0ad2906c5`](https://github.com/dashpay/dash/commit/0ad2906c5) Clarify addlocked description in getbalance RPC (#2364) +- [`547b81dd0`](https://github.com/dashpay/dash/commit/547b81dd0) log `gobject prepare` params (#2317) +- [`d932d2c4e`](https://github.com/dashpay/dash/commit/d932d2c4e) Add instantlock field to getrawtransaction rpc output (#2314) +- [`c3d6b0651`](https://github.com/dashpay/dash/commit/c3d6b0651) Remove redundant check for unknown commands in masternode RPC (#2279) +- [`44706dc88`](https://github.com/dashpay/dash/commit/44706dc88) Implement projection of MN reward winners in "masternode winners" +- [`2d8f1244c`](https://github.com/dashpay/dash/commit/2d8f1244c) Implement 'masternode info ' RPC +- [`e2a9dbbce`](https://github.com/dashpay/dash/commit/e2a9dbbce) Better "masternode status" for deterministic masternodes +- [`50ac6fb3a`](https://github.com/dashpay/dash/commit/50ac6fb3a) Throw exception when trying to invoke start-xxx RPC in deterministic mode +- [`58aa81364`](https://github.com/dashpay/dash/commit/58aa81364) Implement "protx revoke" RPC +- [`185416b97`](https://github.com/dashpay/dash/commit/185416b97) Implement "protx update_registrar" RPC +- [`32951f795`](https://github.com/dashpay/dash/commit/32951f795) Implement "protx update_service" RPC +- [`5e3abeca2`](https://github.com/dashpay/dash/commit/5e3abeca2) Implement "protx list" RPC +- [`c77242346`](https://github.com/dashpay/dash/commit/c77242346) Implement "protx register" RPC +- [`1c2565804`](https://github.com/dashpay/dash/commit/1c2565804) Refactor `masternode` and `gobject` RPCs to support `help command subCommand` syntax (#2240) +- [`fb4d301a2`](https://github.com/dashpay/dash/commit/fb4d301a2) Add extraPayloadSize/extraPayload fields to RPC help +- [`2997d6d26`](https://github.com/dashpay/dash/commit/2997d6d26) add compressed option to `masternode genkey` (#2232) +- [`98ed90cbb`](https://github.com/dashpay/dash/commit/98ed90cbb) adds rpc calls for `setprivatesendrounds` and `setprivatesendamount` (#2230) +- [`50eb98d90`](https://github.com/dashpay/dash/commit/50eb98d90) Prepare for DIP3 operator reward payments and switch to array in getblocktemplate (#2216) +- [`a959f60aa`](https://github.com/dashpay/dash/commit/a959f60aa) De-duplicate "gobject vote-alias" and "gobject "vote-many" code (#2217) +- [`566fa5ec3`](https://github.com/dashpay/dash/commit/566fa5ec3) Add support for "help command subCommand" (#2210) +- [`4cd969e3d`](https://github.com/dashpay/dash/commit/4cd969e3d) Add `previousbits` field to `getblocktemplate` output (#2201) +- [`ac30196bc`](https://github.com/dashpay/dash/commit/ac30196bc) Show some info about the wallet dumped via dumpwallet (#2191) + +### LLMQ and Deterministic Masternodes +- [`3861c6a82`](https://github.com/dashpay/dash/commit/3861c6a82) Add BIP9 deployment for DIP3 on mainnet (#2585) +- [`587911b36`](https://github.com/dashpay/dash/commit/587911b36) Fix IsBlockPayeeValid (#2577) +- [`3c30a6aff`](https://github.com/dashpay/dash/commit/3c30a6aff) Add missing masternodeblsprivkey help text (#2569) +- [`378dadd0f`](https://github.com/dashpay/dash/commit/378dadd0f) Ensure EvoDB consistency for quorum commitments by storing the best block hash (#2537) +- [`2127a426b`](https://github.com/dashpay/dash/commit/2127a426b) Further refactoring of CQuorumBlockProcessor (#2545) +- [`1522656d6`](https://github.com/dashpay/dash/commit/1522656d6) Correctly handle spent collaterals for MNs that were registered in the same block (#2553) +- [`583035337`](https://github.com/dashpay/dash/commit/583035337) Track operator key changes in mempool and handle conflicts (#2540) +- [`88f7bf0d8`](https://github.com/dashpay/dash/commit/88f7bf0d8) Don't delete/add values to the unique property map when it's null (#2538) +- [`15414dac2`](https://github.com/dashpay/dash/commit/15414dac2) Refactor CQuorumBlockProcessor and CDeterministicMNManager (#2536) +- [`d9b28fe1a`](https://github.com/dashpay/dash/commit/d9b28fe1a) Introduce dummy (ping-like) contributions for the dummy DKG (#2542) +- [`df0d0cce7`](https://github.com/dashpay/dash/commit/df0d0cce7) Watch for changes in operator key and disable local MN (#2541) +- [`511dc3714`](https://github.com/dashpay/dash/commit/511dc3714) Remove ProTxs from mempool that refer to a ProRegTx for which the collateral was spent (#2539) +- [`225c2135e`](https://github.com/dashpay/dash/commit/225c2135e) Allow skipping of MN payments with zero duffs (#2534) +- [`60867978d`](https://github.com/dashpay/dash/commit/60867978d) Avoid printing DIP3/DIP4 related logs twice (#2525) +- [`7037f7c99`](https://github.com/dashpay/dash/commit/7037f7c99) Bail out from GetBlockTxOuts in case nBlockHeight is above tip+1 (#2523) +- [`022491420`](https://github.com/dashpay/dash/commit/022491420) Print the state object when ProcessSpecialTxsInBlock fails in ConnectBlock (#2516) +- [`812834dc5`](https://github.com/dashpay/dash/commit/812834dc5) Put height into mined commitments and use it instead of the special handling of quorumVvecHash (#2501) +- [`a4f5ba38b`](https://github.com/dashpay/dash/commit/a4f5ba38b) Implement CDummyDKG and CDummyCommitment until we have the real DKG merged (#2492) +- [`66612cc4b`](https://github.com/dashpay/dash/commit/66612cc4b) Add 0 entry to vTxSigOps when adding quorum commitments (#2489) +- [`f5beeafa1`](https://github.com/dashpay/dash/commit/f5beeafa1) Also restart MNs which didn't have the collateral moved, but do it later (#2483) +- [`0123517b4`](https://github.com/dashpay/dash/commit/0123517b4) Implement PoSe based on information from LLMQ commitments (#2478) +- [`22b5952c5`](https://github.com/dashpay/dash/commit/22b5952c5) Implement and enforce DIP6 commitments (#2477) +- [`d40a5ce31`](https://github.com/dashpay/dash/commit/d40a5ce31) Properly initialize confirmedHash in CSimplifiedMNListEntry (#2479) +- [`5ffc31bce`](https://github.com/dashpay/dash/commit/5ffc31bce) Forbid version=0 in special TXs (#2473) +- [`85157f9a9`](https://github.com/dashpay/dash/commit/85157f9a9) Few trivial fixes for DIP2/DIP3 (#2474) +- [`b5947f299`](https://github.com/dashpay/dash/commit/b5947f299) Implement BuildSimplifiedDiff in CDeterministicMNList +- [`6edad3745`](https://github.com/dashpay/dash/commit/6edad3745) Use ForEachMN and GetMN in BuildDiff instead of directly accessing mnMap +- [`83aac461b`](https://github.com/dashpay/dash/commit/83aac461b) Allow P2SH/multisig addresses for operator rewards +- [`f5864254c`](https://github.com/dashpay/dash/commit/f5864254c) Do not use keyIDCollateralAddress anymore when spork15 is active +- [`5ccf556f3`](https://github.com/dashpay/dash/commit/5ccf556f3) GetMasternodeInfo with payee argument should do nothing when DIP3 is active +- [`927e8bd79`](https://github.com/dashpay/dash/commit/927e8bd79) Also forbid reusing collateral key for owner/voting keys +- [`826e7d063`](https://github.com/dashpay/dash/commit/826e7d063) Move internal collateral check to the else branch of the external collateral check +- [`dc404e755`](https://github.com/dashpay/dash/commit/dc404e755) Allow P2SH for payout scripts +- [`9adf8ad73`](https://github.com/dashpay/dash/commit/9adf8ad73) Remove restriction that forced use of same addresses for payout and collateral +- [`7c1f11089`](https://github.com/dashpay/dash/commit/7c1f11089) Revert #2441 and retry fixing (#2444) +- [`6761fa49f`](https://github.com/dashpay/dash/commit/6761fa49f) More checks for tx type +- [`b84369663`](https://github.com/dashpay/dash/commit/b84369663) Be more specific about tx version in conditions +- [`c975a986b`](https://github.com/dashpay/dash/commit/c975a986b) no cs_main in specialtxes +- [`8bd5b231b`](https://github.com/dashpay/dash/commit/8bd5b231b) Log mempool payload errors instead of crashing via assert +- [`658b7afd1`](https://github.com/dashpay/dash/commit/658b7afd1) Make error messages re payload a bit more specific +- [`153afb906`](https://github.com/dashpay/dash/commit/153afb906) Restart MNs in DIP3 tests even if collateral has not moved (#2441) +- [`4ad2f647c`](https://github.com/dashpay/dash/commit/4ad2f647c) Use proTxHash instead of outpoint when calculating masternode scores (#2440) +- [`c27e62935`](https://github.com/dashpay/dash/commit/c27e62935) Allow reusing of external collaterals in DIP3 (#2427) +- [`9da9d575a`](https://github.com/dashpay/dash/commit/9da9d575a) Allow collaterals for non-DIP3 MNs which were created after DIP3/BIP9 activation (#2412) +- [`30a2b283a`](https://github.com/dashpay/dash/commit/30a2b283a) Sign ProRegTx collaterals with a string message instead of payload hash, split `protx register` into `prepare`/`submit` (#2395) +- [`e34701295`](https://github.com/dashpay/dash/commit/e34701295) Fix crash when deterministic MN list is empty and keep paying superblocks in this case (#2387) +- [`28a6007a4`](https://github.com/dashpay/dash/commit/28a6007a4) Prepare DIP3 for testnet and reuse DIP3 deployment for autoix deployment (#2389) +- [`e3df91082`](https://github.com/dashpay/dash/commit/e3df91082) Allow referencing other TX outputs for ProRegTx collateral (#2366) +- [`fdfb07742`](https://github.com/dashpay/dash/commit/fdfb07742) Update ProRegTx serialization order (#2378) +- [`eaa856eb7`](https://github.com/dashpay/dash/commit/eaa856eb7) Remove nProtocolVersion and add mode/type fields to DIP3 (#2358) +- [`c9d274518`](https://github.com/dashpay/dash/commit/c9d274518) Use BLS keys for the DIP3 operator key (#2352) +- [`eaef90202`](https://github.com/dashpay/dash/commit/eaef90202) Don't use boost range adaptors in CDeterministicMNList (#2327) +- [`6adc236d0`](https://github.com/dashpay/dash/commit/6adc236d0) Only use dataDir in CEvoDB when not in-memory (#2291) +- [`8a878bfcf`](https://github.com/dashpay/dash/commit/8a878bfcf) Call InitializeCurrentBlockTip and activeMasternodeManager->Init after importing has finished (#2286) +- [`6b3d65028`](https://github.com/dashpay/dash/commit/6b3d65028) After DIP3 activation, allow voting with voting keys stored in your wallet (#2281) +- [`d8247dfff`](https://github.com/dashpay/dash/commit/d8247dfff) Use refactored payment logic when spork15 is active +- [`60002b7dd`](https://github.com/dashpay/dash/commit/60002b7dd) Payout and enforce operator reward payments +- [`2c481f0f8`](https://github.com/dashpay/dash/commit/2c481f0f8) Implement deterministic version of CMasternodePayments::IsScheduled +- [`19fbf8ab7`](https://github.com/dashpay/dash/commit/19fbf8ab7) Move cs_main lock from CMasternode::UpdateLastPaid to CMasternodeMan +- [`dc7292afa`](https://github.com/dashpay/dash/commit/dc7292afa) Implement new MN payments logic and add compatibility code +- [`d4530eb7d`](https://github.com/dashpay/dash/commit/d4530eb7d) Put all masternodes in MASTERNODE_ENABLED state when spork15 is active +- [`31b4f8354`](https://github.com/dashpay/dash/commit/31b4f8354) Forbid starting of legacy masternodes with non matching ProTx collateral values +- [`5050a9205`](https://github.com/dashpay/dash/commit/5050a9205) Add compatibility code for FindRandomNotInVec and GetMasternodeScores +- [`cc73422f8`](https://github.com/dashpay/dash/commit/cc73422f8) Add methods to add/remove (non-)deterministic MNs +- [`7d14566bc`](https://github.com/dashpay/dash/commit/7d14566bc) Add compatibility code to CMasternodeMan so that old code is still compatible +- [`27e8b48a6`](https://github.com/dashpay/dash/commit/27e8b48a6) Stop executing legacy MN list code when spork 15 is activated +- [`d90b13996`](https://github.com/dashpay/dash/commit/d90b13996) Implement CActiveDeterministicMasternodeManager +- [`a5e65aa37`](https://github.com/dashpay/dash/commit/a5e65aa37) Erase mnListCache entry on UndoBlock (#2254) +- [`88e7888de`](https://github.com/dashpay/dash/commit/88e7888de) Try using cache in GetListForBlock before reading from disk (#2253) +- [`9653af2f3`](https://github.com/dashpay/dash/commit/9653af2f3) Classes, validation and update logic for CProUpRevTX +- [`1c68d1107`](https://github.com/dashpay/dash/commit/1c68d1107) Classes, validation and update logic for CProUpRegTX +- [`8aca3b040`](https://github.com/dashpay/dash/commit/8aca3b040) Also check duplicate addresses for CProUpServTX in CTxMemPool +- [`923fd6739`](https://github.com/dashpay/dash/commit/923fd6739) Implement CProUpServTx logic in CDeterministicMNManager +- [`6ec0d7aea`](https://github.com/dashpay/dash/commit/6ec0d7aea) Classes and basic validation of ProUpServTx +- [`255403e92`](https://github.com/dashpay/dash/commit/255403e92) Include proTx data in json formatted transactions +- [`25545fc1e`](https://github.com/dashpay/dash/commit/25545fc1e) Split keyIDMasternode into keyIDOwner/keyIDOperator/keyIDVoting (#2248) +- [`2c172873a`](https://github.com/dashpay/dash/commit/2c172873a) Don't allow non-ProTx masternode collaterals after DIP3 activation +- [`9e8a86714`](https://github.com/dashpay/dash/commit/9e8a86714) Implementation of deterministic MNs list +- [`76fd30894`](https://github.com/dashpay/dash/commit/76fd30894) Automatically lock ProTx collaterals when TX is added/loaded to wallet +- [`cdd723ede`](https://github.com/dashpay/dash/commit/cdd723ede) Conflict handling for ProRegTx in mempool +- [`958b84ace`](https://github.com/dashpay/dash/commit/958b84ace) Implementation of ProRegTx with basic validation (no processing) +- [`c9a72e888`](https://github.com/dashpay/dash/commit/c9a72e888) Introduce CEvoDB for all evo related things, e.g. DIP3 +- [`4531f6b89`](https://github.com/dashpay/dash/commit/4531f6b89) Implement CDBTransaction and CScopedDBTransaction +- [`e225cebcd`](https://github.com/dashpay/dash/commit/e225cebcd) Use previous block for CheckSpecialTx (#2243) +- [`b92bd8997`](https://github.com/dashpay/dash/commit/b92bd8997) Fix mninfo search by payee (#2233) +- [`8af7f6223`](https://github.com/dashpay/dash/commit/8af7f6223) Account for extraPayload when calculating fees in FundTransaction +- [`b606bde9a`](https://github.com/dashpay/dash/commit/b606bde9a) Support version 3 transaction serialization in mininode.py +- [`61bbe54ab`](https://github.com/dashpay/dash/commit/61bbe54ab) Add Get-/SetTxPayload helpers +- [`cebf71bbc`](https://github.com/dashpay/dash/commit/cebf71bbc) Stubs for special TX validation and processing +- [`d6c5a72e2`](https://github.com/dashpay/dash/commit/d6c5a72e2) Basic validation of version 3 TXs in CheckTransaction +- [`a3c4ee3fd`](https://github.com/dashpay/dash/commit/a3c4ee3fd) DIP2 changes to CTransaction and CMutableTransaction +- [`d20100ecd`](https://github.com/dashpay/dash/commit/d20100ecd) DIP0003 deployment +- [`4d3518fe0`](https://github.com/dashpay/dash/commit/4d3518fe0) Refactor MN payee logic in preparation for DIP3 (#2215) +- [`d946f21bd`](https://github.com/dashpay/dash/commit/d946f21bd) Masternode related refactorings in preparation of DIP3 (#2212) + +### PrivateSend +- [`07309f0ec`](https://github.com/dashpay/dash/commit/07309f0ec) Allow up to MASTERNODE_MAX_MIXING_TXES (5) DSTXes per MN in a row (#2552) +- [`ed53fce47`](https://github.com/dashpay/dash/commit/ed53fce47) Revert "Apply similar logic to vecMasternodesUsed" (#2503) +- [`69bffed72`](https://github.com/dashpay/dash/commit/69bffed72) Do not sort resulting vector in SelectCoinsGroupedByAddresses (#2493) +- [`bb11f1a63`](https://github.com/dashpay/dash/commit/bb11f1a63) Fix recent changes in DSA conditions (#2494) +- [`6480ad1d5`](https://github.com/dashpay/dash/commit/6480ad1d5) Should check dsq queue regardless of the mixing state (#2491) +- [`67483cd34`](https://github.com/dashpay/dash/commit/67483cd34) Fix dsq/dsa conditions (#2487) +- [`9d4df466b`](https://github.com/dashpay/dash/commit/9d4df466b) Fix CreateDenominated failure for addresses with huge amount of inputs (#2486) +- [`2b400f74b`](https://github.com/dashpay/dash/commit/2b400f74b) Base dsq/dstx thresholold on total number of up to date masternodes (#2465) +- [`262454791`](https://github.com/dashpay/dash/commit/262454791) Add 5th denom, drop deprecated logic and bump min PS version (#2318) +- [`23f169c44`](https://github.com/dashpay/dash/commit/23f169c44) Drop custom PS logic for guessing fees etc. from SelectCoins (#2371) +- [`f7b0b5759`](https://github.com/dashpay/dash/commit/f7b0b5759) Pick rounds with the most inputs available to mix first (#2278) +- [`727e940c0`](https://github.com/dashpay/dash/commit/727e940c0) Fix recently introduced PS bugs (#2330) +- [`85a958a36`](https://github.com/dashpay/dash/commit/85a958a36) Drop dummy copy constructors in CPrivateSend*Session (#2305) +- [`c6a0c5541`](https://github.com/dashpay/dash/commit/c6a0c5541) A couple of small fixes for mixing collaterals (#2294) +- [`d192d642f`](https://github.com/dashpay/dash/commit/d192d642f) Move heavy coin selection out of the loop in SubmitDenominate (#2274) +- [`28e0476f4`](https://github.com/dashpay/dash/commit/28e0476f4) Squash two logic branches in SubmitDenominate into one (#2270) +- [`9b6eb4765`](https://github.com/dashpay/dash/commit/9b6eb4765) Include inputs with max rounds in SelectCoinsDark/SelectCoinsByDenominations (#2277) +- [`55d7bb900`](https://github.com/dashpay/dash/commit/55d7bb900) Add an option to disable popups for PS mixing txes (#2272) +- [`38ccfef3b`](https://github.com/dashpay/dash/commit/38ccfef3b) Identify PS collateral payments in transaction list a bit more accurate (#2271) +- [`ad31dbbd7`](https://github.com/dashpay/dash/commit/ad31dbbd7) Add more variance to coin selection in PS mixing (#2261) +- [`8c9cb2909`](https://github.com/dashpay/dash/commit/8c9cb2909) Revert 2075 (#2259) +- [`b164bcc7a`](https://github.com/dashpay/dash/commit/b164bcc7a) Split PS into Manager and Session and allow running multiple mixing sessions in parallel (client side) (#2203) +- [`d4d11476a`](https://github.com/dashpay/dash/commit/d4d11476a) Fix typo and grammar in PS error message (#2199) +- [`a83ab5501`](https://github.com/dashpay/dash/commit/a83ab5501) Fix wallet lock check in DoAutomaticDenominating (#2196) +- [`30fa8bc33`](https://github.com/dashpay/dash/commit/30fa8bc33) Make sure pwalletMain is not null whenever it's used in PS client (#2190) +- [`3c89983db`](https://github.com/dashpay/dash/commit/3c89983db) Remove DarksendConfig (#2132) +- [`43091a3ef`](https://github.com/dashpay/dash/commit/43091a3ef) PrivateSend Enhancement: Up default round count to 4 and allow user to mix up to 16 rounds (#2128) + +### InstantSend +- [`35550a3f9`](https://github.com/dashpay/dash/commit/35550a3f9) Add quorumModifierHash to instant send lock vote (#2505) +- [`fa8f4a10c`](https://github.com/dashpay/dash/commit/fa8f4a10c) Include masternodeProTxHash in CTxLockVote (#2484) +- [`624e50949`](https://github.com/dashpay/dash/commit/624e50949) Remove few leftovers of `-instantsenddepth` +- [`733cd9512`](https://github.com/dashpay/dash/commit/733cd9512) Remove global fDIP0003ActiveAtTip and fix wrong use of VersionBitsState in auto IX (#2380) +- [`5454bea37`](https://github.com/dashpay/dash/commit/5454bea37) Automatic InstantSend locks for "simple" transactions (#2140) +- [`1e74bcace`](https://github.com/dashpay/dash/commit/1e74bcace) [ZMQ] Notify when an IS double spend is attempted (#2262) +- [`6bcd868de`](https://github.com/dashpay/dash/commit/6bcd868de) Fix lockedByInstantSend initialization (#2197) +- [`0a6f47323`](https://github.com/dashpay/dash/commit/0a6f47323) Remove dummy confirmations in RPC API and GUI for InstantSend transactions (#2040) +- [`ace980834`](https://github.com/dashpay/dash/commit/ace980834) Extend Bloom Filter support to InstantSend related messages (#2184) +- [`2c0d4c9d7`](https://github.com/dashpay/dash/commit/2c0d4c9d7) Save/load InstantSend cache (#2051) + +### Sporks +- [`33f78d70e`](https://github.com/dashpay/dash/commit/33f78d70e) Do not accept sporks with nTimeSigned way too far into the future (#2578) +- [`6c4b3ed8d`](https://github.com/dashpay/dash/commit/6c4b3ed8d) Load sporks before checking blockchain (#2573) +- [`d94092b60`](https://github.com/dashpay/dash/commit/d94092b60) Fix spork propagation while in IBD and fix spork integration tests (#2533) +- [`43e757bee`](https://github.com/dashpay/dash/commit/43e757bee) Amend SERIALIZATION_VERSION_STRING string for spork cache (#2339) +- [`f7ab6c469`](https://github.com/dashpay/dash/commit/f7ab6c469) M-of-N-like sporks (#2288) +- [`c2958733e`](https://github.com/dashpay/dash/commit/c2958733e) CSporkManager::Clear() should not alter sporkPubKeyID and sporkPrivKey (#2313) +- [`8c0dca282`](https://github.com/dashpay/dash/commit/8c0dca282) Add versioning to spork cache (#2312) +- [`5461e92bf`](https://github.com/dashpay/dash/commit/5461e92bf) Add spork to control deterministic MN lists activation +- [`73c2ddde7`](https://github.com/dashpay/dash/commit/73c2ddde7) extract sporkmanager from sporkmessage (#2234) +- [`1767e3457`](https://github.com/dashpay/dash/commit/1767e3457) Save/load spork cache (#2206) +- [`075ca0903`](https://github.com/dashpay/dash/commit/075ca0903) Protect CSporkManager with critical section (#2213) + +### Governance +- [`222e5b4f7`](https://github.com/dashpay/dash/commit/222e5b4f7) Remove proposal/funding votes from MNs that changed the voting key (#2570) +- [`5185dd5b7`](https://github.com/dashpay/dash/commit/5185dd5b7) Use correct time field when removing pre-DIP3 votes (#2535) +- [`d2ca9edde`](https://github.com/dashpay/dash/commit/d2ca9edde) Fix multiple issues with governance voting after spork15 activation (#2526) +- [`08dc17871`](https://github.com/dashpay/dash/commit/08dc17871) Drop pre-DIP3 votes from current votes per MN per object (#2524) +- [`0c1b683a0`](https://github.com/dashpay/dash/commit/0c1b683a0) Clear votes which were created before spork15 activation and use operator key for non-funding votes (#2512) +- [`da4b5fb16`](https://github.com/dashpay/dash/commit/da4b5fb16) Remove an unused function from governance object collateral code (#2480) +- [`8deb8e90f`](https://github.com/dashpay/dash/commit/8deb8e90f) Modernize Gov Methods (#2326) +- [`0471fa884`](https://github.com/dashpay/dash/commit/0471fa884) Drop MAX_GOVERNANCE_OBJECT_DATA_SIZE (and maxgovobjdatasize in rpc) (#2298) +- [`737353c84`](https://github.com/dashpay/dash/commit/737353c84) Fix IsBlockValueValid/IsOldBudgetBlockValueValid (#2276) +- [`a5643f899`](https://github.com/dashpay/dash/commit/a5643f899) Switch RequestGovernanceObjectVotes from pointers to hashes (#2189) +- [`0e689341d`](https://github.com/dashpay/dash/commit/0e689341d) Implement Governance ZMQ notification messages (#2160) + +### GUI +- [`858bb52ad`](https://github.com/dashpay/dash/commit/858bb52ad) Show correct operator payee address in DIP3 MN list GUI (#2563) +- [`190863722`](https://github.com/dashpay/dash/commit/190863722) Remove legacy MN list tabs on spork15 activation (#2567) +- [`3e97b0cbd`](https://github.com/dashpay/dash/commit/3e97b0cbd) Make sure that we can get inputType and fUseInstantSend regardless of the way recipients are sorted (#2550) +- [`1a7c29b97`](https://github.com/dashpay/dash/commit/1a7c29b97) Revert "Sort recipients in SendCoins dialog via BIP69 rule (#2546)" (#2549) +- [`ca0aec2a3`](https://github.com/dashpay/dash/commit/ca0aec2a3) Match recipients with txouts by scriptPubKey in reassignAmounts() (#2548) +- [`09730e1c5`](https://github.com/dashpay/dash/commit/09730e1c5) Bail out from update methods in MasternodeList when shutdown is requested (#2551) +- [`18cd5965c`](https://github.com/dashpay/dash/commit/18cd5965c) Sort recipients in SendCoins dialog via BIP69 rule (#2546) +- [`9100c69eb`](https://github.com/dashpay/dash/commit/9100c69eb) Allow filtering by proTxHash on DIP3 MN tab (#2532) +- [`216119921`](https://github.com/dashpay/dash/commit/216119921) Fix wrong total MN count in UI and "masternode count" RPC (#2527) +- [`8f8878a94`](https://github.com/dashpay/dash/commit/8f8878a94) Add dummy/hidden column to carry the proTxHash per MN list entry... (#2530) +- [`a4ea816b2`](https://github.com/dashpay/dash/commit/a4ea816b2) use aqua gui theme (#2472) +- [`aa495405b`](https://github.com/dashpay/dash/commit/aa495405b) [GUI] Realign tx filter widgets (#2485) +- [`f4ef388de`](https://github.com/dashpay/dash/commit/f4ef388de) Update PS help text for the new denom (#2471) +- [`7cabbadef`](https://github.com/dashpay/dash/commit/7cabbadef) Implement context menu and extra info on double-click for DIP3 masternode list (#2459) +- [`9232a455c`](https://github.com/dashpay/dash/commit/9232a455c) Do not hold cs_main while emitting messages in WalletModel::prepareTransaction (#2463) +- [`cf2b547b7`](https://github.com/dashpay/dash/commit/cf2b547b7) Implement tab for DIP3 MN list (#2454) +- [`46462d682`](https://github.com/dashpay/dash/commit/46462d682) Add a column for IS lock status on Transactions tab (#2433) +- [`5ecd91b05`](https://github.com/dashpay/dash/commit/5ecd91b05) Fix ps collateral/denom creation tx category confusion (#2430) +- [`4a78b161f`](https://github.com/dashpay/dash/commit/4a78b161f) PrivateSend spending txes should have "outgoing" icon on overview screen (#2396) +- [`d7e210341`](https://github.com/dashpay/dash/commit/d7e210341) Fixes inaccurate round count in CoinControlDialog (#2137) + +### Cleanups/Tests/Docs/Other +- [`c950a8f51`](https://github.com/dashpay/dash/commit/c950a8f51) Merge v0.12.3.4 commits into develop (#2582) +- [`6dfceaba5`](https://github.com/dashpay/dash/commit/6dfceaba5) Force FlushStateToDisk on ConnectTip/DisconnectTip while not in IBD (#2560) +- [`552d9089e`](https://github.com/dashpay/dash/commit/552d9089e) Update testnet seeds to point to MNs that are on the new chain (#2558) +- [`63b58b1e9`](https://github.com/dashpay/dash/commit/63b58b1e9) Reintroduce BLSInit to correctly set secure alloctor callbacks (#2543) +- [`cbd030352`](https://github.com/dashpay/dash/commit/cbd030352) Serialize the block header in CBlockHeader::GetHash() (#2531) +- [`3a6bd8d23`](https://github.com/dashpay/dash/commit/3a6bd8d23) Call ProcessTick every second, handle tick cooldown inside (#2522) +- [`973a7f6dd`](https://github.com/dashpay/dash/commit/973a7f6dd) Fix GUI warnings in debug.log (#2521) +- [`c248c48e4`](https://github.com/dashpay/dash/commit/c248c48e4) Try to fix a few sporadic instant send failures in DIP3 tests (#2500) +- [`7a709b81d`](https://github.com/dashpay/dash/commit/7a709b81d) Perform less instant send tests in DIP3 tests (#2499) +- [`245c3220e`](https://github.com/dashpay/dash/commit/245c3220e) Sync blocks before creating TXs (#2496) +- [`65528e9e7`](https://github.com/dashpay/dash/commit/65528e9e7) Bump masternodeman cache version (#2467) +- [`6c190d1bb`](https://github.com/dashpay/dash/commit/6c190d1bb) Fix make deploy error on macos (#2475) +- [`df7d12b41`](https://github.com/dashpay/dash/commit/df7d12b41) Add univalue test for real numbers (#2460) +- [`614ff70b4`](https://github.com/dashpay/dash/commit/614ff70b4) Let ccache compress the cache by itself instead of compressing ccache.tar (#2456) +- [`40fa1bb49`](https://github.com/dashpay/dash/commit/40fa1bb49) Add platform dependent include_directories in CMakeLists.txt (#2455) +- [`12aba2592`](https://github.com/dashpay/dash/commit/12aba2592) Updating translations for de, es, fi, nl, pt, sk, zh_CN, zh_TW (#2451) +- [`52bf5a6b0`](https://github.com/dashpay/dash/commit/52bf5a6b0) Install libxkbcommon0 in gitian-linux.yml +- [`fefe34250`](https://github.com/dashpay/dash/commit/fefe34250) Update manpages +- [`c60687fe6`](https://github.com/dashpay/dash/commit/c60687fe6) Sleep longer between attempts in sync_blocks +- [`88498ba13`](https://github.com/dashpay/dash/commit/88498ba13) Apply suggestions from code review +- [`91af72b18`](https://github.com/dashpay/dash/commit/91af72b18) Allow to specify how log to sleep between attempts in wait_until +- [`f65e74682`](https://github.com/dashpay/dash/commit/f65e74682) Pass "-parallel=3" to reduce load on Travis nodes while testing +- [`3c99d9e35`](https://github.com/dashpay/dash/commit/3c99d9e35) Fix test_fail_create_protx in DIP3 tests +- [`4de70f0ac`](https://github.com/dashpay/dash/commit/4de70f0ac) Test P2SH/multisig payee addresses in DIP3 tests +- [`5fc4072ca`](https://github.com/dashpay/dash/commit/5fc4072ca) Parallel ASN resolve and allow passing of input file names to makeseeds.py (#2432) +- [`76a38f6ce`](https://github.com/dashpay/dash/commit/76a38f6ce) Update defaultAssumeValid, nMinimumChainWork and checkpoints (#2428) +- [`0e9ad207a`](https://github.com/dashpay/dash/commit/0e9ad207a) Update hardcoded seeds (#2429) +- [`42ee369b1`](https://github.com/dashpay/dash/commit/42ee369b1) [Formatting] masternodelist.* clang+manual format (#2426) +- [`e961c7134`](https://github.com/dashpay/dash/commit/e961c7134) Translations 201811 (#2249) +- [`f0df5bffa`](https://github.com/dashpay/dash/commit/f0df5bffa) Clang evo folder and activemasternode.* (#2418) +- [`98bdf35f9`](https://github.com/dashpay/dash/commit/98bdf35f9) bump PS copyright (#2417) +- [`e9bb822c1`](https://github.com/dashpay/dash/commit/e9bb822c1) Clang format PrivateSend files (#2373) +- [`bea590958`](https://github.com/dashpay/dash/commit/bea590958) Fix auto-IS tests (#2414) +- [`f03629d6d`](https://github.com/dashpay/dash/commit/f03629d6d) Explicitly specify which branch of Wine to install (#2411) +- [`5e829a3b1`](https://github.com/dashpay/dash/commit/5e829a3b1) Update Chia bls-signature to latest version (#2409) +- [`51addf9a0`](https://github.com/dashpay/dash/commit/51addf9a0) Fix p2p-instantsend.py test (#2408) +- [`7e8f07bb9`](https://github.com/dashpay/dash/commit/7e8f07bb9) A couple of fixes for shutdown sequence (#2406) +- [`9c455caea`](https://github.com/dashpay/dash/commit/9c455caea) A couple of fixes for init steps (#2405) +- [`6560ac64b`](https://github.com/dashpay/dash/commit/6560ac64b) Properly escape $ in Jenkinsfile.gitian (#2404) +- [`70eb710b1`](https://github.com/dashpay/dash/commit/70eb710b1) Undefine DOUBLE after include Chia BLS headers (#2400) +- [`052af81b4`](https://github.com/dashpay/dash/commit/052af81b4) Ensure correct order of destruction for BLS secure allocator (#2401) +- [`c8804ea5a`](https://github.com/dashpay/dash/commit/c8804ea5a) Do not ignore patches in depends (#2399) +- [`13f2eb449`](https://github.com/dashpay/dash/commit/13f2eb449) Force fvisibility=hidden when compiling on macos (#2398) +- [`9eb9c99d5`](https://github.com/dashpay/dash/commit/9eb9c99d5) Bump version to 0.13.0 (#2386) +- [`8f9b004ca`](https://github.com/dashpay/dash/commit/8f9b004ca) Support "fast" mode when calling sync_masternodes (#2383) +- [`fcea333ba`](https://github.com/dashpay/dash/commit/fcea333ba) Rewrite handling of too long depends builds in .travis.yml (#2385) +- [`d1debfc26`](https://github.com/dashpay/dash/commit/d1debfc26) Implement mt_pooled_secure_allocator and use it for BLS secure allocation (#2375) +- [`0692de1c5`](https://github.com/dashpay/dash/commit/0692de1c5) Fix prepare_masternodes/create_masternodes in DashTestFramework (#2382) +- [`6433a944a`](https://github.com/dashpay/dash/commit/6433a944a) [Trivial] typo Groupped -> Grouped (#2374) +- [`59932401b`](https://github.com/dashpay/dash/commit/59932401b) Change internal references of Darksend to PrivateSend (#2372) +- [`e3046adb3`](https://github.com/dashpay/dash/commit/e3046adb3) Clear devNetParams and mimic behavior of other param types (#2367) +- [`de426e962`](https://github.com/dashpay/dash/commit/de426e962) Give tail calls enough time to print errors (#2376) +- [`0402240a2`](https://github.com/dashpay/dash/commit/0402240a2) Bump CMAKE_CXX_STANDARD to 14 in CMakeLists.txt (#2377) +- [`3c9237aa4`](https://github.com/dashpay/dash/commit/3c9237aa4) Use VersionBitsState instead of VersionBitsTipState to avoid cs_main lock (#2370) +- [`c4351fd32`](https://github.com/dashpay/dash/commit/c4351fd32) revert 737, DEFAULT_TRANSACTION_MAXFEE = 0.1 * COIN (#2362) +- [`1c9ed7806`](https://github.com/dashpay/dash/commit/1c9ed7806) GDB automation with Python script to measure memory usage in dashd (#1609) +- [`d998dc13e`](https://github.com/dashpay/dash/commit/d998dc13e) Add cmake to non-mac gitian descriptors (#2360) +- [`266dd3232`](https://github.com/dashpay/dash/commit/266dd3232) mkdir -p to allow re-start of failed chia build (#2359) +- [`11a0cbf84`](https://github.com/dashpay/dash/commit/11a0cbf84) InstantSend-related tests refactoring (#2333) +- [`3313bbd51`](https://github.com/dashpay/dash/commit/3313bbd51) Backport bitcoin #13623 Migrate gitian-build.sh to python (#2319) +- [`7b76bbb57`](https://github.com/dashpay/dash/commit/7b76bbb57) Update Chia BLS libs to latest master (#2357) +- [`e2de632f8`](https://github.com/dashpay/dash/commit/e2de632f8) Move handling of `size != SerSize` into SetBuf/GetBuf (#2356) +- [`81d60bc28`](https://github.com/dashpay/dash/commit/81d60bc28) Fix the issue with transaction amount precision in IS tests (#2353) +- [`a45055384`](https://github.com/dashpay/dash/commit/a45055384) Fix qt configure to detect clang version correctly (#2344) +- [`b99d94a0f`](https://github.com/dashpay/dash/commit/b99d94a0f) Minor build documentation updates (#2343) +- [`464191698`](https://github.com/dashpay/dash/commit/464191698) Review fixes +- [`9c8e4ac76`](https://github.com/dashpay/dash/commit/9c8e4ac76) Move bls stuff from crypto/ to bls/ +- [`bed1ded8b`](https://github.com/dashpay/dash/commit/bed1ded8b) Remove duplicated check (#2336) +- [`89f744d06`](https://github.com/dashpay/dash/commit/89f744d06) pack of small cleanup fixes / optimizations (#2334) +- [`9603c5290`](https://github.com/dashpay/dash/commit/9603c5290) Trivial: Codestyle fixes in InstantSend code (#2332) +- [`90ad75911`](https://github.com/dashpay/dash/commit/90ad75911) Fix auto-IS and tests (#2331) +- [`b3fc236af`](https://github.com/dashpay/dash/commit/b3fc236af) Fix mnodeman.cs vs cs_vPendingMasternodes vs cs_main deadlock (#2200) +- [`80fd096b0`](https://github.com/dashpay/dash/commit/80fd096b0) Add ECDSA benchmarks +- [`78675d9bb`](https://github.com/dashpay/dash/commit/78675d9bb) Add BLS and DKG benchmarks +- [`3ee27c168`](https://github.com/dashpay/dash/commit/3ee27c168) Add highly parallelized worker/helper for BLS/DKG calculations +- [`aa3b0aa8a`](https://github.com/dashpay/dash/commit/aa3b0aa8a) Add simple helpers/wrappers for BLS+AES based integrated encryption schemes (IES) +- [`9ccf6f584`](https://github.com/dashpay/dash/commit/9ccf6f584) Implement wrappers around Chia BLS lib +- [`3039d44d3`](https://github.com/dashpay/dash/commit/3039d44d3) Add Chia bls-signatures library to depends +- [`f3dcb6916`](https://github.com/dashpay/dash/commit/f3dcb6916) Add cmake to ci/Dockerfile.builder +- [`057d7445e`](https://github.com/dashpay/dash/commit/057d7445e) Add libgmp to depends +- [`b0d0093d7`](https://github.com/dashpay/dash/commit/b0d0093d7) Add helper to rename all threads of a ctpl::thread_pool +- [`47a162255`](https://github.com/dashpay/dash/commit/47a162255) Add ctpl header only library +- [`407baccec`](https://github.com/dashpay/dash/commit/407baccec) Remove obsolete build-openbsd.md (#2328) +- [`8a1b51356`](https://github.com/dashpay/dash/commit/8a1b51356) Backport: Fix Qt build with XCode (for depends) (#2325) +- [`a5aca049d`](https://github.com/dashpay/dash/commit/a5aca049d) rename vars in mnsync to make more sense (#2308) +- [`ee6a5a33b`](https://github.com/dashpay/dash/commit/ee6a5a33b) Gov cleanup + copyright bump (#2324) +- [`d7e5f02ea`](https://github.com/dashpay/dash/commit/d7e5f02ea) Update build documentation (#2323) +- [`bd8c54d12`](https://github.com/dashpay/dash/commit/bd8c54d12) A bit more verbosity for some critical errors (#2316) +- [`a4ff2a19a`](https://github.com/dashpay/dash/commit/a4ff2a19a) Fix some warnings and do a couple of other trivial cleanups (#2315) +- [`07208a4ae`](https://github.com/dashpay/dash/commit/07208a4ae) document spork system with doxygen comments (#2301) +- [`2c1a17909`](https://github.com/dashpay/dash/commit/2c1a17909) cleanup: remove unused vars, includes, functions (#2306) +- [`3d48824b4`](https://github.com/dashpay/dash/commit/3d48824b4) Update .clang-format to more accurately show the actual style (#2299) +- [`8ea40102c`](https://github.com/dashpay/dash/commit/8ea40102c) Remove leftover RBF code from BTC (#2297) +- [`76599aad3`](https://github.com/dashpay/dash/commit/76599aad3) Drop (pre-)70208 compatibility code (#2295) +- [`016681cd3`](https://github.com/dashpay/dash/commit/016681cd3) Add support for serialization of bitsets and tuples (#2293) +- [`2a95dd30c`](https://github.com/dashpay/dash/commit/2a95dd30c) Fix locking issues in DIP3 unit tests (#2285) +- [`47ca06ab3`](https://github.com/dashpay/dash/commit/47ca06ab3) DIP3 integration tests (#2280) +- [`ad6c2893c`](https://github.com/dashpay/dash/commit/ad6c2893c) Docs - Update Core version number in readme files (#2267) +- [`bc7924d41`](https://github.com/dashpay/dash/commit/bc7924d41) Add unit tests for DIP3 and DIP4 +- [`d653ace99`](https://github.com/dashpay/dash/commit/d653ace99) Update CbTx in TestChainSetup +- [`9674be8f9`](https://github.com/dashpay/dash/commit/9674be8f9) Refactor TestChain100Setup to allow different test chain setups +- [`cb37c3972`](https://github.com/dashpay/dash/commit/cb37c3972) Bump PROTOCOL_VERSION to 70211, bump MIN_* protocols to 70210 (#2256) +- [`b99886532`](https://github.com/dashpay/dash/commit/b99886532) add link for developer-notes in contributing (#2260) +- [`fded838c9`](https://github.com/dashpay/dash/commit/fded838c9) RPC folder: Cleaned up brackets on if, while, for, BOOST_FOREACH. Some whitespace fixes (#2257) +- [`5295c78cc`](https://github.com/dashpay/dash/commit/5295c78cc) Fix typo in "penalty" (#2247) +- [`c566ce75d`](https://github.com/dashpay/dash/commit/c566ce75d) Update copyright in specialtx.h/cpp +- [`e002c50b0`](https://github.com/dashpay/dash/commit/e002c50b0) Add "immer" functional/immutable containers library (#2244) +- [`799e3c312`](https://github.com/dashpay/dash/commit/799e3c312) Perform Jenkins builds in /dash-src all the time to fix caching issues (#2242) +- [`b6896387a`](https://github.com/dashpay/dash/commit/b6896387a) Move DIP1 transaction size checks out of ContextualCheckBlock and use ContextualCheckTransaction instead (#2238) +- [`e415fd049`](https://github.com/dashpay/dash/commit/e415fd049) Revert CMasternodePayments::IsTransactionValid to the logic before the recent refactorings (#2237) +- [`8da88ecf6`](https://github.com/dashpay/dash/commit/8da88ecf6) Don't crash when formatting in logging throws exceptions (#2231) +- [`2e06f8133`](https://github.com/dashpay/dash/commit/2e06f8133) fix missed format parameter (#2229) +- [`3d654b981`](https://github.com/dashpay/dash/commit/3d654b981) Build bionic base image in Jenkinsfile.gitian & update docs (#2226) +- [`c09f57bd7`](https://github.com/dashpay/dash/commit/c09f57bd7) Backport move to Ubuntu Bionic and GCC7 in Gitian builds (#2225) +- [`7cf9572c2`](https://github.com/dashpay/dash/commit/7cf9572c2) Backport Bitcoin #11881: Remove Python2 support (#2224) +- [`633879cd2`](https://github.com/dashpay/dash/commit/633879cd2) Only use version 1 and 2 transactions for sighash_tests (#2219) +- [`2d4e18537`](https://github.com/dashpay/dash/commit/2d4e18537) Some useful commits from the DIP3 PR in regard to integration tests (#2218) +- [`106bab1ae`](https://github.com/dashpay/dash/commit/106bab1ae) Add new ParseXXX methods to easily parse UniValue values (#2211) +- [`c4c610783`](https://github.com/dashpay/dash/commit/c4c610783) Use C++14 standard when building (#2209) +- [`589a77013`](https://github.com/dashpay/dash/commit/589a77013) Correction to release date for 0.12.0 (#2205) +- [`96435288f`](https://github.com/dashpay/dash/commit/96435288f) Move block template specific stuff from CBlock to CBlockTemplate (#2195) +- [`3d002c946`](https://github.com/dashpay/dash/commit/3d002c946) Fix active masternode task schedule (#2193) +- [`65b904526`](https://github.com/dashpay/dash/commit/65b904526) Add helpers GetSentinelString() and GetDaemonString() to CMasternodePing (#2192) +- [`eb202e812`](https://github.com/dashpay/dash/commit/eb202e812) Use ccache in gitian builds (#2185) +- [`b47617325`](https://github.com/dashpay/dash/commit/b47617325) Install python3 in gitian builds (#2182) +- [`7a85e24c3`](https://github.com/dashpay/dash/commit/7a85e24c3) Remove deprecated gitian-rpi2.yml descriptor (#2183) +- [`1681d6366`](https://github.com/dashpay/dash/commit/1681d6366) Replace Dash-specific threads with Dash-specific scheduled tasks (#2043) +- [`dac090964`](https://github.com/dashpay/dash/commit/dac090964) remove dashpay.io dns seed entry (#2181) +- [`753c2436b`](https://github.com/dashpay/dash/commit/753c2436b) Fix MissingPropertyException on Jenkins when no cache was found (#2180) +- [`f3e380659`](https://github.com/dashpay/dash/commit/f3e380659) Move to in-docker CI builds and add Jenkins support (#2178) +- [`23dde9f12`](https://github.com/dashpay/dash/commit/23dde9f12) Remove a few annoying debug prints from CMasternodeMan (#2179) +- [`5036d7dfc`](https://github.com/dashpay/dash/commit/5036d7dfc) depends: Update Qt download url (#2177) +- [`e23339d6f`](https://github.com/dashpay/dash/commit/e23339d6f) use nullptr in Dash-specific code (#2166) +- [`42c193df0`](https://github.com/dashpay/dash/commit/42c193df0) replace map count/insert w/emplace in instantx.cpp (#2165) +- [`fd70a1eb9`](https://github.com/dashpay/dash/commit/fd70a1eb9) iterator cleanup in several places (#2164) +- [`df1be90ce`](https://github.com/dashpay/dash/commit/df1be90ce) Update links to obsolete documentation (#2162) +- [`448e92f4a`](https://github.com/dashpay/dash/commit/448e92f4a) GetOutPointPrivateSendRounds readability (#2149) +- [`6da2837bd`](https://github.com/dashpay/dash/commit/6da2837bd) InstantSend Integration tests (#2141) +- [`8ee9333bc`](https://github.com/dashpay/dash/commit/8ee9333bc) remove boost dependency from Dash-specific code (#2072) +- [`a527845e4`](https://github.com/dashpay/dash/commit/a527845e4) Bump to 0.12.4.0 pre-release (#2167) Credits ======= @@ -92,11 +664,23 @@ Credits Thanks to everyone who directly contributed to this release: - Alexander Block -- PaulieD - UdjinM6 +- PastaPastaPasta +- gladcow +- Nathan Marley +- thephez +- strophy +- PaulieD +- InhumanPerfection +- Spencer Lievens +- -k +- Salisbury +- Solar Designer +- Oleg Girko +- Anton Suprunchuk -As well as everyone who submitted issues and reviewed pull requests. - +As well as everyone that submitted issues, reviewed pull requests or helped translating on +[Transifex](https://www.transifex.com/projects/p/dash/). Older releases ============== @@ -121,12 +705,15 @@ Dash Core tree 0.12.1.x was a fork of Bitcoin Core tree 0.12. These release are considered obsolete. Old release notes can be found here: +- [v0.12.3.4](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.4.md) released Dec/14/2018 +- [v0.12.3.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.3.md) released Sep/19/2018 +- [v0.12.3.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.2.md) released Jul/09/2018 - [v0.12.3.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.1.md) released Jul/03/2018 - [v0.12.2.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.3.md) released Jan/12/2018 - [v0.12.2.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.2.md) released Dec/17/2017 - [v0.12.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.md) released Nov/08/2017 - [v0.12.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.1.md) released Feb/06/2017 -- [v0.12.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.0.md) released Jun/15/2015 +- [v0.12.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.0.md) released Aug/15/2015 - [v0.11.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.2.md) released Mar/04/2015 - [v0.11.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.1.md) released Feb/10/2015 - [v0.11.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.0.md) released Jan/15/2015 diff --git a/doc/release-notes/dash/release-notes-0.12.3.2.md b/doc/release-notes/dash/release-notes-0.12.3.2.md new file mode 100644 index 000000000000..a44a8789e2e0 --- /dev/null +++ b/doc/release-notes/dash/release-notes-0.12.3.2.md @@ -0,0 +1,135 @@ +Dash Core version 0.12.3.2 +========================== + +Release is now available from: + + + +This is a new minor version release, bringing various bugfixes and other +improvements. + +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/Dash-Qt (on Mac) or +dashd/dash-qt (on Linux). + +Downgrade warning +----------------- + +### Downgrade to a version < 0.12.2.2 + +Because release 0.12.2.2 included the [per-UTXO fix](release-notes/dash/release-notes-0.12.2.2.md#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. + +### Downgrade to 0.12.2.2/3, 0.12.3.1 + +Downgrading to these versions does not require any additional actions, should be +fully compatible. + + +Notable changes +=============== + +Improve initial sync +-------------------- + +Some users had problems getting their nodes synced. The issue occured due to nodes trying to +get additional data from each available peer but not being able to process this data fast enough. +This was recognized as a stalled sync process and thus the process was reset. To address the issue +we limited sync process to 3 peers max now and the issue should no longer appear as long as there +are at least 4 connections. + +Testnet/Devnet fixes +-------------------- + +Turned out that a low-diff rule for slow blocks backported from Bitcoin works a bit too aggressive for +a blockchain which uses a dynamic per-block difficulty adjustment algorithm (DGW). While blocks are still +produced at a more or less constant rate on average, the rate however is way too high. + +We also lifted multiple ports restriction on devnet and also incuded other fixes which should improve +connectivity on devnets which are using nodes with multiple different ports. + + +0.12.3.2 Change log +=================== + +See detailed [change log](https://github.com/dashpay/dash/compare/v0.12.3.1...dashpay:v0.12.3.2) below. + +### Network: +- [`2474d9cb8`](https://github.com/dashpay/dash/commit/2474d9cb8) Sync mn list and mnw list from 3 peers max (#2169) +- [`2c303cdb1`](https://github.com/dashpay/dash/commit/2c303cdb1) A few devnet related fixes (#2168) + +### Mining: +- [`2ba0c7760`](https://github.com/dashpay/dash/commit/2ba0c7760) Add tests for special rules for slow blocks on devnet/testnet (#2176) +- [`b9a83d2ae`](https://github.com/dashpay/dash/commit/b9a83d2ae) Allow mining min diff for very slow (2h+) blocks (#2175) +- [`050cabdf5`](https://github.com/dashpay/dash/commit/050cabdf5) Adjust diff for slow testnet/devnet blocks a bit smoother (#2161) + +### GUI: +- [`7b9919d18`](https://github.com/dashpay/dash/commit/7b9919d18) Fix issues with selections on Masternode tab (#2170) +- [`c4698d5f3`](https://github.com/dashpay/dash/commit/c4698d5f3) Make PS Buttons not react to spacebar (#2154) + +### Other: +- [`f833e2ed6`](https://github.com/dashpay/dash/commit/f833e2ed6) Bump to 0.12.3.2 (#2173) + + +Credits +======= + +Thanks to everyone who directly contributed to this release: + +- Alexander Block +- PaulieD +- UdjinM6 + +As well as everyone who submitted issues and reviewed pull requests. + + +Older releases +============== + +Dash was previously known as Darkcoin. + +Darkcoin tree 0.8.x was a fork of Litecoin tree 0.8, original name was XCoin +which was first released on Jan/18/2014. + +Darkcoin tree 0.9.x was the open source implementation of masternodes based on +the 0.8.x tree and was first released on Mar/13/2014. + +Darkcoin tree 0.10.x used to be the closed source implementation of Darksend +which was released open source on Sep/25/2014. + +Dash Core tree 0.11.x was a fork of Bitcoin Core tree 0.9, +Darkcoin was rebranded to Dash. + +Dash Core tree 0.12.0.x was a fork of Bitcoin Core tree 0.10. + +Dash Core tree 0.12.1.x was a fork of Bitcoin Core tree 0.12. + +These release are considered obsolete. Old release notes can be found here: + +- [v0.12.3.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.1.md) released Jul/03/2018 +- [v0.12.2.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.3.md) released Jan/12/2018 +- [v0.12.2.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.2.md) released Dec/17/2017 +- [v0.12.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.md) released Nov/08/2017 +- [v0.12.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.1.md) released Feb/06/2017 +- [v0.12.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.0.md) released Jun/15/2015 +- [v0.11.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.2.md) released Mar/04/2015 +- [v0.11.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.1.md) released Feb/10/2015 +- [v0.11.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.0.md) released Jan/15/2015 +- [v0.10.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.10.0.md) released Sep/25/2014 +- [v0.9.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.9.0.md) released Mar/13/2014 + diff --git a/doc/release-notes/dash/release-notes-0.12.3.3.md b/doc/release-notes/dash/release-notes-0.12.3.3.md new file mode 100644 index 000000000000..36576174398b --- /dev/null +++ b/doc/release-notes/dash/release-notes-0.12.3.3.md @@ -0,0 +1,99 @@ +Dash Core version 0.12.3.3 +========================== + +Release is now available from: + + + +This is a critical bugfix release. + +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/Dash-Qt (on Mac) or +dashd/dash-qt (on Linux). + +Downgrade warning +----------------- + +### Downgrade to a version < 0.12.2.2 + +Because release 0.12.2.2 included the [per-UTXO fix](release-notes/dash/release-notes-0.12.2.2.md#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. + +### Downgrade to 0.12.2.2/3, 0.12.3.1/2 + +Downgrading to these versions does not require any additional actions, should be +fully compatible. + + +Notable changes +=============== + +Fix crash bug with duplicate inputs within a transaction +-------------------------------------------------------- + +There was a critical bug discovered in Bitcoin Core's codebase recently which +can cause node receiving a block to crash https://github.com/bitcoin/bitcoin/pull/14247 + +0.12.3.3 Change log +=================== + +See detailed [set of changes](https://github.com/dashpay/dash/compare/v0.12.3.2...dashpay:v0.12.3.3). + +Credits +======= + +Thanks to everyone who directly contributed to this release, +as well as everyone who submitted issues and reviewed pull requests. + + +Older releases +============== + +Dash was previously known as Darkcoin. + +Darkcoin tree 0.8.x was a fork of Litecoin tree 0.8, original name was XCoin +which was first released on Jan/18/2014. + +Darkcoin tree 0.9.x was the open source implementation of masternodes based on +the 0.8.x tree and was first released on Mar/13/2014. + +Darkcoin tree 0.10.x used to be the closed source implementation of Darksend +which was released open source on Sep/25/2014. + +Dash Core tree 0.11.x was a fork of Bitcoin Core tree 0.9, +Darkcoin was rebranded to Dash. + +Dash Core tree 0.12.0.x was a fork of Bitcoin Core tree 0.10. + +Dash Core tree 0.12.1.x was a fork of Bitcoin Core tree 0.12. + +These release are considered obsolete. Old release notes can be found here: + +- [v0.12.3.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.2.md) released Jul/09/2018 +- [v0.12.3.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.1.md) released Jul/03/2018 +- [v0.12.2.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.3.md) released Jan/12/2018 +- [v0.12.2.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.2.md) released Dec/17/2017 +- [v0.12.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.md) released Nov/08/2017 +- [v0.12.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.1.md) released Feb/06/2017 +- [v0.12.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.0.md) released Jun/15/2015 +- [v0.11.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.2.md) released Mar/04/2015 +- [v0.11.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.1.md) released Feb/10/2015 +- [v0.11.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.0.md) released Jan/15/2015 +- [v0.10.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.10.0.md) released Sep/25/2014 +- [v0.9.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.9.0.md) released Mar/13/2014 + diff --git a/doc/release-notes/dash/release-notes-0.12.3.4.md b/doc/release-notes/dash/release-notes-0.12.3.4.md new file mode 100644 index 000000000000..171e178741fe --- /dev/null +++ b/doc/release-notes/dash/release-notes-0.12.3.4.md @@ -0,0 +1,107 @@ +Dash Core version 0.12.3.4 +========================== + +Release is now available from: + + + +This is an optional release and only contains changes for testnet. It is not required to update masternodes on mainnet. + +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/Dash-Qt (on Mac) or +dashd/dash-qt (on Linux). + +Downgrade warning +----------------- + +### Downgrade to a version < 0.12.2.2 + +Because release 0.12.2.2 included the [per-UTXO fix](release-notes/dash/release-notes-0.12.2.2.md#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. + +### Downgrade to 0.12.3.1/2/3 + +Downgrading to these versions does not require any additional actions, should be +fully compatible. + + +Notable changes +=============== + +Fork/Reset testnet at block 4001 +-------------------------------- + +This release is NOT required on mainnet. It is intended to be deployed on testnet and will cause a fork at block 4001. +The plan is to restart all testing for the v0.13.0.0 upgrade process. + +When deployed on testnet, it is required to start with a fresh data directory or call Dash Core with `-reindex-chainstate`. + +0.12.3.4 Change log +=================== + +See detailed [set of changes](https://github.com/dashpay/dash/compare/v0.12.3.3...dashpay:v0.12.3.4). + +Python Support +-------------- + +Support for Python 2 has been discontinued for all test files and tools. + +Credits +======= + +Thanks to everyone who directly contributed to this release, +as well as everyone who submitted issues and reviewed pull requests. + + +Older releases +============== + +Dash was previously known as Darkcoin. + +Darkcoin tree 0.8.x was a fork of Litecoin tree 0.8, original name was XCoin +which was first released on Jan/18/2014. + +Darkcoin tree 0.9.x was the open source implementation of masternodes based on +the 0.8.x tree and was first released on Mar/13/2014. + +Darkcoin tree 0.10.x used to be the closed source implementation of Darksend +which was released open source on Sep/25/2014. + +Dash Core tree 0.11.x was a fork of Bitcoin Core tree 0.9, +Darkcoin was rebranded to Dash. + +Dash Core tree 0.12.0.x was a fork of Bitcoin Core tree 0.10. + +Dash Core tree 0.12.1.x was a fork of Bitcoin Core tree 0.12. + +These release are considered obsolete. Old release notes can be found here: + +- [v0.12.3.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.3.md) released Sep/19/2018 +- [v0.12.3.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.2.md) released Jul/09/2018 +- [v0.12.3.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.1.md) released Jul/03/2018 +- [v0.12.2.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.3.md) released Jan/12/2018 +- [v0.12.2.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.2.md) released Dec/17/2017 +- [v0.12.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.md) released Nov/08/2017 +- [v0.12.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.1.md) released Feb/06/2017 +- [v0.12.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.0.md) released Aug/15/2015 +- [v0.11.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.2.md) released Mar/04/2015 +- [v0.11.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.1.md) released Feb/10/2015 +- [v0.11.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.0.md) released Jan/15/2015 +- [v0.10.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.10.0.md) released Sep/25/2014 +- [v0.9.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.9.0.md) released Mar/13/2014 + diff --git a/doc/release-process.md b/doc/release-process.md index 0a6e4d488f85..c199086d28d3 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -24,7 +24,7 @@ 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. @@ -64,7 +64,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 a708ca5ab666..627f46c2d1a0 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/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 7690ee5511e9..b40da3c13271 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -109,17 +109,22 @@ testScripts = [ # longest test should go first, to favor running tests in parallel + 'dip3-deterministicmns.py', # NOTE: needs dash_hash to pass 'wallet-hd.py', 'walletbackup.py', # vv Tests less than 5m vv 'p2p-fullblocktest.py', # NOTE: needs dash_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 dash_hash to pass 'zapwallettxes.py', @@ -166,6 +171,7 @@ 'listsinceblock.py', 'p2p-leaktests.py', 'p2p-compactblocks.py', + 'sporks.py', ] if ENABLE_ZMQ: testScripts.append('zmq_test.py') @@ -196,7 +202,6 @@ 'invalidateblock.py', 'maxblocksinflight.py', 'p2p-acceptblock.py', # NOTE: needs dash_hash to pass - # 'replace-by-fee.py', # RBF is disabled in Dash Core ] diff --git a/qa/rpc-tests/autoix-mempool.py b/qa/rpc-tests/autoix-mempool.py new file mode 100755 index 000000000000..9266a06b5101 --- /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 dip3 deployment + return info['bip9_softforks']['dip0003']['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/dip3-deterministicmns.py b/qa/rpc-tests/dip3-deterministicmns.py new file mode 100755 index 000000000000..09ed89207582 --- /dev/null +++ b/qa/rpc-tests/dip3-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 DIP3Test(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 dip3 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 {} dash".format(self.nodes[0].getbalance())) + + # Make sure we're below block 143 (which activates dip3) + print("testing rejection of ProTx before dip3 activation") + assert(self.nodes[0].getblockchaininfo()['blocks'] < 143) + dip3_deployment = self.nodes[0].getblockchaininfo()['bip9_softforks']['dip0003'] + assert_equal(dip3_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 dip3 activation (in states defined, started and locked_in)") + while self.nodes[0].getblockchaininfo()['bip9_softforks']['dip0003']['status'] == 'defined': + self.nodes[0].generate(1) + self.test_fail_create_protx(self.nodes[0]) + while self.nodes[0].getblockchaininfo()['bip9_softforks']['dip0003']['status'] == 'started': + self.nodes[0].generate(1) + self.test_fail_create_protx(self.nodes[0]) + + # prepare mn which should still be accepted later when dip3 activates + print("creating collateral for mn-before-dip3") + before_dip3_mn = self.create_mn(self.nodes[0], mn_idx, 'mn-before-dip3') + mn_idx += 1 + + while self.nodes[0].getblockchaininfo()['bip9_softforks']['dip0003']['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) + + # DIP3 has activated here + + print("testing rejection of ProTx right before dip3 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-dip3") + after_dip3_mn = self.create_mn(self.nodes[0], mn_idx, 'mn-after-dip3') + # 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 dip3 activation") + self.write_mnconf(mns + [before_dip3_mn, after_dip3_mn]) + self.restart_controller_node() + self.force_finish_mnsync(self.nodes[0]) + + print("start MN %s" % before_dip3_mn.alias) + mns.append(before_dip3_mn) + self.start_mn(before_dip3_mn) + self.wait_for_sporks() + self.force_finish_mnsync_list(before_dip3_mn.node) + self.start_alias(self.nodes[0], before_dip3_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 dip3 activation") + print("start MN %s" % after_dip3_mn.alias) + mns.append(after_dip3_mn) + self.start_mn(after_dip3_mn) + self.wait_for_sporks() + self.force_finish_mnsync_list(after_dip3_mn.node) + self.start_alias(self.nodes[0], after_dip3_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__': + DIP3Test().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 d727f6d5cf2e..ad0bbe0f8944 100755 --- a/qa/rpc-tests/listtransactions.py +++ b/qa/rpc-tests/listtransactions.py @@ -93,108 +93,6 @@ def run_test(self): {"category":"receive","amount":Decimal("0.1")}, {"txid":txid, "account" : "watchonly"} ) - # rbf is disabled in Dash 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 100755 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..9d1543d96503 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) + dip4_activated = self.lastblockheight + 1 >= 432 + block = create_block(self.tip, create_coinbase(self.lastblockheight + 1, dip4_activated=dip4_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 100755 index 000000000000..3177515141a2 --- /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 dip3 deployment + return info['bip9_softforks']['dip0003']['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 f2a541b61c98..ed34563d8ef7 100755 --- a/qa/rpc-tests/p2p-fullblocktest.py +++ b/qa/rpc-tests/p2p-fullblocktest.py @@ -69,7 +69,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 100755 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/replace-by-fee.py b/qa/rpc-tests/replace-by-fee.py deleted file mode 100755 index 2b29dfdd2bd9..000000000000 --- a/qa/rpc-tests/replace-by-fee.py +++ /dev/null @@ -1,589 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test replace by fee code -# - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from test_framework.script import * -from test_framework.mininode import * - -MAX_REPLACEMENT_LIMIT = 100 - -def txToHex(tx): - return bytes_to_hex_str(tx.serialize()) - -def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])): - """Create a txout with a given amount and scriptPubKey - - Mines coins as needed. - - confirmed - txouts created will be confirmed in the blockchain; - unconfirmed otherwise. - """ - fee = 1*COIN - while node.getbalance() < satoshi_round((amount + fee)/COIN): - node.generate(100) - #print (node.getbalance(), amount, fee) - - new_addr = node.getnewaddress() - #print new_addr - txid = node.sendtoaddress(new_addr, satoshi_round((amount+fee)/COIN)) - tx1 = node.getrawtransaction(txid, 1) - txid = int(txid, 16) - i = None - - for i, txout in enumerate(tx1['vout']): - #print i, txout['scriptPubKey']['addresses'] - if txout['scriptPubKey']['addresses'] == [new_addr]: - #print i - break - assert i is not None - - tx2 = CTransaction() - tx2.vin = [CTxIn(COutPoint(txid, i))] - tx2.vout = [CTxOut(amount, scriptPubKey)] - tx2.rehash() - - signed_tx = node.signrawtransaction(txToHex(tx2)) - - txid = node.sendrawtransaction(signed_tx['hex'], True) - - # If requested, ensure txouts are confirmed. - if confirmed: - mempool_size = len(node.getrawmempool()) - while mempool_size > 0: - node.generate(1) - new_size = len(node.getrawmempool()) - # Error out if we have something stuck in the mempool, as this - # would likely be a bug. - assert(new_size < mempool_size) - mempool_size = new_size - - return COutPoint(int(txid, 16), 0) - -class ReplaceByFeeTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.num_nodes = 1 - self.setup_clean_chain = False - - def setup_network(self): - self.nodes = [] - self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", "-debug", - "-whitelist=127.0.0.1", - "-limitancestorcount=50", - "-limitancestorsize=101", - "-limitdescendantcount=200", - "-limitdescendantsize=101" - ])) - self.is_network_split = False - - def run_test(self): - make_utxo(self.nodes[0], 1*COIN) - - print("Running test simple doublespend...") - self.test_simple_doublespend() - - print("Running test doublespend chain...") - self.test_doublespend_chain() - - print("Running test doublespend tree...") - self.test_doublespend_tree() - - print("Running test replacement feeperkb...") - self.test_replacement_feeperkb() - - print("Running test spends of conflicting outputs...") - self.test_spends_of_conflicting_outputs() - - print("Running test new unconfirmed inputs...") - self.test_new_unconfirmed_inputs() - - print("Running test too many replacements...") - self.test_too_many_replacements() - - print("Running test opt-in...") - self.test_opt_in() - - print("Running test prioritised transactions...") - self.test_prioritised_transactions() - - print("Passed\n") - - def test_simple_doublespend(self): - """Simple doublespend""" - tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) - - tx1a = CTransaction() - tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] - tx1a_hex = txToHex(tx1a) - tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) - - # Should fail because we haven't changed the fee - tx1b = CTransaction() - tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1b.vout = [CTxOut(1*COIN, CScript([b'b']))] - tx1b_hex = txToHex(tx1b) - - try: - tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) - except JSONRPCException as exp: - assert_equal(exp.error['code'], -26) # insufficient fee - else: - assert(False) - - # Extra 0.1 BTC fee - tx1b = CTransaction() - tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] - tx1b_hex = txToHex(tx1b) - tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) - - mempool = self.nodes[0].getrawmempool() - - assert (tx1a_txid not in mempool) - assert (tx1b_txid in mempool) - - assert_equal(tx1b_hex, self.nodes[0].getrawtransaction(tx1b_txid)) - - def test_doublespend_chain(self): - """Doublespend of a long chain""" - - initial_nValue = 50*COIN - tx0_outpoint = make_utxo(self.nodes[0], initial_nValue) - - prevout = tx0_outpoint - remaining_value = initial_nValue - chain_txids = [] - while remaining_value > 10*COIN: - remaining_value -= 1*COIN - tx = CTransaction() - tx.vin = [CTxIn(prevout, nSequence=0)] - tx.vout = [CTxOut(remaining_value, CScript([1]))] - tx_hex = txToHex(tx) - txid = self.nodes[0].sendrawtransaction(tx_hex, True) - chain_txids.append(txid) - prevout = COutPoint(int(txid, 16), 0) - - # Whether the double-spend is allowed is evaluated by including all - # child fees - 40 BTC - so this attempt is rejected. - dbl_tx = CTransaction() - dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(initial_nValue - 30*COIN, CScript([1]))] - dbl_tx_hex = txToHex(dbl_tx) - - try: - self.nodes[0].sendrawtransaction(dbl_tx_hex, True) - except JSONRPCException as exp: - assert_equal(exp.error['code'], -26) # insufficient fee - else: - assert(False) # transaction mistakenly accepted! - - # Accepted with sufficient fee - dbl_tx = CTransaction() - dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(1*COIN, CScript([1]))] - dbl_tx_hex = txToHex(dbl_tx) - self.nodes[0].sendrawtransaction(dbl_tx_hex, True) - - mempool = self.nodes[0].getrawmempool() - for doublespent_txid in chain_txids: - assert(doublespent_txid not in mempool) - - def test_doublespend_tree(self): - """Doublespend of a big tree of transactions""" - - initial_nValue = 50*COIN - tx0_outpoint = make_utxo(self.nodes[0], initial_nValue) - - def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.0001*COIN, _total_txs=None): - if _total_txs is None: - _total_txs = [0] - if _total_txs[0] >= max_txs: - return - - txout_value = (initial_value - fee) // tree_width - if txout_value < fee: - return - - vout = [CTxOut(txout_value, CScript([i+1])) - for i in range(tree_width)] - tx = CTransaction() - tx.vin = [CTxIn(prevout, nSequence=0)] - tx.vout = vout - tx_hex = txToHex(tx) - - assert(len(tx.serialize()) < 100000) - txid = self.nodes[0].sendrawtransaction(tx_hex, True) - yield tx - _total_txs[0] += 1 - - txid = int(txid, 16) - - for i, txout in enumerate(tx.vout): - for x in branch(COutPoint(txid, i), txout_value, - max_txs, - tree_width=tree_width, fee=fee, - _total_txs=_total_txs): - yield x - - fee = int(0.0001*COIN) - n = MAX_REPLACEMENT_LIMIT - tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee)) - assert_equal(len(tree_txs), n) - - # Attempt double-spend, will fail because too little fee paid - dbl_tx = CTransaction() - dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(initial_nValue - fee*n, CScript([1]))] - dbl_tx_hex = txToHex(dbl_tx) - try: - self.nodes[0].sendrawtransaction(dbl_tx_hex, True) - except JSONRPCException as exp: - assert_equal(exp.error['code'], -26) # insufficient fee - else: - assert(False) - - # 1 BTC fee is enough - dbl_tx = CTransaction() - dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(initial_nValue - fee*n - 1*COIN, CScript([1]))] - dbl_tx_hex = txToHex(dbl_tx) - self.nodes[0].sendrawtransaction(dbl_tx_hex, True) - - mempool = self.nodes[0].getrawmempool() - - for tx in tree_txs: - tx.rehash() - assert (tx.hash not in mempool) - - # Try again, but with more total transactions than the "max txs - # double-spent at once" anti-DoS limit. - for n in (MAX_REPLACEMENT_LIMIT+1, MAX_REPLACEMENT_LIMIT*2): - fee = int(0.0001*COIN) - tx0_outpoint = make_utxo(self.nodes[0], initial_nValue) - tree_txs = list(branch(tx0_outpoint, initial_nValue, n, fee=fee)) - assert_equal(len(tree_txs), n) - - dbl_tx = CTransaction() - dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(initial_nValue - 2*fee*n, CScript([1]))] - dbl_tx_hex = txToHex(dbl_tx) - try: - self.nodes[0].sendrawtransaction(dbl_tx_hex, True) - except JSONRPCException as exp: - assert_equal(exp.error['code'], -26) - assert_equal("too many potential replacements" in exp.error['message'], True) - else: - assert(False) - - for tx in tree_txs: - tx.rehash() - self.nodes[0].getrawtransaction(tx.hash) - - def test_replacement_feeperkb(self): - """Replacement requires fee-per-KB to be higher""" - tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) - - tx1a = CTransaction() - tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] - tx1a_hex = txToHex(tx1a) - tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) - - # Higher fee, but the fee per KB is much lower, so the replacement is - # rejected. - tx1b = CTransaction() - tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1b.vout = [CTxOut(int(0.001*COIN), CScript([b'a'*999000]))] - tx1b_hex = txToHex(tx1b) - - try: - tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) - except JSONRPCException as exp: - assert_equal(exp.error['code'], -26) # insufficient fee - else: - assert(False) - - def test_spends_of_conflicting_outputs(self): - """Replacements that spend conflicting tx outputs are rejected""" - utxo1 = make_utxo(self.nodes[0], int(1.2*COIN)) - utxo2 = make_utxo(self.nodes[0], 3*COIN) - - tx1a = CTransaction() - tx1a.vin = [CTxIn(utxo1, nSequence=0)] - tx1a.vout = [CTxOut(int(1.1*COIN), CScript([b'a']))] - tx1a_hex = txToHex(tx1a) - tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) - - tx1a_txid = int(tx1a_txid, 16) - - # Direct spend an output of the transaction we're replacing. - tx2 = CTransaction() - tx2.vin = [CTxIn(utxo1, nSequence=0), CTxIn(utxo2, nSequence=0)] - tx2.vin.append(CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)) - tx2.vout = tx1a.vout - tx2_hex = txToHex(tx2) - - try: - tx2_txid = self.nodes[0].sendrawtransaction(tx2_hex, True) - except JSONRPCException as exp: - assert_equal(exp.error['code'], -26) - else: - assert(False) - - # Spend tx1a's output to test the indirect case. - tx1b = CTransaction() - tx1b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)] - tx1b.vout = [CTxOut(1*COIN, CScript([b'a']))] - tx1b_hex = txToHex(tx1b) - tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) - tx1b_txid = int(tx1b_txid, 16) - - tx2 = CTransaction() - tx2.vin = [CTxIn(utxo1, nSequence=0), CTxIn(utxo2, nSequence=0), - CTxIn(COutPoint(tx1b_txid, 0))] - tx2.vout = tx1a.vout - tx2_hex = txToHex(tx2) - - try: - tx2_txid = self.nodes[0].sendrawtransaction(tx2_hex, True) - except JSONRPCException as exp: - assert_equal(exp.error['code'], -26) - else: - assert(False) - - def test_new_unconfirmed_inputs(self): - """Replacements that add new unconfirmed inputs are rejected""" - confirmed_utxo = make_utxo(self.nodes[0], int(1.1*COIN)) - unconfirmed_utxo = make_utxo(self.nodes[0], int(0.1*COIN), False) - - tx1 = CTransaction() - tx1.vin = [CTxIn(confirmed_utxo)] - tx1.vout = [CTxOut(1*COIN, CScript([b'a']))] - tx1_hex = txToHex(tx1) - tx1_txid = self.nodes[0].sendrawtransaction(tx1_hex, True) - - tx2 = CTransaction() - tx2.vin = [CTxIn(confirmed_utxo), CTxIn(unconfirmed_utxo)] - tx2.vout = tx1.vout - tx2_hex = txToHex(tx2) - - try: - tx2_txid = self.nodes[0].sendrawtransaction(tx2_hex, True) - except JSONRPCException as exp: - assert_equal(exp.error['code'], -26) - else: - assert(False) - - def test_too_many_replacements(self): - """Replacements that evict too many transactions are rejected""" - # Try directly replacing more than MAX_REPLACEMENT_LIMIT - # transactions - - # Start by creating a single transaction with many outputs - initial_nValue = 10*COIN - utxo = make_utxo(self.nodes[0], initial_nValue) - fee = int(0.0001*COIN) - split_value = int((initial_nValue-fee)/(MAX_REPLACEMENT_LIMIT+1)) - - outputs = [] - for i in range(MAX_REPLACEMENT_LIMIT+1): - outputs.append(CTxOut(split_value, CScript([1]))) - - splitting_tx = CTransaction() - splitting_tx.vin = [CTxIn(utxo, nSequence=0)] - splitting_tx.vout = outputs - splitting_tx_hex = txToHex(splitting_tx) - - txid = self.nodes[0].sendrawtransaction(splitting_tx_hex, True) - txid = int(txid, 16) - - # Now spend each of those outputs individually - for i in range(MAX_REPLACEMENT_LIMIT+1): - tx_i = CTransaction() - tx_i.vin = [CTxIn(COutPoint(txid, i), nSequence=0)] - tx_i.vout = [CTxOut(split_value-fee, CScript([b'a']))] - tx_i_hex = txToHex(tx_i) - self.nodes[0].sendrawtransaction(tx_i_hex, True) - - # Now create doublespend of the whole lot; should fail. - # Need a big enough fee to cover all spending transactions and have - # a higher fee rate - double_spend_value = (split_value-100*fee)*(MAX_REPLACEMENT_LIMIT+1) - inputs = [] - for i in range(MAX_REPLACEMENT_LIMIT+1): - inputs.append(CTxIn(COutPoint(txid, i), nSequence=0)) - double_tx = CTransaction() - double_tx.vin = inputs - double_tx.vout = [CTxOut(double_spend_value, CScript([b'a']))] - double_tx_hex = txToHex(double_tx) - - try: - self.nodes[0].sendrawtransaction(double_tx_hex, True) - except JSONRPCException as exp: - assert_equal(exp.error['code'], -26) - assert_equal("too many potential replacements" in exp.error['message'], True) - else: - assert(False) - - # If we remove an input, it should pass - double_tx = CTransaction() - double_tx.vin = inputs[0:-1] - double_tx.vout = [CTxOut(double_spend_value, CScript([b'a']))] - double_tx_hex = txToHex(double_tx) - self.nodes[0].sendrawtransaction(double_tx_hex, True) - - def test_opt_in(self): - """ Replacing should only work if orig tx opted in """ - tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) - - # Create a non-opting in transaction - tx1a = CTransaction() - tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0xffffffff)] - tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] - tx1a_hex = txToHex(tx1a) - tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) - - # Shouldn't be able to double-spend - tx1b = CTransaction() - tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] - tx1b_hex = txToHex(tx1b) - - try: - tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) - except JSONRPCException as exp: - assert_equal(exp.error['code'], -26) - else: - print(tx1b_txid) - assert(False) - - tx1_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) - - # Create a different non-opting in transaction - tx2a = CTransaction() - tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0xfffffffe)] - tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))] - tx2a_hex = txToHex(tx2a) - tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True) - - # Still shouldn't be able to double-spend - tx2b = CTransaction() - tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)] - tx2b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] - tx2b_hex = txToHex(tx2b) - - try: - tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True) - except JSONRPCException as exp: - assert_equal(exp.error['code'], -26) - else: - assert(False) - - # Now create a new transaction that spends from tx1a and tx2a - # opt-in on one of the inputs - # Transaction should be replaceable on either input - - tx1a_txid = int(tx1a_txid, 16) - tx2a_txid = int(tx2a_txid, 16) - - tx3a = CTransaction() - tx3a.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0xffffffff), - CTxIn(COutPoint(tx2a_txid, 0), nSequence=0xfffffffd)] - tx3a.vout = [CTxOut(int(0.9*COIN), CScript([b'c'])), CTxOut(int(0.9*COIN), CScript([b'd']))] - tx3a_hex = txToHex(tx3a) - - self.nodes[0].sendrawtransaction(tx3a_hex, True) - - tx3b = CTransaction() - tx3b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)] - tx3b.vout = [CTxOut(int(0.5*COIN), CScript([b'e']))] - tx3b_hex = txToHex(tx3b) - - tx3c = CTransaction() - tx3c.vin = [CTxIn(COutPoint(tx2a_txid, 0), nSequence=0)] - tx3c.vout = [CTxOut(int(0.5*COIN), CScript([b'f']))] - tx3c_hex = txToHex(tx3c) - - self.nodes[0].sendrawtransaction(tx3b_hex, True) - # If tx3b was accepted, tx3c won't look like a replacement, - # but make sure it is accepted anyway - self.nodes[0].sendrawtransaction(tx3c_hex, True) - - def test_prioritised_transactions(self): - # Ensure that fee deltas used via prioritisetransaction are - # correctly used by replacement logic - - # 1. Check that feeperkb uses modified fees - tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) - - tx1a = CTransaction() - tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] - tx1a_hex = txToHex(tx1a) - tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) - - # Higher fee, but the actual fee per KB is much lower. - tx1b = CTransaction() - tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1b.vout = [CTxOut(int(0.001*COIN), CScript([b'a'*740000]))] - tx1b_hex = txToHex(tx1b) - - # Verify tx1b cannot replace tx1a. - try: - tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) - except JSONRPCException as exp: - assert_equal(exp.error['code'], -26) - else: - assert(False) - - # Use prioritisetransaction to set tx1a's fee to 0. - self.nodes[0].prioritisetransaction(tx1a_txid, 0, int(-0.1*COIN)) - - # Now tx1b should be able to replace tx1a - tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) - - assert(tx1b_txid in self.nodes[0].getrawmempool()) - - # 2. Check that absolute fee checks use modified fee. - tx1_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) - - tx2a = CTransaction() - tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0)] - tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))] - tx2a_hex = txToHex(tx2a) - tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True) - - # Lower fee, but we'll prioritise it - tx2b = CTransaction() - tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)] - tx2b.vout = [CTxOut(int(1.01*COIN), CScript([b'a']))] - tx2b.rehash() - tx2b_hex = txToHex(tx2b) - - # Verify tx2b cannot replace tx2a. - try: - tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True) - except JSONRPCException as exp: - assert_equal(exp.error['code'], -26) - else: - assert(False) - - # Now prioritise tx2b to have a higher modified fee - self.nodes[0].prioritisetransaction(tx2b.hash, 0, int(0.1*COIN)) - - # tx2b should now be accepted - tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True) - - assert(tx2b_txid in self.nodes[0].getrawmempool()) - -if __name__ == '__main__': - ReplaceByFeeTest().main() diff --git a/qa/rpc-tests/sporks.py b/qa/rpc-tests/sporks.py new file mode 100755 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 6266318342d5..558d195fb7f7 100644 --- a/qa/rpc-tests/test_framework/blocktools.py +++ b/qa/rpc-tests/test_framework/blocktools.py @@ -40,7 +40,7 @@ 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, dip4_activated=False): coinbase = CTransaction() coinbase.vin.append(CTxIn(COutPoint(0, 0xffffffff), ser_string(serialize_script_num(height)), 0xffffffff)) @@ -53,6 +53,11 @@ def create_coinbase(height, pubkey = None): else: coinbaseoutput.scriptPubKey = CScript([OP_TRUE]) coinbase.vout = [ coinbaseoutput ] + if dip4_activated: + coinbase.nVersion = 3 + coinbase.nType = 5 + cbtx_payload = CCbTx(1, height, 0) + coinbase.vExtraPayload = cbtx_payload.serialize() coinbase.calc_sha256() return coinbase @@ -80,3 +85,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 dfa231a6568e..d1a141a6fa65 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 668326286f02..95150865d324 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -41,7 +41,7 @@ import dash_hash BIP0031_VERSION = 60000 -MY_VERSION = 70209 # SHORT_IDS_BLOCKS_VERSION to support cmpct blocks +MY_VERSION = 70210 # 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 86b074243e5f..e6d204567648 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -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/Info.plist.in b/share/qt/Info.plist.in index 65c2ebb7dc2d..6db46b192fa3 100644 --- a/share/qt/Info.plist.in +++ b/share/qt/Info.plist.in @@ -30,7 +30,7 @@ CFBundleExecutable Dash-Qt - + CFBundleName Dash-Qt @@ -99,7 +99,10 @@ LSAppNapIsDisabled True - + + NSRequiresAquaSystemAppearance + True + LSApplicationCategoryType public.app-category.finance diff --git a/share/qt/extract_strings_qt.py b/share/qt/extract_strings_qt.py index 43b43e76c7cd..811c88f5a3a1 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 a54e81c1d1bf..deecee050b92 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,6 +25,8 @@ 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=libdash_server.a LIBBITCOIN_COMMON=libdash_common.a LIBBITCOIN_CONSENSUS=libdash_consensus.a @@ -106,6 +108,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 \ @@ -130,6 +139,11 @@ BITCOIN_CORE_H = \ keystore.h \ dbwrapper.h \ limitedmap.h \ + llmq/quorums_commitment.h \ + llmq/quorums_blockprocessor.h \ + llmq/quorums_dummydkg.h \ + llmq/quorums_utils.h \ + llmq/quorums_init.h \ masternode.h \ masternode-payments.h \ masternode-sync.h \ @@ -148,7 +162,6 @@ BITCOIN_CORE_H = \ noui.h \ policy/fees.h \ policy/policy.h \ - policy/rbf.h \ pow.h \ protocol.h \ random.h \ @@ -164,6 +177,8 @@ BITCOIN_CORE_H = \ script/ismine.h \ spork.h \ streams.h \ + support/allocators/mt_pooled_secure.h \ + support/allocators/pooled_secure.h \ support/allocators/secure.h \ support/allocators/zeroafterfree.h \ support/cleanse.h \ @@ -216,6 +231,12 @@ libdash_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 \ @@ -227,6 +248,11 @@ libdash_server_a_SOURCES = \ governance-validators.cpp \ governance-vote.cpp \ governance-votedb.cpp \ + llmq/quorums_commitment.cpp \ + llmq/quorums_blockprocessor.cpp \ + llmq/quorums_dummydkg.cpp \ + llmq/quorums_utils.cpp \ + llmq/quorums_init.cpp \ masternode.cpp \ masternode-payments.cpp \ masternode-sync.cpp \ @@ -252,6 +278,7 @@ libdash_server_a_SOURCES = \ rpc/misc.cpp \ rpc/net.cpp \ rpc/rawtransaction.cpp \ + rpc/rpcevo.cpp \ rpc/server.cpp \ script/sigcache.cpp \ script/ismine.cpp \ @@ -291,7 +318,6 @@ libdash_wallet_a_SOURCES = \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ wallet/walletdb.cpp \ - policy/rbf.cpp \ $(BITCOIN_CORE_H) # crypto primitives library @@ -348,6 +374,8 @@ libdash_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 \ @@ -369,6 +397,7 @@ libdash_consensus_a_SOURCES = \ script/script_error.cpp \ script/script_error.h \ serialize.h \ + streams.h \ tinyformat.h \ uint256.cpp \ uint256.h \ @@ -406,6 +435,10 @@ libdash_common_a_SOURCES = \ libdash_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libdash_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libdash_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 \ @@ -425,6 +458,7 @@ libdash_util_a_SOURCES = \ if GLIBC_BACK_COMPAT libdash_util_a_SOURCES += compat/glibc_compat.cpp +AM_LDFLAGS += -Wl,--wrap=log2f -Wl,--wrap=__divmoddi4 endif # cli: shared between dash-cli and dash-qt @@ -460,7 +494,7 @@ dashd_LDADD = \ $(LIBMEMENV) \ $(LIBSECP256K1) -dashd_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) +dashd_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(BLS_LIBS) # dash-cli binary # dash_cli_SOURCES = dash-cli.cpp @@ -477,7 +511,7 @@ dash_cli_LDADD = \ $(LIBUNIVALUE) \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) -dash_cli_LDADD += $(BOOST_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS) +dash_cli_LDADD += $(BOOST_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS) $(BLS_LIBS) # # dash-tx binary # @@ -498,7 +532,7 @@ dash_tx_LDADD = \ $(LIBBITCOIN_CRYPTO) \ $(LIBSECP256K1) -dash_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) +dash_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(BLS_LIBS) # # dashconsensus library # @@ -511,7 +545,7 @@ if GLIBC_BACK_COMPAT endif libdashconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS) -libdashconsensus_la_LIBADD = $(LIBSECP256K1) +libdashconsensus_la_LIBADD = $(LIBSECP256K1) $(BLS_LIBS) libdashconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL libdashconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -539,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/dash-config.h: config/stamp-h1 diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index c9efb4bdc305..96170ff4e969 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -15,8 +15,11 @@ bench_bench_dash_SOURCES = \ bench/bench_dash.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 \ @@ -52,7 +55,7 @@ bench_bench_dash_SOURCES += bench/coin_selection.cpp bench_bench_dash_LDADD += $(LIBBITCOIN_WALLET) $(LIBBITCOIN_CRYPTO) endif -bench_bench_dash_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) +bench_bench_dash_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(BLS_LIBS) bench_bench_dash_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 c5c95fc614b0..a31cad006b5a 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -8,6 +8,7 @@ EXTRA_LIBRARIES += qt/libdashqt.a # dash qt core # QT_TS = \ + qt/locale/dash_ar.ts \ qt/locale/dash_bg.ts \ qt/locale/dash_de.ts \ qt/locale/dash_en.ts \ @@ -16,13 +17,15 @@ QT_TS = \ qt/locale/dash_fr.ts \ qt/locale/dash_it.ts \ qt/locale/dash_ja.ts \ + qt/locale/dash_ko.ts \ qt/locale/dash_nl.ts \ qt/locale/dash_pl.ts \ qt/locale/dash_pt.ts \ - qt/locale/dash_pt_BR.ts \ qt/locale/dash_ru.ts \ qt/locale/dash_sk.ts \ qt/locale/dash_sv.ts \ + qt/locale/dash_th.ts \ + qt/locale/dash_tr.ts \ qt/locale/dash_vi.ts \ qt/locale/dash_zh_CN.ts \ qt/locale/dash_zh_TW.ts @@ -31,7 +34,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 \ @@ -62,7 +64,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 \ @@ -132,7 +133,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 \ @@ -487,7 +487,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 \ @@ -645,7 +644,7 @@ qt_dash_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) endif qt_dash_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_dash_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) qt_dash_qt_LIBTOOLFLAGS = --tag CXX diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index 8df5a6091bbf..01243392a521 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -51,7 +51,7 @@ endif qt_test_test_dash_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_dash_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) qt_test_test_dash_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index fa6765a083eb..b30a02c1d1ff 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -89,6 +89,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 \ @@ -154,7 +156,7 @@ if ENABLE_WALLET test_test_dash_LDADD += $(LIBBITCOIN_WALLET) endif -test_test_dash_LDADD += $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) +test_test_dash_LDADD += $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(BLS_LIBS) test_test_dash_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static if ENABLE_ZMQ diff --git a/src/activemasternode.cpp b/src/activemasternode.cpp index 726acfa0ea1d..e57bbbb229f9 100644 --- a/src/activemasternode.cpp +++ b/src/activemasternode.cpp @@ -1,76 +1,265 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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 "activemasternode.h" -#include "masternode.h" +#include "evo/deterministicmns.h" +#include "init.h" #include "masternode-sync.h" +#include "masternode.h" #include "masternodeman.h" #include "netbase.h" #include "protocol.h" +#include "warnings.h" // Keep track of the active Masternode -CActiveMasternode activeMasternode; +CActiveMasternodeInfo activeMasternodeInfo; +CActiveLegacyMasternodeManager legacyActiveMasternodeManager; +CActiveDeterministicMasternodeManager* activeMasternodeManager; + +std::string CActiveDeterministicMasternodeManager::GetStateString() const +{ + 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_OPERATOR_KEY_CHANGED: + return "OPERATOR_KEY_CHANGED"; + 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_OPERATOR_KEY_CHANGED: + return "Operator key changed or revoked"; + case MASTERNODE_READY: + return "Ready"; + case MASTERNODE_ERROR: + return "Error. " + strError; + default: + return "Unknown"; + } +} -void CActiveMasternode::ManageState(CConnman& connman) +void CActiveDeterministicMasternodeManager::Init() { - LogPrint("masternode", "CActiveMasternode::ManageState -- Start\n"); - if(!fMasternodeMode) { - LogPrint("masternode", "CActiveMasternode::ManageState -- Not a masternode, returning\n"); + LOCK(cs_main); + + if (!fMasternodeMode) return; + + if (!deterministicMNManager->IsDeterministicMNsSporkActive()) return; + + // Check that our local network configuration is correct + if (!fListen) { + // listen option is probably overwritten by smth else, no good + state = MASTERNODE_ERROR; + strError = "Masternode must accept connections from outside. Make sure listen configuration option is not overwritten by some another parameter."; + LogPrintf("CActiveDeterministicMasternodeManager::Init -- ERROR: %s\n", strError); + 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(Params().NetworkIDString() != CBaseChainParams::REGTEST && !masternodeSync.IsBlockchainSynced()) { + if (Params().NetworkIDString() != CBaseChainParams::REGTEST) { + // Check socket connectivity + LogPrintf("CActiveDeterministicMasternodeManager::Init -- Checking inbound connection to '%s'\n", activeMasternodeInfo.service.ToString()); + SOCKET hSocket; + bool fConnected = ConnectSocket(activeMasternodeInfo.service, hSocket, nConnectTimeout) && IsSelectableSocket(hSocket); + CloseSocket(hSocket); + + if (!fConnected) { + state = MASTERNODE_ERROR; + strError = "Could not connect to " + activeMasternodeInfo.service.ToString(); + LogPrintf("CActiveDeterministicMasternodeManager::Init -- ERROR: %s\n", strError); + return; + } + } + + activeMasternodeInfo.proTxHash = mnListEntry->proTxHash; + activeMasternodeInfo.outpoint = mnListEntry->collateralOutpoint; + 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_READY) { + auto mnList = deterministicMNManager->GetListForBlock(pindexNew->GetBlockHash()); + if (!mnList.IsMNValid(mnListEntry->proTxHash)) { + // MN disappeared from MN list + state = MASTERNODE_REMOVED; + activeMasternodeInfo.proTxHash = uint256(); + activeMasternodeInfo.outpoint.SetNull(); + // MN might have reappeared in same block with a new ProTx + Init(); + } else if (mnList.GetMN(mnListEntry->proTxHash)->pdmnState->pubKeyOperator != mnListEntry->pdmnState->pubKeyOperator) { + // MN operator key changed or revoked + state = MASTERNODE_OPERATOR_KEY_CHANGED; + activeMasternodeInfo.proTxHash = uint256(); + activeMasternodeInfo.outpoint.SetNull(); + // MN might have reappeared in same block with a new ProTx + Init(); + } + } else { + // MN might have (re)appeared with a new ProTx or we've found some peers and figured out our local address + 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) { + bool empty = true; + // If we have some peers, let's try to find our local address from one of them + g_connman->ForEachNodeContinueIf(CConnman::AllNodes, [&fFoundLocal, &empty](CNode* pnode) { + empty = false; + if (pnode->addr.IsIPv4()) + 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) { + 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; } - if(nState == ACTIVE_MASTERNODE_SYNC_IN_PROCESS) { + if (nState == ACTIVE_MASTERNODE_SYNC_IN_PROCESS) { 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) { + if (eType == MASTERNODE_UNKNOWN) { ManageStateInitial(connman); } - if(eType == MASTERNODE_REMOTE) { + if (eType == MASTERNODE_REMOTE) { ManageStateRemote(); } SendMasternodePing(connman); } -std::string CActiveMasternode::GetStateString() const +std::string CActiveLegacyMasternodeManager::GetStateString() const { switch (nState) { - case ACTIVE_MASTERNODE_INITIAL: return "INITIAL"; - case ACTIVE_MASTERNODE_SYNC_IN_PROCESS: return "SYNC_IN_PROCESS"; - case ACTIVE_MASTERNODE_INPUT_TOO_NEW: return "INPUT_TOO_NEW"; - case ACTIVE_MASTERNODE_NOT_CAPABLE: return "NOT_CAPABLE"; - case ACTIVE_MASTERNODE_STARTED: return "STARTED"; - default: return "UNKNOWN"; + case ACTIVE_MASTERNODE_INITIAL: + return "INITIAL"; + case ACTIVE_MASTERNODE_SYNC_IN_PROCESS: + return "SYNC_IN_PROCESS"; + case ACTIVE_MASTERNODE_INPUT_TOO_NEW: + return "INPUT_TOO_NEW"; + case ACTIVE_MASTERNODE_NOT_CAPABLE: + return "NOT_CAPABLE"; + case ACTIVE_MASTERNODE_STARTED: + return "STARTED"; + default: + return "UNKNOWN"; } } -std::string CActiveMasternode::GetStatus() const +std::string CActiveLegacyMasternodeManager::GetStatus() const { switch (nState) { - case ACTIVE_MASTERNODE_INITIAL: return "Node just started, not yet activated"; - case ACTIVE_MASTERNODE_SYNC_IN_PROCESS: return "Sync in progress. Must wait until sync is complete to start Masternode"; - case ACTIVE_MASTERNODE_INPUT_TOO_NEW: return strprintf("Masternode input must have at least %d confirmations", Params().GetConsensus().nMasternodeMinimumConfirmations); - case ACTIVE_MASTERNODE_NOT_CAPABLE: return "Not capable masternode: " + strNotCapableReason; - case ACTIVE_MASTERNODE_STARTED: return "Masternode successfully started"; - default: return "Unknown"; + case ACTIVE_MASTERNODE_INITIAL: + return "Node just started, not yet activated"; + case ACTIVE_MASTERNODE_SYNC_IN_PROCESS: + return "Sync in progress. Must wait until sync is complete to start Masternode"; + case ACTIVE_MASTERNODE_INPUT_TOO_NEW: + return strprintf("Masternode input must have at least %d confirmations", Params().GetConsensus().nMasternodeMinimumConfirmations); + case ACTIVE_MASTERNODE_NOT_CAPABLE: + return "Not capable masternode: " + strNotCapableReason; + case ACTIVE_MASTERNODE_STARTED: + return "Masternode successfully started"; + default: + return "Unknown"; } } -std::string CActiveMasternode::GetTypeString() const +std::string CActiveLegacyMasternodeManager::GetTypeString() const { std::string strType; - switch(eType) { + switch (eType) { case MASTERNODE_REMOTE: strType = "REMOTE"; break; @@ -81,44 +270,46 @@ std::string CActiveMasternode::GetTypeString() const return strType; } -bool CActiveMasternode::SendMasternodePing(CConnman& connman) +bool CActiveLegacyMasternodeManager::SendMasternodePing(CConnman& connman) { - if(!fPingerEnabled) { - LogPrint("masternode", "CActiveMasternode::SendMasternodePing -- %s: masternode ping service is disabled, skipping...\n", GetStateString()); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) return false; + + if (!fPingerEnabled) { + 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"); + (abs(GetAdjustedTime() - nSentinelPingTime) < MASTERNODE_SENTINEL_PING_MAX_SECONDS); + 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(); @@ -126,117 +317,144 @@ bool CActiveMasternode::UpdateSentinelPing(int version) return true; } -void CActiveMasternode::ManageStateInitial(CConnman& connman) +void CActiveLegacyMasternodeManager::DoMaintenance(CConnman& connman) { - LogPrint("masternode", "CActiveMasternode::ManageStateInitial -- status = %s, type = %s, pinger enabled = %d\n", GetStatus(), GetTypeString(), fPingerEnabled); + if (ShutdownRequested()) return; + + ManageState(connman); +} + +void CActiveLegacyMasternodeManager::ManageStateInitial(CConnman& connman) +{ + 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); - if(!fFoundLocal) { + 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) { + 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 (Params().NetworkIDString() == CBaseChainParams::MAIN) { + 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; + + LogPrint("masternode", "CActiveLegacyMasternodeManager::ManageStateRemote -- Start status = %s, type = %s, pinger enabled = %d, keyIDOperator = %s\n", + GetStatus(), GetTypeString(), fPingerEnabled, activeMasternodeInfo.legacyKeyIDOperator.ToString()); - mnodeman.CheckMasternode(pubKeyMasternode, true); + mnodeman.CheckMasternode(activeMasternodeInfo.legacyKeyIDOperator, true); masternode_info_t infoMn; - if(mnodeman.GetMasternodeInfo(pubKeyMasternode, infoMn)) { - if(infoMn.nProtocolVersion != PROTOCOL_VERSION) { + 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)) { + 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; } - if(nState != ACTIVE_MASTERNODE_STARTED) { - LogPrintf("CActiveMasternode::ManageStateRemote -- STARTED!\n"); - outpoint = infoMn.outpoint; - service = infoMn.addr; + auto dmn = deterministicMNManager->GetListAtChainTip().GetMNByCollateral(infoMn.outpoint); + 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("CActiveLegacyMasternodeManager::ManageStateRemote -- STARTED!\n"); + activeMasternodeInfo.outpoint = infoMn.outpoint; + activeMasternodeInfo.service = infoMn.addr; fPingerEnabled = true; nState = ACTIVE_MASTERNODE_STARTED; } - } - else { + } 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 7617b434d3de..e83b4edba7b3 100644 --- a/src/activemasternode.h +++ b/src/activemasternode.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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. @@ -9,8 +9,14 @@ #include "key.h" #include "net.h" #include "primitives/transaction.h" +#include "validationinterface.h" -class CActiveMasternode; +#include "evo/deterministicmns.h" +#include "evo/providertx.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; @@ -18,10 +24,58 @@ 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 + uint256 proTxHash; + 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_OPERATOR_KEY_CHANGED, + 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 { @@ -45,27 +99,16 @@ 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() - : eType(MASTERNODE_UNKNOWN), - fPingerEnabled(false), - pubKeyMasternode(), - keyMasternode(), - outpoint(), - service(), - nState(ACTIVE_MASTERNODE_INITIAL) - {} + CActiveLegacyMasternodeManager() : + eType(MASTERNODE_UNKNOWN), + fPingerEnabled(false), + nState(ACTIVE_MASTERNODE_INITIAL) + { + } /// Manage state of active Masternode void ManageState(CConnman& connman); @@ -76,6 +119,8 @@ class CActiveMasternode bool UpdateSentinelPing(int version); + void DoMaintenance(CConnman& connman); + private: void ManageStateInitial(CConnman& connman); void ManageStateRemote(); diff --git a/src/bench/bench_dash.cpp b/src/bench/bench_dash.cpp index bd768180c6d8..a1d208502470 100644 --- a/src/bench/bench_dash.cpp +++ b/src/bench/bench_dash.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/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..bb44f50296ac --- /dev/null +++ b/src/bls/bls.cpp @@ -0,0 +1,472 @@ +// 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/mt_pooled_secure.h" +#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 + +static std::once_flag init_flag; +static mt_pooled_secure_allocator* secure_allocator_instance; +static void create_secure_allocator() +{ + // make sure LockedPoolManager is initialized first (ensures destruction order) + LockedPoolManager::Instance(); + + // static variable in function scope ensures it's initialized when first accessed + // and destroyed before LockedPoolManager + static mt_pooled_secure_allocator a(sizeof(bn_t) + sizeof(size_t)); + secure_allocator_instance = &a; +} + +static mt_pooled_secure_allocator& get_secure_allocator() +{ + std::call_once(init_flag, create_secure_allocator); + return *secure_allocator_instance; +} + +static void* secure_allocate(size_t n) +{ + uint8_t* ptr = get_secure_allocator().allocate(n + sizeof(size_t)); + *(size_t*)ptr = n; + return ptr + sizeof(size_t); +} + +static void secure_free(void* p) +{ + if (!p) { + return; + } + + uint8_t* ptr = (uint8_t*)p - sizeof(size_t); + size_t n = *(size_t*)ptr; + return get_secure_allocator().deallocate(ptr, n); +} +#endif + +bool BLSInit() +{ +#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..ba614c623116 --- /dev/null +++ b/src/bls/bls.h @@ -0,0 +1,312 @@ +// 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_H +#define DASH_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!=; + + CBLSId() {} + + 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!=; + + CBLSSecretKey() {} + + 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!=; + + CBLSPublicKey() {} + + 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!=; + using CBLSWrapper::CBLSWrapper; + + CBLSSignature() {} + 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 // DASH_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..5c1767126b3e --- /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 DASH_CRYPTO_BLS_WORKER_H +#define DASH_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 //DASH_CRYPTO_BLS_WORKER_H diff --git a/src/chainparams.cpp b/src/chainparams.cpp index e01c9c8c09d2..77f215fad8cb 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -106,6 +106,61 @@ static CBlock FindDevNetGenesisBlock(const Consensus::Params& params, const CBlo assert(false); } +// this one is for testing only +static Consensus::LLMQParams llmq10_60 = { + .type = Consensus::LLMQ_10_60, + .name = "llmq_10", + .size = 10, + .minSize = 6, + .threshold = 6, + + .dkgInterval = 24, // one DKG per hour + .dkgPhaseBlocks = 2, + .dkgMiningWindowStart = 10, // dkgPhaseBlocks * 5 = after finalization + .dkgMiningWindowEnd = 18, +}; + +static Consensus::LLMQParams llmq50_60 = { + .type = Consensus::LLMQ_50_60, + .name = "llmq_50_60", + .size = 50, + .minSize = 40, + .threshold = 30, + + .dkgInterval = 24, // one DKG per hour + .dkgPhaseBlocks = 2, + .dkgMiningWindowStart = 10, // dkgPhaseBlocks * 5 = after finalization + .dkgMiningWindowEnd = 18, +}; + +static Consensus::LLMQParams llmq400_60 = { + .type = Consensus::LLMQ_400_60, + .name = "llmq_400_51", + .size = 400, + .minSize = 300, + .threshold = 240, + + .dkgInterval = 24 * 12, // one DKG every 12 hours + .dkgPhaseBlocks = 4, + .dkgMiningWindowStart = 20, // dkgPhaseBlocks * 5 = after finalization + .dkgMiningWindowEnd = 28, +}; + +// Used for deployment and min-proto-version signalling, so it needs a higher threshold +static Consensus::LLMQParams llmq400_85 = { + .type = Consensus::LLMQ_400_85, + .name = "llmq_400_85", + .size = 400, + .minSize = 350, + .threshold = 340, + + .dkgInterval = 24 * 24, // one DKG every 24 hours + .dkgPhaseBlocks = 4, + .dkgMiningWindowStart = 20, // dkgPhaseBlocks * 5 = after finalization + .dkgMiningWindowEnd = 48, // give it a larger mining window to make sure it is mined +}; + + /** * Main network */ @@ -174,11 +229,18 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nWindowSize = 4032; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nThreshold = 3226; // 80% of 4032 + // Deployment of DIP0003 + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nStartTime = 1546300800; // Jan 1st, 2019 + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nTimeout = 1577836800; // Jan 1st, 2020 + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nWindowSize = 4032; + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nThreshold = 3226; // 80% of 4032 + // The best chain should have at least this much work. - consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000081021b74f9f47bbd7bc"); // 888900 + consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000cca0a6a35241029410f"); // 967800 // By default assume that the signatures in ancestors of this block are valid. - consensus.defaultAssumeValid = uint256S("0x0000000000000026c29d576073ab51ebd1d3c938de02e9a44c7ee9e16f82db28"); // 888900 + consensus.defaultAssumeValid = uint256S("0x0000000000000024e26c7df7e46d673724d223cf4ca2b2adc21297cc095600f4"); // 967800 /** * The message start string is designed to be unlikely to occur in normal data. @@ -202,7 +264,6 @@ class CMainParams : public CChainParams { vSeeds.push_back(CDNSSeedData("dash.org", "dnsseed.dash.org")); vSeeds.push_back(CDNSSeedData("dashdot.io", "dnsseed.dashdot.io")); vSeeds.push_back(CDNSSeedData("masternode.io", "dnsseed.masternode.io")); - vSeeds.push_back(CDNSSeedData("dashpay.io", "dnsseed.dashpay.io")); // Dash addresses start with 'X' base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,76); @@ -220,9 +281,15 @@ class CMainParams : public CChainParams { vFixedSeeds = std::vector(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); + // long living quorum params + consensus.llmqs[Consensus::LLMQ_50_60] = llmq50_60; + consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60; + consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85; + fMiningRequiresPeers = true; fDefaultConsistencyChecks = false; fRequireStandard = true; + fRequireRoutableExternalIP = true; fMineBlocksOnDemand = false; fAllowMultipleAddressesFromGroup = false; fAllowMultiplePorts = false; @@ -230,7 +297,10 @@ class CMainParams : public CChainParams { nPoolMaxTransactions = 3; nFulfilledRequestExpireTime = 60*60; // fulfilled requests expire in 1 hour - strSporkAddress = "Xgtyuk76vhuFW2iT7UAiHgNdWXCf3J34wh"; + vSporkAddresses = {"Xgtyuk76vhuFW2iT7UAiHgNdWXCf3J34wh"}; + nMinSporkKeys = 1; + fBIP9CheckMasternodesUpgraded = true; + consensus.fLLMQAllowDummyCommitments = false; checkpointData = (CCheckpointData) { boost::assign::map_list_of @@ -256,11 +326,12 @@ class CMainParams : public CChainParams { ( 523930, uint256S("0x0000000000000bccdb11c2b1cfb0ecab452abf267d89b7f46eaf2d54ce6e652c")) ( 750000, uint256S("0x00000000000000b4181bbbdddbae464ce11fede5d0292fb63fdede1e7c8ab21c")) ( 888900, uint256S("0x0000000000000026c29d576073ab51ebd1d3c938de02e9a44c7ee9e16f82db28")) + ( 967800, uint256S("0x0000000000000024e26c7df7e46d673724d223cf4ca2b2adc21297cc095600f4")) }; chainTxData = ChainTxData{ - 1529305236, // * UNIX timestamp of last known number of transactions - 6155435, // * total number of transactions between genesis and that timestamp + 1541738255, // * UNIX timestamp of last known number of transactions + 8822293, // * total number of transactions between genesis and that timestamp // (the tx=... number in the SetBestChain debug.log lines) 0.1 // * estimated number of transactions per second after that timestamp }; @@ -285,7 +356,7 @@ class CTestNetParams : public CChainParams { consensus.nBudgetPaymentsCycleBlocks = 50; consensus.nBudgetPaymentsWindowBlocks = 10; consensus.nSuperblockStartBlock = 4200; // NOTE: Should satisfy nSuperblockStartBlock > nBudgetPeymentsStartBlock - consensus.nSuperblockStartHash = uint256S("00000000cffabc0f646867fba0550afd6e30e0f4b0fc54e34d3e101a1552df5d"); + consensus.nSuperblockStartHash = uint256(); // do not check this on testnet consensus.nSuperblockCycle = 24; // Superblocks can be issued hourly on testnet consensus.nGovernanceMinQuorum = 1; consensus.nGovernanceFilterElements = 500; @@ -300,8 +371,8 @@ class CTestNetParams : public CChainParams { consensus.nPowTargetSpacing = 2.5 * 60; // Dash: 2.5 minutes consensus.fPowAllowMinDifficultyBlocks = true; consensus.fPowNoRetargeting = false; - consensus.nPowKGWHeight = 4001; // nPowKGWHeight >= nPowDGWHeight means "no KGW" - consensus.nPowDGWHeight = 4001; + consensus.nPowKGWHeight = 4002; // nPowKGWHeight >= nPowDGWHeight means "no KGW" + consensus.nPowDGWHeight = 4002; consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; @@ -310,28 +381,35 @@ class CTestNetParams : public CChainParams { // Deployment of BIP68, BIP112, and BIP113. consensus.vDeployments[Consensus::DEPLOYMENT_CSV].bit = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1506556800; // September 28th, 2017 - consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1538092800; // September 28th, 2018 + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nStartTime = 1544655600; // Dec 13th, 2018 + consensus.vDeployments[Consensus::DEPLOYMENT_CSV].nTimeout = 1576191600; // Dec 13th, 2019 // Deployment of DIP0001 consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].bit = 1; - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nStartTime = 1505692800; // Sep 18th, 2017 - consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nTimeout = 1537228800; // Sep 18th, 2018 + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nStartTime = 1544655600; // Dec 13th, 2018 + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nTimeout = 1576191600; // Dec 13th, 2019 consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nWindowSize = 100; consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nThreshold = 50; // 50% of 100 // 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].nStartTime = 1544655600; // Dec 13th, 2018 + consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nTimeout = 1576191600; // Dec 13th, 2019 consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nWindowSize = 100; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nThreshold = 50; // 50% of 100 + // Deployment of DIP0003 + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nStartTime = 1544655600; // Dec 13th, 2018 + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nTimeout = 1576191600; // Dec 13th, 2019 + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nWindowSize = 100; + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nThreshold = 50; // 50% of 100 + // The best chain should have at least this much work. - consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000003be69c34b1244f"); // 143200 + consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000000000003cd72a542"); // 4000 // By default assume that the signatures in ancestors of this block are valid. - consensus.defaultAssumeValid = uint256S("0x0000000004a7878409189b7a8f75b3815d9b8c45ee8f79955a6c727d83bddb04"); // 143200 + consensus.defaultAssumeValid = uint256S("0x00000ce22113f3eb8636e225d6a1691e132fdd587aed993e1bc9b07a0235eea4"); // 4000 pchMessageStart[0] = 0xce; pchMessageStart[1] = 0xe2; @@ -347,6 +425,8 @@ class CTestNetParams : public CChainParams { assert(genesis.hashMerkleRoot == uint256S("0xe0028eb9648db56b1ac77cf090b99048a8007e2bb64b68f092c03c7f56a662c7")); 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("dashdot.io", "testnet-seed.dashdot.io")); @@ -366,9 +446,15 @@ class CTestNetParams : public CChainParams { // Testnet Dash BIP44 coin type is '1' (All coin's testnet default) nExtCoinType = 1; + // long living quorum params + consensus.llmqs[Consensus::LLMQ_50_60] = llmq50_60; + consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60; + consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85; + fMiningRequiresPeers = true; fDefaultConsistencyChecks = false; fRequireStandard = false; + fRequireRoutableExternalIP = true; fMineBlocksOnDemand = false; fAllowMultipleAddressesFromGroup = false; fAllowMultiplePorts = false; @@ -376,20 +462,21 @@ class CTestNetParams : public CChainParams { nPoolMaxTransactions = 3; nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes - strSporkAddress = "yjPtiKh2uwk3bDutTEA2q9mCtXyiZRWn55"; + vSporkAddresses = {"yjPtiKh2uwk3bDutTEA2q9mCtXyiZRWn55"}; + nMinSporkKeys = 1; + fBIP9CheckMasternodesUpgraded = true; + consensus.fLLMQAllowDummyCommitments = true; checkpointData = (CCheckpointData) { boost::assign::map_list_of ( 261, uint256S("0x00000c26026d0815a7e2ce4fa270775f61403c040647ff2c3091f99e894a4618")) ( 1999, uint256S("0x00000052e538d27fa53693efe6fb6892a0c1d26c0235f599171c48a3cce553b1")) ( 2999, uint256S("0x0000024bc3f4f4cb30d29827c13d921ad77d2c6072e586c7f60d83c2722cdcc5")) - ( 100000, uint256S("0x0000000003aa53e24b6e60ef97642e4193611f2bcb75ea1fa8105f0b5ffd5242")) - ( 143200, uint256S("0x0000000004a7878409189b7a8f75b3815d9b8c45ee8f79955a6c727d83bddb04")) }; - chainTxData = ChainTxData{ - 1529294335, // * UNIX timestamp of last known number of transactions - 5810120, // * total number of transactions between genesis and that timestamp + chainTxData = ChainTxData{ + 1544707462, // * UNIX timestamp of last known number of transactions + 4100, // * total number of transactions between genesis and that timestamp // (the tx=... number in the SetBestChain debug.log lines) 0.01 // * estimated number of transactions per second after that timestamp }; @@ -456,6 +543,13 @@ class CDevNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nWindowSize = 100; consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nThreshold = 50; // 50% of 100 + // Deployment of DIP0003 + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nStartTime = 1535752800; // Sep 1st, 2018 + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nTimeout = 1567288800; // Sep 1st, 2019 + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nWindowSize = 100; + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nThreshold = 50; // 50% of 100 + // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000000000000000"); @@ -496,6 +590,11 @@ class CDevNetParams : public CChainParams { // Testnet Dash BIP44 coin type is '1' (All coin's testnet default) nExtCoinType = 1; + // long living quorum params + consensus.llmqs[Consensus::LLMQ_50_60] = llmq50_60; + consensus.llmqs[Consensus::LLMQ_400_60] = llmq400_60; + consensus.llmqs[Consensus::LLMQ_400_85] = llmq400_85; + fMiningRequiresPeers = true; fDefaultConsistencyChecks = false; fRequireStandard = false; @@ -506,7 +605,11 @@ class CDevNetParams : public CChainParams { nPoolMaxTransactions = 3; nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes - strSporkAddress = "yjPtiKh2uwk3bDutTEA2q9mCtXyiZRWn55"; + vSporkAddresses = {"yjPtiKh2uwk3bDutTEA2q9mCtXyiZRWn55"}; + nMinSporkKeys = 1; + // devnets are started with no blocks and no MN, so we can't check for upgraded MN (as there are none) + fBIP9CheckMasternodesUpgraded = false; + consensus.fLLMQAllowDummyCommitments = true; checkpointData = (CCheckpointData) { boost::assign::map_list_of @@ -520,6 +623,13 @@ class CDevNetParams : public CChainParams { 0.01 // * estimated number of transactions per second }; } + + void UpdateSubsidyAndDiffParams(int nMinimumDifficultyBlocks, int nHighSubsidyBlocks, int nHighSubsidyFactor) + { + consensus.nMinimumDifficultyBlocks = nMinimumDifficultyBlocks; + consensus.nHighSubsidyBlocks = nHighSubsidyBlocks; + consensus.nHighSubsidyFactor = nHighSubsidyFactor; + } }; static CDevNetParams *devNetParams; @@ -572,6 +682,9 @@ 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_DIP0003].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nStartTime = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nTimeout = 999999999999ULL; // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); @@ -597,6 +710,7 @@ class CRegTestParams : public CChainParams { fMiningRequiresPeers = false; fDefaultConsistencyChecks = true; fRequireStandard = false; + fRequireRoutableExternalIP = false; fMineBlocksOnDemand = true; fAllowMultipleAddressesFromGroup = true; fAllowMultiplePorts = true; @@ -604,7 +718,11 @@ class CRegTestParams : public CChainParams { nFulfilledRequestExpireTime = 5*60; // fulfilled requests expire in 5 minutes // privKey: cP4EKFyJsHT39LDqgdcB43Y3YXjNyjb5Fuas1GQSeAtjnZWmZEQK - strSporkAddress = "yj949n1UH6fDhw6HtVE5VMj2iSTaSWBMcW"; + vSporkAddresses = {"yj949n1UH6fDhw6HtVE5VMj2iSTaSWBMcW"}; + nMinSporkKeys = 1; + // regtest usually has no masternodes in most tests, so don't check for upgraged MNs + fBIP9CheckMasternodesUpgraded = false; + consensus.fLLMQAllowDummyCommitments = true; checkpointData = (CCheckpointData){ boost::assign::map_list_of @@ -630,13 +748,24 @@ class CRegTestParams : public CChainParams { // Regtest Dash BIP44 coin type is '1' (All coin's testnet default) nExtCoinType = 1; - } + + // long living quorum params + consensus.llmqs[Consensus::LLMQ_10_60] = llmq10_60; + consensus.llmqs[Consensus::LLMQ_50_60] = llmq50_60; + } void UpdateBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout) { consensus.vDeployments[d].nStartTime = nStartTime; consensus.vDeployments[d].nTimeout = nTimeout; } + + void UpdateBudgetParameters(int nMasternodePaymentsStartBlock, int nBudgetPaymentsStartBlock, int nSuperblockStartBlock) + { + consensus.nMasternodePaymentsStartBlock = nMasternodePaymentsStartBlock; + consensus.nBudgetPaymentsStartBlock = nBudgetPaymentsStartBlock; + consensus.nSuperblockStartBlock = nSuperblockStartBlock; + } }; static CRegTestParams regTestParams; @@ -665,7 +794,9 @@ CChainParams& Params(const std::string& chain) void SelectParams(const std::string& network) { if (network == CBaseChainParams::DEVNET) { - devNetParams = new CDevNetParams(); + devNetParams = (CDevNetParams*)new uint8_t[sizeof(CDevNetParams)]; + memset(devNetParams, 0, sizeof(CDevNetParams)); + new (devNetParams) CDevNetParams(); } SelectBaseParams(network); @@ -676,3 +807,14 @@ void UpdateRegtestBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, { regTestParams.UpdateBIP9Parameters(d, nStartTime, nTimeout); } + +void UpdateRegtestBudgetParameters(int nMasternodePaymentsStartBlock, int nBudgetPaymentsStartBlock, int nSuperblockStartBlock) +{ + regTestParams.UpdateBudgetParameters(nMasternodePaymentsStartBlock, nBudgetPaymentsStartBlock, nSuperblockStartBlock); +} + +void UpdateDevnetSubsidyAndDiffParams(int nMinimumDifficultyBlocks, int nHighSubsidyBlocks, int nHighSubsidyFactor) +{ + assert(devNetParams); + devNetParams->UpdateSubsidyAndDiffParams(nMinimumDifficultyBlocks, nHighSubsidyBlocks, nHighSubsidyFactor); +} diff --git a/src/chainparams.h b/src/chainparams.h index 034e186bc73d..52606b17561c 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,9 @@ class CChainParams const ChainTxData& TxData() const { return chainTxData; } int PoolMaxTransactions() const { return nPoolMaxTransactions; } int FulfilledRequestExpireTime() const { return nFulfilledRequestExpireTime; } - const std::string& SporkAddress() const { return strSporkAddress; } + const std::vector& SporkAddresses() const { return vSporkAddresses; } + int MinSporkKeys() const { return nMinSporkKeys; } + bool BIP9CheckMasternodesUpgraded() const { return fBIP9CheckMasternodesUpgraded; } protected: CChainParams() {} @@ -106,6 +110,7 @@ class CChainParams bool fMiningRequiresPeers; bool fDefaultConsistencyChecks; bool fRequireStandard; + bool fRequireRoutableExternalIP; bool fMineBlocksOnDemand; bool fAllowMultipleAddressesFromGroup; bool fAllowMultiplePorts; @@ -113,7 +118,9 @@ class CChainParams ChainTxData chainTxData; int nPoolMaxTransactions; int nFulfilledRequestExpireTime; - std::string strSporkAddress; + std::vector vSporkAddresses; + int nMinSporkKeys; + bool fBIP9CheckMasternodesUpgraded; }; /** @@ -138,4 +145,14 @@ void SelectParams(const std::string& chain); */ void UpdateRegtestBIP9Parameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout); +/** + * Allows modifying the budget regtest parameters. + */ +void UpdateRegtestBudgetParameters(int nMasternodePaymentsStartBlock, int nBudgetPaymentsStartBlock, int nSuperblockStartBlock); + +/** + * Allows modifying the subsidy and difficulty devnet parameters. + */ +void UpdateDevnetSubsidyAndDiffParams(int nMinimumDifficultyBlocks, int nHighSubsidyBlocks, int nHighSubsidyFactor); + #endif // BITCOIN_CHAINPARAMS_H diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 6f07818c5a1e..80a00654dfde 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -109,7 +109,10 @@ void SelectBaseParams(const std::string& chain) if (chain == CBaseChainParams::DEVNET) { std::string devNetName = GetDevNetName(); assert(!devNetName.empty()); - devNetParams = new CBaseDevNetParams(devNetName); + + devNetParams = (CBaseDevNetParams*)new uint8_t[sizeof(CBaseDevNetParams)]; + memset(devNetParams, 0, sizeof(CBaseDevNetParams)); + new (devNetParams) CBaseDevNetParams(devNetName); } pCurrentBaseParams = &BaseParams(chain); diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h index a6a10d20a472..c2853703745e 100644 --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -8,311 +8,318 @@ * 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,0x05,0x02,0x42,0x90}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x84,0x9f,0x06}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x84,0x9f,0x08}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x84,0x9f,0x09}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x84,0x9f,0x0a}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x09,0x63,0x50}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x84,0xbf,0x6d}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x84,0xbf,0xd3}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x84,0xbf,0xd5}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x84,0xbf,0xd8}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0x86,0x73,0x25}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0xbd,0x81,0x29}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0xbd,0xab,0x40}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0xc8,0x17,0x21}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x05,0xc8,0x35,0x60}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x12,0xdd,0x60,0x95}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xaf,0x00,0x6f}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xaf,0x00,0x70}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xaf,0x00,0x71}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xaf,0x00,0x72}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xaf,0x00,0x73}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xb6,0x80,0x15}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xb6,0x80,0x16}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xb6,0x80,0x17}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xb6,0x80,0x18}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xb6,0x80,0x19}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xe3,0xa0,0x4c}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x1f,0x0d,0xd9,0x34}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x1f,0x0d,0xd9,0x35}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x1f,0x0d,0xd9,0x36}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x1f,0x0d,0xd9,0x37}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x1f,0x0e,0x86,0xed}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xe3,0xa3,0x17}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x17,0xe3,0xa3,0x34}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x1b,0x66,0x72,0x3d}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x1f,0x1f,0x49,0x52}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x22,0xc4,0x60,0x63}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x22,0xc7,0x82,0x45}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x22,0xce,0x00,0x3f}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x22,0xe1,0x65,0x61}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xa7,0xc0,0xa5}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xc4,0xf2,0xe3}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x23,0xc5,0xbb,0x01}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0x61,0xe3,0x15}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0x78,0xa1,0x91}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0x9d,0xc7,0xd2}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0xc8,0x7a,0xdf}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0xdd,0xc0,0xdc}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0xdd,0xc2,0x2a}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0xdd,0xc2,0xdc}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0xdd,0xc2,0xde}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x26,0x7f,0xa9,0x93}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x25,0xdd,0xc2,0x2b}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x28,0x71,0x06,0x2c}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2b,0xe5,0x4d,0x2e}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x20,0xc0,0x45}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x3a,0x30,0x11}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x20,0xce,0xb9}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x4c,0x40,0x06}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x4c,0x80,0x3d}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0x4a,0x17,0xcf}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0x1c,0x6d,0xc3}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0x25,0x9e,0xb1}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2e,0xa3,0xa6,0x39}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0x34,0xe5,0x48}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0x4b,0x31,0xe4}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0x5a,0x0e,0x9c}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0x5a,0x35,0x67}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0x5b,0xd9,0x31}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0x64,0x19,0x76}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x32,0x03,0x4a,0x51}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x32,0x03,0x4a,0x52}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x32,0x03,0x4a,0x54}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x32,0x03,0x4a,0x55}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x32,0x63,0x00,0xf3}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0x0e,0xc0,0xd3}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0x2c,0x80,0x19}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0x4e,0xa5,0x5b}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0x62,0x42,0x5e}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0x68,0x60,0xcf}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2f,0xf4,0x66,0x51}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x33,0x0f,0x2a,0x8f}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x33,0x26,0x69,0x24}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x33,0x26,0x80,0xfb}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x33,0xff,0x20,0xd5}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0x4f,0x70,0x16}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0x50,0x37,0xdf}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xaa,0x66,0xfd}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xbb,0x87,0xbb}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xeb,0x24,0xbb}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xf2,0x25,0x08}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xf2,0x27,0x60}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x36,0x46,0xe7,0xc3}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x36,0xac,0x91,0xa2}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3e,0x8a,0x03,0xd6}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3e,0x8a,0x08,0x07}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3e,0xd2,0x05,0xf6}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3e,0xd2,0xa1,0xf8}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x40,0x13,0xd3,0x29}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3e,0x8a,0x08,0x67}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3e,0x92,0x0d,0x32}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3f,0x8e,0xfd,0x55}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x40,0x22,0xda,0x30}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x40,0x8c,0x9d,0x72}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x40,0x8c,0x9e,0x60}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x40,0xd2,0x29,0xe2}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x40,0x8c,0x9f,0xe2}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x42,0x17,0xf2,0x56}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x42,0x17,0xf2,0x59}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x42,0x17,0xf2,0x5a}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x42,0x17,0xf2,0x5b}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x42,0xac,0x0b,0xf9}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x42,0xac,0x0c,0xf4}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x42,0xac,0x0b,0x55}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x42,0xac,0x1b,0x46}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x42,0xac,0x21,0x6c}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x0c,0x43,0xab}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x0c,0x43,0xc1}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x0c,0x43,0xc5}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x1b,0xad,0xa5}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x1b,0xad,0xa6}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x1e,0xc1,0x9a}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x5a,0xba,0x09}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x5a,0xba,0x0a}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x5a,0xba,0x0d}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x48,0x12,0xd5,0x6b}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4c,0x4a,0xb2,0xdd}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4d,0x49,0x45,0xbe}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x33,0x0d,0x5a}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x33,0x0d,0x65}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x33,0x14,0x46}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x45,0x33,0x14,0x47}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4a,0xcf,0xe4,0xb4}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4b,0x7f,0x04,0xf8}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4c,0x4a,0xdb,0x94}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4d,0x51,0xe2,0x92}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4e,0x2e,0xea,0xf1}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4e,0x29,0xcf,0x46}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4e,0x53,0x13,0x00}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x4e,0x85,0xa9,0x46}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x50,0x78,0x1b,0x96}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x50,0xd3,0xc5,0xeb}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x51,0xa9,0x8b,0x95}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x51,0xa9,0xbc,0x58}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x51,0xa9,0xcd,0xdc}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x51,0xa9,0xe5,0x54}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x51,0xab,0x02,0xf5}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x51,0xe5,0x77,0xd8}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0x76,0xe3,0x34}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xa5,0x1d,0x67}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xd3,0x15,0x80}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xd3,0x15,0x83}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xd3,0x15,0xad}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xd3,0x22,0x67}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xef,0x4f,0x58}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x53,0x01,0x63,0x01}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x53,0x01,0x63,0x06}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0x19,0xc2,0xcc}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xd3,0x15,0x88}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x52,0xd3,0x15,0x8b}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x54,0xea,0x60,0x52}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x54,0xea,0x60,0x56}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x54,0xea,0x60,0x57}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0x19,0xc7,0xd7}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xb8,0xfe,0x1d}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xb8,0xfe,0xb5}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xd9,0xaa,0xce}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xd9,0xab,0xf3}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xff,0x01,0x41}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x55,0xff,0x04,0xd4}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x56,0x69,0xeb,0x67}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x57,0x6a,0x3f,0x34}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x57,0x75,0xfd,0x37}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x58,0x77,0xb3,0xb2}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x58,0x77,0xb3,0xb3}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x58,0xa6,0xc8,0x99}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x59,0x24,0xdc,0xf1}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x59,0x26,0x90,0x47}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x59,0x28,0x00,0x0e}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x59,0x28,0x72,0x45}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x59,0x2d,0x43,0x21}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x59,0xa3,0x86,0xe3}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5a,0xbb,0x05,0x3d}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5b,0x7b,0xc3,0x53}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5b,0x7b,0xc9,0x5e}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x59,0xee,0xb5,0x4a}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5b,0xdb,0xef,0x52}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5b,0xdb,0xef,0x53}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5c,0x3f,0x39,0x1c}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5c,0x3f,0x39,0x78}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5d,0x5a,0xcd,0x50}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5d,0x68,0xd5,0xd0}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5d,0x9e,0xd8,0x99}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5d,0xb9,0x67,0x46}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5d,0xb9,0x67,0x47}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0x82,0x68,0xa9}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0x9c,0xae,0xa6}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0xb0,0xef,0x3e}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0xb1,0xaa,0xdf}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0xb1,0xe0,0xe1}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0xb1,0xe1,0x73}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0xb1,0xe1,0xd2}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0xb1,0xe8,0x21}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5e,0xb1,0xfa,0x3d}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0x2b,0x8b,0xa2}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xb5,0xe2,0x2f}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xb7,0x32,0x61}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xb7,0x33,0x62}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xb7,0x33,0x8d}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xb7,0x35,0x38}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xb7,0x35,0x60}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xd7,0x2d,0xe1}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xb7,0x35,0x86}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xd8,0x0b,0x39}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x5f,0xd8,0x93,0x2f}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x60,0x7e,0x60,0xa4}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x65,0x84,0x19,0xbc}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x67,0x0b,0x40,0x5e}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x68,0x80,0xe2,0x46}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x68,0x80,0xe2,0x48}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x68,0xee,0xc0,0x92}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x65,0x25,0x60,0x00}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x65,0x25,0x60,0x60}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x68,0xc4,0xa9,0x51}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x68,0xdf,0x06,0xa4}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6b,0x06,0x8c,0x23}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6b,0x06,0x8c,0x37}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6b,0x06,0xaf,0xce}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6b,0x06,0xaf,0x96}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6b,0x9b,0x76,0x89}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6b,0xbf,0x65,0xd4}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6c,0x3d,0xe0,0x16}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6c,0x3d,0xe0,0x21}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x6e,0x0a,0x19,0x4c}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x74,0x3e,0xed,0xbe}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x76,0x1f,0x26,0xe8}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x78,0x84,0x1b,0x27}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x73,0x9f,0x56,0x76}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x7a,0x6a,0x37,0xd6}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x7a,0x72,0xad,0xda}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x7b,0xc1,0x40,0xa6}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x7d,0x59,0x3e,0x24}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x7d,0x59,0x3e,0x25}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x82,0xb9,0xfb,0x45}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x82,0xb9,0xfb,0x71}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x85,0x82,0x66,0x16}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0xa2,0x0b,0x52}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8d,0xff,0xb8,0xbf}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8e,0x2c,0xe5,0x3d}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8e,0x36,0xa2,0xb3}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x95,0x38,0x80,0xc7}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9a,0x7f,0x39,0x42}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8b,0x63,0xc1,0x17}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x94,0x42,0x32,0x2a}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x97,0xec,0x0a,0x6d}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9a,0x7f,0x39,0xf0}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9a,0x7f,0x39,0xfe}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9a,0x7f,0x3b,0x1b}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9a,0x7f,0x3c,0x08}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9b,0xfe,0x20,0x8b}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9f,0x45,0x9b,0x5e}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9f,0x59,0x20,0x0c}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9f,0x64,0x13,0xa6}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9f,0x64,0x13,0xa7}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa2,0xdd,0xc3,0x11}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa2,0xdd,0xc3,0xf3}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa2,0xfa,0x91,0x6b}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x9f,0xcb,0x14,0x83}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa3,0x2c,0xa7,0x90}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa3,0x2c,0xa7,0xed}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa3,0x2c,0xab,0x33}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa3,0xac,0x80,0xbb}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa3,0xac,0x81,0x0d}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa4,0x84,0x2a,0xa2}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa7,0x58,0x0f,0x62}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa8,0xeb,0x60,0xca}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa3,0x2c,0xa8,0xbf}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa7,0x58,0x0f,0x61}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa8,0xeb,0x60,0xcb}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa8,0xeb,0x60,0xcd}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa8,0xeb,0x63,0x2d}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa8,0xeb,0x63,0x2f}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xaa,0x4b,0xa2,0x3c}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xaa,0x4b,0xa2,0xdb}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xaa,0x4b,0xa3,0x6c}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xac,0x51,0xb1,0x2a}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xac,0x56,0x79,0x94}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xac,0x68,0x40,0x0d}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xac,0x6e,0x06,0xa7}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xac,0x6e,0x05,0x62}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xac,0x6e,0x06,0xa9}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xad,0xd4,0xf1,0x32}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb0,0x1f,0x91,0x32}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0x3e,0x80,0x32}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb0,0x7a,0x14,0xcf}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0x3e,0xa0,0x1d}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0x3e,0xc0,0x2a}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0xd1,0x32,0x1e}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0xee,0x2a,0x07}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0xfe,0x17,0xa0}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0xfe,0x20,0x30}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x02,0x52,0x89}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x16,0xae,0x25}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x1a,0x7e,0xfa}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x1c,0x65,0x5b}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x1c,0x65,0x85}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x23,0x40,0xd9}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x23,0x43,0x75}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x2b,0xd2,0x7d}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x3a,0xc2,0x71}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x3a,0xe0,0xea}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x40,0x68,0xdd}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x40,0x68,0xde}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x40,0x68,0xdf}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x52,0x16,0xe4}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x6a,0x79,0xa3}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x6a,0x7a,0x2e}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x77,0x55,0x1e}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x85,0x25,0x69}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x8b,0xed,0x9a}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x8d,0x1a,0x6a}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x8e,0xd4,0x90}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x9c,0xb2,0x4a}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x9c,0xb2,0x7d}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x9c,0xb2,0xd8}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x9c,0xb2,0xdd}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x9c,0xb2,0xda}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0x9c,0xb2,0xde}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xa5,0xa8,0x15}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xa5,0xa8,0x16}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xa5,0xa8,0x17}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xa5,0xa8,0x19}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xa5,0xa8,0x1b}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xa6,0xef,0x30}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xa6,0xef,0xa2}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xa6,0xef,0xbb}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xa6,0xef,0xbf}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xa8,0x08,0x90}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xb7,0x61,0x83}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xb9,0x28,0x0d}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xcb,0x75,0xe5}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xd4,0x2c,0xfa}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xd5,0x25,0x01}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xd5,0x25,0x06}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xd9,0x01,0x63}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0x28,0x3e,0x16}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0x28,0x5d,0xcd}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0xa6,0x4a,0xad}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0xd5,0xab,0x1e}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0xe3,0x10,0x10}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0xe3,0x10,0x53}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xd9,0x01,0x64}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xf3,0x70,0x30}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xf3,0x70,0x50}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xf3,0x70,0xbb}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xf3,0x70,0xdd}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xfd,0xbd,0x12}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xfd,0xbd,0x42}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xfd,0xbd,0x46}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb9,0xfd,0xbd,0x50}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0xa6,0x45,0x58}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0xe3,0x4a,0xab}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0xe3,0x4a,0xc1}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbc,0xe3,0x4a,0xc4}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbe,0x04,0xb8,0xb4}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xbe,0x0a,0x08,0xfa}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0x51,0x82,0xe4}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0xa1,0xb6,0x68}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0xa1,0xb6,0x6c}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0xa1,0xb6,0x6d}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0xe3,0x8f,0x29}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0xe3,0xe4,0xc4}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0xe3,0xe4,0xc5}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0xf3,0xd6,0x5a}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc0,0xfa,0xe6,0x11}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc1,0x1d,0xbb,0x55}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc1,0xea,0xe0,0x48}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc1,0xea,0xe0,0x64}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc1,0xea,0xe0,0x67}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc1,0xea,0xe0,0x91}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc1,0xea,0xe0,0x98}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc2,0x63,0x14,0x64}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc3,0x9a,0x69,0x6d}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc3,0x9a,0x69,0x7d}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0x14,0x65,0x74}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0x17,0x80,0x4b}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0x17,0xe2,0x29}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0x35,0x4a,0x0e}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0x3d,0xbe,0x8e}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc6,0xcc,0xfb,0x3a}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc7,0xc9,0x6e,0xc3}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xc8,0x7a,0x80,0xac}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xca,0x47,0x62,0x7b}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xca,0x47,0x62,0x7c}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xca,0x47,0x62,0x7d}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xca,0x47,0x62,0x7e}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xcc,0x0c,0xf0,0x62}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xcc,0x10,0xf3,0x6a}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xcc,0x10,0xf5,0x62}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xcc,0x1b,0x3a,0xd4}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd1,0xb1,0x57,0x94}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd1,0xb1,0x58,0x57}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd3,0x63,0xe0,0xa3}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd3,0x63,0xe0,0xa7}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd3,0x63,0xe0,0xae}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd3,0x63,0xe0,0xca}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd1,0xb1,0x5b,0xcb}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd4,0x18,0x60,0x10}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd4,0x18,0x60,0x1a}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd4,0x18,0x60,0x78}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd4,0x18,0x60,0x9f}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd4,0x18,0x60,0xb2}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd4,0xe3,0x88,0xeb}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd4,0x2f,0xeb,0xfd}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd4,0xe3,0xc9,0x2f}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd4,0xed,0x25,0xf0}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd5,0x88,0x50,0x89}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd5,0x40,0xc5,0x5f}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd5,0x88,0x50,0x5d}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd5,0x88,0x50,0x9f}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd5,0xa9,0x49,0x4a}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd9,0x0d,0x65,0x5e}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd9,0x0d,0x6b,0x1a}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd8,0x6b,0xd9,0x3e}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd8,0xbd,0x91,0xa1}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd8,0xbd,0x93,0xb2}, 9999}, {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd9,0x3d,0x02,0x35}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd9,0x3d,0x03,0xc1}, 9999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xdb,0x02,0x82,0x6e}, 9999} + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xdd,0x9c,0x85,0x6e}, 9999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xde,0xe7,0x3b,0x1e}, 9999} }; static SeedSpec6 pnSeed6_test[] = { - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x22,0xed,0xa7,0xf8}, 19999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x22,0xee,0x5d,0xe0}, 19999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2b,0xe5,0x4d,0x2e}, 19999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x20,0x68,0x5f}, 19999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x20,0xd7,0x1f}, 19999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x20,0xdd,0xe6}, 19999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x2d,0x20,0xed,0x4c}, 19999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0x07,0x2e,0x71}, 19999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0xc9,0x17,0x81}, 19999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x8a,0x44,0x14,0x8e}, 19999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xa5,0xe3,0x08,0x50}, 19999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd9,0xb6,0xe5,0x80}, 19999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd9,0xb6,0xe5,0x84}, 19999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd9,0xb6,0xe5,0x86}, 19999}, - {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xd9,0xb6,0xe5,0x88}, 19999} + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x12,0xca,0x34,0xaa}, 20008}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x12,0xca,0x34,0xaa}, 20004}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x12,0xca,0x34,0xaa}, 20000}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x12,0xca,0x34,0xaa}, 20012}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x12,0xca,0x34,0xaa}, 20016}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x12,0xca,0x34,0xaa}, 20020}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x22,0xff,0x0f,0x14}, 20007}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x22,0xff,0x0f,0x14}, 20003}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x22,0xff,0x0f,0x14}, 20015}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x22,0xff,0x0f,0x14}, 20011}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x22,0xff,0x0f,0x14}, 20019}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x22,0xff,0x0f,0x14}, 20023}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0x32,0xd0,0x35}, 20009}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0x32,0xd0,0x35}, 20005}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0x32,0xd0,0x35}, 20001}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0x32,0xd0,0x35}, 20013}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0x32,0xd0,0x35}, 20017}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x34,0x32,0xd0,0x35}, 20021}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3f,0x21,0xee,0x55}, 20010}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3f,0x21,0xee,0x55}, 20006}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3f,0x21,0xee,0x55}, 20002}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3f,0x21,0xee,0x55}, 20014}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3f,0x21,0xee,0x55}, 20018}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x3f,0x21,0xee,0x55}, 20022}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x91,0xef,0xeb,0x10}, 19999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x91,0xef,0xeb,0x11}, 19999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x91,0xef,0xeb,0x12}, 19999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x91,0xef,0xeb,0x13}, 19999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x91,0xef,0xeb,0x14}, 19999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x91,0xef,0xeb,0x15}, 19999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x91,0xef,0xeb,0x16}, 19999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x91,0xef,0xeb,0x17}, 19999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x91,0xef,0xeb,0x18}, 19999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0x91,0xef,0xeb,0x19}, 19999}, + {{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xb2,0x3e,0xcb,0xf9}, 19999} }; #endif // DASH_CHAINPARAMSSEEDS_H diff --git a/src/clientversion.h b/src/clientversion.h index 0a890f9e439b..7a35e56b13cf 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -15,12 +15,12 @@ //! These need to be macros, as clientversion.cpp's and dash*-res.rc's voodoo requires it #define CLIENT_VERSION_MAJOR 0 -#define CLIENT_VERSION_MINOR 12 -#define CLIENT_VERSION_REVISION 3 -#define CLIENT_VERSION_BUILD 2 +#define CLIENT_VERSION_MINOR 13 +#define CLIENT_VERSION_REVISION 0 +#define CLIENT_VERSION_BUILD 0 //! Set to true for release, false for prerelease or test build -#define CLIENT_VERSION_IS_RELEASE true +#define CLIENT_VERSION_IS_RELEASE false /** * Copyright year (2009-this) diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp index f5f61a3abfce..1df6e217fbe9 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); +} diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index ee199b875ce3..0efe580c1e08 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 = 100; diff --git a/src/consensus/params.h b/src/consensus/params.h index ccba1d4996c7..c6dc9d5f40e8 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -18,6 +18,7 @@ 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_DIP0003, // Deployment of DIP0002 and DIP0003 (txv3 and deterministic MN lists) // NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp MAX_VERSION_BITS_DEPLOYMENTS }; @@ -38,6 +39,66 @@ struct BIP9Deployment { int64_t nThreshold; }; +enum LLMQType : uint8_t +{ + LLMQ_NONE = 0xff, + + LLMQ_50_60 = 1, // 50 members, 30 (60%) threshold, one per hour + LLMQ_400_60 = 2, // 400 members, 240 (60%) threshold, one every 12 hours + LLMQ_400_85 = 3, // 400 members, 340 (85%) threshold, one every 24 hours + + // for testing only + LLMQ_10_60 = 100, // 10 members, 6 (60%) threshold, one per hour +}; + +// Configures a LLMQ and its DKG +// See https://github.com/dashpay/dips/blob/master/dip-0006.md for more details +struct LLMQParams { + LLMQType type; + + // not consensus critical, only used in logging, RPC and UI + std::string name; + + // the size of the quorum, e.g. 50 or 400 + int size; + + // The minimum number of valid members after the DKK. If less members are determined valid, no commitment can be + // created. Should be higher then the threshold to allow some room for failing nodes, otherwise quorum might end up + // not being able to ever created a recovered signature if more nodes fail after the DKG + int minSize; + + // The threshold required to recover a final signature. Should be at least 50%+1 of the quorum size. This value + // also controls the size of the public key verification vector and has a large influence on the performance of + // recovery. It also influences the amount of minimum messages that need to be exchanged for a single signing session. + // This value has the most influence on the security of the quorum. The number of total malicious masternodes + // required to negatively influence signing sessions highly correlates to the threshold percentage. + int threshold; + + // The interval in number blocks for DKGs and the creation of LLMQs. If set to 24 for example, a DKG will start + // every 24 blocks, which is approximately once every hour. + int dkgInterval; + + // The number of blocks per phase in a DKG session. There are 6 phases plus the mining phase that need to be processed + // per DKG. Set this value to a number of blocks so that each phase has enough time to propagate all required + // messages to all members before the next phase starts. If blocks are produced too fast, whole DKG sessions will + // fail. + int dkgPhaseBlocks; + + // The starting block inside the DKG interval for when mining of commitments starts. The value is inclusive. + // Starting from this block, the inclusion of (possibly null) commitments is enforced until the first non-null + // commitment is mined. The chosen value should be at least 5 * dkgPhaseBlocks so that it starts right after the + // finalization phase. + int dkgMiningWindowStart; + + // The ending block inside the DKG interval for when mining of commitments ends. The value is inclusive. + // Choose a value so that miners have enough time to receive the commitment and mine it. Also take into consideration + // that miners might omit real commitments and revert to always including null commitments. The mining window should + // be large enough so that other miners have a chance to produce a block containing a non-null commitment. The window + // should at the same time not be too large so that not too much space is wasted with null commitments in case a DKG + // session failed. + int dkgMiningWindowEnd; +}; + /** * Parameters that influence chain consensus. */ @@ -89,6 +150,14 @@ struct Params { int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; } uint256 nMinimumChainWork; uint256 defaultAssumeValid; + + /** these parameters are only used on devnet and can be configured from the outside */ + int nMinimumDifficultyBlocks{0}; + int nHighSubsidyBlocks{0}; + int nHighSubsidyFactor{1}; + + std::map llmqs; + bool fLLMQAllowDummyCommitments; }; } // namespace Consensus 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 d417c80c7261..625641d01110 100644 --- a/src/dsnotificationinterface.cpp +++ b/src/dsnotificationinterface.cpp @@ -14,6 +14,10 @@ #include "privatesend-client.h" #endif // ENABLE_WALLET +#include "evo/deterministicmns.h" + +#include "llmq/quorums_dummydkg.h" + void CDSNotificationInterface::InitializeCurrentBlockTip() { LOCK(cs_main); @@ -35,10 +39,16 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con if (pindexNew == pindexFork) // blocks were disconnected without any new ones return; + deterministicMNManager->UpdatedBlockTip(pindexNew); + llmq::quorumDummyDKG->UpdatedBlockTip(pindexNew, fInitialDownload); + masternodeSync.UpdatedBlockTip(pindexNew, fInitialDownload, connman); // Update global DIP0001 activation status fDIP0001ActiveAtTip = pindexNew->nHeight >= Params().GetConsensus().DIP0001Height; + // update instantsend autolock activation flag (we reuse the DIP3 deployment) + instantsend.isAutoLockBip9Active = + (VersionBitsState(pindexNew, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE); if (fInitialDownload) return; diff --git a/src/evo/cbtx.cpp b/src/evo/cbtx.cpp new file mode 100644 index 000000000000..e757c6084b8a --- /dev/null +++ b/src/evo/cbtx.cpp @@ -0,0 +1,94 @@ +// Copyright (c) 2017-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 "cbtx.h" +#include "deterministicmns.h" +#include "simplifiedmns.h" +#include "specialtx.h" + +#include "chainparams.h" +#include "univalue.h" +#include "validation.h" + +bool CheckCbTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + if (tx.nType != TRANSACTION_COINBASE) { + return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-type"); + } + + 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-cbtx-payload"); + } + + if (cbTx.nVersion == 0 || cbTx.nVersion > CCbTx::CURRENT_VERSION) { + return state.DoS(100, false, REJECT_INVALID, "bad-cbtx-version"); + } + + if (pindexPrev && 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) +{ + 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-cbtx-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) +{ + LOCK(deterministicMNManager->cs); + + CDeterministicMNList tmpMNList; + if (!deterministicMNManager->BuildNewListFromBlock(block, pindexPrev, state, tmpMNList, false)) { + 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..c9b1c0e1eb02 --- /dev/null +++ b/src/evo/cbtx.h @@ -0,0 +1,46 @@ +// Copyright (c) 2017-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_CBTX_H +#define DASH_CBTX_H + +#include "consensus/validation.h" +#include "primitives/transaction.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 //DASH_CBTX_H diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp new file mode 100644 index 000000000000..ba0f7d0190aa --- /dev/null +++ b/src/evo/deterministicmns.cpp @@ -0,0 +1,889 @@ +// 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 "base58.h" +#include "chainparams.h" +#include "core_io.h" +#include "script/standard.h" +#include "spork.h" +#include "validation.h" +#include "validationinterface.h" + +#include "llmq/quorums_commitment.h" +#include "llmq/quorums_utils.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, payoutAddress=%s, operatorRewardAddress=%s)", + nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight, nRevocationReason, + keyIDOwner.ToString(), pubKeyOperator.ToString(), keyIDVoting.ToString(), addr.ToStringIPPort(false), 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("ownerKeyAddr", CBitcoinAddress(keyIDOwner).ToString())); + obj.push_back(Pair("votingKeyAddr", CBitcoinAddress(keyIDVoting).ToString())); + obj.push_back(Pair("addr", addr.ToStringIPPort(false))); + + 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, collateralOutpoint=%s, nOperatorReward=%f, state=%s", proTxHash.ToString(), collateralOutpoint.ToStringShort(), (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("collateralHash", collateralOutpoint.hash.ToString())); + obj.push_back(Pair("collateralIndex", (int)collateralOutpoint.n)); + 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; +} + +CDeterministicMNCPtr CDeterministicMNList::GetMNByCollateral(const COutPoint& collateralOutpoint) const +{ + return GetUniquePropertyMN(collateralOutpoint); +} + +static int CompareByLastPaid_GetHeight(const CDeterministicMN& dmn) +{ + int height = dmn.pdmnState->nLastPaidHeight; + if (dmn.pdmnState->nPoSeRevivedHeight != -1 && dmn.pdmnState->nPoSeRevivedHeight > height) { + height = dmn.pdmnState->nPoSeRevivedHeight; + } else if (height == 0) { + height = dmn.pdmnState->nRegisteredHeight; + } + return height; +} + +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; +} + +std::vector CDeterministicMNList::CalculateQuorum(size_t maxSize, const uint256& modifier) const +{ + auto scores = CalculateScores(modifier); + + // sort is descending order + std::sort(scores.rbegin(), scores.rend(), [](const std::pair& a, std::pair& b) { + if (a.first == b.first) { + // this should actually never happen, but we should stay compatible with how the non deterministic MNs did the sorting + return a.second->collateralOutpoint < b.second->collateralOutpoint; + } + return a.first < b.first; + }); + + // take top maxSize entries and return it + std::vector result; + result.resize(std::min(maxSize, scores.size())); + for (size_t i = 0; i < result.size(); i++) { + result[i] = std::move(scores[i].second); + } + return result; +} + +std::vector> CDeterministicMNList::CalculateScores(const uint256& modifier) const +{ + std::vector> scores; + scores.reserve(GetAllMNsCount()); + ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) { + if (dmn->pdmnState->confirmedHash.IsNull()) { + // we only take confirmed MNs into account to avoid hash grinding on the ProRegTxHash to sneak MNs into a + // future quorums + return; + } + // calculate sha256(sha256(proTxHash, confirmedHash), modifier) per MN + // Please note that this is not a double-sha256 but a single-sha256 + // The first part is already precalculated (confirmedHashWithProRegTxHash) + // TODO When https://github.com/bitcoin/bitcoin/pull/13191 gets backported, implement something that is similar but for single-sha256 + uint256 h; + CSHA256 sha256; + sha256.Write(dmn->pdmnState->confirmedHashWithProRegTxHash.begin(), dmn->pdmnState->confirmedHashWithProRegTxHash.size()); + sha256.Write(modifier.begin(), modifier.size()); + sha256.Finalize(h.begin()); + + scores.emplace_back(UintToArith256(h), dmn); + }); + + return scores; +} + +int CDeterministicMNList::CalcMaxPoSePenalty() const +{ + // Maximum PoSe penalty is dynamic and equals the number of registered MNs + // It's however at least 100. + // This means that the max penalty is usually equal to a full payment cycle + return std::max(100, (int)GetAllMNsCount()); +} + +int CDeterministicMNList::CalcPenalty(int percent) const +{ + assert(percent > 0); + return (CalcMaxPoSePenalty() * percent) / 100; +} + +void CDeterministicMNList::PoSePunish(const uint256& proTxHash, int penalty, bool debugLogs) +{ + assert(penalty > 0); + + auto dmn = GetMN(proTxHash); + assert(dmn); + + int maxPenalty = CalcMaxPoSePenalty(); + + auto newState = std::make_shared(*dmn->pdmnState); + newState->nPoSePenalty += penalty; + newState->nPoSePenalty = std::min(maxPenalty, newState->nPoSePenalty); + + if (debugLogs) { + LogPrintf("CDeterministicMNList::%s -- punished MN %s, penalty %d->%d (max=%d)\n", + __func__, proTxHash.ToString(), dmn->pdmnState->nPoSePenalty, newState->nPoSePenalty, maxPenalty); + } + + if (newState->nPoSePenalty >= maxPenalty && newState->nPoSeBanHeight == -1) { + newState->nPoSeBanHeight = nHeight; + if (debugLogs) { + LogPrintf("CDeterministicMNList::%s -- banned MN %s at height %d\n", + __func__, proTxHash.ToString(), nHeight); + } + } + UpdateMN(proTxHash, newState); +} + +void CDeterministicMNList::PoSeDecrease(const uint256& proTxHash) +{ + auto dmn = GetMN(proTxHash); + assert(dmn); + assert(dmn->pdmnState->nPoSePenalty > 0 && dmn->pdmnState->nPoSeBanHeight == -1); + + auto newState = std::make_shared(*dmn->pdmnState); + newState->nPoSePenalty--; + UpdateMN(proTxHash, newState); +} + +CDeterministicMNListDiff CDeterministicMNList::BuildDiff(const CDeterministicMNList& to) const +{ + CDeterministicMNListDiff diffRet; + diffRet.prevBlockHash = blockHash; + diffRet.blockHash = to.blockHash; + diffRet.nHeight = to.nHeight; + + to.ForEachMN(false, [&](const CDeterministicMNCPtr& toPtr) { + auto fromPtr = GetMN(toPtr->proTxHash); + if (fromPtr == nullptr) { + diffRet.addedMNs.emplace(toPtr->proTxHash, toPtr); + } else if (*toPtr->pdmnState != *fromPtr->pdmnState) { + diffRet.updatedMNs.emplace(toPtr->proTxHash, toPtr->pdmnState); + } + }); + ForEachMN(false, [&](const CDeterministicMNCPtr& fromPtr) { + auto toPtr = to.GetMN(fromPtr->proTxHash); + if (toPtr == nullptr) { + diffRet.removedMns.insert(fromPtr->proTxHash); + } + }); + + return diffRet; +} + +CSimplifiedMNListDiff CDeterministicMNList::BuildSimplifiedDiff(const CDeterministicMNList& to) const +{ + CSimplifiedMNListDiff diffRet; + diffRet.baseBlockHash = blockHash; + diffRet.blockHash = to.blockHash; + + to.ForEachMN(false, [&](const CDeterministicMNCPtr& toPtr) { + auto fromPtr = GetMN(toPtr->proTxHash); + if (fromPtr == nullptr) { + diffRet.mnList.emplace_back(*toPtr); + } else { + CSimplifiedMNListEntry sme1(*toPtr); + CSimplifiedMNListEntry sme2(*fromPtr); + if (sme1 != sme2) { + diffRet.mnList.emplace_back(*toPtr); + } + } + }); + ForEachMN(false, [&](const CDeterministicMNCPtr& fromPtr) { + auto toPtr = to.GetMN(fromPtr->proTxHash); + if (toPtr == nullptr) { + diffRet.deletedMNs.emplace_back(fromPtr->proTxHash); + } + }); + + 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->collateralOutpoint); + if (dmn->pdmnState->addr != CService()) { + AddUniqueProperty(dmn, dmn->pdmnState->addr); + } + AddUniqueProperty(dmn, dmn->pdmnState->keyIDOwner); + if (dmn->pdmnState->pubKeyOperator.IsValid()) { + 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->collateralOutpoint); + if (dmn->pdmnState->addr != CService()) { + DeleteUniqueProperty(dmn, dmn->pdmnState->addr); + } + DeleteUniqueProperty(dmn, dmn->pdmnState->keyIDOwner); + if (dmn->pdmnState->pubKeyOperator.IsValid()) { + DeleteUniqueProperty(dmn, dmn->pdmnState->pubKeyOperator); + } + mnMap = mnMap.erase(proTxHash); +} + +CDeterministicMNManager::CDeterministicMNManager(CEvoDB& _evoDb) : + evoDb(_evoDb) +{ +} + +bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& _state) +{ + LOCK(cs); + + int nHeight = pindex->nHeight; + + CDeterministicMNList newList; + if (!BuildNewListFromBlock(block, pindex->pprev, _state, newList, true)) { + return false; + } + + if (newList.GetHeight() == -1) { + newList.SetHeight(nHeight); + } + + newList.SetBlockHash(block.GetHash()); + + CDeterministicMNList oldList = GetListForBlock(pindex->pprev->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, bool debugLogs) +{ + 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 iterate the oldList here and update the newList + // this is only valid as long these have not diverged at this point, which is the case as long as we don't add + // code above this loop that modifies newList + oldList.ForEachMN(false, [&](const CDeterministicMNCPtr& dmn) { + if (!dmn->pdmnState->confirmedHash.IsNull()) { + // already confirmed + return; + } + // this works on the previous block, so confirmation will happen one block after nMasternodeMinimumConfirmations + // has been reached, but the block hash will then point to the block at nMasternodeMinimumConfirmations + int nConfirmations = pindexPrev->nHeight - dmn->pdmnState->nRegisteredHeight; + if (nConfirmations >= Params().GetConsensus().nMasternodeMinimumConfirmations) { + CDeterministicMNState newState = *dmn->pdmnState; + newState.UpdateConfirmedHash(dmn->proTxHash, pindexPrev->GetBlockHash()); + newList.UpdateMN(dmn->proTxHash, std::make_shared(newState)); + } + }); + + DecreasePoSePenalties(newList); + + // we skip the coinbase + for (int i = 1; i < (int)block.vtx.size(); i++) { + const CTransaction& tx = *block.vtx[i]; + + if (tx.nVersion != 3) { + // only interested in special TXs + continue; + } + + if (tx.nType == TRANSACTION_PROVIDER_REGISTER) { + CProRegTx proTx; + if (!GetTxPayload(tx, proTx)) { + assert(false); // this should have been handled already + } + + auto dmn = std::make_shared(); + dmn->proTxHash = tx.GetHash(); + + // collateralOutpoint is either pointing to an external collateral or to the ProRegTx itself + if (proTx.collateralOutpoint.hash.IsNull()) { + dmn->collateralOutpoint = COutPoint(tx.GetHash(), proTx.collateralOutpoint.n); + } else { + dmn->collateralOutpoint = proTx.collateralOutpoint; + } + + Coin coin; + if (!proTx.collateralOutpoint.hash.IsNull() && (!GetUTXOCoin(dmn->collateralOutpoint, coin) || coin.out.nValue != 1000 * COIN)) { + // should actually never get to this point as CheckProRegTx should have handled this case. + // We do this additional check nevertheless to be 100% sure + return _state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral"); + } + + auto replacedDmn = newList.GetMNByCollateral(dmn->collateralOutpoint); + if (replacedDmn != nullptr) { + // This might only happen with a ProRegTx that refers an external collateral + // In that case the new ProRegTx will replace the old one. This means the old one is removed + // and the new one is added like a completely fresh one, which is also at the bottom of the payment list + newList.RemoveMN(replacedDmn->proTxHash); + if (debugLogs) { + LogPrintf("CDeterministicMNManager::%s -- MN %s removed from list because collateral was used for a new ProRegTx. collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d\n", + __func__, replacedDmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort(), nHeight, newList.GetAllMNsCount()); + } + } + + 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"); + } + + dmn->nOperatorReward = proTx.nOperatorReward; + dmn->pdmnState = std::make_shared(proTx); + + CDeterministicMNState dmnState = *dmn->pdmnState; + dmnState.nRegisteredHeight = nHeight; + + if (proTx.addr == CService()) { + // start in banned pdmnState as we need to wait for a ProUpServTx + dmnState.nPoSeBanHeight = nHeight; + } + + dmn->pdmnState = std::make_shared(dmnState); + + newList.AddMN(dmn); + + if (debugLogs) { + 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->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->nPoSePenalty = 0; + newState->nPoSeBanHeight = -1; + newState->nPoSeRevivedHeight = nHeight; + + if (debugLogs) { + LogPrintf("CDeterministicMNManager::%s -- MN %s revived at height %d\n", + __func__, proTx.proTxHash.ToString(), nHeight); + } + } + } + + newList.UpdateMN(proTx.proTxHash, newState); + if (debugLogs) { + 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); + + if (debugLogs) { + 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); + + if (debugLogs) { + LogPrintf("CDeterministicMNManager::%s -- MN %s revoked operator key at height %d: %s\n", + __func__, proTx.proTxHash.ToString(), nHeight, proTx.ToString()); + } + } else if (tx.nType == TRANSACTION_QUORUM_COMMITMENT) { + llmq::CFinalCommitmentTxPayload qc; + if (!GetTxPayload(tx, qc)) { + assert(false); // this should have been handled already + } + if (!qc.commitment.IsNull()) { + HandleQuorumCommitment(qc.commitment, newList, debugLogs); + } + } + } + + // 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) { + auto dmn = newList.GetMNByCollateral(in.prevout); + if (dmn && dmn->collateralOutpoint == in.prevout) { + newList.RemoveMN(dmn->proTxHash); + + if (debugLogs) { + LogPrintf("CDeterministicMNManager::%s -- MN %s removed from list because collateral was spent. collateralOutpoint=%s, nHeight=%d, mapCurMNs.allMNsCount=%d\n", + __func__, dmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort(), nHeight, newList.GetAllMNsCount()); + } + } + } + } + + // 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; +} + +void CDeterministicMNManager::HandleQuorumCommitment(llmq::CFinalCommitment& qc, CDeterministicMNList& mnList, bool debugLogs) +{ + // The commitment has already been validated at this point so it's safe to use members of it + + auto members = llmq::CLLMQUtils::GetAllQuorumMembers((Consensus::LLMQType)qc.llmqType, qc.quorumHash); + + for (size_t i = 0; i < members.size(); i++) { + if (!mnList.HasMN(members[i]->proTxHash)) { + continue; + } + if (!qc.validMembers[i]) { + // punish MN for failed DKG participation + // The idea is to immediately ban a MN when it fails 2 DKG sessions with only a few blocks in-between + // If there were enough blocks between failures, the MN has a chance to recover as he reduces his penalty by 1 for every block + // If it however fails 3 times in the timespan of a single payment cycle, it should definitely get banned + mnList.PoSePunish(members[i]->proTxHash, mnList.CalcPenalty(66), debugLogs); + } + } +} + +void CDeterministicMNManager::DecreasePoSePenalties(CDeterministicMNList& mnList) +{ + std::vector toDecrease; + toDecrease.reserve(mnList.GetValidMNsCount() / 10); + // only iterate and decrease for valid ones (not PoSe banned yet) + // if a MN ever reaches the maximum, it stays in PoSe banned state until revived + mnList.ForEachMN(true, [&](const CDeterministicMNCPtr& dmn) { + if (dmn->pdmnState->nPoSePenalty > 0 && dmn->pdmnState->nPoSeBanHeight == -1) { + toDecrease.emplace_back(dmn->proTxHash); + } + }); + + for (const auto& proTxHash : toDecrease) { + mnList.PoSeDecrease(proTxHash); + } +} + +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); +} + +bool CDeterministicMNManager::HasValidMNCollateralAtChainTip(const COutPoint& outpoint) +{ + auto mnList = GetListAtChainTip(); + auto dmn = mnList.GetMNByCollateral(outpoint); + return dmn && mnList.IsMNValid(dmn); +} + +bool CDeterministicMNManager::HasMNCollateralAtChainTip(const COutPoint& outpoint) +{ + auto mnList = GetListAtChainTip(); + auto dmn = mnList.GetMNByCollateral(outpoint); + return dmn != nullptr; +} + +int64_t CDeterministicMNManager::GetSpork15Value() +{ + return sporkManager.GetSporkValue(SPORK_15_DETERMINISTIC_MNS_ENABLED); +} + +bool CDeterministicMNManager::IsProTxWithCollateral(const CTransactionRef& tx, uint32_t n) +{ + if (tx->nVersion != 3 || tx->nType != TRANSACTION_PROVIDER_REGISTER) { + return false; + } + CProRegTx proTx; + if (!GetTxPayload(*tx, proTx)) { + return false; + } + + if (!proTx.collateralOutpoint.hash.IsNull()) { + return false; + } + if (proTx.collateralOutpoint.n >= tx->vout.size() || proTx.collateralOutpoint.n != n) { + return false; + } + if (tx->vout[n].nValue != 1000 * COIN) { + return false; + } + return true; +} + +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..ba0d1fa8541c --- /dev/null +++ b/src/evo/deterministicmns.h @@ -0,0 +1,498 @@ +// 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_DETERMINISTICMNS_H +#define DASH_DETERMINISTICMNS_H + +#include "arith_uint256.h" +#include "bls/bls.h" +#include "dbwrapper.h" +#include "evodb.h" +#include "providertx.h" +#include "simplifiedmns.h" +#include "sync.h" + +#include "immer/map.hpp" +#include "immer/map_transient.hpp" + +#include + +class CBlock; +class CBlockIndex; +class CValidationState; + +namespace llmq +{ + class CFinalCommitment; +} + +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}; + + // the block hash X blocks after registration, used in quorum calculations + uint256 confirmedHash; + // sha256(proTxHash, confirmedHash) to speed up quorum calculations + // please note that this is NOT a double-sha256 hash + uint256 confirmedHashWithProRegTxHash; + + CKeyID keyIDOwner; + CBLSPublicKey pubKeyOperator; + CKeyID keyIDVoting; + CService addr; + CScript scriptPayout; + CScript scriptOperatorPayout; + +public: + CDeterministicMNState() {} + CDeterministicMNState(const CProRegTx& proTx) + { + keyIDOwner = proTx.keyIDOwner; + pubKeyOperator = proTx.pubKeyOperator; + keyIDVoting = proTx.keyIDVoting; + addr = proTx.addr; + 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(confirmedHash); + READWRITE(confirmedHashWithProRegTxHash); + READWRITE(keyIDOwner); + READWRITE(pubKeyOperator); + READWRITE(keyIDVoting); + READWRITE(addr); + READWRITE(*(CScriptBase*)(&scriptPayout)); + READWRITE(*(CScriptBase*)(&scriptOperatorPayout)); + } + + void ResetOperatorFields() + { + pubKeyOperator = CBLSPublicKey(); + addr = CService(); + scriptOperatorPayout = CScript(); + nRevocationReason = CProUpRevTx::REASON_NOT_SPECIFIED; + } + void BanIfNotBanned(int height) + { + if (nPoSeBanHeight == -1) { + nPoSeBanHeight = height; + } + } + void UpdateConfirmedHash(const uint256& _proTxHash, const uint256& _confirmedHash) + { + confirmedHash = _confirmedHash; + CSHA256 h; + h.Write(_proTxHash.begin(), _proTxHash.size()); + h.Write(_confirmedHash.begin(), _confirmedHash.size()); + h.Finalize(confirmedHashWithProRegTxHash.begin()); + } + + 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 && + confirmedHash == rhs.confirmedHash && + confirmedHashWithProRegTxHash == rhs.confirmedHashWithProRegTxHash && + keyIDOwner == rhs.keyIDOwner && + pubKeyOperator == rhs.pubKeyOperator && + keyIDVoting == rhs.keyIDVoting && + addr == rhs.addr && + 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() {} + template + CDeterministicMN(deserialize_type, Stream& s) + { + s >> *this; + } + + uint256 proTxHash; + COutPoint collateralOutpoint; + uint16_t nOperatorReward; + CDeterministicMNStateCPtr pdmnState; + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(proTxHash); + READWRITE(collateralOutpoint); + 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(); + } + + size_t GetValidMNsCount() const + { + size_t count = 0; + for (const auto& p : mnMap) { + if (IsMNValid(p.second)) { + count++; + } + } + return count; + } + + 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 IsMNValid(const CDeterministicMNCPtr& dmn) const; + bool IsMNPoSeBanned(const CDeterministicMNCPtr& dmn) 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 GetMNByCollateral(const COutPoint& collateralOutpoint) const; + 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; + + /** + * Calculate a quorum based on the modifier. The resulting list is deterministically sorted by score + * @param maxSize + * @param modifier + * @return + */ + std::vector CalculateQuorum(size_t maxSize, const uint256& modifier) const; + std::vector> CalculateScores(const uint256& modifier) const; + + /** + * Calculates the maximum penalty which is allowed at the height of this MN list. It is dynamic and might change + * for every block. + * @return + */ + int CalcMaxPoSePenalty() const; + + /** + * Returns a the given percentage from the max penalty for this MN list. Always use this method to calculate the + * value later passed to PoSePunish. The percentage should be high enough to take per-block penalty decreasing for MNs + * into account. This means, if you want to accept 2 failures per payment cycle, you should choose a percentage that + * is higher then 50%, e.g. 66%. + * @param percent + * @return + */ + int CalcPenalty(int percent) const; + + /** + * Punishes a MN for misbehavior. If the resulting penalty score of the MN reaches the max penalty, it is banned. + * Penalty scores are only increased when the MN is not already banned, which means that after banning the penalty + * might appear lower then the current max penalty, while the MN is still banned. + * @param proTxHash + * @param penalty + */ + void PoSePunish(const uint256& proTxHash, int penalty, bool debugLogs); + + /** + * Decrease penalty score of MN by 1. + * Only allowed on non-banned MNs. + * @param proTxHash + */ + void PoSeDecrease(const uint256& proTxHash); + + CDeterministicMNListDiff BuildDiff(const CDeterministicMNList& to) const; + CSimplifiedMNListDiff BuildSimplifiedDiff(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: + template + void AddUniqueProperty(const CDeterministicMNCPtr& dmn, const T& v) + { + static const T nullValue; + assert(v != nullValue); + + 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) + { + static const T nullValue; + assert(oldValue != nullValue); + + 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; + } + static const T nullValue; + + if (oldValue != nullValue) { + DeleteUniqueProperty(dmn, oldValue); + } + + if (newValue != nullValue) { + 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* pindex, 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, bool debugLogs); + void HandleQuorumCommitment(llmq::CFinalCommitment& qc, CDeterministicMNList& mnList, bool debugLogs); + void DecreasePoSePenalties(CDeterministicMNList& mnList); + + CDeterministicMNList GetListForBlock(const uint256& blockHash); + CDeterministicMNList GetListAtChainTip(); + + // TODO remove after removal of old non-deterministic lists + bool HasValidMNCollateralAtChainTip(const COutPoint& outpoint); + bool HasMNCollateralAtChainTip(const COutPoint& outpoint); + + // Test if given TX is a ProRegTx which also contains the collateral at index n + bool IsProTxWithCollateral(const CTransactionRef& tx, uint32_t n); + + bool IsDeterministicMNsSporkActive(int nHeight = -1); + +private: + int64_t GetSpork15Value(); + void CleanupCache(int nHeight); +}; + +extern CDeterministicMNManager* deterministicMNManager; + +#endif //DASH_DETERMINISTICMNS_H diff --git a/src/evo/evodb.cpp b/src/evo/evodb.cpp new file mode 100644 index 000000000000..b041be3d99dd --- /dev/null +++ b/src/evo/evodb.cpp @@ -0,0 +1,30 @@ +// 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) +{ +} + +bool CEvoDB::VerifyBestBlock(const uint256& hash) +{ + // Make sure evodb is consistent. + // If we already have best block hash saved, the previous block should match it. + uint256 hashBestBlock; + bool fHasBestBlock = Read(EVODB_BEST_BLOCK, hashBestBlock); + uint256 hashBlockIndex = fHasBestBlock ? hash : uint256(); + assert(hashBestBlock == hashBlockIndex); + + return fHasBestBlock; +} + +void CEvoDB::WriteBestBlock(const uint256& hash) +{ + Write(EVODB_BEST_BLOCK, hash); +} diff --git a/src/evo/evodb.h b/src/evo/evodb.h new file mode 100644 index 000000000000..2506264c04c7 --- /dev/null +++ b/src/evo/evodb.h @@ -0,0 +1,70 @@ +// 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_EVODB_H +#define DASH_EVODB_H + +#include "dbwrapper.h" +#include "sync.h" +#include "uint256.h" + +static const std::string EVODB_BEST_BLOCK = "b_b"; + +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; + } + + bool VerifyBestBlock(const uint256& hash); + void WriteBestBlock(const uint256& hash); +}; + +extern CEvoDB* evoDb; + +#endif //DASH_EVODB_H diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp new file mode 100644 index 000000000000..b4a045cd99d6 --- /dev/null +++ b/src/evo/providertx.cpp @@ -0,0 +1,515 @@ +// 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 "providertx.h" +#include "specialtx.h" + +#include "base58.h" +#include "chainparams.h" +#include "clientversion.h" +#include "core_io.h" +#include "hash.h" +#include "messagesigner.h" +#include "script/standard.h" +#include "streams.h" +#include "univalue.h" +#include "validation.h" + +template +static bool CheckService(const uint256& proTxHash, const ProTx& proTx, CValidationState& state) +{ + 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"); + } + + int mainnetDefaultPort = Params(CBaseChainParams::MAIN).GetDefaultPort(); + if (Params().NetworkIDString() == CBaseChainParams::MAIN) { + if (proTx.addr.GetPort() != mainnetDefaultPort) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr-port"); + } + } else if (proTx.addr.GetPort() == mainnetDefaultPort) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr-port"); + } + + if (!proTx.addr.IsIPv4()) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-addr"); + } + + return true; +} + +template +static bool CheckHashSig(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 CheckStringSig(const ProTx& proTx, const CKeyID& keyID, CValidationState& state) +{ + std::string strError; + if (!CMessageSigner::VerifyMessage(keyID, proTx.vchSig, proTx.MakeSignString(), strError)) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig", false, strError); + } + return true; +} + +template +static bool CheckHashSig(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 CheckInputsHash(const CTransaction& tx, const ProTx& proTx, CValidationState& state) +{ + uint256 inputsHash = CalcTxInputsHash(tx); + if (inputsHash != proTx.inputsHash) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-inputs-hash"); + } + + return true; +} + +bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + if (tx.nType != TRANSACTION_PROVIDER_REGISTER) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-type"); + } + + CProRegTx ptx; + if (!GetTxPayload(tx, ptx)) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload"); + } + + if (ptx.nVersion == 0 || ptx.nVersion > CProRegTx::CURRENT_VERSION) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-version"); + } + if (ptx.nType != 0) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-type"); + } + if (ptx.nMode != 0) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-mode"); + } + + if (ptx.keyIDOwner.IsNull() || !ptx.pubKeyOperator.IsValid() || ptx.keyIDVoting.IsNull()) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-null"); + } + if (!ptx.scriptPayout.IsPayToPublicKeyHash() && !ptx.scriptPayout.IsPayToScriptHash()) { + 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 payout key for other keys (don't allow people to put the payee 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"); + } + + // It's allowed to set addr 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() && !CheckService(tx.GetHash(), ptx, state)) { + return false; + } + + if (ptx.nOperatorReward > 10000) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-operator-reward"); + } + + CTxDestination collateralTxDest; + CKeyID keyForPayloadSig; + COutPoint collateralOutpoint; + + if (!ptx.collateralOutpoint.hash.IsNull()) { + Coin coin; + if (!GetUTXOCoin(ptx.collateralOutpoint, coin) || coin.out.nValue != 1000 * COIN) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral"); + } + + if (!ExtractDestination(coin.out.scriptPubKey, collateralTxDest)) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-dest"); + } + + // Extract key from collateral. This only works for P2PK and P2PKH collaterals and will fail for P2SH. + // Issuer of this ProRegTx must prove ownership with this key by signing the ProRegTx + if (!CBitcoinAddress(collateralTxDest).GetKeyID(keyForPayloadSig)) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-pkh"); + } + + collateralOutpoint = ptx.collateralOutpoint; + } else { + if (ptx.collateralOutpoint.n >= tx.vout.size()) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-index"); + } + if (tx.vout[ptx.collateralOutpoint.n].nValue != 1000 * COIN) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral"); + } + + if (!ExtractDestination(tx.vout[ptx.collateralOutpoint.n].scriptPubKey, collateralTxDest)) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-dest"); + } + + collateralOutpoint = COutPoint(tx.GetHash(), ptx.collateralOutpoint.n); + } + + // don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server) + // this check applies to internal and external collateral, but internal collaterals are not necessarely a P2PKH + if (collateralTxDest == CTxDestination(ptx.keyIDOwner) || collateralTxDest == CTxDestination(ptx.keyIDVoting)) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-reuse"); + } + + if (pindexPrev) { + auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash()); + + // only allow reusing of addresses when it's for the same collateral (which replaces the old MN) + if (mnList.HasUniqueProperty(ptx.addr) && mnList.GetUniquePropertyMN(ptx.addr)->collateralOutpoint != collateralOutpoint) { + return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-addr"); + } + + // never allow duplicate keys, even if this ProTx would replace an existing MN + 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 (!CheckInputsHash(tx, ptx, state)) { + return false; + } + + if (!keyForPayloadSig.IsNull()) { + // collateral is not part of this ProRegTx, so we must verify ownership of the collateral + if (!CheckStringSig(ptx, keyForPayloadSig, state)) { + return false; + } + } else { + // collateral is part of this ProRegTx, so we know the collateral is owned by the issuer + if (!ptx.vchSig.empty()) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-sig"); + } + } + + return true; +} + +bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + if (tx.nType != TRANSACTION_PROVIDER_UPDATE_SERVICE) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-type"); + } + + CProUpServTx ptx; + if (!GetTxPayload(tx, ptx)) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload"); + } + + if (ptx.nVersion == 0 || ptx.nVersion > CProRegTx::CURRENT_VERSION) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-version"); + } + + if (!CheckService(ptx.proTxHash, ptx, state)) { + return false; + } + + if (pindexPrev) { + auto mnList = deterministicMNManager->GetListForBlock(pindexPrev->GetBlockHash()); + auto mn = mnList.GetMN(ptx.proTxHash); + if (!mn) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-hash"); + } + + // don't allow updating to addresses already used by other MNs + if (mnList.HasUniqueProperty(ptx.addr) && mnList.GetUniquePropertyMN(ptx.addr)->proTxHash != ptx.proTxHash) { + return state.DoS(10, false, REJECT_DUPLICATE, "bad-protx-dup-addr"); + } + + 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"); + } + if (!ptx.scriptOperatorPayout.IsPayToPublicKeyHash() && !ptx.scriptOperatorPayout.IsPayToScriptHash()) { + 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 (!CheckInputsHash(tx, ptx, state)) { + return false; + } + if (!CheckHashSig(ptx, mn->pdmnState->pubKeyOperator, state)) { + return false; + } + } + + return true; +} + +bool CheckProUpRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + if (tx.nType != TRANSACTION_PROVIDER_UPDATE_REGISTRAR) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-type"); + } + + CProUpRegTx ptx; + if (!GetTxPayload(tx, ptx)) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload"); + } + + if (ptx.nVersion == 0 || ptx.nVersion > CProRegTx::CURRENT_VERSION) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-version"); + } + if (ptx.nMode != 0) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-mode"); + } + + if (!ptx.pubKeyOperator.IsValid() || ptx.keyIDVoting.IsNull()) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-key-null"); + } + if (!ptx.scriptPayout.IsPayToPublicKeyHash() && !ptx.scriptPayout.IsPayToScriptHash()) { + 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 payee key for other keys (don't allow people to put the payee 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"); + } + + Coin coin; + if (!GetUTXOCoin(dmn->collateralOutpoint, coin)) { + // this should never happen (there would be no dmn otherwise) + return state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral"); + } + + // don't allow reuse of collateral key for other keys (don't allow people to put the collateral key onto an online server) + CTxDestination collateralTxDest; + if (!ExtractDestination(coin.out.scriptPubKey, collateralTxDest)) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-collateral-dest"); + } + if (collateralTxDest == CTxDestination(dmn->pdmnState->keyIDOwner) || collateralTxDest == CTxDestination(ptx.keyIDVoting)) { + return state.DoS(10, false, REJECT_INVALID, "bad-protx-collateral-reuse"); + } + + 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 (!CheckInputsHash(tx, ptx, state)) { + return false; + } + if (!CheckHashSig(ptx, dmn->pdmnState->keyIDOwner, state)) { + return false; + } + } + + return true; +} + +bool CheckProUpRevTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + if (tx.nType != TRANSACTION_PROVIDER_UPDATE_REVOKE) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-type"); + } + + CProUpRevTx ptx; + if (!GetTxPayload(tx, ptx)) { + return state.DoS(100, false, REJECT_INVALID, "bad-protx-payload"); + } + + if (ptx.nVersion == 0 || 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 (!CheckInputsHash(tx, ptx, state)) + return false; + if (!CheckHashSig(ptx, dmn->pdmnState->pubKeyOperator, state)) + return false; + } + + return true; +} + +std::string CProRegTx::MakeSignString() const +{ + std::string s; + + // We only include the important stuff in the string form... + + CTxDestination destPayout; + CBitcoinAddress addrPayout; + std::string strPayout; + if (ExtractDestination(scriptPayout, destPayout) && addrPayout.Set(destPayout)) { + strPayout = addrPayout.ToString(); + } else { + strPayout = HexStr(scriptPayout.begin(), scriptPayout.end()); + } + + s += strPayout + "|"; + s += strprintf("%d", nOperatorReward) + "|"; + s += CBitcoinAddress(keyIDOwner).ToString() + "|"; + s += CBitcoinAddress(keyIDVoting).ToString() + "|"; + + // ... and also the full hash of the payload as a protection agains malleability and replays + s += ::SerializeHash(*this).ToString(); + + return s; +} + +std::string CProRegTx::ToString() const +{ + CTxDestination dest; + std::string payee = "unknown"; + if (ExtractDestination(scriptPayout, dest)) { + payee = CBitcoinAddress(dest).ToString(); + } + + return strprintf("CProRegTx(nVersion=%d, collateralOutpoint=%s, addr=%s, nOperatorReward=%f, keyIDOwner=%s, pubKeyOperator=%s, keyIDVoting=%s, scriptPayout=%s)", + nVersion, collateralOutpoint.ToStringShort(), 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("collateralHash", collateralOutpoint.hash.ToString())); + obj.push_back(Pair("collateralIndex", (int)collateralOutpoint.n)); + 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, addr=%s, operatorPayoutAddress=%s)", + nVersion, proTxHash.ToString(), 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("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())); +} diff --git a/src/evo/providertx.h b/src/evo/providertx.h new file mode 100644 index 000000000000..bb5c0f18ebc0 --- /dev/null +++ b/src/evo/providertx.h @@ -0,0 +1,186 @@ +// 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_PROVIDERTX_H +#define DASH_PROVIDERTX_H + +#include "bls/bls.h" +#include "consensus/validation.h" +#include "primitives/transaction.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 + uint16_t nType{0}; // only 0 supported for now + uint16_t nMode{0}; // only 0 supported for now + COutPoint collateralOutpoint{uint256(), (uint32_t)-1}; // if hash is null, we refer to a ProRegTx output + 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(nType); + READWRITE(nMode); + READWRITE(collateralOutpoint); + READWRITE(addr); + READWRITE(keyIDOwner); + READWRITE(pubKeyOperator); + READWRITE(keyIDVoting); + READWRITE(nOperatorReward); + READWRITE(*(CScriptBase*)(&scriptPayout)); + READWRITE(inputsHash); + if (!(s.GetType() & SER_GETHASH)) { + READWRITE(vchSig); + } + } + + // When signing with the collateral key, we don't sign the hash but a generated message instead + // This is needed for HW wallet support which can only sign text messages as of now + std::string MakeSignString() const; + + 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; + 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(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; + uint16_t nMode{0}; // only 0 supported for now + 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(nMode); + 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); + +#endif //DASH_PROVIDERTX_H diff --git a/src/evo/simplifiedmns.cpp b/src/evo/simplifiedmns.cpp new file mode 100644 index 000000000000..b630e06d318e --- /dev/null +++ b/src/evo/simplifiedmns.cpp @@ -0,0 +1,168 @@ +// Copyright (c) 2017-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 "cbtx.h" +#include "core_io.h" +#include "deterministicmns.h" +#include "simplifiedmns.h" +#include "specialtx.h" + +#include "chainparams.h" +#include "consensus/merkle.h" +#include "univalue.h" +#include "validation.h" + +CSimplifiedMNListEntry::CSimplifiedMNListEntry(const CDeterministicMN& dmn) : + proRegTxHash(dmn.proTxHash), + confirmedHash(dmn.pdmnState->confirmedHash), + 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, confirmedHash=%s, service=%s, pubKeyOperator=%s, keyIDVoting=%s, isValie=%d)", + proRegTxHash.ToString(), confirmedHash.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("confirmedHash", confirmedHash.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())); + + CDataStream ssCbTxMerkleTree(SER_NETWORK, PROTOCOL_VERSION); + ssCbTxMerkleTree << cbTxMerkleTree; + obj.push_back(Pair("cbTxMerkleTree", HexStr(ssCbTxMerkleTree.begin(), ssCbTxMerkleTree.end()))); + + obj.push_back(Pair("cbTx", EncodeHexTx(*cbTx))); + + 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); + mnListDiffRet = baseDmnList.BuildSimplifiedDiff(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.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); + + return true; +} diff --git a/src/evo/simplifiedmns.h b/src/evo/simplifiedmns.h new file mode 100644 index 000000000000..4266652197ab --- /dev/null +++ b/src/evo/simplifiedmns.h @@ -0,0 +1,130 @@ +// Copyright (c) 2017-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_SIMPLIFIEDMNS_H +#define DASH_SIMPLIFIEDMNS_H + +#include "bls/bls.h" +#include "merkleblock.h" +#include "netaddress.h" +#include "pubkey.h" +#include "serialize.h" + +class UniValue; +class CDeterministicMNList; +class CDeterministicMN; + +class CSimplifiedMNListEntry +{ +public: + uint256 proRegTxHash; + uint256 confirmedHash; + CService service; + CBLSPublicKey pubKeyOperator; + CKeyID keyIDVoting; + bool isValid; + +public: + CSimplifiedMNListEntry() {} + CSimplifiedMNListEntry(const CDeterministicMN& dmn); + + bool operator==(const CSimplifiedMNListEntry& rhs) const + { + return proRegTxHash == rhs.proRegTxHash && + confirmedHash == rhs.confirmedHash && + service == rhs.service && + pubKeyOperator == rhs.pubKeyOperator && + keyIDVoting == rhs.keyIDVoting && + isValid == rhs.isValid; + } + + bool operator!=(const CSimplifiedMNListEntry& rhs) const + { + return !(rhs == *this); + } + +public: + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(proRegTxHash); + READWRITE(confirmedHash); + 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 //DASH_SIMPLIFIEDMNS_H diff --git a/src/evo/specialtx.cpp b/src/evo/specialtx.cpp new file mode 100644 index 000000000000..9e916bb45698 --- /dev/null +++ b/src/evo/specialtx.cpp @@ -0,0 +1,143 @@ +// 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 "chainparams.h" +#include "clientversion.h" +#include "consensus/validation.h" +#include "hash.h" +#include "primitives/block.h" +#include "primitives/transaction.h" +#include "validation.h" + +#include "cbtx.h" +#include "deterministicmns.h" +#include "specialtx.h" + +#include "llmq/quorums_commitment.h" +#include "llmq/quorums_blockprocessor.h" + +bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + if (tx.nVersion != 3 || tx.nType == TRANSACTION_NORMAL) + return true; + + if (pindexPrev && VersionBitsState(pindexPrev, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0003, 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); + case TRANSACTION_QUORUM_COMMITMENT: + return llmq::CheckLLMQCommitment(tx, pindexPrev, state); + } + + return state.DoS(10, false, REJECT_INVALID, "bad-tx-type-check"); +} + +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 + case TRANSACTION_QUORUM_COMMITMENT: + return true; // handled per block + } + + return state.DoS(100, false, REJECT_INVALID, "bad-tx-type-proc"); +} + +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 + case TRANSACTION_QUORUM_COMMITMENT: + return true; // handled per block + } + + 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 (!llmq::quorumBlockProcessor->ProcessBlock(block, pindex, state)) { + return false; + } + + if (!deterministicMNManager->ProcessBlock(block, pindex, 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; + } + + if (!llmq::quorumBlockProcessor->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..9eb49ea02017 --- /dev/null +++ b/src/evo/specialtx.h @@ -0,0 +1,52 @@ +// 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_SPECIALTX_H +#define DASH_SPECIALTX_H + +#include "primitives/transaction.h" +#include "streams.h" +#include "version.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 //DASH_SPECIALTX_H diff --git a/src/governance-classes.cpp b/src/governance-classes.cpp index 5f6553ec1805..89a8b75056df 100644 --- a/src/governance-classes.cpp +++ b/src/governance-classes.cpp @@ -1,14 +1,14 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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. //#define ENABLE_DASH_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 @@ -24,19 +24,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()) { @@ -44,7 +44,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; @@ -70,7 +70,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()); @@ -91,7 +91,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; } @@ -102,44 +102,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; } @@ -153,33 +151,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"); @@ -195,16 +193,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) { @@ -215,13 +212,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;); } /** @@ -236,22 +232,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; } @@ -275,21 +267,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; } @@ -297,14 +288,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; } @@ -312,14 +303,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;); } } @@ -329,7 +319,7 @@ bool CSuperblockManager::IsSuperblockTriggered(int nBlockHeight) bool CSuperblockManager::GetBestSuperblock(CSuperblock_sptr& pSuperblockRet, int nBlockHeight) { - if(!CSuperblock::IsValidBlockHeight(nBlockHeight)) { + if (!CSuperblock::IsValidBlockHeight(nBlockHeight)) { return false; } @@ -338,31 +328,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;); } } @@ -370,47 +360,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 @@ -421,15 +410,17 @@ void CSuperblockManager::CreateSuperblock(CMutableTransaction& txNewRet, int nBl // TODO: PRINT NICE N.N DASH 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) @@ -438,7 +429,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); } @@ -450,7 +441,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(); @@ -458,36 +449,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"); } @@ -502,9 +494,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;); } /** @@ -517,7 +509,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) @@ -530,7 +522,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 { @@ -543,7 +535,7 @@ CAmount CSuperblock::GetPaymentsLimit(int nBlockHeight) { const Consensus::Params& consensusParams = Params().GetConsensus(); - if(!IsValidBlockHeight(nBlockHeight)) { + if (!IsValidBlockHeight(nBlockHeight)) { return 0; } @@ -588,13 +580,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 Dash Address : " << vecParsed1[i]; + ostr << "CSuperblock::ParsePaymentSchedule -- Invalid Dash Address : " << vecParsed1[i]; LogPrintf("%s\n", ostr.str()); throw std::runtime_error(ostr.str()); } @@ -604,29 +596,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() @@ -639,7 +630,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; } @@ -652,7 +643,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; } @@ -672,7 +663,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; } @@ -683,15 +674,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? @@ -702,22 +693,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; @@ -736,7 +727,7 @@ bool CSuperblock::IsValid(const CTransaction& txNew, int nBlockHeight, CAmount b } } - if(!fPaymentMatch) { + if (!fPaymentMatch) { // Superblock payment not found! CTxDestination address1; @@ -758,26 +749,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(); @@ -801,16 +792,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; @@ -819,10 +810,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 b75454675917..2e0946bea62c 100644 --- a/src/governance-classes.h +++ b/src/governance-classes.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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 GOVERNANCE_CLASSES_H @@ -12,17 +12,11 @@ #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; - -typedef boost::shared_ptr CSuperblock_sptr; +typedef std::shared_ptr CSuperblock_sptr; // DECLARE GLOBAL VARIABLES FOR GOVERNANCE CLASSES extern CGovernanceTriggerManager triggerman; @@ -42,7 +36,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; @@ -51,7 +44,8 @@ class CGovernanceTriggerManager void CleanAndRemove(); public: - CGovernanceTriggerManager() : mapTrigger() {} + CGovernanceTriggerManager() : + mapTrigger() {} }; /** @@ -66,10 +60,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); @@ -90,33 +83,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); } } @@ -153,7 +142,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 6df354f71133..100c4e4942dd 100644 --- a/src/governance-exceptions.h +++ b/src/governance-exceptions.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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. @@ -25,7 +25,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; @@ -63,11 +63,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; @@ -76,7 +76,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(); } @@ -91,7 +91,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 8ef5bf56dd33..06608d663a51 100644 --- a/src/governance-object.cpp +++ b/src/governance-object.cpp @@ -1,22 +1,23 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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 "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 -CGovernanceObject::CGovernanceObject(): +CGovernanceObject::CGovernanceObject() : cs(), nObjectType(GOVERNANCE_OBJECT_UNKNOWN), nHashParent(), @@ -44,7 +45,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), @@ -72,7 +73,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), @@ -95,12 +96,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); @@ -114,17 +116,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; @@ -133,14 +134,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()); @@ -151,7 +152,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()); @@ -161,9 +162,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() @@ -176,20 +177,22 @@ bool CGovernanceObject::ProcessVote(CNode* pfrom, } } + bool onlyVotingKeyAllowed = nObjectType == GOVERNANCE_OBJECT_PROPOSAL && vote.GetSignal() == VOTE_SIGNAL_FUNDING; + // Finally check that the vote is actually valid (done last because of cost of signature verification) - if(!vote.IsValid(true)) { + if (!vote.IsValid(onlyVotingKeyAllowed)) { 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() @@ -210,26 +213,70 @@ 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; } } } +std::set CGovernanceObject::RemoveInvalidProposalVotes(const COutPoint& mnOutpoint) +{ + LOCK(cs); + + if (nObjectType != GOVERNANCE_OBJECT_PROPOSAL) { + return {}; + } + + auto it = mapCurrentMNVotes.find(mnOutpoint); + if (it == mapCurrentMNVotes.end()) { + // don't even try as we don't have any votes from this MN + return {}; + } + + auto removedVotes = fileVotes.RemoveInvalidProposalVotes(mnOutpoint); + if (removedVotes.empty()) { + return {}; + } + + auto nParentHash = GetHash(); + for (auto jt = it->second.mapInstances.begin(); jt != it->second.mapInstances.end(); ) { + CGovernanceVote tmpVote(mnOutpoint, nParentHash, (vote_signal_enum_t)jt->first, jt->second.eOutcome); + tmpVote.SetTime(jt->second.nCreationTime); + if (removedVotes.count(tmpVote.GetHash())) { + jt = it->second.mapInstances.erase(jt); + } else { + ++jt; + } + } + if (it->second.mapInstances.empty()) { + mapCurrentMNVotes.erase(it); + } + + if (!removedVotes.empty()) { + std::string removedStr; + for (auto& h : removedVotes) { + removedStr += strprintf(" %s\n", h.ToString()); + } + LogPrintf("CGovernanceObject::%s -- Removed %d invalid votes for %s from MN %s:\n%s\n", __func__, removedVotes.size(), nParentHash.ToString(), mnOutpoint.ToString(), removedStr); + fDirtyCache = true; + } + + return removedVotes; +} + 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; } @@ -249,7 +296,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(); } @@ -264,53 +311,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; @@ -319,7 +366,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; } @@ -328,6 +375,27 @@ bool CGovernanceObject::CheckSignature(const CPubKey& pubKeyMasternode) const return true; } +bool CGovernanceObject::Sign(const CBLSSecretKey& key) +{ + CBLSSignature sig = key.Sign(GetSignatureHash()); + if (!key.IsValid()) { + return false; + } + 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. @@ -336,7 +404,7 @@ bool CGovernanceObject::CheckSignature(const CPubKey& pubKeyMasternode) const UniValue CGovernanceObject::GetJSONObject() { UniValue obj(UniValue::VOBJ); - if(vchData.empty()) { + if (vchData.empty()) { return obj; } @@ -347,8 +415,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; @@ -367,36 +435,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; } @@ -460,75 +526,85 @@ 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 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; - } - 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_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; } } @@ -544,19 +620,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; @@ -567,44 +643,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) { @@ -615,7 +689,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; @@ -623,7 +698,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; } @@ -640,7 +715,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; } } @@ -685,13 +760,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; } @@ -705,7 +780,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 @@ -722,40 +797,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) @@ -763,29 +814,65 @@ 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); } } } + +std::vector CGovernanceObject::RemoveOldVotes(unsigned int nMinTime) +{ + LOCK(cs); + + // Drop pre-DIP3 votes from vote db + auto removed = fileVotes.RemoveOldVotes(nMinTime); + + if (!removed.empty()) { + std::string removedStr; + for (auto& h : removed) { + removedStr += strprintf(" %s\n", h.ToString()); + } + LogPrintf("CGovernanceObject::RemoveOldVotes -- Removed %d old (pre-DIP3) votes for %s:\n%s\n", removed.size(), GetHash().ToString(), removedStr); + fDirtyCache = true; + } + + // Same for current votes per MN for this specific object + auto itMnPair = mapCurrentMNVotes.begin(); + while (itMnPair != mapCurrentMNVotes.end()) { + auto& miRef = itMnPair->second.mapInstances; + auto itVotePair = miRef.begin(); + while (itVotePair != miRef.end()) { + if (itVotePair->second.nCreationTime < nMinTime) { + miRef.erase(itVotePair++); + } else { + ++itVotePair; + } + } + if (miRef.empty()) { + mapCurrentMNVotes.erase(itMnPair++); + } else { + ++itMnPair; + } + } + + return removed; +} diff --git a/src/governance-object.h b/src/governance-object.h index 1fcb8f2978a1..ae3f55321a4e 100644 --- a/src/governance-object.h +++ b/src/governance-object.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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. @@ -16,6 +16,7 @@ #include "sync.h" #include "util.h" #include "utilstrencodings.h" +#include "bls/bls.h" #include @@ -24,8 +25,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; @@ -35,20 +35,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; @@ -58,16 +58,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; @@ -78,13 +78,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; @@ -95,11 +95,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); + } }; /** @@ -194,67 +194,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; @@ -305,44 +313,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 == 70208 && (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 == 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); } - 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); @@ -355,27 +336,27 @@ 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(); + // Revalidate all votes from this MN and delete them if validation fails + // This is the case for DIP3 MNs that change voting keys. Returns deleted vote hashes + std::set RemoveInvalidProposalVotes(const COutPoint& mnOutpoint); + void CheckOrphanVotes(CConnman& connman); + // TODO can be removed after DIP3 is fully deployed + std::vector RemoveOldVotes(unsigned int nMinTime); }; diff --git a/src/governance-validators.cpp b/src/governance-validators.cpp index dc70494a3f91..3e9a36a1a377 100644 --- a/src/governance-validators.cpp +++ b/src/governance-validators.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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. @@ -11,15 +11,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); } } @@ -36,27 +36,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; } @@ -66,12 +66,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; } @@ -80,7 +80,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; } @@ -93,22 +93,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; } @@ -120,12 +120,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; } @@ -141,23 +141,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; } @@ -168,22 +168,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; } @@ -195,7 +195,7 @@ void CProposalValidator::ParseJSONData(const std::string& strJSONData) { fJSONValid = false; - if(strJSONData.empty()) { + if (strJSONData.empty()) { return; } @@ -213,11 +213,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;"; } } @@ -225,14 +223,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; @@ -241,9 +237,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; @@ -251,11 +247,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; @@ -264,9 +258,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; @@ -274,11 +268,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; @@ -295,32 +287,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 bcdb49fc883f..e9de02ae1079 100644 --- a/src/governance-validators.h +++ b/src/governance-validators.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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. @@ -12,9 +12,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 815497a978de..15a145df12c4 100644 --- a/src/governance-vote.cpp +++ b/src/governance-vote.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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. @@ -9,73 +9,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()) { @@ -85,26 +75,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(); } @@ -122,7 +113,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; } @@ -154,33 +145,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; } @@ -189,21 +179,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; @@ -211,11 +201,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; } @@ -224,36 +214,63 @@ 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); + if (!sig.IsValid()) { + return false; + } + sig.GetBuf(vchSig); + return true; +} + +bool CGovernanceVote::CheckSignature(const CBLSPublicKey& pubKey) const +{ + 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)); + 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) @@ -269,25 +286,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 19909e49c003..6879e2ba0191 100644 --- a/src/governance-vote.h +++ b/src/governance-vote.h @@ -1,20 +1,19 @@ -// Copyright (c) 2014-2017 The Dash Core developers - +// Copyright (c) 2014-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 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, @@ -23,7 +22,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 @@ -49,7 +48,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 @@ -59,8 +58,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; @@ -82,25 +81,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; } /** @@ -117,22 +118,9 @@ class CGovernanceVote ADD_SERIALIZE_METHODS; template - inline void SerializationOp(Stream& s, Operation ser_action) { - 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); - } + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(masternodeOutpoint); READWRITE(nParentHash); READWRITE(nVoteOutcome); READWRITE(nVoteSignal); @@ -143,7 +131,6 @@ class CGovernanceVote if (ser_action.ForRead()) UpdateHash(); } - }; #endif diff --git a/src/governance-votedb.cpp b/src/governance-votedb.cpp index 403c0e882911..c45586c3276f 100644 --- a/src/governance-votedb.cpp +++ b/src/governance-votedb.cpp @@ -1,19 +1,20 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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 "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(); } @@ -37,7 +38,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); @@ -47,7 +48,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; @@ -56,16 +57,53 @@ 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 { + ++it; + } + } +} + +std::set CGovernanceObjectVoteFile::RemoveInvalidProposalVotes(const COutPoint& outpointMasternode) +{ + std::set removedVotes; + + vote_l_it it = listVotes.begin(); + while (it != listVotes.end()) { + if (it->GetSignal() == VOTE_SIGNAL_FUNDING && it->GetMasternodeOutpoint() == outpointMasternode) { + if (!it->IsValid(true)) { + removedVotes.emplace(it->GetHash()); + --nMemoryVotes; + mapVoteIndex.erase(it->GetHash()); + listVotes.erase(it++); + continue; + } } - else { + ++it; + } + + return removedVotes; +} + +std::vector CGovernanceObjectVoteFile::RemoveOldVotes(unsigned int nMinTime) +{ + std::vector removed; + vote_l_it it = listVotes.begin(); + while (it != listVotes.end()) { + if (it->GetTimestamp() < nMinTime) { + --nMemoryVotes; + removed.emplace_back(it->GetHash()); + mapVoteIndex.erase(it->GetHash()); + listVotes.erase(it++); + } else { ++it; } } + return removed; } void CGovernanceObjectVoteFile::RebuildIndex() @@ -73,15 +111,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 73638c8694ef..5d98f826bbd4 100644 --- a/src/governance-votedb.h +++ b/src/governance-votedb.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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. @@ -30,7 +30,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; @@ -65,13 +65,18 @@ class CGovernanceObjectVoteFile */ bool SerializeVoteToStream(const uint256& nHash, CDataStream& ss) const; - int GetVoteCount() { + int GetVoteCount() + { return nMemoryVotes; } std::vector GetVotes() const; void RemoveVotesFromMasternode(const COutPoint& outpointMasternode); + std::set RemoveInvalidProposalVotes(const COutPoint& outpointMasternode); + + // TODO can be removed after full DIP3 deployment + std::vector RemoveOldVotes(unsigned int nMinTime); ADD_SERIALIZE_METHODS; @@ -80,13 +85,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 4d33b6dde224..e2917a07131c 100644 --- a/src/governance.cpp +++ b/src/governance.cpp @@ -1,44 +1,47 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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 "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 "init.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 std::string CGovernanceManager::SERIALIZATION_VERSION_STRING = "CGovernanceManager-Version-14"; +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 @@ -64,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); } @@ -78,24 +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; } @@ -109,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); @@ -126,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; @@ -135,16 +133,18 @@ void CGovernanceManager::ProcessMessage(CNode* pfrom, const std::string& strComm uint256 nHash = govobj.GetHash(); - pfrom->setAskFor.erase(nHash); + { + LOCK(cs_main); + connman.RemoveAskFor(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; } @@ -153,22 +153,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; } @@ -180,16 +180,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()); @@ -203,7 +202,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 { @@ -219,23 +218,30 @@ 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; + // TODO remove this check after full DIP3 deployment + if (vote.GetTimestamp() < GetMinVoteTime()) { + // Ignore votes pre-DIP3 + return; + } + uint256 nHash = vote.GetHash(); - pfrom->setAskFor.erase(nHash); + { + LOCK(cs_main); + connman.RemoveAskFor(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; } @@ -244,27 +250,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); } } @@ -277,19 +283,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); } } @@ -297,7 +301,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(); @@ -311,7 +315,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; } @@ -322,20 +326,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; @@ -345,10 +349,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 @@ -361,7 +365,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() @@ -372,9 +380,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(); @@ -389,11 +397,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; } @@ -402,7 +409,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(); @@ -415,30 +422,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 { @@ -466,11 +472,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()); @@ -480,10 +487,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 @@ -492,7 +498,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; } @@ -506,28 +512,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); @@ -545,24 +550,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; @@ -572,16 +568,21 @@ 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() || ShutdownRequested()) return; + + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + ClearPreDIP3Votes(); + RemoveInvalidProposalVotes(); + } // CHECK OBJECTS WE'VE ASKED FOR, REMOVE OLD ENTRIES @@ -597,38 +598,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; - case MSG_GOVERNANCE_OBJECT_VOTE: - { - if(cmapVoteToObject.HasKey(inv.hash)) { + break; + } + 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; @@ -640,7 +639,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"); } @@ -652,7 +651,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; @@ -664,7 +663,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; } @@ -673,9 +672,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; } @@ -687,7 +686,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 onlyVotingKeyAllowed = govobj.GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL && vote.GetSignal() == VOTE_SIGNAL_FUNDING; + + if (filter.contains(nVoteHash) || !vote.IsValid(onlyVotingKeyAllowed)) { continue; } pnode->PushInventory(CInv(MSG_GOVERNANCE_OBJECT_VOTE, nVoteHash)); @@ -703,9 +705,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__); @@ -724,21 +726,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; } @@ -750,14 +753,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); @@ -782,15 +785,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; } @@ -801,23 +800,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; } @@ -831,15 +829,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; } @@ -850,17 +849,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); @@ -868,12 +867,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()); @@ -887,7 +886,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; @@ -904,8 +903,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); } } @@ -915,19 +914,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; } @@ -937,7 +936,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++); @@ -946,13 +945,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; @@ -960,14 +958,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; @@ -982,11 +980,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(); @@ -994,8 +990,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 { @@ -1014,15 +1010,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; } @@ -1031,16 +1027,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()); } } } @@ -1051,7 +1047,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); @@ -1061,14 +1057,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, @@ -1077,52 +1073,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) { @@ -1130,33 +1127,36 @@ 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; - // to early to ask the same node - if(mapAskedRecently[nHashGovobj].count(pnode->addr)) continue; + { + LOCK(cs_main); + size_t nProjectedSize = pnode->setAskFor.size() + nProjectedVotes; + if (nProjectedSize > SETASKFOR_MAX_SZ / 2) continue; + // to early to ask the same node + 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) @@ -1174,7 +1174,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; } @@ -1188,10 +1188,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); } } @@ -1204,7 +1204,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; } @@ -1236,27 +1236,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 @@ -1268,16 +1265,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; } } @@ -1291,20 +1288,25 @@ 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; } nCachedBlockHeight = pindex->nHeight; LogPrint("gobject", "CGovernanceManager::UpdatedBlockTip -- nCachedBlockHeight: %d\n", nCachedBlockHeight); + if (deterministicMNManager->IsDeterministicMNsSporkActive(pindex->nHeight)) { + ClearPreDIP3Votes(); + RemoveInvalidProposalVotes(); + } + CheckPostponedObjects(connman); CSuperblockManager::ExecuteBestSuperblock(pindex->nHeight); @@ -1319,20 +1321,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); @@ -1350,12 +1349,85 @@ 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); } } } + +void CGovernanceManager::RemoveInvalidProposalVotes() +{ + auto curMNList = deterministicMNManager->GetListAtChainTip(); + auto diff = lastMNListForVotingKeys.BuildDiff(curMNList); + + LOCK(cs); + + std::vector changedKeyMNs; + for (const auto& p : diff.updatedMNs) { + auto oldDmn = lastMNListForVotingKeys.GetMN(p.first); + if (p.second->keyIDVoting != oldDmn->pdmnState->keyIDVoting) { + changedKeyMNs.emplace_back(oldDmn->collateralOutpoint); + } + } + for (const auto& proTxHash : diff.removedMns) { + auto oldDmn = lastMNListForVotingKeys.GetMN(proTxHash); + changedKeyMNs.emplace_back(oldDmn->collateralOutpoint); + } + + for (const auto& outpoint : changedKeyMNs) { + for (auto& p : mapObjects) { + auto removed = p.second.RemoveInvalidProposalVotes(outpoint); + if (removed.empty()) { + continue; + } + for (auto& voteHash : removed) { + cmapVoteToObject.Erase(voteHash); + cmapInvalidVotes.Erase(voteHash); + cmmapOrphanVotes.Erase(voteHash); + setRequestedVotes.erase(voteHash); + } + } + } + + // store current MN list for the next run so that we can determine which keys changed + lastMNListForVotingKeys = curMNList; +} + + +unsigned int CGovernanceManager::GetMinVoteTime() +{ + LOCK(cs_main); + if (!deterministicMNManager->IsDeterministicMNsSporkActive()) { + return 0; + } + int64_t dip3SporkHeight = sporkManager.GetSporkValue(SPORK_15_DETERMINISTIC_MNS_ENABLED); + return chainActive[dip3SporkHeight]->nTime; +} + +void CGovernanceManager::ClearPreDIP3Votes() +{ + // This removes all votes which were created before DIP3 spork15 activation + // All these votes are invalid immediately after spork15 activation due to the introduction of voting keys, which + // are not equal to the old masternode private keys + + unsigned int minVoteTime = GetMinVoteTime(); + + LOCK(cs); + for (auto& p : mapObjects) { + auto& obj = p.second; + auto removed = obj.RemoveOldVotes(minVoteTime); + if (removed.empty()) { + continue; + } + for (auto& voteHash : removed) { + cmapVoteToObject.Erase(voteHash); + cmapInvalidVotes.Erase(voteHash); + cmmapOrphanVotes.Erase(voteHash); + setRequestedVotes.erase(voteHash); + } + } +} diff --git a/src/governance.h b/src/governance.h index 73be71e533e2..6571347795e8 100644 --- a/src/governance.h +++ b/src/governance.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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. @@ -19,6 +19,8 @@ #include "timedata.h" #include "util.h" +#include "evo/deterministicmns.h" + #include class CGovernanceManager; @@ -29,7 +31,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; @@ -51,16 +54,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; } @@ -73,15 +77,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; } @@ -89,28 +93,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; } @@ -120,12 +123,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; } @@ -153,10 +156,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; @@ -192,7 +196,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; @@ -259,13 +263,17 @@ class CGovernanceManager bool fRateChecksEnabled; + // used to check for changed voting keys + CDeterministicMNList lastMNListForVotingKeys; + class ScopedLockBool { bool& ref; 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; @@ -307,11 +315,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() { @@ -332,13 +340,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); } @@ -348,13 +356,14 @@ class CGovernanceManager READWRITE(cmmapOrphanVotes); READWRITE(mapObjects); READWRITE(mapLastMasternodeObject); - if(ser_action.ForRead() && (strVersion != SERIALIZATION_VERSION_STRING)) { + READWRITE(lastMNListForVotingKeys); + 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; } @@ -387,9 +396,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; @@ -401,7 +411,8 @@ class CGovernanceManager void CheckPostponedObjects(CConnman& connman); - bool AreRateChecksEnabled() const { + bool AreRateChecksEnabled() const + { LOCK(cs); return fRateChecksEnabled; } @@ -444,6 +455,11 @@ class CGovernanceManager void CleanOrphanObjects(); + void RemoveInvalidProposalVotes(); + + // TODO can be removed after full DIP3 deployment + unsigned int GetMinVoteTime(); + void ClearPreDIP3Votes(); }; #endif diff --git a/src/hdchain.cpp b/src/hdchain.cpp index 19bdc0e23da4..ae31a052ad9a 100644 --- a/src/hdchain.cpp +++ b/src/hdchain.cpp @@ -169,9 +169,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/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 e60cd794e085..92e94a383ec2 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -66,10 +66,16 @@ #include "spork.h" #include "warnings.h" +#include "evo/deterministicmns.h" + +#include "llmq/quorums_init.h" + #include #include #include +#include "bls/bls.h" + #ifndef WIN32 #include #endif @@ -232,7 +238,18 @@ void PrepareShutdown() StopREST(); StopRPC(); StopHTTPServer(); + + // fRPCInWarmup should be `false` if we completed the loading sequence + // before a shutdown request was received + std::string statusmessage; + bool fRPCInWarmup = RPCIsInWarmup(&statusmessage); + #ifdef ENABLE_WALLET + if (!fLiteMode && !fRPCInWarmup) { + // Stop PrivateSend, release keys + privateSendClient.fEnablePrivateSend = false; + privateSendClient.ResetPool(); + } if (pwalletMain) pwalletMain->Flush(false); #endif @@ -241,8 +258,8 @@ void PrepareShutdown() peerLogic.reset(); g_connman.reset(); - // STORE DATA CACHES INTO SERIALIZED DAT FILES - if (!fLiteMode) { + if (!fLiteMode && !fRPCInWarmup) { + // STORE DATA CACHES INTO SERIALIZED DAT FILES CFlatDB flatdb1("mncache.dat", "magicMasternodeCache"); flatdb1.Dump(mnodeman); CFlatDB flatdb2("mnpayments.dat", "magicMasternodePaymentsCache"); @@ -251,6 +268,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 +305,11 @@ void PrepareShutdown() pcoinsdbview = NULL; delete pblocktree; pblocktree = NULL; + llmq::DestroyLLMQSystem(); + delete deterministicMNManager; + deterministicMNManager = NULL; + delete evoDb; + evoDb = NULL; } #ifdef ENABLE_WALLET if (pwalletMain) @@ -300,6 +329,13 @@ void PrepareShutdown() delete pdsNotificationInterface; pdsNotificationInterface = NULL; } + if (fMasternodeMode) { + UnregisterValidationInterface(activeMasternodeManager); + } + + // make sure to clean up BLS keys before global destructors are called (they have allocated from the secure memory pool) + activeMasternodeInfo.blsKeyOperator.reset(); + activeMasternodeInfo.blsPubKeyOperator.reset(); #ifndef WIN32 try { @@ -485,9 +521,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:")); @@ -545,17 +585,20 @@ std::string HelpMessage(HelpMessageMode mode) AppendParamsHelpMessages(strUsage, showDebug); strUsage += HelpMessageOpt("-litemode=", strprintf(_("Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u)"), 0)); strUsage += HelpMessageOpt("-sporkaddr=", strprintf(_("Override spork address. Only useful for regtest and devnet. 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 devnet. 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)); strUsage += HelpMessageOpt("-mnconf=", strprintf(_("Specify masternode configuration file (default: %s)"), "masternode.conf")); strUsage += HelpMessageOpt("-mnconflock=", strprintf(_("Lock masternodes from masternode configuration file (default: %u)"), 1)); strUsage += HelpMessageOpt("-masternodeprivkey=", _("Set the masternode private key")); + strUsage += HelpMessageOpt("-masternodeblsprivkey=", _("Set the masternode BLS private key")); #ifdef ENABLE_WALLET 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 DASH 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)"), @@ -564,7 +607,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)")); @@ -577,7 +619,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)); @@ -774,6 +815,30 @@ 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(); + + bool fDIP003Active; + { + LOCK(cs_main); + if (chainActive.Tip()->pprev) { + fDIP003Active = VersionBitsState(chainActive.Tip()->pprev, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE; + } + } + + if (activeMasternodeManager && fDIP003Active) + activeMasternodeManager->Init(); + +#ifdef ENABLE_WALLET + // we can't do this before DIP3 is fully initialized + if (pwalletMain) { + pwalletMain->AutoLockMasternodeCollaterals(); + } +#endif + LoadMempool(); fDumpMempoolLater = !fRequestShutdown; } @@ -791,6 +856,10 @@ bool InitSanityCheck(void) if (!glibc_sanity_test() || !glibcxx_sanity_test()) return false; + if (!BLSInit()) { + return false; + } + return true; } @@ -886,16 +955,13 @@ void InitParameterInteraction() LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__); } - if(!GetBoolArg("-enableinstantsend", fEnableInstantSend)){ - if (SoftSetArg("-instantsenddepth", "0")) - LogPrintf("%s: parameter interaction: -enableinstantsend=false -> setting -nInstantSendDepth=0\n", __func__); - } - #ifdef ENABLE_WALLET int nLiqProvTmp = GetArg("-liquidityprovider", DEFAULT_PRIVATESEND_LIQUIDITY); 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)); @@ -1222,15 +1288,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()) { @@ -1266,6 +1323,40 @@ bool AppInitParameterInteraction() } } + if (IsArgSet("-budgetparams")) { + // Allow overriding budget parameters for testing + if (!chainparams.MineBlocksOnDemand()) { + return InitError("Budget parameters may only be overridden on regtest."); + } + + std::string strBudgetParams = GetArg("-budgetparams", ""); + std::vector vBudgetParams; + boost::split(vBudgetParams, strBudgetParams, boost::is_any_of(":")); + if (vBudgetParams.size() != 3) { + return InitError("Budget parameters malformed, expecting masternodePaymentsStartBlock:budgetPaymentsStartBlock:superblockStartBlock"); + } + int nMasternodePaymentsStartBlock, nBudgetPaymentsStartBlock, nSuperblockStartBlock; + if (!ParseInt32(vBudgetParams[0], &nMasternodePaymentsStartBlock)) { + return InitError(strprintf("Invalid nMasternodePaymentsStartBlock (%s)", vBudgetParams[0])); + } + if (!ParseInt32(vBudgetParams[1], &nBudgetPaymentsStartBlock)) { + return InitError(strprintf("Invalid nBudgetPaymentsStartBlock (%s)", vBudgetParams[1])); + } + if (!ParseInt32(vBudgetParams[2], &nSuperblockStartBlock)) { + return InitError(strprintf("Invalid nSuperblockStartBlock (%s)", vBudgetParams[2])); + } + UpdateRegtestBudgetParameters(nMasternodePaymentsStartBlock, nBudgetPaymentsStartBlock, nSuperblockStartBlock); + } + + if (chainparams.NetworkIDString() == CBaseChainParams::DEVNET) { + int nMinimumDifficultyBlocks = GetArg("-minimumdifficultyblocks", chainparams.GetConsensus().nMinimumDifficultyBlocks); + int nHighSubsidyBlocks = GetArg("-highsubsidyblocks", chainparams.GetConsensus().nHighSubsidyBlocks); + int nHighSubsidyFactor = GetArg("-highsubsidyfactor", chainparams.GetConsensus().nHighSubsidyFactor); + UpdateDevnetSubsidyAndDiffParams(nMinimumDifficultyBlocks, nHighSubsidyBlocks, nHighSubsidyFactor); + } else if (IsArgSet("-minimumdifficultyblocks") || IsArgSet("-highsubsidyblocks") || IsArgSet("-highsubsidyfactor")) { + return InitError("Difficulty and subsidy parameters may only be overridden on devnet."); + } + return true; } @@ -1347,13 +1438,28 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) threadGroup.create_thread(&ThreadScriptCheck); } - if (!sporkManager.SetSporkAddress(GetArg("-sporkaddr", Params().SporkAddress()))) - return InitError(_("Invalid spork address specified with -sporkaddr")); + 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", ""))) + 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 @@ -1508,7 +1614,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) @@ -1548,7 +1654,30 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) nMaxOutboundLimit = GetArg("-maxuploadtarget", DEFAULT_MAX_UPLOAD_TARGET)*1024*1024; } - // ********************************************************* Step 7: load block chain + // ********************************************************* Step 7a: check lite mode and load sporks + + // lite mode disables all Dash-specific functionality + fLiteMode = GetBoolArg("-litemode", false); + LogPrintf("fLiteMode %d\n", fLiteMode); + + if(fLiteMode) { + InitWarning(_("You are starting in lite mode, all Dash-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 + return InitError(_("Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index.")); + } + + if (!fLiteMode) { + uiInterface.InitMessage(_("Loading sporks cache...")); + CFlatDB flatdb6("sporks.dat", "magicSporkCache"); + if (!flatdb6.Load(sporkManager)) { + return InitError(_("Failed to load sporks cache from") + "\n" + (GetDataDir() / "sporks.dat").string()); + } + } + + // ********************************************************* Step 7b: load block chain fReindex = GetBoolArg("-reindex", false); bool fReindexChainState = GetBoolArg("-reindex-chainstate", false); @@ -1592,6 +1721,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)); @@ -1614,11 +1744,17 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) delete pcoinsdbview; delete pcoinscatcher; delete pblocktree; + llmq::DestroyLLMQSystem(); + delete deterministicMNManager; + delete evoDb; + evoDb = new CEvoDB(nEvoDbCache, false, fReindex || fReindexChainState); + deterministicMNManager = new CDeterministicMNManager(*evoDb); pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex); pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex || fReindexChainState); pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview); pcoinsTip = new CCoinsViewCache(pcoinscatcher); + llmq::InitLLMQSystem(*evoDb); if (fReindex) { pblocktree->WriteReindexing(true); @@ -1790,22 +1926,10 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) uiInterface.NotifyBlockTip.disconnect(BlockNotifyGenesisWait); } - // ********************************************************* Step 11a: setup PrivateSend + // ********************************************************* Step 11a: setup Masternode related stuff fMasternodeMode = GetBoolArg("-masternode", false); // TODO: masternode should have no wallet - //lite mode disables all Dash-specific functionality - fLiteMode = GetBoolArg("-litemode", false); - - if(fLiteMode) { - InitWarning(_("You are starting in lite mode, all Dash-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 - return InitError(_("Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index.")); - } - if(fLiteMode && fMasternodeMode) { return InitError(_("You can not start a masternode in lite mode.")); } @@ -1815,13 +1939,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 @@ -1846,6 +2000,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) } } + // ********************************************************* Step 11b: setup PrivateSend + privateSendClient.nLiquidityProvider = std::min(std::max((int)GetArg("-liquidityprovider", DEFAULT_PRIVATESEND_LIQUIDITY), MIN_PRIVATESEND_LIQUIDITY), MAX_PRIVATESEND_LIQUIDITY); int nMaxRounds = MAX_PRIVATESEND_ROUNDS; if(privateSendClient.nLiquidityProvider) { @@ -1856,17 +2012,10 @@ 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); LogPrintf("PrivateSend amount: %d\n", privateSendClient.nPrivateSendAmount); @@ -1874,7 +2023,11 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) CPrivateSend::InitStandardDenominations(); - // ********************************************************* Step 11b: Load cache data + // ********************************************************* Step 11c: setup InstantSend + + fEnableInstantSend = GetBoolArg("-enableinstantsend", 1); + + // ********************************************************* Step 11d: Load cache data // LOAD SERIALIZED DAT FILES INTO DATA CACHES FOR INTERNAL USE @@ -1914,25 +2067,38 @@ 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()); + } + } } + // ********************************************************* Step 11c: schedule Dash-specific tasks - // ********************************************************* Step 11c: update block tip in Dash 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)), 1); + 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 dash-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 acccc7ea4c50..49bd568528aa 100644 --- a/src/instantx.cpp +++ b/src/instantx.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "activemasternode.h" +#include "init.h" #include "instantx.h" #include "key.h" #include "validation.h" @@ -33,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 // @@ -51,13 +55,12 @@ CInstantSend instantsend; void CInstantSend::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) { - if(fLiteMode) return; // disable all Dash specific functionality - if(!sporkManager.IsSporkActive(SPORK_2_INSTANTSEND_ENABLED)) return; + if (fLiteMode) return; // disable all Dash 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, @@ -71,10 +74,13 @@ void CInstantSend::ProcessMessage(CNode* pfrom, const std::string& strCommand, C uint256 nVoteHash = vote.GetHash(); - pfrom->setAskFor.erase(nVoteHash); + { + LOCK(cs_main); + connman.RemoveAskFor(nVoteHash); + } // Ignore any InstantSend messages until masternode list is synced - if(!masternodeSync.IsMasternodeListSynced()) return; + if (!masternodeSync.IsMasternodeListSynced()) return; { LOCK(cs_instantsend); @@ -101,7 +107,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()); @@ -112,18 +118,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; @@ -142,19 +154,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)); @@ -168,7 +180,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 { @@ -211,76 +223,77 @@ 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; } int nLockInputHeight = nPrevoutHeight + Params().GetConsensus().nInstantSendConfirmationsRequired - 2; int nRank; + uint256 quorumModifierHash; 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, quorumModifierHash, nLockInputHeight, nMinRequiredProtocol)) { + LogPrint("instantsend", "CInstantSend::Vote -- Can't calculate rank for masternode %s\n", activeMasternodeInfo.outpoint.ToStringShort()); continue; } + if (!deterministicMNManager->IsDeterministicMNsSporkActive()) { + // not used until spork15 activation + quorumModifierHash = uint256(); + } 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); + // Please note that activeMasternodeInfo.proTxHash is only valid after spork15 activation + CTxLockVote vote(txHash, outpointLockPair.first, activeMasternodeInfo.outpoint, quorumModifierHash, activeMasternodeInfo.proTxHash); - 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; } @@ -288,27 +301,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; } } @@ -317,7 +328,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; @@ -336,9 +347,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); } @@ -351,10 +362,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); @@ -379,7 +390,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; } @@ -408,7 +419,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; @@ -422,7 +433,7 @@ bool CInstantSend::ProcessOrphanTxLockVote(const CTxLockVote& vote) UpdateVotedOutpoints(vote, txLockCandidate); - if(!txLockCandidate.AddVote(vote)) { + if (!txLockCandidate.AddVote(vote)) { // this should never happen return false; } @@ -442,14 +453,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, @@ -478,8 +489,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; @@ -489,16 +500,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); } @@ -510,22 +521,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 } @@ -539,19 +551,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()); } @@ -560,7 +569,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; } @@ -573,18 +582,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()); @@ -614,7 +623,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; @@ -623,14 +632,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; @@ -645,14 +654,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(); @@ -660,23 +666,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); @@ -688,8 +693,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++); @@ -700,8 +705,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); @@ -713,8 +718,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++); @@ -725,8 +730,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++); @@ -768,7 +773,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; @@ -779,44 +784,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; @@ -824,28 +830,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); @@ -891,63 +892,73 @@ 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()); } +void CInstantSend::DoMaintenance() +{ + if (ShutdownRequested()) return; + + CheckAndRemove(); +} + +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; } @@ -960,7 +971,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; } @@ -969,7 +980,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; @@ -978,14 +989,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; } @@ -993,8 +1004,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)); } @@ -1004,20 +1018,38 @@ 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; } + // Verify that masternodeProTxHash belongs to the same MN referred by the collateral + // Only v13 nodes will send us locks with this field set, and only after spork15 activation + if (!masternodeProTxHash.IsNull()) { + masternode_info_t mnInfo; + if (!mnodeman.GetMasternodeInfo(masternodeProTxHash, mnInfo) || mnInfo.outpoint != outpointMasternode) { + LogPrint("instantsend", "CTxLockVote::IsValid -- invalid masternodeProTxHash %s\n", masternodeProTxHash.ToString()); + return false; + } + } else if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + LogPrint("instantsend", "CTxLockVote::IsValid -- missing masternodeProTxHash while DIP3 is active\n"); + 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; } @@ -1025,22 +1057,33 @@ bool CTxLockVote::IsValid(CNode* pnode, CConnman& connman) const int nLockInputHeight = coin.nHeight + Params().GetConsensus().nInstantSendConfirmationsRequired - 2; int nRank; + uint256 expectedQuorumModifierHash; int nMinRequiredProtocol = std::max(MIN_INSTANTSEND_PROTO_VERSION, mnpayments.GetMinMasternodePaymentsProto()); - if(!mnodeman.GetMasternodeRank(outpointMasternode, nRank, nLockInputHeight, nMinRequiredProtocol)) { + if (!mnodeman.GetMasternodeRank(outpointMasternode, nRank, expectedQuorumModifierHash, 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; } + if (!quorumModifierHash.IsNull()) { + if (quorumModifierHash != expectedQuorumModifierHash) { + LogPrint("instantsend", "CTxLockVote::IsValid -- invalid quorumModifierHash %s, expected %s\n", quorumModifierHash.ToString(), expectedQuorumModifierHash.ToString()); + return false; + } + } else if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + LogPrint("instantsend", "CTxLockVote::IsValid -- missing quorumModifierHash while DIP3 is active\n"); + return false; + } + 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; } @@ -1064,18 +1107,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; @@ -1083,7 +1135,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; } @@ -1096,27 +1148,35 @@ 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); + if (!sig.IsValid()) { + return false; + } + 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; } @@ -1128,7 +1188,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) && request) { + connman.RelayInvFiltered(inv, *request.tx); + } else { + connman.RelayInvFiltered(inv, txHash); + } } bool CTxLockVote::IsExpired(int nHeight) const @@ -1153,19 +1218,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; } @@ -1177,10 +1237,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); } } @@ -1196,25 +1254,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; } @@ -1229,10 +1285,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; } @@ -1251,9 +1305,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 ac2c79749fce..81f18c23b66c 100644 --- a/src/instantx.h +++ b/src/instantx.h @@ -8,6 +8,8 @@ #include "net.h" #include "primitives/transaction.h" +#include "evo/deterministicmns.h" + class CTxLockVote; class COutPointLock; class CTxLockRequest; @@ -26,15 +28,7 @@ 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 = 70208; +static const int MIN_INSTANTSEND_PROTO_VERSION = 70210; /// For how long we are going to accept votes/locks /// after we saw the first one for a specific transaction @@ -44,7 +38,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; /** @@ -53,6 +46,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; @@ -88,10 +86,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); @@ -113,8 +139,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(); @@ -126,7 +150,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(); + + /// checks if we can automatically lock "simple" transactions + static bool CanAutoLock(); + + /// flag of the AutoLock Bip9 activation + static std::atomic isAutoLockBip9Active; }; /** @@ -136,6 +168,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 @@ -146,6 +181,7 @@ class CTxLockRequest CTxLockRequest() : tx(MakeTransactionRef()) {} CTxLockRequest(const CTransaction& _tx) : tx(MakeTransactionRef(_tx)) {}; + CTxLockRequest(const CTransactionRef& _tx) : tx(_tx) {}; ADD_SERIALIZE_METHODS; @@ -155,9 +191,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(); } @@ -194,7 +233,10 @@ class CTxLockVote private: uint256 txHash; COutPoint outpoint; + // TODO remove this member when the legacy masternode code is removed after DIP3 deployment COutPoint outpointMasternode; + uint256 quorumModifierHash; + uint256 masternodeProTxHash; std::vector vchMasternodeSignature; // local memory only int nConfirmedHeight; ///< When corresponding tx is 0-confirmed or conflicted, nConfirmedHeight is -1 @@ -205,15 +247,19 @@ class CTxLockVote txHash(), outpoint(), outpointMasternode(), + quorumModifierHash(), + masternodeProTxHash(), vchMasternodeSignature(), nConfirmedHeight(-1), nTimeCreated(GetTime()) {} - CTxLockVote(const uint256& txHashIn, const COutPoint& outpointIn, const COutPoint& outpointMasternodeIn) : + CTxLockVote(const uint256& txHashIn, const COutPoint& outpointIn, const COutPoint& outpointMasternodeIn, const uint256& quorumModifierHashIn, const uint256& masternodeProTxHashIn) : txHash(txHashIn), outpoint(outpointIn), outpointMasternode(outpointMasternodeIn), + quorumModifierHash(quorumModifierHashIn), + masternodeProTxHash(masternodeProTxHashIn), vchMasternodeSignature(), nConfirmedHeight(-1), nTimeCreated(GetTime()) @@ -226,6 +272,12 @@ class CTxLockVote READWRITE(txHash); READWRITE(outpoint); READWRITE(outpointMasternode); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + // Starting with spork15 activation, the proTxHash and quorumModifierHash is included. When we bump to >= 70214, we can remove + // the surrounding if. We might also remove outpointMasternode as well later + READWRITE(quorumModifierHash); + READWRITE(masternodeProTxHash); + } if (!(s.GetType() & SER_GETHASH)) { READWRITE(vchMasternodeSignature); } @@ -264,6 +316,8 @@ class COutPointLock static const int SIGNATURES_REQUIRED = 6; static const int SIGNATURES_TOTAL = 10; + COutPointLock() {} + COutPointLock(const COutPoint& outpointIn) : outpoint(outpointIn), mapMasternodeVotes() @@ -271,6 +325,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; @@ -291,6 +354,11 @@ class CTxLockCandidate int64_t nTimeCreated; public: + CTxLockCandidate() : + nConfirmedHeight(-1), + nTimeCreated(GetTime()) + {} + CTxLockCandidate(const CTxLockRequest& txLockRequestIn) : nConfirmedHeight(-1), nTimeCreated(GetTime()), @@ -301,6 +369,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/llmq/quorums_blockprocessor.cpp b/src/llmq/quorums_blockprocessor.cpp new file mode 100644 index 000000000000..f014a3f44ff5 --- /dev/null +++ b/src/llmq/quorums_blockprocessor.cpp @@ -0,0 +1,409 @@ +// 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 "quorums_blockprocessor.h" +#include "quorums_commitment.h" +#include "quorums_utils.h" + +#include "evo/specialtx.h" + +#include "chain.h" +#include "chainparams.h" +#include "consensus/validation.h" +#include "net.h" +#include "net_processing.h" +#include "primitives/block.h" +#include "validation.h" + +namespace llmq +{ + +CQuorumBlockProcessor* quorumBlockProcessor; + +static const std::string DB_MINED_COMMITMENT = "q_mc"; + +void CQuorumBlockProcessor::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) +{ + if (strCommand == NetMsgType::QFCOMMITMENT) { + CFinalCommitment qc; + vRecv >> qc; + + if (qc.IsNull()) { + LOCK(cs_main); + LogPrintf("CQuorumBlockProcessor::%s -- null commitment from peer=%d\n", __func__, pfrom->id); + Misbehaving(pfrom->id, 100); + return; + } + + if (!Params().GetConsensus().llmqs.count((Consensus::LLMQType)qc.llmqType)) { + LOCK(cs_main); + LogPrintf("CQuorumBlockProcessor::%s -- invalid commitment type %d from peer=%d\n", __func__, + qc.llmqType, pfrom->id); + Misbehaving(pfrom->id, 100); + return; + } + auto type = (Consensus::LLMQType)qc.llmqType; + const auto& params = Params().GetConsensus().llmqs.at(type); + + // Verify that quorumHash is part of the active chain and that it's the first block in the DKG interval + { + LOCK(cs_main); + if (!mapBlockIndex.count(qc.quorumHash)) { + LogPrintf("CQuorumBlockProcessor::%s -- unknown block %s in commitment, peer=%d\n", __func__, + qc.quorumHash.ToString(), pfrom->id); + // can't really punish the node here, as we might simply be the one that is on the wrong chain or not + // fully synced + return; + } + auto pquorumIndex = mapBlockIndex[qc.quorumHash]; + if (chainActive.Tip()->GetAncestor(pquorumIndex->nHeight) != pquorumIndex) { + LogPrintf("CQuorumBlockProcessor::%s -- block %s not in active chain, peer=%d\n", __func__, + qc.quorumHash.ToString(), pfrom->id); + // same, can't punish + return; + } + int quorumHeight = pquorumIndex->nHeight - (pquorumIndex->nHeight % params.dkgInterval); + if (quorumHeight != pquorumIndex->nHeight) { + LogPrintf("CQuorumBlockProcessor::%s -- block %s is not the first block in the DKG interval, peer=%d\n", __func__, + qc.quorumHash.ToString(), pfrom->id); + Misbehaving(pfrom->id, 100); + return; + } + } + + { + // Check if we already got a better one locally + // We do this before verifying the commitment to avoid DoS + LOCK(minableCommitmentsCs); + auto k = std::make_pair(type, qc.quorumHash); + auto it = minableCommitmentsByQuorum.find(k); + if (it != minableCommitmentsByQuorum.end()) { + auto jt = minableCommitments.find(it->second); + if (jt != minableCommitments.end()) { + if (jt->second.CountSigners() <= qc.CountSigners()) { + return; + } + } + } + } + + auto members = CLLMQUtils::GetAllQuorumMembers(type, qc.quorumHash); + + if (!qc.Verify(members, true)) { + LOCK(cs_main); + LogPrintf("CQuorumBlockProcessor::%s -- commitment for quorum %s:%d is not valid, peer=%d\n", __func__, + qc.quorumHash.ToString(), qc.llmqType, pfrom->id); + Misbehaving(pfrom->id, 100); + return; + } + + LogPrintf("CQuorumBlockProcessor::%s -- received commitment for quorum %s:%d, validMembers=%d, signers=%d, peer=%d\n", __func__, + qc.quorumHash.ToString(), qc.llmqType, qc.CountValidMembers(), qc.CountSigners(), pfrom->id); + + AddMinableCommitment(qc); + } +} + +bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& state) +{ + AssertLockHeld(cs_main); + + bool fDIP0003Active = VersionBitsState(pindex->pprev, Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE; + if (!fDIP0003Active) { + return true; + } + + std::map qcs; + if (!GetCommitmentsFromBlock(block, pindex->pprev, qcs, state)) { + return false; + } + + // The following checks make sure that there is always a (possibly null) commitment while in the mining phase + // until the first non-null commitment has been mined. After the non-null commitment, no other commitments are + // allowed, including null commitments. + for (const auto& p : Params().GetConsensus().llmqs) { + auto type = p.first; + + // does the currently processed block contain a (possibly null) commitment for the current session? + bool hasCommitmentInNewBlock = qcs.count(type) != 0; + bool isCommitmentRequired = IsCommitmentRequired(type, pindex->nHeight); + + if (hasCommitmentInNewBlock && !isCommitmentRequired) { + // If we're either not in the mining phase or a non-null commitment was mined already, reject the block + return state.DoS(100, false, REJECT_INVALID, "bad-qc-not-allowed"); + } + + if (!hasCommitmentInNewBlock && isCommitmentRequired) { + // If no non-null commitment was mined for the mining phase yet and the new block does not include + // a (possibly null) commitment, the block should be rejected. + return state.DoS(100, false, REJECT_INVALID, "bad-qc-missing"); + } + } + + for (auto& p : qcs) { + auto& qc = p.second; + if (!ProcessCommitment(pindex, qc, state)) { + return false; + } + } + return true; +} + +bool CQuorumBlockProcessor::ProcessCommitment(const CBlockIndex* pindex, const CFinalCommitment& qc, CValidationState& state) +{ + auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)qc.llmqType); + + uint256 quorumHash = GetQuorumBlockHash((Consensus::LLMQType)qc.llmqType, pindex->nHeight); + if (quorumHash.IsNull()) { + return state.DoS(100, false, REJECT_INVALID, "bad-qc-block"); + } + if (quorumHash != qc.quorumHash) { + return state.DoS(100, false, REJECT_INVALID, "bad-qc-block"); + } + + if (qc.IsNull()) { + if (!qc.VerifyNull()) { + return state.DoS(100, false, REJECT_INVALID, "bad-qc-invalid-null"); + } + return true; + } + + if (HasMinedCommitment(params.type, quorumHash)) { + // should not happen as it's already handled in ProcessBlock + return state.DoS(100, false, REJECT_INVALID, "bad-qc-dup"); + } + + if (!IsMiningPhase(params.type, pindex->nHeight)) { + // should not happen as it's already handled in ProcessBlock + return state.DoS(100, false, REJECT_INVALID, "bad-qc-height"); + } + + auto members = CLLMQUtils::GetAllQuorumMembers(params.type, quorumHash); + + if (!qc.Verify(members, true)) { + return state.DoS(100, false, REJECT_INVALID, "bad-qc-invalid"); + } + + // Store commitment in DB + evoDb.Write(std::make_pair(DB_MINED_COMMITMENT, std::make_pair((uint8_t)params.type, quorumHash)), qc); + + LogPrintf("CQuorumBlockProcessor::%s -- processed commitment from block. type=%d, quorumHash=%s, signers=%s, validMembers=%d, quorumPublicKey=%s\n", __func__, + qc.llmqType, quorumHash.ToString(), qc.CountSigners(), qc.CountValidMembers(), qc.quorumPublicKey.ToString()); + + return true; +} + +bool CQuorumBlockProcessor::UndoBlock(const CBlock& block, const CBlockIndex* pindex) +{ + AssertLockHeld(cs_main); + + std::map qcs; + CValidationState dummy; + if (!GetCommitmentsFromBlock(block, pindex->pprev, qcs, dummy)) { + return false; + } + + for (auto& p : qcs) { + auto& qc = p.second; + if (qc.IsNull()) { + continue; + } + + evoDb.Erase(std::make_pair(DB_MINED_COMMITMENT, std::make_pair(qc.llmqType, qc.quorumHash))); + + if (!qc.IsNull()) { + // if a reorg happened, we should allow to mine this commitment later + AddMinableCommitment(qc); + } + } + + return true; +} + +bool CQuorumBlockProcessor::GetCommitmentsFromBlock(const CBlock& block, const CBlockIndex* pindexPrev, std::map& ret, CValidationState& state) +{ + AssertLockHeld(cs_main); + + auto& consensus = Params().GetConsensus(); + bool fDIP0003Active = VersionBitsState(pindexPrev, consensus, Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE; + + ret.clear(); + + for (const auto& tx : block.vtx) { + if (tx->nType == TRANSACTION_QUORUM_COMMITMENT) { + CFinalCommitmentTxPayload qc; + if (!GetTxPayload(*tx, qc)) { + // should not happen as it was verified before processing the block + return state.DoS(100, false, REJECT_INVALID, "bad-tx-payload"); + } + + // only allow one commitment per type and per block + if (ret.count((Consensus::LLMQType)qc.commitment.llmqType)) { + return state.DoS(100, false, REJECT_INVALID, "bad-qc-dup"); + } + + ret.emplace((Consensus::LLMQType)qc.commitment.llmqType, std::move(qc.commitment)); + } + } + + if (!fDIP0003Active && !ret.empty()) { + return state.DoS(100, false, REJECT_INVALID, "bad-qc-premature"); + } + + return true; +} + +bool CQuorumBlockProcessor::IsMiningPhase(Consensus::LLMQType llmqType, int nHeight) +{ + const auto& params = Params().GetConsensus().llmqs.at(llmqType); + int phaseIndex = nHeight % params.dkgInterval; + if (phaseIndex >= params.dkgMiningWindowStart && phaseIndex <= params.dkgMiningWindowEnd) { + return true; + } + return false; +} + +bool CQuorumBlockProcessor::IsCommitmentRequired(Consensus::LLMQType llmqType, int nHeight) +{ + uint256 quorumHash = GetQuorumBlockHash(llmqType, nHeight); + + // perform extra check for quorumHash.IsNull as the quorum hash is unknown for the first block of a session + // this is because the currently processed block's hash will be the quorumHash of this session + bool isMiningPhase = !quorumHash.IsNull() && IsMiningPhase(llmqType, nHeight); + + // did we already mine a non-null commitment for this session? + bool hasMinedCommitment = !quorumHash.IsNull() && HasMinedCommitment(llmqType, quorumHash); + + return isMiningPhase && !hasMinedCommitment; +} + +// WARNING: This method returns uint256() on the first block of the DKG interval (because the block hash is not known yet) +uint256 CQuorumBlockProcessor::GetQuorumBlockHash(Consensus::LLMQType llmqType, int nHeight) +{ + AssertLockHeld(cs_main); + + auto& params = Params().GetConsensus().llmqs.at(llmqType); + + int quorumStartHeight = nHeight - (nHeight % params.dkgInterval); + uint256 quorumBlockHash; + if (!GetBlockHash(quorumBlockHash, quorumStartHeight)) { + return uint256(); + } + return quorumBlockHash; +} + +bool CQuorumBlockProcessor::HasMinedCommitment(Consensus::LLMQType llmqType, const uint256& quorumHash) +{ + auto key = std::make_pair(DB_MINED_COMMITMENT, std::make_pair((uint8_t)llmqType, quorumHash)); + return evoDb.Exists(key); +} + +bool CQuorumBlockProcessor::GetMinedCommitment(Consensus::LLMQType llmqType, const uint256& quorumHash, CFinalCommitment& ret) +{ + auto key = std::make_pair(DB_MINED_COMMITMENT, std::make_pair((uint8_t)llmqType, quorumHash)); + return evoDb.Read(key, ret); +} + +bool CQuorumBlockProcessor::HasMinableCommitment(const uint256& hash) +{ + LOCK(minableCommitmentsCs); + return minableCommitments.count(hash) != 0; +} + +void CQuorumBlockProcessor::AddMinableCommitment(const CFinalCommitment& fqc) +{ + bool relay = false; + uint256 commitmentHash = ::SerializeHash(fqc); + + { + LOCK(minableCommitmentsCs); + + auto k = std::make_pair((Consensus::LLMQType) fqc.llmqType, fqc.quorumHash); + auto ins = minableCommitmentsByQuorum.emplace(k, commitmentHash); + if (ins.second) { + minableCommitments.emplace(commitmentHash, fqc); + relay = true; + } else { + auto& oldFqc = minableCommitments.at(ins.first->second); + if (fqc.CountSigners() > oldFqc.CountSigners()) { + // new commitment has more signers, so override the known one + ins.first->second = commitmentHash; + minableCommitments.erase(ins.first->second); + minableCommitments.emplace(commitmentHash, fqc); + relay = true; + } + } + } + + // We only relay the new commitment if it's new or better then the old one + if (relay) { + CInv inv(MSG_QUORUM_FINAL_COMMITMENT, commitmentHash); + g_connman->RelayInv(inv, DMN_PROTO_VERSION); + } +} + +bool CQuorumBlockProcessor::GetMinableCommitmentByHash(const uint256& commitmentHash, llmq::CFinalCommitment& ret) +{ + LOCK(minableCommitmentsCs); + auto it = minableCommitments.find(commitmentHash); + if (it == minableCommitments.end()) { + return false; + } + ret = it->second; + return true; +} + +// Will return false if no commitment should be mined +// Will return true and a null commitment if no minable commitment is known and none was mined yet +bool CQuorumBlockProcessor::GetMinableCommitment(Consensus::LLMQType llmqType, int nHeight, CFinalCommitment& ret) +{ + AssertLockHeld(cs_main); + + if (!IsCommitmentRequired(llmqType, nHeight)) { + // no commitment required + return false; + } + + uint256 quorumHash = GetQuorumBlockHash(llmqType, nHeight); + if (quorumHash.IsNull()) { + return false; + } + + LOCK(minableCommitmentsCs); + + auto k = std::make_pair(llmqType, quorumHash); + auto it = minableCommitmentsByQuorum.find(k); + if (it == minableCommitmentsByQuorum.end()) { + // null commitment required + ret = CFinalCommitment(Params().GetConsensus().llmqs.at(llmqType), quorumHash); + return true; + } + + ret = minableCommitments.at(it->second); + + return true; +} + +bool CQuorumBlockProcessor::GetMinableCommitmentTx(Consensus::LLMQType llmqType, int nHeight, CTransactionRef& ret) +{ + AssertLockHeld(cs_main); + + CFinalCommitmentTxPayload qc; + if (!GetMinableCommitment(llmqType, nHeight, qc.commitment)) { + return false; + } + + qc.nHeight = nHeight; + + CMutableTransaction tx; + tx.nVersion = 3; + tx.nType = TRANSACTION_QUORUM_COMMITMENT; + SetTxPayload(tx, qc); + + ret = MakeTransactionRef(tx); + + return true; +} + +} diff --git a/src/llmq/quorums_blockprocessor.h b/src/llmq/quorums_blockprocessor.h new file mode 100644 index 000000000000..ac97687644ba --- /dev/null +++ b/src/llmq/quorums_blockprocessor.h @@ -0,0 +1,61 @@ +// 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 DASH_QUORUMS_BLOCKPROCESSOR_H +#define DASH_QUORUMS_BLOCKPROCESSOR_H + +#include "llmq/quorums_commitment.h" + +#include "consensus/params.h" +#include "primitives/transaction.h" +#include "sync.h" + +#include + +class CNode; +class CConnman; + +namespace llmq +{ + +class CQuorumBlockProcessor +{ +private: + CEvoDB& evoDb; + + // TODO cleanup + CCriticalSection minableCommitmentsCs; + std::map, uint256> minableCommitmentsByQuorum; + std::map minableCommitments; + +public: + CQuorumBlockProcessor(CEvoDB& _evoDb) : evoDb(_evoDb) {} + + void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman); + + bool ProcessBlock(const CBlock& block, const CBlockIndex* pindex, CValidationState& state); + bool UndoBlock(const CBlock& block, const CBlockIndex* pindex); + + void AddMinableCommitment(const CFinalCommitment& fqc); + bool HasMinableCommitment(const uint256& hash); + bool GetMinableCommitmentByHash(const uint256& commitmentHash, CFinalCommitment& ret); + bool GetMinableCommitment(Consensus::LLMQType llmqType, int nHeight, CFinalCommitment& ret); + bool GetMinableCommitmentTx(Consensus::LLMQType llmqType, int nHeight, CTransactionRef& ret); + + bool HasMinedCommitment(Consensus::LLMQType llmqType, const uint256& quorumHash); + bool GetMinedCommitment(Consensus::LLMQType llmqType, const uint256& quorumHash, CFinalCommitment& ret); + +private: + bool GetCommitmentsFromBlock(const CBlock& block, const CBlockIndex* pindexPrev, std::map& ret, CValidationState& state); + bool ProcessCommitment(const CBlockIndex* pindex, const CFinalCommitment& qc, CValidationState& state); + bool IsMiningPhase(Consensus::LLMQType llmqType, int nHeight); + bool IsCommitmentRequired(Consensus::LLMQType llmqType, int nHeight); + uint256 GetQuorumBlockHash(Consensus::LLMQType llmqType, int nHeight); +}; + +extern CQuorumBlockProcessor* quorumBlockProcessor; + +} + +#endif//DASH_QUORUMS_BLOCKPROCESSOR_H diff --git a/src/llmq/quorums_commitment.cpp b/src/llmq/quorums_commitment.cpp new file mode 100644 index 000000000000..83d9c7727e78 --- /dev/null +++ b/src/llmq/quorums_commitment.cpp @@ -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. + +#include "quorums_commitment.h" +#include "quorums_utils.h" + +#include "chainparams.h" +#include "validation.h" + +#include "evo/specialtx.h" + +#include + +namespace llmq +{ + +CFinalCommitment::CFinalCommitment(const Consensus::LLMQParams& params, const uint256& _quorumHash) : + llmqType(params.type), + quorumHash(_quorumHash), + signers(params.size), + validMembers(params.size) +{ +} + +#define LogPrintfFinalCommitment(...) do { \ + LogPrintStr(strprintf("CFinalCommitment::%s -- %s", __func__, tinyformat::format(__VA_ARGS__))); \ +} while(0) + +bool CFinalCommitment::Verify(const std::vector& members, bool checkSigs) const +{ + if (nVersion == 0 || nVersion > CURRENT_VERSION) { + return false; + } + + if (!Params().GetConsensus().llmqs.count((Consensus::LLMQType)llmqType)) { + LogPrintfFinalCommitment("invalid llmqType=%d\n", llmqType); + return false; + } + const auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)llmqType); + + if (!VerifySizes(params)) { + return false; + } + + if (CountValidMembers() < params.minSize) { + LogPrintfFinalCommitment("invalid validMembers count. validMembersCount=%d\n", CountValidMembers()); + return false; + } + if (CountSigners() < params.minSize) { + LogPrintfFinalCommitment("invalid signers count. signersCount=%d\n", CountSigners()); + return false; + } + if (!quorumPublicKey.IsValid()) { + LogPrintfFinalCommitment("invalid quorumPublicKey\n"); + return false; + } + if (quorumVvecHash.IsNull()) { + LogPrintfFinalCommitment("invalid quorumVvecHash\n"); + return false; + } + if (!membersSig.IsValid()) { + LogPrintfFinalCommitment("invalid membersSig\n"); + return false; + } + if (!quorumSig.IsValid()) { + LogPrintfFinalCommitment("invalid vvecSig\n"); + return false; + } + + for (size_t i = members.size(); i < params.size; i++) { + if (validMembers[i]) { + LogPrintfFinalCommitment("invalid validMembers bitset. bit %d should not be set\n", i); + return false; + } + if (signers[i]) { + LogPrintfFinalCommitment("invalid signers bitset. bit %d should not be set\n", i); + return false; + } + } + + // sigs are only checked when the block is processed + if (checkSigs) { + uint256 commitmentHash = CLLMQUtils::BuildCommitmentHash((uint8_t)params.type, quorumHash, validMembers, quorumPublicKey, quorumVvecHash); + + std::vector memberPubKeys; + for (size_t i = 0; i < members.size(); i++) { + if (!signers[i]) { + continue; + } + memberPubKeys.emplace_back(members[i]->pdmnState->pubKeyOperator); + } + + if (!membersSig.VerifySecureAggregated(memberPubKeys, commitmentHash)) { + LogPrintfFinalCommitment("invalid aggregated members signature\n"); + return false; + } + + if (!quorumSig.VerifyInsecure(quorumPublicKey, commitmentHash)) { + LogPrintfFinalCommitment("invalid quorum signature\n"); + return false; + } + } + + return true; +} + +bool CFinalCommitment::VerifyNull() const +{ + if (!Params().GetConsensus().llmqs.count((Consensus::LLMQType)llmqType)) { + LogPrintfFinalCommitment("invalid llmqType=%d\n", llmqType); + return false; + } + const auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)llmqType); + + if (!IsNull() || !VerifySizes(params)) { + return false; + } + + return true; +} + +bool CFinalCommitment::VerifySizes(const Consensus::LLMQParams& params) const +{ + if (signers.size() != params.size) { + LogPrintfFinalCommitment("invalid signers.size=%d\n", signers.size()); + return false; + } + if (validMembers.size() != params.size) { + LogPrintfFinalCommitment("invalid signers.size=%d\n", signers.size()); + return false; + } + return true; +} + +void CFinalCommitment::ToJson(UniValue& obj) const +{ + obj.setObject(); + obj.push_back(Pair("version", (int)nVersion)); + obj.push_back(Pair("llmqType", (int)llmqType)); + obj.push_back(Pair("quorumHash", quorumHash.ToString())); + obj.push_back(Pair("signersCount", CountSigners())); + obj.push_back(Pair("validMembersCount", CountValidMembers())); + obj.push_back(Pair("quorumPublicKey", quorumPublicKey.ToString())); +} + +void CFinalCommitmentTxPayload::ToJson(UniValue& obj) const +{ + obj.setObject(); + obj.push_back(Pair("version", (int)nVersion)); + obj.push_back(Pair("height", (int)nHeight)); + + UniValue qcObj; + commitment.ToJson(qcObj); + obj.push_back(Pair("commitment", qcObj)); +} + +bool CheckLLMQCommitment(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state) +{ + CFinalCommitmentTxPayload qcTx; + if (!GetTxPayload(tx, qcTx)) { + return state.DoS(100, false, REJECT_INVALID, "bad-qc-payload"); + } + + if (qcTx.nVersion == 0 || qcTx.nVersion > CFinalCommitmentTxPayload::CURRENT_VERSION) { + return state.DoS(100, false, REJECT_INVALID, "bad-qc-version"); + } + + if (qcTx.nHeight != pindexPrev->nHeight + 1) { + return state.DoS(100, false, REJECT_INVALID, "bad-qc-height"); + } + + if (!mapBlockIndex.count(qcTx.commitment.quorumHash)) { + return state.DoS(100, false, REJECT_INVALID, "bad-qc-quorum-hash"); + } + + const CBlockIndex* pindexQuorum = mapBlockIndex[qcTx.commitment.quorumHash]; + + if (pindexQuorum != pindexPrev->GetAncestor(pindexQuorum->nHeight)) { + // not part of active chain + return state.DoS(100, false, REJECT_INVALID, "bad-qc-quorum-hash"); + } + + if (!Params().GetConsensus().llmqs.count((Consensus::LLMQType)qcTx.commitment.llmqType)) { + return state.DoS(100, false, REJECT_INVALID, "bad-qc-type"); + } + const auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)qcTx.commitment.llmqType); + + if (qcTx.commitment.IsNull()) { + if (!qcTx.commitment.VerifyNull()) { + return state.DoS(100, false, REJECT_INVALID, "bad-qc-invalid-null"); + } + return true; + } + + auto members = CLLMQUtils::GetAllQuorumMembers(params.type, qcTx.commitment.quorumHash); + if (!qcTx.commitment.Verify(members, false)) { + return state.DoS(100, false, REJECT_INVALID, "bad-qc-invalid"); + } + + return true; +} + +} diff --git a/src/llmq/quorums_commitment.h b/src/llmq/quorums_commitment.h new file mode 100644 index 000000000000..de5ccbf2deb0 --- /dev/null +++ b/src/llmq/quorums_commitment.h @@ -0,0 +1,119 @@ +// 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 DASH_QUORUMS_COMMITMENT_H +#define DASH_QUORUMS_COMMITMENT_H + +#include "consensus/params.h" + +#include "evo/deterministicmns.h" + +#include "bls/bls.h" + +namespace llmq +{ + +// This message is an aggregation of all received premature commitments and only valid if +// enough (>=threshold) premature commitments were aggregated +// This is mined on-chain as part of TRANSACTION_QUORUM_COMMITMENT +class CFinalCommitment +{ +public: + static const uint16_t CURRENT_VERSION = 1; + +public: + uint16_t nVersion{CURRENT_VERSION}; + uint8_t llmqType{Consensus::LLMQ_NONE}; + uint256 quorumHash; + std::vector signers; + std::vector validMembers; + + CBLSPublicKey quorumPublicKey; + uint256 quorumVvecHash; + + CBLSSignature quorumSig; // recovered threshold sig of blockHash+validMembers+pubKeyHash+vvecHash + CBLSSignature membersSig; // aggregated member sig of blockHash+validMembers+pubKeyHash+vvecHash + +public: + CFinalCommitment() {} + CFinalCommitment(const Consensus::LLMQParams& params, const uint256& _quorumHash); + + int CountSigners() const + { + return (int)std::count(signers.begin(), signers.end(), true); + } + int CountValidMembers() const + { + return (int)std::count(validMembers.begin(), validMembers.end(), true); + } + + bool Verify(const std::vector& members, bool checkSigs) const; + bool VerifyNull() const; + bool VerifySizes(const Consensus::LLMQParams& params) const; + + void ToJson(UniValue& obj) const; + +public: + ADD_SERIALIZE_METHODS + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(nVersion); + READWRITE(llmqType); + READWRITE(quorumHash); + READWRITE(DYNBITSET(signers)); + READWRITE(DYNBITSET(validMembers)); + READWRITE(quorumPublicKey); + READWRITE(quorumVvecHash); + READWRITE(quorumSig); + READWRITE(membersSig); + } + +public: + bool IsNull() const + { + if (std::count(signers.begin(), signers.end(), true) || + std::count(validMembers.begin(), validMembers.end(), true)) { + return false; + } + if (quorumPublicKey.IsValid() || + !quorumVvecHash.IsNull() || + membersSig.IsValid() || + quorumSig.IsValid()) { + return false; + } + return true; + } +}; + +class CFinalCommitmentTxPayload +{ +public: + static const uint16_t CURRENT_VERSION = 1; + +public: + uint16_t nVersion{CURRENT_VERSION}; + uint32_t nHeight{(uint32_t)-1}; + CFinalCommitment commitment; + +public: + ADD_SERIALIZE_METHODS + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(nVersion); + READWRITE(nHeight); + READWRITE(commitment); + } + + void ToJson(UniValue& obj) const; +}; + +bool CheckLLMQCommitment(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidationState& state); + +} + +#endif //DASH_QUORUMS_COMMITMENT_H diff --git a/src/llmq/quorums_dummydkg.cpp b/src/llmq/quorums_dummydkg.cpp new file mode 100644 index 000000000000..896785300080 --- /dev/null +++ b/src/llmq/quorums_dummydkg.cpp @@ -0,0 +1,608 @@ +// 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 "quorums_dummydkg.h" + +#include "quorums_blockprocessor.h" +#include "quorums_commitment.h" +#include "quorums_utils.h" + +#include "evo/specialtx.h" + +#include "activemasternode.h" +#include "chain.h" +#include "chainparams.h" +#include "consensus/validation.h" +#include "net.h" +#include "net_processing.h" +#include "primitives/block.h" +#include "spork.h" +#include "validation.h" + +namespace llmq +{ + +CDummyDKG* quorumDummyDKG; + +void CDummyDKG::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) +{ + if (strCommand == NetMsgType::QCONTRIB) { + if (!sporkManager.IsSporkActive(SPORK_17_QUORUM_DKG_ENABLED)) { + return; + } + + CDummyContribution qc; + try { + vRecv >> qc; + } catch (...) { + // When we switch to the real DKG, non-upgraded nodes will get to this point. Let them just ignore the + // incompatible messages + return; + } + + uint256 hash = ::SerializeHash(qc); + { + LOCK(cs_main); + connman.RemoveAskFor(hash); + } + + ProcessDummyContribution(pfrom->id, qc); + } else if (strCommand == NetMsgType::QDCOMMITMENT) { + if (!Params().GetConsensus().fLLMQAllowDummyCommitments) { + Misbehaving(pfrom->id, 100); + return; + } + if (!sporkManager.IsSporkActive(SPORK_17_QUORUM_DKG_ENABLED)) { + return; + } + + CDummyCommitment qc; + vRecv >> qc; + + uint256 hash = ::SerializeHash(qc); + { + LOCK(cs_main); + connman.RemoveAskFor(hash); + } + + ProcessDummyCommitment(pfrom->id, qc); + } +} + +void CDummyDKG::ProcessDummyContribution(NodeId from, const llmq::CDummyContribution& qc) +{ + if (!Params().GetConsensus().llmqs.count((Consensus::LLMQType)qc.llmqType)) { + LOCK(cs_main); + LogPrintf("CDummyDKG::%s -- invalid commitment type %d, peer=%d\n", __func__, + qc.llmqType, from); + if (from != -1) { + Misbehaving(from, 100); + } + return; + } + + auto type = (Consensus::LLMQType)qc.llmqType; + const auto& params = Params().GetConsensus().llmqs.at(type); + + int curQuorumHeight; + const CBlockIndex* quorumIndex; + { + LOCK(cs_main); + curQuorumHeight = chainActive.Height() - (chainActive.Height() % params.dkgInterval); + quorumIndex = chainActive[curQuorumHeight]; + } + uint256 quorumHash = quorumIndex->GetBlockHash(); + if (qc.quorumHash != quorumHash) { + LogPrintf("CDummyDKG::%s -- dummy contrinution for wrong quorum, peer=%d\n", __func__, + from); + return; + } + + auto members = CLLMQUtils::GetAllQuorumMembers(type, qc.quorumHash); + if (members.size() != params.size) { + LOCK(cs_main); + LogPrintf("CDummyDKG::%s -- invalid members count %d, peer=%d\n", __func__, + members.size(), from); + if (from != -1) { + Misbehaving(from, 100); + } + return; + } + if (qc.signer >= members.size()) { + LOCK(cs_main); + LogPrintf("CDummyDKG::%s -- invalid signer %d, peer=%d\n", __func__, + qc.signer, from); + if (from != -1) { + Misbehaving(from, 100); + } + return; + } + + auto signer = members[qc.signer]; + + { + LOCK(sessionCs); + if (curSessions[type].dummyContributionsFromMembers.count(signer->proTxHash)) { + return; + } + } + + // verify member sig + if (!qc.sig.VerifyInsecure(signer->pdmnState->pubKeyOperator, qc.GetSignHash())) { + LOCK(cs_main); + LogPrintf("CDummyDKG::%s -- invalid memberSig, peer=%d\n", __func__, + from); + if (from != -1) { + Misbehaving(from, 100); + } + return; + } + + LogPrintf("CDummyDKG::%s -- processed dummy contribution for quorum %s:%d, signer=%d, peer=%d\n", __func__, + qc.quorumHash.ToString(), qc.llmqType, qc.signer, from); + + uint256 hash = ::SerializeHash(qc); + { + LOCK(sessionCs); + curSessions[type].dummyContributions[hash] = qc; + curSessions[type].dummyContributionsFromMembers[signer->proTxHash] = hash; + } + + CInv inv(MSG_QUORUM_DUMMY_CONTRIBUTION, hash); + g_connman->RelayInv(inv, DMN_PROTO_VERSION); +} + +void CDummyDKG::ProcessDummyCommitment(NodeId from, const llmq::CDummyCommitment& qc) +{ + if (!Params().GetConsensus().llmqs.count((Consensus::LLMQType)qc.llmqType)) { + LOCK(cs_main); + LogPrintf("CDummyDKG::%s -- invalid commitment type %d, peer=%d\n", __func__, + qc.llmqType, from); + if (from != -1) { + Misbehaving(from, 100); + } + return; + } + + auto type = (Consensus::LLMQType)qc.llmqType; + const auto& params = Params().GetConsensus().llmqs.at(type); + + if (qc.validMembers.size() != params.size) { + LOCK(cs_main); + LogPrintf("CDummyDKG::%s -- invalid validMembers size %d, peer=%d\n", __func__, + qc.validMembers.size(), from); + if (from != -1) { + Misbehaving(from, 100); + } + return; + } + + int curQuorumHeight; + const CBlockIndex* quorumIndex; + { + LOCK(cs_main); + curQuorumHeight = chainActive.Height() - (chainActive.Height() % params.dkgInterval); + quorumIndex = chainActive[curQuorumHeight]; + } + uint256 quorumHash = quorumIndex->GetBlockHash(); + if (qc.quorumHash != quorumHash) { + LogPrintf("CDummyDKG::%s -- dummy commitment for wrong quorum, peer=%d\n", __func__, + from); + return; + } + + auto members = CLLMQUtils::GetAllQuorumMembers(type, qc.quorumHash); + if (members.size() != params.size) { + LOCK(cs_main); + LogPrintf("CDummyDKG::%s -- invalid members count %d, peer=%d\n", __func__, + members.size(), from); + if (from != -1) { + Misbehaving(from, 100); + } + return; + } + if (qc.signer >= members.size()) { + LOCK(cs_main); + LogPrintf("CDummyDKG::%s -- invalid signer %d, peer=%d\n", __func__, + qc.signer, from); + if (from != -1) { + Misbehaving(from, 100); + } + return; + } + if (qc.CountValidMembers() < params.minSize) { + LOCK(cs_main); + LogPrintf("CDummyDKG::%s -- invalid validMembers count %d, peer=%d\n", __func__, + qc.CountValidMembers(), from); + if (from != -1) { + Misbehaving(from, 100); + } + return; + } + + auto signer = members[qc.signer]; + + { + LOCK(sessionCs); + for (const auto& p : curSessions[type].dummyCommitmentsFromMembers) { + if (p.second.count(signer->proTxHash)) { + return; + } + } + } + + auto svec = BuildDeterministicSvec(type, qc.quorumHash); + auto vvec = BuildVvec(svec); + auto vvecHash = ::SerializeHash(vvec); + + auto commitmentHash = CLLMQUtils::BuildCommitmentHash((uint8_t)type, qc.quorumHash, qc.validMembers, vvec[0], vvecHash); + + // verify member sig + if (!qc.membersSig.VerifyInsecure(signer->pdmnState->pubKeyOperator, commitmentHash)) { + LOCK(cs_main); + LogPrintf("CDummyDKG::%s -- invalid memberSig, peer=%d\n", __func__, + from); + if (from != -1) { + Misbehaving(from, 100); + } + return; + } + + // recover public key share + CBLSPublicKey sharePk; + if (!sharePk.PublicKeyShare(vvec, CBLSId::FromHash(signer->proTxHash))) { + LOCK(cs_main); + LogPrintf("CDummyDKG::%s -- failed to recover public key share, peer=%d\n", __func__, + from); + if (from != -1) { + Misbehaving(from, 100); + } + return; + } + + // verify sig share + if (!qc.quorumSig.VerifyInsecure(sharePk, commitmentHash)) { + LOCK(cs_main); + LogPrintf("CDummyDKG::%s -- invalid quorumSig, peer=%d\n", __func__, + from); + if (from != -1) { + Misbehaving(from, 100); + } + return; + } + + LogPrintf("CDummyDKG::%s -- processed dummy commitment for quorum %s:%d, validMembers=%d, signer=%d, peer=%d\n", __func__, + qc.quorumHash.ToString(), qc.llmqType, qc.CountValidMembers(), qc.signer, from); + + uint256 hash = ::SerializeHash(qc); + { + LOCK(sessionCs); + curSessions[type].dummyCommitments[hash] = qc; + curSessions[type].dummyCommitmentsFromMembers[commitmentHash][signer->proTxHash] = hash; + } + + CInv inv(MSG_QUORUM_DUMMY_COMMITMENT, hash); + g_connman->RelayInv(inv, DMN_PROTO_VERSION); +} + +void CDummyDKG::UpdatedBlockTip(const CBlockIndex* pindex, bool fInitialDownload) +{ + if (fInitialDownload) { + return; + } + + if (!fMasternodeMode) { + return; + } + + bool fDIP0003Active = VersionBitsState(chainActive.Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE; + if (!fDIP0003Active) { + return; + } + + if (!sporkManager.IsSporkActive(SPORK_17_QUORUM_DKG_ENABLED)) { + return; + } + + for (const auto& p : Params().GetConsensus().llmqs) { + const auto& params = p.second; + int phaseIndex = pindex->nHeight % params.dkgInterval; + if (phaseIndex == 0) { + CreateDummyContribution(params.type, pindex); + } else if (phaseIndex == params.dkgPhaseBlocks * 2) { + CreateDummyCommitment(params.type, pindex); + } else if (phaseIndex == params.dkgPhaseBlocks * 4) { + CreateFinalCommitment(params.type, pindex); + } + } +} + +void CDummyDKG::CreateDummyContribution(Consensus::LLMQType llmqType, const CBlockIndex* pindex) +{ + const auto& params = Params().GetConsensus().llmqs.at(llmqType); + int quorumHeight = pindex->nHeight - (pindex->nHeight % params.dkgInterval); + const CBlockIndex* quorumIndex; + { + LOCK(cs_main); + quorumIndex = chainActive[quorumHeight]; + } + uint256 quorumHash = quorumIndex->GetBlockHash(); + + auto members = CLLMQUtils::GetAllQuorumMembers(llmqType, quorumHash); + if (members.size() != params.size) { + return; + } + + int myIdx = -1; + for (size_t i = 0; i < members.size(); i++) { + if (members[i]->collateralOutpoint == activeMasternodeInfo.outpoint) { + myIdx = (int)i; + break; + } + } + if (myIdx == -1) { + return; + } + auto signer = members[myIdx]; + + CDummyContribution qc; + qc.llmqType = (uint8_t)llmqType; + qc.quorumHash = quorumHash; + qc.signer = (uint16_t)myIdx; + qc.sig = activeMasternodeInfo.blsKeyOperator->Sign(qc.GetSignHash()); + + ProcessDummyContribution(-1, qc); +} + +void CDummyDKG::CreateDummyCommitment(Consensus::LLMQType llmqType, const CBlockIndex* pindex) +{ + const auto& params = Params().GetConsensus().llmqs.at(llmqType); + int quorumHeight = pindex->nHeight - (pindex->nHeight % params.dkgInterval); + const CBlockIndex* quorumIndex; + { + LOCK(cs_main); + quorumIndex = chainActive[quorumHeight]; + } + uint256 quorumHash = quorumIndex->GetBlockHash(); + + auto members = CLLMQUtils::GetAllQuorumMembers(llmqType, quorumHash); + if (members.size() != params.size) { + return; + } + + int myIdx = -1; + for (size_t i = 0; i < members.size(); i++) { + if (members[i]->collateralOutpoint == activeMasternodeInfo.outpoint) { + myIdx = (int)i; + break; + } + } + if (myIdx == -1) { + return; + } + auto signer = members[myIdx]; + + auto svec = BuildDeterministicSvec(llmqType, quorumHash); + auto vvec = BuildVvec(svec); + auto vvecHash = ::SerializeHash(vvec); + auto validMembers = GetValidMembers(llmqType, members); + if (std::count(validMembers.begin(), validMembers.end(), true) < params.minSize) { + return; + } + + auto commitmentHash = CLLMQUtils::BuildCommitmentHash((uint8_t)llmqType, quorumHash, validMembers, vvec[0], vvecHash); + + CDummyCommitment qc; + qc.llmqType = (uint8_t)llmqType; + qc.quorumHash = quorumHash; + qc.signer = (uint16_t)myIdx; + qc.validMembers = validMembers; + + qc.membersSig = activeMasternodeInfo.blsKeyOperator->Sign(commitmentHash); + + CBLSSecretKey skShare; + if (!skShare.SecretKeyShare(svec, CBLSId::FromHash(signer->proTxHash))) { + return; + } + qc.quorumSig = skShare.Sign(commitmentHash); + + ProcessDummyCommitment(-1, qc); +} + +void CDummyDKG::CreateFinalCommitment(Consensus::LLMQType llmqType, const CBlockIndex* pindex) +{ + const auto& params = Params().GetConsensus().llmqs.at(llmqType); + int quorumHeight = pindex->nHeight - (pindex->nHeight % params.dkgInterval); + const CBlockIndex* quorumIndex; + { + LOCK(cs_main); + quorumIndex = chainActive[quorumHeight]; + } + uint256 quorumHash = quorumIndex->GetBlockHash(); + + auto members = CLLMQUtils::GetAllQuorumMembers(llmqType, quorumHash); + if (members.size() != params.size) { + return; + } + + auto svec = BuildDeterministicSvec(llmqType, quorumHash); + auto vvec = BuildVvec(svec); + auto vvecHash = ::SerializeHash(vvec); + + LOCK(sessionCs); + auto& session = curSessions[llmqType]; + + for (const auto& p : session.dummyCommitmentsFromMembers) { + const auto& commitmentHash = p.first; + if (p.second.size() < params.minSize) { + continue; + } + + const auto& firstQc = session.dummyCommitments[p.second.begin()->second]; + + CFinalCommitment fqc(params, quorumHash); + fqc.validMembers = firstQc.validMembers; + fqc.quorumPublicKey = vvec[0]; + fqc.quorumVvecHash = vvecHash; + + std::vector quorumSigs; + std::vector quorumSigIds; + std::vector memberSigs; + std::vector memberPubKeys; + + for (const auto& p2 : p.second) { + const auto& proTxHash = p2.first; + const auto& qc = session.dummyCommitments[p2.second]; + + int signerIdx = -1; + for (size_t i = 0; i < members.size(); i++) { + if (members[i]->proTxHash == proTxHash) { + signerIdx = (int)i; + break; + } + } + if (signerIdx == -1) { + break; + } + fqc.signers[signerIdx] = true; + if (quorumSigs.size() < params.threshold) { + quorumSigs.emplace_back(qc.quorumSig); + quorumSigIds.emplace_back(CBLSId::FromHash(proTxHash)); + } + memberSigs.emplace_back(qc.membersSig); + memberPubKeys.emplace_back(members[signerIdx]->pdmnState->pubKeyOperator); + } + + if (!fqc.quorumSig.Recover(quorumSigs, quorumSigIds)) { + LogPrintf("CDummyDKG::%s -- recovery failed for quorum %s:%d, validMembers=%d, signers=%d\n", __func__, + fqc.quorumHash.ToString(), fqc.llmqType, fqc.CountValidMembers(), fqc.CountSigners()); + continue; + } + fqc.membersSig = CBLSSignature::AggregateSecure(memberSigs, memberPubKeys, commitmentHash); + + if (!fqc.Verify(members, true)) { + LogPrintf("CDummyDKG::%s -- self created final commitment is invalid for quorum %s:%d, validMembers=%d, signers=%d\n", __func__, + fqc.quorumHash.ToString(), fqc.llmqType, fqc.CountValidMembers(), fqc.CountSigners()); + continue; + } + + LogPrintf("CDummyDKG::%s -- self created final commitment for quorum %s:%d, validMembers=%d, signers=%d\n", __func__, + fqc.quorumHash.ToString(), fqc.llmqType, fqc.CountValidMembers(), fqc.CountSigners()); + quorumBlockProcessor->AddMinableCommitment(fqc); + } + + // reset for next round + curSessions[llmqType] = CDummyDKGSession(); +} + +std::vector CDummyDKG::GetValidMembers(Consensus::LLMQType llmqType, const std::vector& members) +{ + const auto& params = Params().GetConsensus().llmqs.at(llmqType); + std::vector ret(params.size, false); + + LOCK(sessionCs); + + // Valid members are members that sent us a dummy contribution in this session + + for (size_t i = 0; i < params.size; i++) { + if (curSessions[llmqType].dummyContributionsFromMembers.count(members[i]->proTxHash)) { + ret[i] = true; + } + } + + return ret; +} + +// The returned secret key vector is NOT SECURE AT ALL!! +// It is known by everyone. This is only for testnet/devnet/regtest, so this is fine. Also, we won't do any meaningful +// things with the commitments. This is only needed to make the final commitments validate +BLSSecretKeyVector CDummyDKG::BuildDeterministicSvec(Consensus::LLMQType llmqType, const uint256& quorumHash) +{ + const auto& params = Params().GetConsensus().llmqs.at(llmqType); + + CHash256 seed_; + seed_.Write((unsigned char*)&llmqType, 1); + seed_.Write(quorumHash.begin(), quorumHash.size()); + + uint256 seed; + seed_.Finalize(seed.begin()); + + BLSSecretKeyVector svec; + svec.reserve(params.size); + for (size_t i = 0; i < params.threshold; i++) { + CBLSSecretKey sk; + while (true) { + seed = ::SerializeHash(seed); + sk.SetBuf(seed.begin(), seed.size()); + if (sk.IsValid()) { + break; + } + } + svec.emplace_back(sk); + } + + return svec; +} + +BLSPublicKeyVector CDummyDKG::BuildVvec(const BLSSecretKeyVector& svec) +{ + BLSPublicKeyVector vvec; + vvec.reserve(svec.size()); + for (size_t i = 0; i < svec.size(); i++) { + vvec.emplace_back(svec[i].GetPublicKey()); + } + return vvec; +} + +bool CDummyDKG::HasDummyContribution(const uint256& hash) +{ + LOCK(sessionCs); + for (const auto& p : curSessions) { + auto it = p.second.dummyContributions.find(hash); + if (it != p.second.dummyContributions.end()) { + return true; + } + } + return false; +} + +bool CDummyDKG::GetDummyContribution(const uint256& hash, CDummyContribution& ret) +{ + LOCK(sessionCs); + for (const auto& p : curSessions) { + auto it = p.second.dummyContributions.find(hash); + if (it != p.second.dummyContributions.end()) { + ret = it->second; + return true; + } + } + return false; +} + +bool CDummyDKG::HasDummyCommitment(const uint256& hash) +{ + LOCK(sessionCs); + for (const auto& p : curSessions) { + auto it = p.second.dummyCommitments.find(hash); + if (it != p.second.dummyCommitments.end()) { + return true; + } + } + return false; +} + +bool CDummyDKG::GetDummyCommitment(const uint256& hash, CDummyCommitment& ret) +{ + LOCK(sessionCs); + for (const auto& p : curSessions) { + auto it = p.second.dummyCommitments.find(hash); + if (it != p.second.dummyCommitments.end()) { + ret = it->second; + return true; + } + } + return false; +} + +} diff --git a/src/llmq/quorums_dummydkg.h b/src/llmq/quorums_dummydkg.h new file mode 100644 index 000000000000..c41cf9c1c267 --- /dev/null +++ b/src/llmq/quorums_dummydkg.h @@ -0,0 +1,157 @@ +// 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 DASH_QUORUMS_DUMMYDKG_H +#define DASH_QUORUMS_DUMMYDKG_H + +#include "llmq/quorums_commitment.h" + +#include "consensus/params.h" +#include "net.h" +#include "primitives/transaction.h" +#include "sync.h" + +#include "bls/bls.h" + +#include + +class CNode; +class CConnman; + +/** + * Implementation of an insecure dummy DKG + * + * This is only used on testnet/devnet/regtest and will NEVER be used on + * mainnet. It is NOT SECURE AT ALL! It will actually be removed later when the real DKG is introduced. + * + * It works by using a deterministic secure vector as the secure polynomial. Everyone can calculate this + * polynomial by himself, which makes it insecure by definition. + * + * The purpose of this dummy implementation is to test final LLMQ commitments and simple PoSe on-chain. + * The dummy DKG first creates dummy commitments and propagates these to all nodes. They can then create + * a valid LLMQ commitment from these, which validates with the normal commitment validation code. + * + * After these have been mined on-chain, they are indistinguishable from commitments created from the real + * DKG, making them good enough for testing. + * + * The validMembers bitset is created from information of past dummy DKG sessions. If nodes failed to provide + * the dummy commitments, they will be marked as bad in the next session. This might create some chaos and + * finalizable commitments, but this is ok and will sort itself out after some sessions. + */ + +namespace llmq +{ + +// This is more like a PING than a contribution +// We will later replace this message (reusing same inv type) for the real contribution +// Deserialization will then be incompatible between peers, but that is fine (let them reject the messages) +class CDummyContribution +{ +public: + uint8_t llmqType{Consensus::LLMQ_NONE}; + uint256 quorumHash; + uint16_t signer{(uint16_t)-1}; + + CBLSSignature sig; + +public: + ADD_SERIALIZE_METHODS + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(llmqType); + READWRITE(quorumHash); + READWRITE(signer); + READWRITE(sig); + } + + uint256 GetSignHash() const + { + CDummyContribution tmp(*this); + tmp.sig = CBLSSignature(); + return ::SerializeHash(tmp); + } +}; + +// This message is only allowed on testnet/devnet/regtest +// If any peer tries to send this message on mainnet, it is banned immediately +// It is used to test commitments on testnet without actually running a full-blown DKG. +class CDummyCommitment +{ +public: + uint8_t llmqType{Consensus::LLMQ_NONE}; + uint256 quorumHash; + uint16_t signer{(uint16_t)-1}; + + std::vector validMembers; + + CBLSSignature quorumSig; + CBLSSignature membersSig; + +public: + int CountValidMembers() const + { + return (int)std::count(validMembers.begin(), validMembers.end(), true); + } + +public: + ADD_SERIALIZE_METHODS + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(llmqType); + READWRITE(quorumHash); + READWRITE(signer); + READWRITE(DYNBITSET(validMembers)); + READWRITE(quorumSig); + READWRITE(membersSig); + } +}; + +class CDummyDKGSession +{ +public: + std::map dummyContributions; + std::map dummyCommitments; + std::map dummyContributionsFromMembers; + std::map> dummyCommitmentsFromMembers; +}; + +// It simulates the result of a DKG session by deterministically calculating a secret/public key vector +// !!!THIS IS NOT SECURE AT ALL AND WILL NEVER BE USED ON MAINNET!!! +// The whole dummy DKG will be removed when we add the real DKG +class CDummyDKG +{ +private: + CCriticalSection sessionCs; + std::map curSessions; + +public: + void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman); + void ProcessDummyContribution(NodeId from, const CDummyContribution& qc); + void ProcessDummyCommitment(NodeId from, const CDummyCommitment& qc); + + void UpdatedBlockTip(const CBlockIndex* pindex, bool fInitialDownload); + void CreateDummyContribution(Consensus::LLMQType llmqType, const CBlockIndex* pindex); + void CreateDummyCommitment(Consensus::LLMQType llmqType, const CBlockIndex* pindex); + void CreateFinalCommitment(Consensus::LLMQType llmqType, const CBlockIndex* pindex); + + bool HasDummyContribution(const uint256& hash); + bool GetDummyContribution(const uint256& hash, CDummyContribution& ret); + bool HasDummyCommitment(const uint256& hash); + bool GetDummyCommitment(const uint256& hash, CDummyCommitment& ret); + +private: + std::vector GetValidMembers(Consensus::LLMQType llmqType, const std::vector& members); + BLSSecretKeyVector BuildDeterministicSvec(Consensus::LLMQType llmqType, const uint256& quorumHash); + BLSPublicKeyVector BuildVvec(const BLSSecretKeyVector& svec); +}; + +extern CDummyDKG* quorumDummyDKG; + +} + +#endif//DASH_QUORUMS_DUMMYDKG_H diff --git a/src/llmq/quorums_init.cpp b/src/llmq/quorums_init.cpp new file mode 100644 index 000000000000..fa29034601f3 --- /dev/null +++ b/src/llmq/quorums_init.cpp @@ -0,0 +1,28 @@ +// 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 "quorums_init.h" + +#include "quorums_blockprocessor.h" +#include "quorums_commitment.h" +#include "quorums_dummydkg.h" + +namespace llmq +{ + +void InitLLMQSystem(CEvoDB& evoDb) +{ + quorumBlockProcessor = new CQuorumBlockProcessor(evoDb); + quorumDummyDKG = new CDummyDKG(); +} + +void DestroyLLMQSystem() +{ + delete quorumDummyDKG; + quorumDummyDKG = nullptr; + delete quorumBlockProcessor; + quorumBlockProcessor = nullptr; +} + +} diff --git a/src/llmq/quorums_init.h b/src/llmq/quorums_init.h new file mode 100644 index 000000000000..137248b88546 --- /dev/null +++ b/src/llmq/quorums_init.h @@ -0,0 +1,18 @@ +// 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 DASH_QUORUMS_INIT_H +#define DASH_QUORUMS_INIT_H + +class CEvoDB; + +namespace llmq +{ + +void InitLLMQSystem(CEvoDB& evoDb); +void DestroyLLMQSystem(); + +} + +#endif //DASH_QUORUMS_INIT_H diff --git a/src/llmq/quorums_utils.cpp b/src/llmq/quorums_utils.cpp new file mode 100644 index 000000000000..452909e07b58 --- /dev/null +++ b/src/llmq/quorums_utils.cpp @@ -0,0 +1,33 @@ +// 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 "quorums_utils.h" + +#include "chainparams.h" +#include "random.h" + +namespace llmq +{ + +std::vector CLLMQUtils::GetAllQuorumMembers(Consensus::LLMQType llmqType, const uint256& blockHash) +{ + auto& params = Params().GetConsensus().llmqs.at(llmqType); + auto allMns = deterministicMNManager->GetListForBlock(blockHash); + auto modifier = ::SerializeHash(std::make_pair((uint8_t)llmqType, blockHash)); + return allMns.CalculateQuorum(params.size, modifier); +} + +uint256 CLLMQUtils::BuildCommitmentHash(uint8_t llmqType, const uint256& blockHash, const std::vector& validMembers, const CBLSPublicKey& pubKey, const uint256& vvecHash) +{ + CHashWriter hw(SER_NETWORK, 0); + hw << llmqType; + hw << blockHash; + hw << DYNBITSET(validMembers); + hw << pubKey; + hw << vvecHash; + return hw.GetHash(); +} + + +} diff --git a/src/llmq/quorums_utils.h b/src/llmq/quorums_utils.h new file mode 100644 index 000000000000..2aab41628dcf --- /dev/null +++ b/src/llmq/quorums_utils.h @@ -0,0 +1,28 @@ +// 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_QUORUMS_UTILS_H +#define DASH_QUORUMS_UTILS_H + +#include "consensus/params.h" + +#include "evo/deterministicmns.h" + +#include + +namespace llmq +{ + +class CLLMQUtils +{ +public: + // includes members which failed DKG + static std::vector GetAllQuorumMembers(Consensus::LLMQType llmqType, const uint256& blockHash); + + static uint256 BuildCommitmentHash(uint8_t llmqType, const uint256& blockHash, const std::vector& validMembers, const CBLSPublicKey& pubKey, const uint256& vvecHash); +}; + +} + +#endif//DASH_QUORUMS_UTILS_H diff --git a/src/masternode-payments.cpp b/src/masternode-payments.cpp index 73c97c24c594..b3b00e0270bd 100644 --- a/src/masternode-payments.cpp +++ b/src/masternode-payments.cpp @@ -5,6 +5,7 @@ #include "activemasternode.h" #include "consensus/validation.h" #include "governance-classes.h" +#include "init.h" #include "masternode-payments.h" #include "masternode-sync.h" #include "masternodeman.h" @@ -14,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; @@ -23,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 * @@ -36,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) { //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; } @@ -149,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; } @@ -159,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 + LogPrintf("%s -- ERROR: Invalid masternode payment detected at height %d: %s", __func__, nBlockHeight, txNew.ToString()); 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); + } + + 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)) { + LogPrint("mnpayments", "%s -- no masternode to pay (MN list probably empty)\n", __func__); + } + + 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(); } - // 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()); + 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)) { @@ -217,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); @@ -226,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); @@ -242,45 +356,45 @@ 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)) { + if (deterministicMNManager->IsDeterministicMNsSporkActive(nBlockHeight)) { + LogPrintf("CMasternodePayments::%s -- deterministic masternode lists enabled and no payee\n", __func__); + return false; + } - 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 { @@ -291,6 +405,9 @@ int CMasternodePayments::GetMinMasternodePaymentsProto() const { void CMasternodePayments::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + if(fLiteMode) return; // disable all Dash specific functionality if (strCommand == NetMsgType::MASTERNODEPAYMENTSYNC) { //Masternode Payments Request Sync @@ -307,12 +424,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 @@ -339,7 +450,10 @@ void CMasternodePayments::ProcessMessage(CNode* pfrom, const std::string& strCom uint256 nHash = vote.GetHash(); - pfrom->setAskFor.erase(nHash); + { + LOCK(cs_main); + connman.RemoveAskFor(nHash); + } // TODO: clear setAskFor for MSG_MASTERNODE_PAYMENT_BLOCK too @@ -384,7 +498,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"); @@ -444,27 +558,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; } } @@ -472,12 +586,57 @@ 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(); + + CAmount masternodeReward = GetMasternodePayment(nBlockHeight, blockReward); + + if (deterministicMNManager->IsDeterministicMNsSporkActive(nBlockHeight)) { + uint256 blockHash; + { + LOCK(cs_main); + if (nBlockHeight - 1 > chainActive.Height()) { + // there are some cases (e.g. IsScheduled) where legacy/compatibility code runs into this method with + // block heights above the chain tip. Return false in this case + // TODO remove this when removing the compatibility code and make sure this method is only called with + // correct block height + return false; + } + blockHash = chainActive[nBlockHeight - 1]->GetBlockHash(); + } + uint256 proTxHash; + auto dmnPayee = deterministicMNManager->GetListForBlock(blockHash).GetMNPayee(); + if (!dmnPayee) { + return false; + } + + CAmount operatorReward = 0; + if (dmnPayee->nOperatorReward != 0 && dmnPayee->pdmnState->scriptOperatorPayout != CScript()) { + // This calculation might eventually turn out to result in 0 even if an operator reward percentage is given. + // This will however only happen in a few years when the block rewards drops very low. + operatorReward = (masternodeReward * dmnPayee->nOperatorReward) / 10000; + masternodeReward -= operatorReward; + } + + if (masternodeReward > 0) { + voutMasternodePaymentsRet.emplace_back(masternodeReward, dmnPayee->pdmnState->scriptPayout); + } + if (operatorReward > 0) { + voutMasternodePaymentsRet.emplace_back(operatorReward, dmnPayee->pdmnState->scriptOperatorPayout); + } - auto it = mapMasternodeBlocks.find(nBlockHeight); - return it != mapMasternodeBlocks.end() && it->second.GetBestPayee(payeeRet); + 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? @@ -486,16 +645,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->collateralOutpoint == mnInfo.outpoint) { + 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; + } } } @@ -518,7 +690,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; } @@ -551,7 +723,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; } @@ -576,7 +748,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; } @@ -604,7 +776,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; } } @@ -621,7 +793,7 @@ bool CMasternodeBlockPayees::IsTransactionValid(const CTransaction& txNew) const } } - LogPrintf("CMasternodeBlockPayees::IsTransactionValid -- ERROR: Missing required payment, possible payees: '%s', amount: %f DASH\n", strPayeesPossible, (float)nMasternodePayment/COIN); + LogPrintf("CMasternodeBlockPayees::%s -- ERROR: Missing required payment, possible payees: '%s', amount: %f DASH\n", __func__, strPayeesPossible, (float)nMasternodePayment/COIN); return false; } @@ -652,21 +824,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); @@ -678,14 +878,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 @@ -723,7 +923,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; } @@ -736,7 +936,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 @@ -748,6 +948,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; @@ -759,48 +963,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); @@ -817,13 +1021,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); @@ -883,9 +1087,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; } @@ -893,7 +1101,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; @@ -902,12 +1110,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 @@ -921,10 +1129,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. @@ -974,7 +1182,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)); } @@ -999,7 +1207,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(); @@ -1009,12 +1217,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; @@ -1025,38 +1232,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)); } } @@ -1087,11 +1292,22 @@ 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; CheckBlockVotes(nFutureBlock - 1); ProcessBlock(nFutureBlock, connman); } + +void CMasternodePayments::DoMaintenance() +{ + if (ShutdownRequested()) return; + + CheckAndRemove(); +} diff --git a/src/masternode-payments.h b/src/masternode-payments.h index d539aad8a017..984551c923f4 100644 --- a/src/masternode-payments.h +++ b/src/masternode-payments.h @@ -23,20 +23,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 = 70206; +static const int MIN_MASTERNODE_PAYMENT_PROTO_VERSION_1 = 70210; static const int MIN_MASTERNODE_PAYMENT_PROTO_VERSION_2 = 70210; 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 { @@ -133,21 +132,7 @@ class CMasternodePaymentVote template inline void SerializationOp(Stream& s, Operation ser_action) { - 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); READWRITE(nBlockHeight); READWRITE(*(CScriptBase*)(&payee)); if (!(s.GetType() & SER_GETHASH)) { @@ -159,7 +144,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; @@ -213,8 +198,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); @@ -222,7 +207,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(); } @@ -232,6 +217,8 @@ class CMasternodePayments int GetStorageLimit() const; void UpdatedBlockTip(const CBlockIndex *pindex, CConnman& connman); + + void DoMaintenance(); }; #endif diff --git a/src/masternode-sync.cpp b/src/masternode-sync.cpp index 589225260a90..b5cf4dc950fe 100644 --- a/src/masternode-sync.cpp +++ b/src/masternode-sync.cpp @@ -3,18 +3,16 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "activemasternode.h" -#include "checkpoints.h" #include "governance.h" +#include "init.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; @@ -22,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; @@ -43,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"; @@ -58,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"); @@ -96,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..."); @@ -133,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(); @@ -144,6 +150,12 @@ void CMasternodeSync::ProcessTick(CConnman& connman) nTimeLastProcess = GetTime(); return; } + + if(GetTime() - nTimeLastProcess < MASTERNODE_SYNC_TICK_SECONDS) { + // too early, nothing to do here + return; + } + nTimeLastProcess = GetTime(); // reset sync status in case of any other sync failure @@ -165,8 +177,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); @@ -184,22 +196,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; } @@ -221,12 +234,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); @@ -242,12 +255,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(); @@ -260,7 +279,7 @@ void CMasternodeSync::ProcessTick(CConnman& connman) } // request from three peers max - if (nRequestedMasternodeAttempt > 2) { + if (nTriedPeerCount > 2) { connman.ReleaseNodeVector(vNodesCopy); return; } @@ -270,7 +289,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); @@ -280,14 +299,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(); @@ -302,15 +327,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; } @@ -320,15 +345,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); @@ -338,13 +358,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? } @@ -374,7 +394,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); @@ -389,7 +409,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); @@ -483,7 +503,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. @@ -491,3 +511,10 @@ void CMasternodeSync::UpdatedBlockTip(const CBlockIndex *pindexNew, bool fInitia SwitchToNextAsset(connman); } } + +void CMasternodeSync::DoMaintenance(CConnman &connman) +{ + if (ShutdownRequested()) return; + + ProcessTick(connman); +} diff --git a/src/masternode-sync.h b/src/masternode-sync.h index 40b13b2589c3..2d98c8a1cb10 100644 --- a/src/masternode-sync.h +++ b/src/masternode-sync.h @@ -7,8 +7,6 @@ #include "chain.h" #include "net.h" -#include - class CMasternodeSync; static const int MASTERNODE_SYNC_FAILED = -1; @@ -24,8 +22,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.5 minutes so 30 seconds should be fine -static const int MASTERNODE_SYNC_ENOUGH_PEERS = 6; - extern CMasternodeSync masternodeSync; // @@ -36,9 +32,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; @@ -55,14 +51,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(); @@ -77,6 +73,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); }; #endif diff --git a/src/masternode.cpp b/src/masternode.cpp index d1a7628e0e44..e89e49d6c8e2 100644 --- a/src/masternode.cpp +++ b/src/masternode.cpp @@ -18,18 +18,18 @@ #include "wallet/wallet.h" #endif // ENABLE_WALLET -#include +#include "evo/deterministicmns.h" + +#include CMasternode::CMasternode() : - masternode_info_t{ MASTERNODE_ENABLED, PROTOCOL_VERSION, GetAdjustedTime()}, - fAllowMixingTx(true) + masternode_info_t{ MASTERNODE_ENABLED, PROTOCOL_VERSION, GetAdjustedTime()} {} -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}, - fAllowMixingTx(true) + outpoint, addr, pubKeyCollateralAddressNew, pubKeyMasternodeNew} {} CMasternode::CMasternode(const CMasternode& other) : @@ -40,7 +40,7 @@ CMasternode::CMasternode(const CMasternode& other) : nBlockLastPaid(other.nBlockLastPaid), nPoSeBanScore(other.nPoSeBanScore), nPoSeBanHeight(other.nPoSeBanHeight), - fAllowMixingTx(other.fAllowMixingTx), + nMixingTxCount(other.nMixingTxCount), fUnitTest(other.fUnitTest) {} @@ -48,10 +48,15 @@ CMasternode::CMasternode(const CMasternodeBroadcast& mnb) : masternode_info_t{ mnb.nActiveState, mnb.nProtocolVersion, mnb.sigTime, mnb.outpoint, mnb.addr, mnb.pubKeyCollateralAddress, mnb.pubKeyMasternode}, lastPing(mnb.lastPing), - vchSig(mnb.vchSig), - fAllowMixingTx(true) + vchSig(mnb.vchSig) {} +CMasternode::CMasternode(const uint256 &proTxHash, const CDeterministicMNCPtr& dmn) : + masternode_info_t{ MASTERNODE_ENABLED, DMN_PROTO_VERSION, GetAdjustedTime(), + dmn->collateralOutpoint, dmn->pdmnState->addr, CKeyID() /* not valid with DIP3 */, dmn->pdmnState->keyIDOwner, dmn->pdmnState->pubKeyOperator, dmn->pdmnState->keyIDVoting} +{ +} + // // When a new masternode broadcast is sent, update our information // @@ -60,6 +65,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; @@ -73,11 +81,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 @@ -95,19 +103,21 @@ bool CMasternode::UpdateFromNewBroadcast(CMasternodeBroadcast& mnb, CConnman& co // arith_uint256 CMasternode::CalculateScore(const uint256& blockHash) const { + // NOTE not called when deterministic masternodes (spork15) are activated + // Deterministically calculate a "score" for a Masternode based on any given (block)hash CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); ss << outpoint << nCollateralMinConfBlockHash << blockHash; 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); @@ -120,7 +130,7 @@ 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; } @@ -171,7 +181,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 +325,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()).GetMNByCollateral(outpoint); + 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 +367,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 +420,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 +433,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 +481,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 +535,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 +547,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,14 +564,14 @@ 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; @@ -560,7 +584,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; } @@ -635,21 +659,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; } @@ -666,13 +690,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; @@ -680,11 +704,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; @@ -742,7 +766,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; @@ -756,20 +780,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; } @@ -778,7 +802,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; @@ -786,11 +810,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; @@ -798,9 +822,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; @@ -847,7 +871,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; } @@ -884,7 +908,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 @@ -930,22 +954,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); } /** @@ -956,15 +986,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 38253aa44a2f..81e396d07eae 100644 --- a/src/masternode.h +++ b/src/masternode.h @@ -8,6 +8,9 @@ #include "key.h" #include "validation.h" #include "spork.h" +#include "bls/bls.h" + +#include "evo/deterministicmns.h" class CMasternode; class CMasternodeBroadcast; @@ -21,6 +24,7 @@ static const int MASTERNODE_EXPIRATION_SECONDS = 120 * 60; static const int MASTERNODE_NEW_START_REQUIRED_SECONDS = 180 * 60; static const int MASTERNODE_POSE_BAN_MAX_SCORE = 5; +static const int MASTERNODE_MAX_MIXING_TXES = 5; // // The Masternode Ping Class : Contains a different serialize method for sending pings from masternodes throughout the network @@ -51,43 +55,15 @@ class CMasternodePing template inline void SerializationOp(Stream& s, Operation ser_action) { - 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); 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 70209 - nDaemonVersion = DEFAULT_DAEMON_VERSION; - return; - } - if (!(nVersion == 70208 && (s.GetType() & SER_NETWORK))) { - READWRITE(nDaemonVersion); - } + READWRITE(nDaemonVersion); } uint256 GetHash() const; @@ -95,12 +71,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; }; @@ -127,12 +106,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; @@ -140,8 +128,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{}; // this is only used in compatibility code and won't be used when spork15 gets activated + 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; @@ -176,7 +169,7 @@ class CMasternode : public masternode_info_t COLLATERAL_OK, COLLATERAL_UTXO_NOT_FOUND, COLLATERAL_INVALID_AMOUNT, - COLLATERAL_INVALID_PUBKEY + COLLATERAL_INVALID_PUBKEY, }; @@ -187,7 +180,7 @@ class CMasternode : public masternode_info_t int nBlockLastPaid{}; int nPoSeBanScore{}; int nPoSeBanHeight{}; - bool fAllowMixingTx{}; + int nMixingTxCount{}; bool fUnitTest = false; // KEEP TRACK OF GOVERNANCE ITEMS EACH MASTERNODE HAS VOTE UPON FOR RECALCULATION @@ -197,30 +190,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 == 70208 && (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); @@ -233,7 +218,7 @@ class CMasternode : public masternode_info_t READWRITE(nProtocolVersion); READWRITE(nPoSeBanScore); READWRITE(nPoSeBanHeight); - READWRITE(fAllowMixingTx); + READWRITE(nMixingTxCount); READWRITE(fUnitTest); READWRITE(mapGovernanceObjectsVotedOn); } @@ -243,8 +228,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; } @@ -291,6 +276,11 @@ class CMasternode : public masternode_info_t return false; } + bool IsValidForMixingTxes() const + { + return nMixingTxCount <= MASTERNODE_MAX_MIXING_TXES; + } + bool IsValidNetAddr(); static bool IsValidNetAddr(CService addrIn); @@ -324,7 +314,7 @@ class CMasternode : public masternode_info_t nBlockLastPaid = from.nBlockLastPaid; nPoSeBanScore = from.nPoSeBanScore; nPoSeBanHeight = from.nPoSeBanHeight; - fAllowMixingTx = from.fAllowMixingTx; + nMixingTxCount = from.nMixingTxCount; fUnitTest = from.fUnitTest; mapGovernanceObjectsVotedOn = from.mapGovernanceObjectsVotedOn; return *this; @@ -360,21 +350,7 @@ class CMasternodeBroadcast : public CMasternode template inline void SerializationOp(Stream& s, Operation ser_action) { - int nVersion = s.GetVersion(); - if (nVersion == 70208 && (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); @@ -386,6 +362,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; @@ -427,27 +410,8 @@ class CMasternodeVerification template inline void SerializationOp(Stream& s, Operation ser_action) { - int nVersion = s.GetVersion(); - if (nVersion == 70208 && (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 2dac44eaf195..0e2e67be796a 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:19999 93HaYBVUCYjEMeeH1Y4sBGLALQZE1Yc1K64xiqgX37tGBDQL8Xg 2bcd3c84c84f87eaa86e4e56834c92927a07f9e18718810b92e0d0324456a67c 0\n"; diff --git a/src/masternodeconfig.h b/src/masternodeconfig.h index 39066cd3b904..7d978d9dc7d4 100644 --- a/src/masternodeconfig.h +++ b/src/masternodeconfig.h @@ -36,41 +36,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 bec58a45dc9c..b118b3839f54 100644 --- a/src/masternodeman.cpp +++ b/src/masternodeman.cpp @@ -6,6 +6,7 @@ #include "addrman.h" #include "alert.h" #include "clientversion.h" +#include "init.h" #include "governance.h" #include "masternode-payments.h" #include "masternode-sync.h" @@ -21,10 +22,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-12"; const int CMasternodeMan::LAST_PAID_SCAN_BLOCKS = 100; struct CompareLastPaidBlock @@ -78,6 +82,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); @@ -93,6 +100,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()) { @@ -114,11 +124,7 @@ void CMasternodeMan::AskForMN(CNode* pnode, const COutPoint& outpoint, CConnman& } mWeAskedForMasternodeListEntry[outpoint][addrSquashed] = GetTime() + DSEG_UPDATE_SECONDS; - if (pnode->GetSendVersion() == 70208) { - 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) @@ -130,7 +136,7 @@ bool CMasternodeMan::AllowMixing(const COutPoint &outpoint) } nDsqCount++; pmn->nLastDsq = nDsqCount; - pmn->fAllowMixingTx = true; + pmn->nMixingTxCount = 0; return true; } @@ -142,7 +148,7 @@ bool CMasternodeMan::DisallowMixing(const COutPoint &outpoint) if (!pmn) { return false; } - pmn->fAllowMixingTx = false; + pmn->nMixingTxCount++; return true; } @@ -150,6 +156,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; @@ -163,7 +173,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 @@ -174,6 +185,9 @@ void CMasternodeMan::Check() void CMasternodeMan::CheckAndRemove(CConnman& connman) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + if(!masternodeSync.IsMasternodeListSynced()) return; LogPrintf("CMasternodeMan::CheckAndRemove\n"); @@ -253,7 +267,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++); @@ -356,6 +370,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(dmn->collateralOutpoint); + 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_PROTO_VERSION; + + // 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(dmn->collateralOutpoint); + }); + 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); @@ -372,26 +449,37 @@ 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(); + nCount = (int)mnList.GetAllMNsCount(); + } else { + for (const auto& mnpair : mapMasternodes) { + if(mnpair.second.nProtocolVersion < nProtocolVersion) continue; + nCount++; + } } - return nCount; } int CMasternodeMan::CountEnabled(int nProtocolVersion) { LOCK(cs); + int nCount = 0; nProtocolVersion = nProtocolVersion == -1 ? mnpayments.GetMinMasternodePaymentsProto() : nProtocolVersion; - for (const auto& mnpair : mapMasternodes) { - if(mnpair.second.nProtocolVersion < nProtocolVersion || !mnpair.second.IsEnabled()) continue; - nCount++; + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + auto mnList = deterministicMNManager->GetListAtChainTip(); + nCount = (int)mnList.GetValidMNsCount(); + } else { + for (const auto& mnpair : mapMasternodes) { + if (mnpair.second.nProtocolVersion < nProtocolVersion || !mnpair.second.IsEnabled()) continue; + nCount++; + } } return nCount; @@ -419,6 +507,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())) { @@ -430,11 +521,8 @@ void CMasternodeMan::DsegUpdate(CNode* pnode, CConnman& connman) } } - if (pnode->GetSendVersion() == 70208) { - 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; @@ -444,63 +532,111 @@ 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(); + auto dmn = mnList.GetMNByCollateral(outpoint); + if (!dmn || !mnList.IsMNValid(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(dmn->collateralOutpoint, 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) { - LOCK(cs); - for (const auto& mnpair : mapMasternodes) { - CScript scriptCollateralAddress = GetScriptForDestination(mnpair.second.pubKeyCollateralAddress.GetID()); - if (scriptCollateralAddress == payee) { - mnInfoRet = mnpair.second.GetInfo(); - return true; + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + // we can't reliably search by payee as there might be duplicates. Also, keyIDCollateralAddress is not + // always the payout address as DIP3 allows using different keys for collateral and payouts + // this method is only used from ComputeBlockVersion, which has a different logic for deterministic MNs + // this method won't be reimplemented when removing the compatibility code + return false; + } else { + CTxDestination dest; + if (!ExtractDestination(payee, dest) || !boost::get(&dest)) + return false; + CKeyID keyId = *boost::get(&dest); + LOCK(cs); + for (const auto& mnpair : mapMasternodes) { + if (mnpair.second.keyIDCollateralAddress == keyId) { + mnInfoRet = mnpair.second.GetInfo(); + return true; + } } + return false; } - return false; } bool CMasternodeMan::Has(const COutPoint& outpoint) { LOCK(cs); - return mapMasternodes.find(outpoint) != mapMasternodes.end(); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + return deterministicMNManager->HasValidMNCollateralAtChainTip(outpoint); + } else { + return mapMasternodes.find(outpoint) != mapMasternodes.end(); + } } // @@ -513,6 +649,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; @@ -571,7 +711,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){ @@ -621,6 +761,8 @@ masternode_info_t CMasternodeMan::FindRandomNotInVec(const std::vectorIsDeterministicMNsSporkActive() && !deterministicMNManager->HasValidMNCollateralAtChainTip(pmn->outpoint)) + continue; // found the one not in vecToExclude LogPrint("masternode", "CMasternodeMan::FindRandomNotInVec -- found, masternode=%s\n", pmn->outpoint.ToStringShort()); return pmn->GetInfo(); @@ -630,30 +772,63 @@ masternode_info_t CMasternodeMan::FindRandomNotInVec(const std::vector CMasternodeMan::GetFullMasternodeMap() { - vecMasternodeScoresRet.clear(); + LOCK(cs); - if (!masternodeSync.IsMasternodeListSynced()) - return false; + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + std::map result; + auto mnList = deterministicMNManager->GetListAtChainTip(); + for (const auto &p : mapMasternodes) { + auto dmn = mnList.GetMNByCollateral(p.first); + if (dmn && mnList.IsMNValid(dmn)) { + 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) +{ AssertLockHeld(cs); - if (mapMasternodes.empty()) - return false; + vecMasternodeScoresRet.clear(); - // calculate scores - for (const auto& mnpair : mapMasternodes) { - if (mnpair.second.nProtocolVersion >= nMinProtocol) { - vecMasternodeScoresRet.push_back(std::make_pair(mnpair.second.CalculateScore(nBlockHash), &mnpair.second)); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + auto mnList = deterministicMNManager->GetListAtChainTip(); + auto scores = mnList.CalculateScores(nBlockHash); + for (const auto& p : scores) { + auto* mn = Find(p.second->collateralOutpoint); + vecMasternodeScoresRet.emplace_back(p.first, mn); } - } + } else { + if (!masternodeSync.IsMasternodeListSynced()) + return false; + + if (mapMasternodes.empty()) + return false; + // calculate scores + for (const auto& mnpair : mapMasternodes) { + if (mnpair.second.nProtocolVersion >= nMinProtocol) { + vecMasternodeScoresRet.push_back(std::make_pair(mnpair.second.CalculateScore(nBlockHash), &mnpair.second)); + } + } + } sort(vecMasternodeScoresRet.rbegin(), vecMasternodeScoresRet.rend(), CompareScoreMN()); return !vecMasternodeScoresRet.empty(); } bool CMasternodeMan::GetMasternodeRank(const COutPoint& outpoint, int& nRankRet, int nBlockHeight, int nMinProtocol) +{ + uint256 tmp; + return GetMasternodeRank(outpoint, nRankRet, tmp, nBlockHeight, nMinProtocol); +} + +bool CMasternodeMan::GetMasternodeRank(const COutPoint& outpoint, int& nRankRet, uint256& blockHashRet, int nBlockHeight, int nMinProtocol) { nRankRet = -1; @@ -661,8 +836,8 @@ bool CMasternodeMan::GetMasternodeRank(const COutPoint& outpoint, int& nRankRet, return false; // make sure we know about this block - uint256 nBlockHash = uint256(); - if (!GetBlockHash(nBlockHash, nBlockHeight)) { + blockHashRet = uint256(); + if (!GetBlockHash(blockHashRet, nBlockHeight)) { LogPrintf("CMasternodeMan::%s -- ERROR: GetBlockHash() failed at nBlockHeight %d\n", __func__, nBlockHeight); return false; } @@ -670,7 +845,7 @@ bool CMasternodeMan::GetMasternodeRank(const COutPoint& outpoint, int& nRankRet, LOCK(cs); score_pair_vec_t vecMasternodeScores; - if (!GetMasternodeScores(nBlockHash, vecMasternodeScores, nMinProtocol)) + if (!GetMasternodeScores(blockHashRet, vecMasternodeScores, nMinProtocol)) return false; int nRank = 0; @@ -716,14 +891,22 @@ bool CMasternodeMan::GetMasternodeRanks(CMasternodeMan::rank_pair_vec_t& vecMast void CMasternodeMan::ProcessMasternodeConnections(CConnman& connman) { - //we don't care about this for regtest - if(Params().NetworkIDString() == CBaseChainParams::REGTEST) return; + std::vector vecMnInfo; // will be empty when no wallet +#ifdef ENABLE_WALLET + privateSendClient.GetMixingMasternodesInfo(vecMnInfo); +#endif // ENABLE_WALLET - connman.ForEachNode(CConnman::AllNodes, [](CNode* pnode) { + connman.ForEachNode(CConnman::AllNodes, [&vecMnInfo](CNode* pnode) { + if (pnode->fMasternode) { #ifdef ENABLE_WALLET - if(pnode->fMasternode && !privateSendClient.IsMixingMasternode(pnode)) { -#else - if(pnode->fMasternode) { + 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; @@ -734,6 +917,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()); } @@ -760,6 +946,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; @@ -772,14 +961,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 @@ -798,11 +984,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 Dash specific functionality if (strCommand == NetMsgType::MNANNOUNCE) { //Masternode Broadcast @@ -810,7 +998,10 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, const std::string& strCommand, CMasternodeBroadcast mnb; vRecv >> mnb; - pfrom->setAskFor.erase(mnb.GetHash()); + { + LOCK(cs_main); + connman.RemoveAskFor(mnb.GetHash()); + } if(!masternodeSync.IsBlockchainSynced()) return; @@ -836,7 +1027,10 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, const std::string& strCommand, uint256 nHash = mnp.GetHash(); - pfrom->setAskFor.erase(nHash); + { + LOCK(cs_main); + connman.RemoveAskFor(nHash); + } if(!masternodeSync.IsBlockchainSynced()) return; @@ -865,7 +1059,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; } @@ -881,14 +1075,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()); @@ -906,7 +1093,10 @@ void CMasternodeMan::ProcessMessage(CNode* pfrom, const std::string& strCommand, CMasternodeVerification mnv; vRecv >> mnv; - pfrom->setAskFor.erase(mnv.GetHash()); + { + LOCK(cs_main); + connman.RemoveAskFor(mnv.GetHash()); + } if(!masternodeSync.IsMasternodeListSynced()) return; @@ -968,7 +1158,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); @@ -997,34 +1189,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 @@ -1035,14 +1229,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", @@ -1057,16 +1244,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 + + for (const auto& addr : vAddr) { + PrepareVerifyRequest(addr, connman); + } - LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Sent verification requests to %d masternodes\n", nCount); + LogPrint("masternode", "CMasternodeMan::DoFullVerificationStep -- Prepared verification requests for %d masternodes\n", vAddr.size()); } // This function tries to find masternodes with the same addr, @@ -1076,6 +1269,9 @@ void CMasternodeMan::DoFullVerificationStep(CConnman& connman) void CMasternodeMan::CheckSameAddr() { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; + if(!masternodeSync.IsSynced() || mapMasternodes.empty()) return; std::vector vBan; @@ -1084,8 +1280,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); @@ -1099,7 +1295,7 @@ void CMasternodeMan::CheckSameAddr() // initial step if(!pprevMasternode) { pprevMasternode = pmn; - pverifiedMasternode = pmn->IsPoSeVerified() ? pmn : NULL; + pverifiedMasternode = pmn->IsPoSeVerified() ? pmn : nullptr; continue; } // second+ step @@ -1114,7 +1310,7 @@ void CMasternodeMan::CheckSameAddr() pverifiedMasternode = pmn; } } else { - pverifiedMasternode = pmn->IsPoSeVerified() ? pmn : NULL; + pverifiedMasternode = pmn->IsPoSeVerified() ? pmn : nullptr; } pprevMasternode = pmn; } @@ -1127,29 +1323,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()) { @@ -1173,13 +1377,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 @@ -1205,24 +1411,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; } @@ -1237,6 +1443,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 @@ -1279,7 +1488,7 @@ void CMasternodeMan::ProcessVerifyReply(CNode* pnode, CMasternodeVerification& m { LOCK(cs); - CMasternode* prealMasternode = NULL; + CMasternode* prealMasternode = nullptr; std::vector vpMasternodesToBan; uint256 hash1 = mnv.GetSignatureHash1(blockHash); @@ -1289,10 +1498,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! @@ -1303,23 +1512,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; } @@ -1327,12 +1536,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; } @@ -1373,6 +1582,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()) { @@ -1442,12 +1654,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; } @@ -1456,12 +1668,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; } @@ -1494,12 +1706,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(); } @@ -1508,6 +1725,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; @@ -1573,13 +1793,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 @@ -1598,7 +1818,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; @@ -1620,6 +1840,8 @@ void CMasternodeMan::UpdateLastPaid(const CBlockIndex* pindex) void CMasternodeMan::UpdateLastSentinelPingTime() { LOCK(cs); + if (deterministicMNManager->IsDeterministicMNsSporkActive()) + return; nLastSentinelPingTime = GetTime(); } @@ -1649,11 +1871,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; } @@ -1670,6 +1894,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; @@ -1692,6 +1918,9 @@ void CMasternodeMan::UpdatedBlockTip(const CBlockIndex *pindex) nCachedBlockHeight = pindex->nHeight; LogPrint("masternode", "CMasternodeMan::UpdatedBlockTip -- nCachedBlockHeight=%d\n", nCachedBlockHeight); + AddDeterministicMasternodes(); + RemoveNonDeterministicMasternodes(); + CheckSameAddr(); if(fMasternodeMode) { @@ -1741,7 +1970,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; @@ -1752,11 +1981,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(); } @@ -1764,3 +1993,31 @@ void CMasternodeMan::NotifyMasternodeUpdates(CConnman& connman) fMasternodesAdded = false; fMasternodesRemoved = false; } + +void CMasternodeMan::DoMaintenance(CConnman& connman) +{ + if(fLiteMode) return; // disable all Dash 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 cfceaedb7cc4..853fed868301 100644 --- a/src/masternodeman.h +++ b/src/masternodeman.h @@ -94,7 +94,7 @@ class CMasternodeMan std::map mapSeenMasternodePing; // Keep track of all verifications I've seen std::map mapSeenMasternodeVerification; - // keep track of dsq count to prevent masternodes from gaming darksend queue + // keep track of dsq count to prevent masternodes from gaming privatesend queue int64_t nDsqCount; @@ -135,7 +135,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); @@ -149,6 +148,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(); @@ -168,8 +170,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 @@ -180,10 +183,11 @@ 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); + bool GetMasternodeRank(const COutPoint &outpoint, int& nRankRet, uint256& blockHashRet, int nBlockHeight = -1, int nMinProtocol = 0); void ProcessMasternodeConnections(CConnman& connman); std::pair > PopScheduledMnbRequestConnection(); @@ -193,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); @@ -221,7 +226,7 @@ class CMasternodeMan LOCK(cs); std::vector vecTmp = vecDirtyGovernanceObjectHashes; vecDirtyGovernanceObjectHashes.clear(); - return vecTmp;; + return vecTmp; } bool IsSentinelPingActive(); @@ -229,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); @@ -242,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/miner.cpp b/src/miner.cpp index fcb03eae3ad0..217804ac5cdd 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -28,6 +28,13 @@ #include "masternode-sync.h" #include "validationinterface.h" +#include "evo/specialtx.h" +#include "evo/cbtx.h" +#include "evo/simplifiedmns.h" +#include "evo/deterministicmns.h" + +#include "llmq/quorums_blockprocessor.h" + #include #include #include @@ -126,10 +133,13 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc pblocktemplate->vTxSigOps.push_back(-1); // updated at end LOCK2(cs_main, mempool.cs); + + bool fDIP0003Active_context = VersionBitsState(chainActive.Tip(), chainparams.GetConsensus(), Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE; + CBlockIndex* pindexPrev = chainActive.Tip(); nHeight = pindexPrev->nHeight + 1; - pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); + pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus(), chainparams.BIP9CheckMasternodesUpgraded()); // -regtest only: allow overriding block.nVersion with // -blockversion=N to test forking scenarios if (chainparams.MineBlocksOnDemand()) @@ -142,6 +152,17 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc ? nMedianTimePast : pblock->GetBlockTime(); + if (fDIP0003Active_context) { + for (auto& p : Params().GetConsensus().llmqs) { + CTransactionRef qcTx; + if (llmq::quorumBlockProcessor->GetMinableCommitmentTx(p.first, nHeight, qcTx)) { + pblock->vtx.emplace_back(qcTx); + pblocktemplate->vTxFees.emplace_back(0); + pblocktemplate->vTxSigOps.emplace_back(0); + } + } + } + addPriorityTxs(); int nPackagesSelected = 0; @@ -166,13 +187,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 (!fDIP0003Active_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; @@ -182,6 +221,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 5934492c77f6..9b78776a00d5 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 f0bd58abbe5a..651e672c0d60 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -205,7 +205,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) @@ -2253,8 +2253,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; @@ -2629,6 +2629,14 @@ void CConnman::RelayTransaction(const CTransaction& tx) LOCK(cs_vNodes); BOOST_FOREACH(CNode* pnode, vNodes) { + if (nInv == MSG_TXLOCK_REQUEST) { + // Additional filtering for lock requests. + // Make it here because lock request processing + // differs from simple tx processing in PushInventory + // and tx info will not be available there. + LOCK(pnode->cs_filter); + if(pnode->pfilter && !pnode->pfilter->IsRelevantAndUpdate(tx)) continue; + } pnode->PushInventory(inv); } } @@ -2640,6 +2648,44 @@ 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::RelayInvFiltered(CInv &inv, const uint256& relatedTxHash, 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->contains(relatedTxHash)) continue; + } + pnode->PushInventory(inv); + } +} + +void CConnman::RemoveAskFor(const uint256& hash) +{ + mapAlreadyAskedFor.erase(hash); + + LOCK(cs_vNodes); + for (const auto& pnode : vNodes) { + pnode->RemoveAskFor(hash); + } +} + void CConnman::RecordBytesRecv(uint64_t bytes) { LOCK(cs_totalBytesRecv); @@ -2891,6 +2937,18 @@ void CNode::AskFor(const CInv& inv) mapAskFor.insert(std::make_pair(nRequestTime, inv)); } +void CNode::RemoveAskFor(const uint256& hash) +{ + setAskFor.erase(hash); + for (auto it = mapAskFor.begin(); it != mapAskFor.end();) { + if (it->second.hash == hash) { + it = mapAskFor.erase(it); + } else { + ++it; + } + } +} + bool CConnman::NodeFullyConnected(const CNode* pnode) { return pnode && pnode->fSuccessfullyConnected && !pnode->fDisconnect; diff --git a/src/net.h b/src/net.h index cd135e5f230d..3fdb14cc3cfc 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,10 @@ 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); + // This overload will not update node filters, so use it only for the cases when other messages will update related transaction data in filters + void RelayInvFiltered(CInv &inv, const uint256 &relatedTxHash, const int minProtoVersion = MIN_PEER_PROTO_VERSION); + void RemoveAskFor(const uint256& hash); // Addrman functions size_t GetAddressCount() const; @@ -886,10 +890,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 +916,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); + } } } @@ -919,6 +932,7 @@ class CNode } void AskFor(const CInv& inv); + void RemoveAskFor(const uint256& hash); void CloseSocketDisconnect(); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index a4e8446f8ce8..3010fbd78108 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -42,6 +42,12 @@ #endif // ENABLE_WALLET #include "privatesend-server.h" +#include "evo/deterministicmns.h" +#include "evo/simplifiedmns.h" +#include "llmq/quorums_commitment.h" +#include "llmq/quorums_dummydkg.h" +#include "llmq/quorums_blockprocessor.h" + #include #if defined(NDEBUG) @@ -929,7 +935,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); @@ -956,6 +965,13 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) case MSG_MASTERNODE_VERIFY: return mnodeman.mapSeenMasternodeVerification.count(inv.hash); + + case MSG_QUORUM_FINAL_COMMITMENT: + return llmq::quorumBlockProcessor->HasMinableCommitment(inv.hash); + case MSG_QUORUM_DUMMY_COMMITMENT: + return llmq::quorumDummyDKG->HasDummyCommitment(inv.hash); + case MSG_QUORUM_DUMMY_CONTRIBUTION: + return llmq::quorumDummyDKG->HasDummyContribution(inv.hash); } // Don't know what it is, just say we already got one @@ -1165,51 +1181,60 @@ 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; + } } } if (!push && inv.type == MSG_DSTX) { - CDarksendBroadcastTx dstx = CPrivateSend::GetDSTX(inv.hash); + CPrivateSendBroadcastTx dstx = CPrivateSend::GetDSTX(inv.hash); if(dstx) { connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::DSTX, dstx)); push = true; @@ -1260,6 +1285,36 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } + if (!push && (inv.type == MSG_QUORUM_FINAL_COMMITMENT)) { + llmq::CFinalCommitment o; + if (llmq::quorumBlockProcessor->GetMinableCommitmentByHash(inv.hash, o)) { + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::QFCOMMITMENT, o)); + push = true; + } + } + + if (!push && (inv.type == MSG_QUORUM_DUMMY_CONTRIBUTION)) { + llmq::CDummyContribution o; + if (llmq::quorumDummyDKG->GetDummyContribution(inv.hash, o)) { + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::QCONTRIB, o)); + push = true; + } + } + + if (!push && (inv.type == MSG_QUORUM_DUMMY_COMMITMENT)) { + if (!consensusParams.fLLMQAllowDummyCommitments) { + Misbehaving(pfrom->id, 100); + pfrom->fDisconnect = true; + return; + } + + llmq::CDummyCommitment o; + if (llmq::quorumDummyDKG->GetDummyCommitment(inv.hash, o)) { + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::QDCOMMITMENT, o)); + push = true; + } + } + if (!push) vNotFound.push_back(inv); } @@ -1312,6 +1367,19 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr return true; } + // BEGIN TEMPORARY CODE + bool fDIP0003Active; + { + LOCK(cs_main); + fDIP0003Active = VersionBitsState(chainActive.Tip(), chainparams.GetConsensus(), Consensus::DEPLOYMENT_DIP0003, versionbitscache) == THRESHOLD_ACTIVE; + } + // TODO delete this in next release after v13 + int nMinPeerProtoVersion = MIN_PEER_PROTO_VERSION; + if (fDIP0003Active) { + nMinPeerProtoVersion = MIN_PEER_PROTO_VERSION_DIP3; + } + // END TEMPORARY CODE + if (!(pfrom->GetLocalServices() & NODE_BLOOM) && (strCommand == NetMsgType::FILTERLOAD || strCommand == NetMsgType::FILTERADD)) @@ -1328,25 +1396,34 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (strCommand == NetMsgType::REJECT) { - if (fDebug) { - try { - std::string strMsg; unsigned char ccode; std::string strReason; - vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH); + std::string strMsg; unsigned char ccode; std::string strReason; + uint256 hash; + try { + vRecv >> LIMITED_STRING(strMsg, CMessageHeader::COMMAND_SIZE) >> ccode >> LIMITED_STRING(strReason, MAX_REJECT_MESSAGE_LENGTH); + if (strMsg == NetMsgType::BLOCK || strMsg == NetMsgType::TX) { + vRecv >> hash; + } + } catch (const std::ios_base::failure&) { + // Avoid feedback loops by preventing reject messages from triggering a new reject message. + LogPrint("net", "Unparseable reject message received\n"); + } - std::ostringstream ss; - ss << strMsg << " code " << itostr(ccode) << ": " << strReason; + if (strMsg == NetMsgType::BLOCK) { + // The node requested a block from us and then rejected it, which indicates that it's most likely running + // on rules which are incompatible to ours. Better to ban him after some time as it might otherwise keep + // asking for the same block (if -addnode/-connect was used on the other side). + LOCK(cs_main); + Misbehaving(pfrom->id, 1); + } - if (strMsg == NetMsgType::BLOCK || strMsg == NetMsgType::TX) - { - uint256 hash; - vRecv >> hash; - ss << ": hash " << hash.ToString(); - } - LogPrint("net", "Reject %s\n", SanitizeString(ss.str())); - } catch (const std::ios_base::failure&) { - // Avoid feedback loops by preventing reject messages from triggering a new reject message. - LogPrint("net", "Unparseable reject message received\n"); + if (fDebug) { + std::ostringstream ss; + ss << strMsg << " code " << itostr(ccode) << ": " << strReason; + + if (strMsg == NetMsgType::BLOCK || strMsg == NetMsgType::TX) { + ss << ": hash " << hash.ToString(); } + LogPrint("net", "Reject %s\n", SanitizeString(ss.str())); } } @@ -1390,12 +1467,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr return false; } - if (nVersion < MIN_PEER_PROTO_VERSION) + if (nVersion < nMinPeerProtoVersion) { // disconnect from peers older than this proto version LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, nVersion); connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, - strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION))); + strprintf("Version must be %d or greater", nMinPeerProtoVersion))); pfrom->fDisconnect = true; return false; } @@ -1536,6 +1613,17 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // At this point, the outgoing message serialization version can't change. const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); + // BEGIN TEMPORARY CODE + if (pfrom->nVersion < nMinPeerProtoVersion) { + // disconnect from peers with version < 70213 after DIP3 has activated through the BIP9 deployment + LogPrintf("peer=%d using obsolete version %i after DIP3 activation; disconnecting\n", pfrom->id, pfrom->GetSendVersion()); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, + strprintf("Version must be %d or greater", nMinPeerProtoVersion))); + pfrom->fDisconnect = true; + return false; + } + // END TEMPORARY CODE + if (strCommand == NetMsgType::VERACK) { pfrom->SetRecvVersion(std::min(pfrom->nVersion.load(), PROTOCOL_VERSION)); @@ -1708,11 +1796,31 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } else { + static std::set legacyMNObjs = { + MSG_MASTERNODE_PAYMENT_VOTE, + MSG_MASTERNODE_PAYMENT_BLOCK, + MSG_MASTERNODE_ANNOUNCE, + MSG_MASTERNODE_PING, + MSG_MASTERNODE_VERIFY, + }; + static std::set allowWhileInIBDObjs = { + MSG_SPORK + }; + if (legacyMNObjs.count(inv.type) && deterministicMNManager->IsDeterministicMNsSporkActive()) { + LogPrint("net", "ignoring (%s) inv of legacy type %d peer=%d\n", inv.hash.ToString(), inv.type, pfrom->id); + continue; + } + pfrom->AddInventoryKnown(inv); - if (fBlocksOnly) - LogPrint("net", "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), pfrom->id); - else if (!fAlreadyHave && !fImporting && !fReindex && !IsInitialBlockDownload()) - pfrom->AskFor(inv); + if (fBlocksOnly) { + LogPrint("net", "transaction (%s) inv sent in violation of protocol peer=%d\n", inv.hash.ToString(), + pfrom->id); + } else if (!fAlreadyHave) { + bool allowWhileInIBD = allowWhileInIBDObjs.count(inv.type); + if (allowWhileInIBD || (!fImporting && !fReindex && !IsInitialBlockDownload())) { + pfrom->AskFor(inv); + } + } } // Track requests for our stuff @@ -1925,12 +2033,15 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr std::vector vEraseQueue; CTransactionRef ptx; CTxLockRequest txLockRequest; - CDarksendBroadcastTx dstx; + CPrivateSendBroadcastTx 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; @@ -1944,13 +2055,22 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr CInv inv(nInvType, tx.GetHash()); pfrom->AddInventoryKnown(inv); - pfrom->setAskFor.erase(inv.hash); + { + LOCK(cs_main); + connman.RemoveAskFor(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(); @@ -1966,14 +2086,14 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr return false; } - if(!mn.fAllowMixingTx) { + if(!mn.IsValidForMixingTxes()) { LogPrint("privatesend", "DSTX -- Masternode %s is sending too many transactions %s\n", dstx.masternodeOutpoint.ToStringShort(), hashTx.ToString()); return true; // TODO: Not an error? Could it be that someone is relaying old DSTXes // 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; } @@ -1988,17 +2108,13 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr bool fMissingInputs = false; CValidationState state; - 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 +2158,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 +2261,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)) { @@ -2814,6 +2927,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. @@ -2842,6 +2980,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr sporkManager.ProcessSpork(pfrom, strCommand, vRecv, connman); masternodeSync.ProcessMessage(pfrom, strCommand, vRecv); governance.ProcessMessage(pfrom, strCommand, vRecv, connman); + llmq::quorumBlockProcessor->ProcessMessage(pfrom, strCommand, vRecv, connman); + llmq::quorumDummyDKG->ProcessMessage(pfrom, strCommand, vRecv, connman); } else { @@ -3186,12 +3326,21 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr break; } pBestIndex = pindex; + bool isPrevDevnetGenesisBlock = false; + if (!consensusParams.hashDevnetGenesisBlock.IsNull() && + pindex->pprev != nullptr && + pindex->pprev->GetBlockHash() == consensusParams.hashDevnetGenesisBlock) { + // even though the devnet genesis block was never transferred through the wire and thus not + // appear anywhere in the node state where we track what other nodes have or not have, we can + // assume that the other node already knows the devnet genesis block + isPrevDevnetGenesisBlock = true; + } if (fFoundStartingHeader) { // add this to the headers message vHeaders.push_back(pindex->GetBlockHeader()); } else if (PeerHasHeader(&state, pindex)) { continue; // keep looking for the first new block - } else if (pindex->pprev == NULL || PeerHasHeader(&state, pindex->pprev)) { + } else if (pindex->pprev == NULL || PeerHasHeader(&state, pindex->pprev) || isPrevDevnetGenesisBlock) { // Peer doesn't have this header but they do have the prior one. // Start sending headers. fFoundStartingHeader = true; @@ -3278,7 +3427,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) { @@ -3291,11 +3440,14 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic& interr pto->vInventoryBlockToSend.clear(); // Check whether periodic sends should happen - bool fSendTrickle = pto->fWhitelisted; + // Note: If this node is running in a Masternode mode, it makes no sense to delay outgoing txes + // because we never produce any txes ourselves i.e. no privacy is lost in this case. + bool fSendTrickle = pto->fWhitelisted || fMasternodeMode; if (pto->nNextInvSend < nNow) { fSendTrickle = true; - // Use half the delay for outbound peers, as there is less privacy concern for them. - pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound); + // Use half the delay for regular outbound peers, as there is less privacy concern for them, + // and quarter the delay for Masternode outbound peers, as there is even less privacy concern in this case. + pto->nNextInvSend = PoissonNextSend(nNow, INVENTORY_BROADCAST_INTERVAL >> !pto->fInbound >> pto->fMasternode); } // Time to send but the peer has requested we not relay transactions. @@ -3347,7 +3499,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(); @@ -3391,7 +3543,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 54888d80d772..4df96b87a0a2 100644 --- a/src/netfulfilledman.cpp +++ b/src/netfulfilledman.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "chainparams.h" +#include "init.h" #include "netfulfilledman.h" #include "util.h" @@ -73,3 +74,10 @@ std::string CNetFulfilledRequestManager::ToString() const info << "Nodes with fulfilled requests: " << (int)mapFulfilledRequests.size(); return info.str(); } + +void CNetFulfilledRequestManager::DoMaintenance() +{ + if (ShutdownRequested()) return; + + CheckAndRemove(); +} diff --git a/src/netfulfilledman.h b/src/netfulfilledman.h index c1b2e1c171e1..17503e0be9ae 100644 --- a/src/netfulfilledman.h +++ b/src/netfulfilledman.h @@ -44,6 +44,8 @@ class CNetFulfilledRequestManager void Clear(); std::string ToString() const; + + void DoMaintenance(); }; #endif 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/pow.cpp b/src/pow.cpp index 749ff9db2849..d51025fdce7f 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -89,43 +89,18 @@ unsigned int static DarkGravityWave(const CBlockIndex* pindexLast, const CBlockH return bnPowLimit.GetCompact(); } - if (params.fPowAllowMinDifficultyBlocks && ( - // testnet ... - (params.hashDevnetGenesisBlock.IsNull() && pindexLast->nChainWork >= UintToArith256(uint256S("0x000000000000000000000000000000000000000000000000003e9ccfe0e03e01"))) || - // or devnet - !params.hashDevnetGenesisBlock.IsNull())) { - // NOTE: 000000000000000000000000000000000000000000000000003e9ccfe0e03e01 is the work of the "wrong" chain, - // so this rule activates there immediately and new blocks with high diff from that chain are going - // to be rejected by updated nodes. Note, that old nodes are going to reject blocks from updated nodes - // after the "right" chain reaches this amount of work too. This is a temporary condition which should - // be removed when we decide to hard-fork testnet again. - // TODO: remove "testnet+work OR devnet" part on next testnet hard-fork - // Special difficulty rule for testnet/devnet: - // If the new block's timestamp is more than 2* 2.5 minutes - // then allow mining of a min-difficulty block. - - // start using smoother adjustment on testnet when total work hits - // 000000000000000000000000000000000000000000000000003ff00000000000 - if (pindexLast->nChainWork >= UintToArith256(uint256S("0x000000000000000000000000000000000000000000000000003ff00000000000")) - // and immediately on devnet - || !params.hashDevnetGenesisBlock.IsNull()) { - // recent block is more than 2 hours old - if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + 2 * 60 * 60) { - return bnPowLimit.GetCompact(); - } - // recent block is more than 10 minutes old - if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + params.nPowTargetSpacing*4) { - arith_uint256 bnNew = arith_uint256().SetCompact(pindexLast->nBits) * 10; - if (bnNew > bnPowLimit) { - bnNew = bnPowLimit; - } - return bnNew.GetCompact(); - } - } else { - // old stuff - if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + params.nPowTargetSpacing*2) { - return bnPowLimit.GetCompact(); + if (params.fPowAllowMinDifficultyBlocks) { + // recent block is more than 2 hours old + if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + 2 * 60 * 60) { + return bnPowLimit.GetCompact(); + } + // recent block is more than 10 minutes old + if (pblock->GetBlockTime() > pindexLast->GetBlockTime() + params.nPowTargetSpacing * 4) { + arith_uint256 bnNew = arith_uint256().SetCompact(pindexLast->nBits) * 10; + if (bnNew > bnPowLimit) { + bnNew = bnPowLimit; } + return bnNew.GetCompact(); } } @@ -210,6 +185,12 @@ unsigned int GetNextWorkRequiredBTC(const CBlockIndex* pindexLast, const CBlockH unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params) { + // this is only active on devnets + if (pindexLast->nHeight < params.nMinimumDifficultyBlocks) { + unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact(); + return nProofOfWorkLimit; + } + // Most recent algo first if (pindexLast->nHeight + 1 >= params.nPowDGWHeight) { return DarkGravityWave(pindexLast, pblock, params); diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index 4c41a02b9951..6459d0e957be 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -6,13 +6,17 @@ #include "primitives/block.h" #include "hash.h" +#include "streams.h" #include "tinyformat.h" #include "utilstrencodings.h" #include "crypto/common.h" uint256 CBlockHeader::GetHash() const { - return HashX11(BEGIN(nVersion), END(nNonce)); + std::vector vch(80); + CVectorWriter ss(SER_NETWORK, PROTOCOL_VERSION, vch, 0); + ss << *this; + return HashX11((const char *)vch.data(), (const char *)vch.data() + vch.size()); } std::string CBlock::ToString() const diff --git a/src/primitives/block.h b/src/primitives/block.h index dbf80237cdb1..dda9a9d609b7 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -76,8 +76,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() @@ -103,8 +101,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 e29af2bcef65..ec99b589f226 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -11,6 +11,17 @@ #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, + TRANSACTION_QUORUM_COMMITMENT = 6, +}; + /** An outpoint - a combination of a transaction hash and an index n into its vout */ class COutPoint { @@ -142,7 +153,7 @@ class CTxOut SetNull(); } - CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn); + CTxOut(const CAmount& nValueIn, CScript scriptPubKeyIn, int nRoundsIn = -10); ADD_SERIALIZE_METHODS; @@ -169,7 +180,7 @@ class CTxOut // "Dust" is defined in terms of CTransaction::minRelayTxFee, which has units duffs-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 1000 duffs per kB + // i.e. total is 148 + 34 = 182 bytes. Default -minrelaytxfee is 1000 duffs per kB // and that means that fee per spendable txout is 182 * 1000 / 1000 = 182 duffs. // So dust is a spendable txout less than 546 * minRelayTxFee / 1000 (in duffs) // i.e. 182 * 3 = 546 duffs with default -minrelaytxfee = minRelayTxFee = 1000 duffs per kB. @@ -215,17 +226,19 @@ class CTransaction // 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 +256,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 +317,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 +331,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 02e43e8d0c85..13b43e97f821 100644 --- a/src/privatesend-client.cpp +++ b/src/privatesend-client.cpp @@ -1,9 +1,9 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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 "privatesend-client.h" -#include "wallet/coincontrol.h" #include "consensus/validation.h" #include "core_io.h" #include "init.h" @@ -15,103 +15,132 @@ #include "txmempool.h" #include "util.h" #include "utilmoneystr.h" +#include "wallet/coincontrol.h" #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 Dash related functionality - if(!masternodeSync.IsBlockchainSynced()) return; + if (fMasternodeMode) return; + if (fLiteMode) return; // ignore all Dash 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(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + 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, - strprintf("Version must be %d or greater", MIN_PRIVATESEND_PEER_PROTO_VERSION))); + connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PRIVATESEND_PEER_PROTO_VERSION))); return; } - CDarksendQueue dsq; + CPrivateSendQueue 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 : vecPrivateSendQueue) { + 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; + if (dsq.IsExpired()) return; masternode_info_t infoMn; - if(!mnodeman.GetMasternodeInfo(dsq.masternodeOutpoint, infoMn)) return; + 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; } // 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); + if (dsq.fReady) { + 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 { - for (const auto& q : vecDarksendQueue) { - if(q.masternodeOutpoint == dsq.masternodeOutpoint) { + 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 : vecPrivateSendQueue) { + if (q.masternodeOutpoint == dsq.masternodeOutpoint) { // no way same mn can send another "not yet ready" dsq this soon LogPrint("privatesend", "DSQUEUE -- Masternode %s is sending WAY too many dsq messages\n", infoMn.addr.ToString()); return; } } - int nThreshold = infoMn.nLastDsq + mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION)/5; + int nThreshold = infoMn.nLastDsq + mnodeman.CountMasternodes() / 5; LogPrint("privatesend", "DSQUEUE -- nLastDsq: %d threshold: %d nDsqCount: %d\n", infoMn.nLastDsq, nThreshold, mnodeman.nDsqCount); //don't allow a few nodes to dominate the queuing process - if(infoMn.nLastDsq != 0 && nThreshold > mnodeman.nDsqCount) { + if (infoMn.nLastDsq != 0 && nThreshold > mnodeman.nDsqCount) { LogPrint("privatesend", "DSQUEUE -- Masternode %s is sending too many dsq messages\n", infoMn.addr.ToString()); return; } - if(!mnodeman.AllowMixing(dsq.masternodeOutpoint)) return; + 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); + vecPrivateSendQueue.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); + } + } +} - if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { +void CPrivateSendClientSession::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) +{ + if (fMasternodeMode) return; + if (fLiteMode) return; // ignore all Dash 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); - connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, - strprintf("Version must be %d or greater", MIN_PRIVATESEND_PEER_PROTO_VERSION))); + connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PRIVATESEND_PEER_PROTO_VERSION))); return; } - if(!infoMixingMasternode.fInfoValid) return; - if(infoMixingMasternode.addr != pfrom->addr) { + if (!infoMixingMasternode.fInfoValid) return; + if (infoMixingMasternode.addr != pfrom->addr) { //LogPrintf("DSSTATUSUPDATE -- message doesn't match current Masternode: infoMixingMasternode %s addr %s\n", infoMixingMasternode.addr.ToString(), pfrom->addr.ToString()); return; } @@ -123,39 +152,37 @@ void CPrivateSendClient::ProcessMessage(CNode* pfrom, const std::string& strComm int nMsgMessageID; vRecv >> nMsgSessionID >> nMsgState >> nMsgEntriesCount >> nMsgStatusUpdate >> nMsgMessageID; - if(nMsgState < POOL_STATE_MIN || nMsgState > POOL_STATE_MAX) { + if (nMsgState < POOL_STATE_MIN || nMsgState > POOL_STATE_MAX) { LogPrint("privatesend", "DSSTATUSUPDATE -- nMsgState is out of bounds: %d\n", nMsgState); return; } - if(nMsgStatusUpdate < STATUS_REJECTED || nMsgStatusUpdate > STATUS_ACCEPTED) { + if (nMsgStatusUpdate < STATUS_REJECTED || nMsgStatusUpdate > STATUS_ACCEPTED) { LogPrint("privatesend", "DSSTATUSUPDATE -- nMsgStatusUpdate is out of bounds: %d\n", nMsgStatusUpdate); return; } - if(nMsgMessageID < MSG_POOL_MIN || nMsgMessageID > MSG_POOL_MAX) { + if (nMsgMessageID < MSG_POOL_MIN || nMsgMessageID > MSG_POOL_MAX) { LogPrint("privatesend", "DSSTATUSUPDATE -- nMsgMessageID is out of bounds: %d\n", nMsgMessageID); return; } LogPrint("privatesend", "DSSTATUSUPDATE -- nMsgSessionID %d nMsgState: %d nEntriesCount: %d nMsgStatusUpdate: %d nMsgMessageID %d (%s)\n", - nMsgSessionID, nMsgState, nEntriesCount, nMsgStatusUpdate, nMsgMessageID, CPrivateSend::GetMessageByID(PoolMessage(nMsgMessageID))); + nMsgSessionID, nMsgState, nEntriesCount, nMsgStatusUpdate, nMsgMessageID, CPrivateSend::GetMessageByID(PoolMessage(nMsgMessageID))); - if(!CheckPoolStateUpdate(PoolState(nMsgState), nMsgEntriesCount, PoolStatusUpdate(nMsgStatusUpdate), PoolMessage(nMsgMessageID), nMsgSessionID)) { + if (!CheckPoolStateUpdate(PoolState(nMsgState), nMsgEntriesCount, PoolStatusUpdate(nMsgStatusUpdate), PoolMessage(nMsgMessageID), nMsgSessionID)) { LogPrint("privatesend", "DSSTATUSUPDATE -- CheckPoolStateUpdate failed\n"); } - } else if(strCommand == NetMsgType::DSFINALTX) { - - if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + } else if (strCommand == NetMsgType::DSFINALTX) { + if (pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { LogPrint("privatesend", "DSFINALTX -- 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_PRIVATESEND_PEER_PROTO_VERSION))); + connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PRIVATESEND_PEER_PROTO_VERSION))); return; } - if(!infoMixingMasternode.fInfoValid) return; - if(infoMixingMasternode.addr != pfrom->addr) { + if (!infoMixingMasternode.fInfoValid) return; + if (infoMixingMasternode.addr != pfrom->addr) { //LogPrintf("DSFINALTX -- message doesn't match current Masternode: infoMixingMasternode %s addr %s\n", infoMixingMasternode.addr.ToString(), pfrom->addr.ToString()); return; } @@ -164,7 +191,7 @@ void CPrivateSendClient::ProcessMessage(CNode* pfrom, const std::string& strComm vRecv >> nMsgSessionID; CTransaction txNew(deserialize, vRecv); - if(nSessionID != nMsgSessionID) { + if (nSessionID != nMsgSessionID) { LogPrint("privatesend", "DSFINALTX -- message doesn't match current PrivateSend session: nSessionID: %d nMsgSessionID: %d\n", nSessionID, nMsgSessionID); return; } @@ -174,17 +201,15 @@ void CPrivateSendClient::ProcessMessage(CNode* pfrom, const std::string& strComm //check to see if input is spent already? (and probably not confirmed) SignFinalTransaction(txNew, pfrom, connman); - } else if(strCommand == NetMsgType::DSCOMPLETE) { - - if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + } else if (strCommand == NetMsgType::DSCOMPLETE) { + if (pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { LogPrint("privatesend", "DSCOMPLETE -- 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_PRIVATESEND_PEER_PROTO_VERSION))); + connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PRIVATESEND_PEER_PROTO_VERSION))); return; } - if(!infoMixingMasternode.fInfoValid) return; - if(infoMixingMasternode.addr != pfrom->addr) { + if (!infoMixingMasternode.fInfoValid) return; + if (infoMixingMasternode.addr != pfrom->addr) { LogPrint("privatesend", "DSCOMPLETE -- message doesn't match current Masternode: infoMixingMasternode=%s addr=%s\n", infoMixingMasternode.addr.ToString(), pfrom->addr.ToString()); return; } @@ -193,12 +218,12 @@ void CPrivateSendClient::ProcessMessage(CNode* pfrom, const std::string& strComm int nMsgMessageID; vRecv >> nMsgSessionID >> nMsgMessageID; - if(nMsgMessageID < MSG_POOL_MIN || nMsgMessageID > MSG_POOL_MAX) { + if (nMsgMessageID < MSG_POOL_MIN || nMsgMessageID > MSG_POOL_MAX) { LogPrint("privatesend", "DSCOMPLETE -- nMsgMessageID is out of bounds: %d\n", nMsgMessageID); return; } - if(nSessionID != nMsgSessionID) { + if (nSessionID != nMsgSessionID) { LogPrint("privatesend", "DSCOMPLETE -- message doesn't match current PrivateSend session: nSessionID: %d nMsgSessionID: %d\n", nSessionID, nMsgSessionID); return; } @@ -209,17 +234,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; @@ -227,17 +261,22 @@ 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() { - while(true) { + if (!pwalletMain) return; + + while (true) { TRY_LOCK(pwalletMain->cs_wallet, lockWallet); - if(!lockWallet) {MilliSleep(50); continue;} + if (!lockWallet) { + MilliSleep(50); + continue; + } for (const auto& outpoint : vecOutPointLocked) pwalletMain->UnlockCoin(outpoint); break; @@ -246,74 +285,115 @@ 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) { - case POOL_STATE_IDLE: - return _("PrivateSend is idle."); - case POOL_STATE_QUEUE: - 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);; - case POOL_STATE_ACCEPTING_ENTRIES: - if(nEntriesCount == 0) { + switch (nState) { + case POOL_STATE_IDLE: + return _("PrivateSend is idle."); + case POOL_STATE_QUEUE: + 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); + case POOL_STATE_ACCEPTING_ENTRIES: + if (nEntriesCount == 0) { + nStatusMessageProgress = 0; + return strAutoDenomResult; + } else if (fLastEntryAccepted) { + if (nStatusMessageProgress % 10 > 8) { + fLastEntryAccepted = false; nStatusMessageProgress = 0; - return strAutoDenomResult; - } else if(fLastEntryAccepted) { - if(nStatusMessageProgress % 10 > 8) { - fLastEntryAccepted = false; - nStatusMessageProgress = 0; - } - return _("PrivateSend request complete:") + " " + _("Your transaction was accepted into the pool!"); - } else { - if( nStatusMessageProgress % 70 <= 40) return strprintf(_("Submitted following entries to masternode: %u / %d"), nEntriesCount, CPrivateSend::GetMaxPoolTransactions()); - else if(nStatusMessageProgress % 70 <= 50) strSuffix = "."; - else if(nStatusMessageProgress % 70 <= 60) strSuffix = ".."; - else if(nStatusMessageProgress % 70 <= 70) strSuffix = "..."; - return strprintf(_("Submitted to masternode, waiting for more entries ( %u / %d ) %s"), nEntriesCount, CPrivateSend::GetMaxPoolTransactions(), strSuffix); } - case POOL_STATE_SIGNING: - if( nStatusMessageProgress % 70 <= 40) return _("Found enough users, signing ..."); - else if(nStatusMessageProgress % 70 <= 50) strSuffix = "."; - else if(nStatusMessageProgress % 70 <= 60) strSuffix = ".."; - else if(nStatusMessageProgress % 70 <= 70) strSuffix = "..."; - return strprintf(_("Found enough users, signing ( waiting %s )"), strSuffix); - case POOL_STATE_ERROR: - return _("PrivateSend request incomplete:") + " " + strLastMessage + " " + _("Will retry..."); - case POOL_STATE_SUCCESS: - return _("PrivateSend request complete:") + " " + strLastMessage; - default: - return strprintf(_("Unknown state: id = %u"), nState); + return _("PrivateSend request complete:") + " " + _("Your transaction was accepted into the pool!"); + } else { + if (nStatusMessageProgress % 70 <= 40) + return strprintf(_("Submitted following entries to masternode: %u / %d"), nEntriesCount, CPrivateSend::GetMaxPoolTransactions()); + else if (nStatusMessageProgress % 70 <= 50) + strSuffix = "."; + else if (nStatusMessageProgress % 70 <= 60) + strSuffix = ".."; + else if (nStatusMessageProgress % 70 <= 70) + strSuffix = "..."; + return strprintf(_("Submitted to masternode, waiting for more entries ( %u / %d ) %s"), nEntriesCount, CPrivateSend::GetMaxPoolTransactions(), strSuffix); + } + case POOL_STATE_SIGNING: + if (nStatusMessageProgress % 70 <= 40) + return _("Found enough users, signing ..."); + else if (nStatusMessageProgress % 70 <= 50) + strSuffix = "."; + else if (nStatusMessageProgress % 70 <= 60) + strSuffix = ".."; + else if (nStatusMessageProgress % 70 <= 70) + strSuffix = "..."; + return strprintf(_("Found enough users, signing ( waiting %s )"), strSuffix); + case POOL_STATE_ERROR: + return _("PrivateSend request incomplete:") + " " + strLastMessage + " " + _("Will retry..."); + case POOL_STATE_SUCCESS: + return _("PrivateSend request complete:") + " " + strLastMessage; + default: + return strprintf(_("Unknown state: id = %u"), nState); } } -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"); + if ((nState == POOL_STATE_ERROR || nState == POOL_STATE_SUCCESS) && GetTime() - nTimeLastSuccessfulStep >= 10) { + LogPrint("privatesend", "CPrivateSendClientSession::CheckPool -- timeout, RESETTING\n"); UnlockCoins(); if (nState == POOL_STATE_ERROR) { keyHolderStorage.ReturnAll(); @@ -325,42 +405,59 @@ 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"); - CheckPool(); - break; - case POOL_STATE_SUCCESS: - LogPrint("privatesend", "CPrivateSendClient::CheckTimeout -- Pool success -- Running CheckPool\n"); - CheckPool(); - break; - default: - break; + switch (nState) { + case POOL_STATE_ERROR: + LogPrint("privatesend", "CPrivateSendClientSession::CheckTimeout -- Pool error -- Running CheckPool\n"); + CheckPool(); + break; + case POOL_STATE_SUCCESS: + LogPrint("privatesend", "CPrivateSendClientSession::CheckTimeout -- Pool success -- Running CheckPool\n"); + CheckPool(); + break; + default: + break; } int nLagTime = 10; // give the server a few extra seconds before resetting. 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; + + CheckQueue(); + + if (!fEnablePrivateSend) return; + + LOCK(cs_deqsessions); + for (auto& session : deqSessions) { + if (session.CheckTimeout()) { + strAutoDenomResult = _("Session timed out."); + } } } @@ -368,14 +465,14 @@ 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 >& vecPSInOutPairsIn, CConnman& connman) { - if(fMasternodeMode) { - LogPrintf("CPrivateSendClient::SendDenominate -- PrivateSend from a Masternode is not supported currently.\n"); + if (fMasternodeMode) { + LogPrintf("CPrivateSendClientSession::SendDenominate -- PrivateSend from a Masternode is not supported currently.\n"); return false; } - if(txMyCollateral == CMutableTransaction()) { + if (txMyCollateral == CMutableTransaction()) { LogPrintf("CPrivateSendClient:SendDenominate -- PrivateSend collateral not set\n"); return false; } @@ -384,72 +481,65 @@ 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"); + if (!nSessionID) { + LogPrintf("CPrivateSendClientSession::SendDenominate -- No Masternode has been selected yet.\n"); UnlockCoins(); keyHolderStorage.ReturnAll(); SetNull(); return false; } - if(!CheckDiskSpace()) { + if (!CheckDiskSpace()) { 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; + if (fMasternodeMode) return false; // do not update state when mixing client state is one of these - if(nState == POOL_STATE_IDLE || nState == POOL_STATE_ERROR || nState == POOL_STATE_SUCCESS) return false; + if (nState == POOL_STATE_IDLE || nState == POOL_STATE_ERROR || nState == POOL_STATE_SUCCESS) return false; strAutoDenomResult = _("Masternode:") + " " + CPrivateSend::GetMessageByID(nMessageID); // if rejected at any state - if(nStatusUpdate == STATUS_REJECTED) { - LogPrintf("CPrivateSendClient::CheckPoolStateUpdate -- entry is rejected by Masternode\n"); + if (nStatusUpdate == STATUS_REJECTED) { + LogPrintf("CPrivateSendClientSession::CheckPoolStateUpdate -- entry is rejected by Masternode\n"); UnlockCoins(); keyHolderStorage.ReturnAll(); SetNull(); @@ -458,19 +548,18 @@ bool CPrivateSendClient::CheckPoolStateUpdate(PoolState nStateNew, int nEntriesC return true; } - if(nStatusUpdate == STATUS_ACCEPTED && nState == nStateNew) { - if(nStateNew == POOL_STATE_QUEUE && nSessionID == 0 && nSessionIDNew != 0) { + if (nStatusUpdate == STATUS_ACCEPTED && nState == nStateNew) { + if (nStateNew == POOL_STATE_QUEUE && nSessionID == 0 && nSessionIDNew != 0) { // 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) { + } 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; } } @@ -484,19 +573,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()); + if (finalMutableTransaction.GetHash() != finalTransactionNew.GetHash()) { + LogPrintf("CPrivateSendClientSession::SignFinalTransaction -- WARNING! Masternode %s is not BIP69 compliant!\n", infoMixingMasternode.outpoint.ToStringShort()); UnlockCoins(); keyHolderStorage.ReturnAll(); SetNull(); @@ -513,22 +605,22 @@ bool CPrivateSendClient::SignFinalTransaction(const CTransaction& finalTransacti CScript prevPubKey = CScript(); CTxIn txin = CTxIn(); - for(unsigned int i = 0; i < finalMutableTransaction.vin.size(); i++) { - if(finalMutableTransaction.vin[i] == txdsin) { + for (unsigned int i = 0; i < finalMutableTransaction.vin.size(); i++) { + if (finalMutableTransaction.vin[i] == txdsin) { nMyInputIndex = i; prevPubKey = txdsin.prevPubKey; txin = txdsin; } } - if(nMyInputIndex >= 0) { //might have to do this one input at a time? + if (nMyInputIndex >= 0) { //might have to do this one input at a time? int nFoundOutputsCount = 0; CAmount nValue1 = 0; CAmount nValue2 = 0; for (const auto& txoutFinal : finalMutableTransaction.vout) { - for (const auto& txout: entry.vecTxOut) { - if(txoutFinal == txout) { + for (const auto& txout : entry.vecTxOut) { + if (txoutFinal == txout) { nFoundOutputsCount++; nValue1 += txoutFinal.nValue; } @@ -539,10 +631,10 @@ bool CPrivateSendClient::SignFinalTransaction(const CTransaction& finalTransacti nValue2 += txout.nValue; int nTargetOuputsCount = entry.vecTxOut.size(); - if(nFoundOutputsCount < nTargetOuputsCount || nValue1 != nValue2) { + 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(); @@ -552,20 +644,20 @@ bool CPrivateSendClient::SignFinalTransaction(const CTransaction& finalTransacti const CKeyStore& keystore = *pwalletMain; - LogPrint("privatesend", "CPrivateSendClient::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 -- Signing my input %i\n", nMyInputIndex); + if (!SignSignature(keystore, prevPubKey, finalMutableTransaction, nMyInputIndex, int(SIGHASH_ALL | SIGHASH_ANYONECANPAY))) { // changes scriptSig + 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"); + if (sigs.empty()) { + LogPrintf("CPrivateSendClientSession::SignFinalTransaction -- can't sign anything!\n"); UnlockCoins(); keyHolderStorage.ReturnAll(); SetNull(); @@ -574,7 +666,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); @@ -584,13 +676,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 (fMasternodeMode) return; - if(nMessageID == MSG_SUCCESS) { + if (nMessageID == MSG_SUCCESS) { LogPrintf("CompletedTransaction -- success\n"); - nCachedLastSuccessBlock = nCachedBlockHeight; + privateSendClient.UpdatedSuccessBlock(); keyHolderStorage.KeepAll(); } else { LogPrintf("CompletedTransaction -- error\n"); @@ -601,72 +693,90 @@ 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) { - if(!masternodeSync.IsMasternodeListSynced()) + vecDenominationsSkipped.push_back(nDenomValue); +} + +bool CPrivateSendClientManager::WaitForAnotherBlock() +{ + if (!masternodeSync.IsMasternodeListSynced()) return true; - if(fPrivateSendMultiSession) + if (fPrivateSendMultiSession) return false; return nCachedBlockHeight - nCachedLastSuccessBlock < nMinBlocksToWait; } -bool CPrivateSendClient::CheckAutomaticBackup() +bool CPrivateSendClientManager::CheckAutomaticBackup() { - switch(nWalletBackups) { - case 0: - LogPrint("privatesend", "CPrivateSendClient::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" - return false; - case -1: - // 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"); - 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"); - strAutoDenomResult = _("WARNING! Failed to replenish keypool, please unlock your wallet to do so.") + ", " + _("see debug.log for details."); - return false; + 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", "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" + return false; + case -1: + // 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", "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", "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) { + 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) { + } 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"); + if (fCreateAutoBackups) { + LogPrint("privatesend", "CPrivateSendClientManager::CheckAutomaticBackup -- Trying to create new backup.\n"); std::string warningString; std::string errorString; - if(!AutoBackupWallet(pwalletMain, "", warningString, errorString)) { - if(!warningString.empty()) { + 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()) { + 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; } @@ -677,7 +787,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; } @@ -685,127 +795,161 @@ 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 (fMasternodeMode) return false; // no client-side mixing on masternodes + if (nState != POOL_STATE_IDLE) return false; - if(!masternodeSync.IsMasternodeListSynced()) { + if (!masternodeSync.IsMasternodeListSynced()) { strAutoDenomResult = _("Can't mix while sync in progress."); return false; } - if(!CheckAutomaticBackup()) - return false; - - if(GetEntriesCount() > 0) { - strAutoDenomResult = _("Mixing in progress..."); + if (!pwalletMain) { + strAutoDenomResult = _("Wallet is not initialized"); return false; } - TRY_LOCK(cs_darksend, lockDS); - if(!lockDS) { - strAutoDenomResult = _("Lock is already in place."); - return false; - } + CAmount nBalanceNeedsAnonymized; + CAmount nValueMin = CPrivateSend::GetSmallestDenomination(); - if(!fDryRun && pwalletMain->IsLocked(true)) { - strAutoDenomResult = _("Wallet is locked."); - return false; - } + { + LOCK2(cs_main, pwalletMain->cs_wallet); - if(WaitForAnotherBlock()) { - LogPrintf("CPrivateSendClient::DoAutomaticDenominating -- Last successful PrivateSend action was too recent\n"); - strAutoDenomResult = _("Last successful PrivateSend action was too recent."); - return false; - } + if (!fDryRun && pwalletMain->IsLocked(true)) { + strAutoDenomResult = _("Wallet is locked."); + return false; + } - if(mnodeman.size() == 0) { - LogPrint("privatesend", "CPrivateSendClient::DoAutomaticDenominating -- No Masternodes detected\n"); - strAutoDenomResult = _("No Masternodes detected."); - return false; - } + if (GetEntriesCount() > 0) { + strAutoDenomResult = _("Mixing in progress..."); + return false; + } - CAmount nValueMin = CPrivateSend::GetSmallestDenomination(); + TRY_LOCK(cs_privatesend, lockDS); + if (!lockDS) { + strAutoDenomResult = _("Lock is already in place."); + return false; + } - // if there are no confirmed DS collateral inputs yet - if(!pwalletMain->HasCollateralInputs()) { - // should have some additional amount for them - nValueMin += CPrivateSend::GetMaxCollateralAmount(); - } + if (mnodeman.size() == 0) { + LogPrint("privatesend", "CPrivateSendClientSession::DoAutomaticDenominating -- No Masternodes detected\n"); + strAutoDenomResult = _("No Masternodes detected."); + return false; + } - // including denoms but applying some restrictions - CAmount nBalanceNeedsAnonymized = pwalletMain->GetNeedsToBeAnonymizedBalance(nValueMin); + // if there are no confirmed DS collateral inputs yet + if (!pwalletMain->HasCollateralInputs()) { + // should have some additional amount for them + nValueMin += CPrivateSend::GetMaxCollateralAmount(); + } - // anonymizable balance is way too small - if(nBalanceNeedsAnonymized < nValueMin) { - LogPrintf("CPrivateSendClient::DoAutomaticDenominating -- Not enough funds to anonymize\n"); - strAutoDenomResult = _("Not enough funds to anonymize."); - return false; - } + // including denoms but applying some restrictions + nBalanceNeedsAnonymized = pwalletMain->GetNeedsToBeAnonymizedBalance(nValueMin); + + // anonymizable balance is way too small + if (nBalanceNeedsAnonymized < nValueMin) { + LogPrintf("CPrivateSendClientSession::DoAutomaticDenominating -- Not enough funds to anonymize\n"); + strAutoDenomResult = _("Not enough funds to anonymize."); + return false; + } + + // excluding denoms + CAmount nBalanceAnonimizableNonDenom = pwalletMain->GetAnonymizableBalance(true); + // denoms + CAmount nBalanceDenominatedConf = pwalletMain->GetDenominatedBalance(); + CAmount nBalanceDenominatedUnconf = pwalletMain->GetDenominatedBalance(true); + CAmount nBalanceDenominated = nBalanceDenominatedConf + nBalanceDenominatedUnconf; + + 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, + (float)nBalanceDenominatedConf / COIN, + (float)nBalanceDenominatedUnconf / COIN, + (float)nBalanceDenominated / COIN); + + if (fDryRun) return true; + + // 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 < privateSendClient.nPrivateSendAmount * COIN) + return CreateDenominated(connman); + + //check if we have the collateral sized inputs + if (!pwalletMain->HasCollateralInputs()) + return !pwalletMain->HasCollateralInputs(false) && MakeCollateralAmounts(connman); + + if (nSessionID) { + strAutoDenomResult = _("Mixing in progress..."); + return false; + } - // excluding denoms - CAmount nBalanceAnonimizableNonDenom = pwalletMain->GetAnonymizableBalance(true); - // denoms - CAmount nBalanceDenominatedConf = pwalletMain->GetDenominatedBalance(); - CAmount nBalanceDenominatedUnconf = pwalletMain->GetDenominatedBalance(true); - CAmount nBalanceDenominated = nBalanceDenominatedConf + nBalanceDenominatedUnconf; + // Initial phase, find a Masternode + // Clean if there is anything left from previous session + UnlockCoins(); + keyHolderStorage.ReturnAll(); + SetNull(); + + // should be no unconfirmed denoms in non-multi-session mode + 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; + } + + //check our collateral and create new if needed + std::string strReason; + if (txMyCollateral == CMutableTransaction()) { + if (!pwalletMain->CreateCollateralTransaction(txMyCollateral, strReason)) { + LogPrintf("CPrivateSendClientSession::DoAutomaticDenominating -- create collateral error:%s\n", strReason); + return false; + } + } else { + if (!CPrivateSend::IsCollateralValid(txMyCollateral)) { + LogPrintf("CPrivateSendClientSession::DoAutomaticDenominating -- invalid collateral, recreating...\n"); + if (!pwalletMain->CreateCollateralTransaction(txMyCollateral, 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; - LogPrint("privatesend", "CPrivateSendClient::DoAutomaticDenominating -- nValueMin: %f, nBalanceNeedsAnonymized: %f, nBalanceAnonimizableNonDenom: %f, nBalanceDenominatedConf: %f, nBalanceDenominatedUnconf: %f, nBalanceDenominated: %f\n", - (float)nValueMin/COIN, - (float)nBalanceNeedsAnonymized/COIN, - (float)nBalanceAnonimizableNonDenom/COIN, - (float)nBalanceDenominatedConf/COIN, - (float)nBalanceDenominatedUnconf/COIN, - (float)nBalanceDenominated/COIN); + // do not initiate queue if we are a liquidity provider to avoid useless inter-mixing + if (privateSendClient.nLiquidityProvider) return false; - if(fDryRun) return true; + if (StartNewQueue(nValueMin, nBalanceNeedsAnonymized, connman)) + return true; - // 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) - return CreateDenominated(connman); + strAutoDenomResult = _("No compatible Masternode found."); + return false; +} - //check if we have the collateral sized inputs - if(!pwalletMain->HasCollateralInputs()) - return !pwalletMain->HasCollateralInputs(false) && MakeCollateralAmounts(connman); +bool CPrivateSendClientManager::DoAutomaticDenominating(CConnman& connman, bool fDryRun) +{ + if (fMasternodeMode) return false; // no client-side mixing on masternodes + if (!fEnablePrivateSend) return false; - if(nSessionID) { - strAutoDenomResult = _("Mixing in progress..."); + if (!masternodeSync.IsMasternodeListSynced()) { + strAutoDenomResult = _("Can't mix while sync in progress."); return false; } - // Initial phase, find a Masternode - // Clean if there is anything left from previous session - UnlockCoins(); - keyHolderStorage.ReturnAll(); - 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"); - strAutoDenomResult = _("Found unconfirmed denominated outputs, will wait till they confirm to continue."); + if (!pwalletMain) { + strAutoDenomResult = _("Wallet is not initialized"); return false; } - //check our collateral and create new if needed - std::string strReason; - if(txMyCollateral == CMutableTransaction()) { - if(!pwalletMain->CreateCollateralTransaction(txMyCollateral, strReason)) { - LogPrintf("CPrivateSendClient::DoAutomaticDenominating -- create collateral error:%s\n", strReason); - return false; - } - } else { - if(!CPrivateSend::IsCollateralValid(txMyCollateral)) { - LogPrintf("CPrivateSendClient::DoAutomaticDenominating -- invalid collateral, recreating...\n"); - if(!pwalletMain->CreateCollateralTransaction(txMyCollateral, strReason)) { - LogPrintf("CPrivateSendClient::DoAutomaticDenominating -- create collateral error: %s\n", strReason); - return false; - } - } + if (!fDryRun && pwalletMain->IsLocked(true)) { + strAutoDenomResult = _("Wallet is locked."); + return false; } int nMnCountEnabled = mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION); @@ -815,99 +959,103 @@ bool CPrivateSendClient::DoAutomaticDenominating(CConnman& connman, bool fDryRun int nThreshold_low = nThreshold_high * 0.7; LogPrint("privatesend", "Checking vecMasternodesUsed: size: %d, threshold: %d\n", (int)vecMasternodesUsed.size(), nThreshold_high); - if((int)vecMasternodesUsed.size() > nThreshold_high) { + if ((int)vecMasternodesUsed.size() > nThreshold_high) { vecMasternodesUsed.erase(vecMasternodesUsed.begin(), vecMasternodesUsed.begin() + vecMasternodesUsed.size() - nThreshold_low); 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 + CPrivateSendQueue 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()); + if (!mnodeman.GetMasternodeInfo(dsq.masternodeOutpoint, infoMn)) { + LogPrintf("CPrivateSendClientSession::JoinExistingQueue -- dsq masternode is not in masternode list, masternode=%s\n", dsq.masternodeOutpoint.ToStringShort()); continue; } - if(infoMn.nProtocolVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) continue; + if (infoMn.nProtocolVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) continue; // 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; } std::vector vecBits; - if(!CPrivateSend::GetDenominationsBits(dsq.nDenom, vecBits)) { + if (!CPrivateSend::GetDenominationsBits(dsq.nDenom, vecBits)) { // incompatible denom continue; } // mixing rate limit i.e. nLastDsq check should already pass in DSQUEUE ProcessMessage - // in order for dsq to get into vecDarksendQueue, so we should be safe to mix already, + // in order for dsq to get into vecPrivateSendQueue, 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 > 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, CPrivateSendAccept(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; } @@ -915,95 +1063,74 @@ 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); + int nMnCount = mnodeman.CountMasternodes(); // ** find the coins we'll use std::vector vecTxIn; CAmount nValueInTmp = 0; - if(!pwalletMain->SelectCoinsDark(nValueMin, nBalanceNeedsAnonymized, vecTxIn, nValueInTmp, 0, nPrivateSendRounds)) { + if (!pwalletMain->SelectPrivateCoins(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); + while (nTries < 10) { + masternode_info_t infoMn = privateSendClient.GetNotUsedMasternode(); - if(!infoMn.fInfoValid) { - LogPrintf("CPrivateSendClient::StartNewQueue -- Can't find random masternode!\n"); + if (!infoMn.fInfoValid) { + 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!" - " masternode=%s addr=%s nLastDsq=%d CountEnabled/5=%d nDsqCount=%d\n", - infoMn.outpoint.ToStringShort(), infoMn.addr.ToString(), infoMn.nLastDsq, - nMnCountEnabled/5, mnodeman.nDsqCount); + if (infoMn.nLastDsq != 0 && infoMn.nLastDsq + nMnCount / 5 > mnodeman.nDsqCount) { + 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, + nMnCount / 5, mnodeman.nDsqCount); nTries++; continue; } 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); // try to get a single random denom out of vecAmounts - while(nSessionDenom == 0) { + while (nSessionDenom == 0) { 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, CPrivateSendAccept(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; } @@ -1011,16 +1138,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; @@ -1029,53 +1155,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 > vecPSInOutPairs, vecPSInOutPairsTmp; + + if (!SelectDenominate(strError, vecPSInOutPairs)) { + LogPrintf("CPrivateSendClientSession::SubmitDenominate -- SelectDenominate failed, error: %s\n", strError); + return false; + } + + std::vector > 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 >& vecPSInOutPairsRet) { - if(!pwalletMain) { + if (!pwalletMain) { strErrorRet = "Wallet is not initialized"; return false; } @@ -1090,144 +1245,139 @@ bool CPrivateSendClient::PrepareDenominate(int nMinRounds, int nMaxRounds, std:: return false; } - // make sure returning vectors are empty before filling them up - vecTxDSInRet.clear(); - vecTxOutRet.clear(); - - // ** find the coins we'll use - std::vector vecTxDSIn; - std::vector vCoins; - CAmount nValueIn = 0; - - /* - Select the coins we'll use + vecPSInOutPairsRet.clear(); - 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 >& vecPSInOutPairsIn, std::vector >& 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->SelectCoinsGroupedByAddresses(vecTally, false, false)) { + LogPrint("privatesend", "CPrivateSendClientSession::MakeCollateralAmounts -- SelectCoinsGroupedByAddresses can't find any inputs!\n"); return false; } + // Start from smallest balances first to consume tiny amounts and cleanup UTXO a bit + std::sort(vecTally.begin(), vecTally.end(), [](const CompactTallyItem& a, const CompactTallyItem& b) { + return a.nAmount < b.nAmount; + }); + // First try to use only non-denominated funds for (const auto& item : vecTally) { - if(!MakeCollateralAmounts(item, false, connman)) continue; + if (!MakeCollateralAmounts(item, false, connman)) continue; return true; } // There should be at least some denominated funds we should be able to break in pieces to continue mixing for (const auto& item : vecTally) { - if(!MakeCollateralAmounts(item, true, connman)) continue; + if (!MakeCollateralAmounts(item, true, connman)) continue; return true; } // 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 - if(!fTryDenominated && tallyItem.vecOutPoints.size() == 1 && CPrivateSend::IsDenominatedAmount(tallyItem.nAmount)) + if (!fTryDenominated && tallyItem.vecOutPoints.size() == 1 && CPrivateSend::IsDenominatedAmount(tallyItem.nAmount)) return false; CWalletTx wtx; @@ -1258,15 +1408,15 @@ bool CPrivateSendClient::MakeCollateralAmounts(const CompactTallyItem& tallyItem coinControl.Select(outpoint); bool fSuccess = pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, - nFeeRet, nChangePosRet, strFail, &coinControl, true, ONLY_NONDENOMINATED); - if(!fSuccess) { - LogPrintf("CPrivateSendClient::MakeCollateralAmounts -- ONLY_NONDENOMINATED: %s\n", strFail); + nFeeRet, nChangePosRet, strFail, &coinControl, true, ONLY_NONDENOMINATED); + if (!fSuccess) { + LogPrintf("CPrivateSendClientSession::MakeCollateralAmounts -- ONLY_NONDENOMINATED: %s\n", strFail); // If we failed then most likely there are not enough funds on this address. - if(fTryDenominated) { + 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); + if (!pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, + nFeeRet, nChangePosRet, strFail, &coinControl, true, ALL_COINS)) { + LogPrintf("CPrivateSendClientSession::MakeCollateralAmounts -- ALL_COINS Error: %s\n", strFail); reservekeyCollateral.ReturnKey(); return false; } @@ -1279,58 +1429,71 @@ 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()); + if (!pwalletMain->CommitTransaction(wtx, reservekeyChange, &connman, state)) { + 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); + // NOTE: We do not allow txes larger than 100kB, so we have to limit number of inputs here. + // We still want to consume a lot of inputs to avoid creating only smaller denoms though. + // Knowing that each CTxIn is at least 148b big, 400 inputs should take 400 x ~148b = ~60kB. + // This still leaves more than enough room for another data of typical CreateDenominated tx. std::vector vecTally; - if(!pwalletMain->SelectCoinsGrouppedByAddresses(vecTally)) { - LogPrint("privatesend", "CPrivateSendClient::CreateDenominated -- SelectCoinsGrouppedByAddresses can't find any inputs!\n"); + if (!pwalletMain->SelectCoinsGroupedByAddresses(vecTally, true, true, true, 400)) { + LogPrint("privatesend", "CPrivateSendClientSession::CreateDenominated -- SelectCoinsGroupedByAddresses can't find any inputs!\n"); return false; } + // Start from largest balances first to speed things up by creating txes with larger/largest denoms included + std::sort(vecTally.begin(), vecTally.end(), [](const CompactTallyItem& a, const CompactTallyItem& b) { + return a.nAmount > b.nAmount; + }); + bool fCreateMixingCollaterals = !pwalletMain->HasCollateralInputs(); for (const auto& item : vecTally) { - if(!CreateDenominated(item, fCreateMixingCollaterals, connman)) continue; + if (!CreateDenominated(item, fCreateMixingCollaterals, connman)) continue; 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 ************ / - if(fCreateMixingCollaterals) { + if (fCreateMixingCollaterals) { CScript scriptCollateral = keyHolderStorageDenom.AddKey(pwalletMain); - vecSend.push_back((CRecipient){ scriptCollateral, CPrivateSend::GetMaxCollateralAmount(), false }); + vecSend.push_back((CRecipient){scriptCollateral, CPrivateSend::GetMaxCollateralAmount(), false}); nValueLeft -= CPrivateSend::GetMaxCollateralAmount(); } @@ -1342,20 +1505,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) { + 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); + if (nDenomValue != vecStandardDenoms.front() && pwalletMain->CountInputsWithAmount(nDenomValue) > DENOMS_COUNT_MAX) { + strAutoDenomResult = strprintf(_("Too many %f denominations, removing."), (float)nDenomValue / COIN); + LogPrintf("CPrivateSendClientSession::CreateDenominated -- %s\n", strAutoDenomResult); + privateSendClient.AddSkippedDenom(nDenomValue); continue; } } @@ -1363,25 +1531,28 @@ bool CPrivateSendClient::CreateDenominated(const CompactTallyItem& tallyItem, bo int nOutputs = 0; // add each output up to 11 times until it can't be added again - while(nValueLeft - nDenomValue >= 0 && nOutputs <= 10) { + while (nValueLeft - nDenomValue >= 0 && nOutputs <= 10) { CScript scriptDenom = keyHolderStorageDenom.AddKey(pwalletMain); - vecSend.push_back((CRecipient){ scriptDenom, nDenomValue, false }); + vecSend.push_back((CRecipient){scriptDenom, nDenomValue, false}); //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; + 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 @@ -1401,9 +1572,9 @@ bool CPrivateSendClient::CreateDenominated(const CompactTallyItem& tallyItem, bo CReserveKey reservekeyChange(pwalletMain); bool fSuccess = pwalletMain->CreateTransaction(vecSend, wtx, reservekeyChange, - nFeeRet, nChangePosRet, strFail, &coinControl, true, ONLY_NONDENOMINATED); - if(!fSuccess) { - LogPrintf("CPrivateSendClient::CreateDenominated -- Error: %s\n", strFail); + nFeeRet, nChangePosRet, strFail, &coinControl, true, ONLY_NONDENOMINATED); + if (!fSuccess) { + LogPrintf("CPrivateSendClientSession::CreateDenominated -- Error: %s\n", strFail); keyHolderStorageDenom.ReturnAll(); return false; } @@ -1411,71 +1582,58 @@ bool CPrivateSendClient::CreateDenominated(const CompactTallyItem& tallyItem, bo keyHolderStorageDenom.KeepAll(); CValidationState state; - if(!pwalletMain->CommitTransaction(wtx, reservekeyChange, &connman, state)) { - LogPrintf("CPrivateSendClient::CreateDenominated -- CommitTransaction failed! Reason given: %s\n", state.GetRejectReason()); + if (!pwalletMain->CommitTransaction(wtx, reservekeyChange, &connman, state)) { + 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 CPrivateSendEntry& entry, CConnman& connman) { - if(!infoMixingMasternode.fInfoValid) return; + 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 Dash specific functionality - if(fMasternodeMode) return; // no client-side mixing on masternodes + if (fLiteMode) return; // disable all Dash specific functionality + if (fMasternodeMode) return; // no client-side mixing on masternodes - static bool fOneThread; - if(fOneThread) return; - fOneThread = true; + if (!masternodeSync.IsBlockchainSynced() || ShutdownRequested()) + return; - // Make this thread recognisable as the PrivateSend thread - RenameThread("dash-ps-client"); + static unsigned int nTick = 0; + static unsigned int nDoAutoNextRun = nTick + PRIVATESEND_AUTO_TIMEOUT_MIN; - unsigned int nTick = 0; - 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 02b9d2be588c..aec8a97d3db7 100644 --- a/src/privatesend-client.h +++ b/src/privatesend-client.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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. @@ -6,35 +6,38 @@ #define PRIVATESENDCLIENT_H #include "masternode.h" +#include "privatesend-util.h" #include "privatesend.h" #include "wallet/wallet.h" -#include "privatesend-util.h" -class CPrivateSendClient; +class CPrivateSendClientManager; class CConnman; -static const int DENOMS_COUNT_MAX = 100; +static const int DENOMS_COUNT_MAX = 100; -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_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_AMOUNT = 1000; -static const int DEFAULT_PRIVATESEND_LIQUIDITY = 0; +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_SESSIONS = 4; +static const int DEFAULT_PRIVATESEND_ROUNDS = 4; +static const int DEFAULT_PRIVATESEND_AMOUNT = 1000; +static const int DEFAULT_PRIVATESEND_LIQUIDITY = 0; -static const bool DEFAULT_PRIVATESEND_MULTISESSION = false; +static const bool DEFAULT_PRIVATESEND_MULTISESSION = false; // Warn user if mixing in gui or try to create backup if mixing in daemon mode // when we have only this many keys left static const int PRIVATESEND_KEYS_THRESHOLD_WARNING = 100; // Stop mixing completely, it's too dangerous to continue when we have only this many keys left -static const int PRIVATESEND_KEYS_THRESHOLD_STOP = 50; +static const int PRIVATESEND_KEYS_THRESHOLD_STOP = 50; // The main object for accessing mixing -extern CPrivateSendClient privateSendClient; +extern CPrivateSendClientManager privateSendClient; class CPendingDsaRequest { @@ -42,23 +45,26 @@ class CPendingDsaRequest static const int TIMEOUT = 15; CService addr; - CDarksendAccept dsa; + CPrivateSendAccept dsa; int64_t nTimeCreated; public: - CPendingDsaRequest(): + CPendingDsaRequest() : addr(CService()), - dsa(CDarksendAccept()), + dsa(CPrivateSendAccept()), nTimeCreated(0) - {}; + { + } - CPendingDsaRequest(const CService& addr_, const CDarksendAccept& dsa_): + CPendingDsaRequest(const CService& addr_, const CPrivateSendAccept& dsa_) : addr(addr_), - dsa(dsa_) - { nTimeCreated = GetTime(); } + dsa(dsa_), + nTimeCreated(GetTime()) + { + } CService GetAddr() { return addr; } - CDarksendAccept GetDSA() { return dsa; } + CPrivateSendAccept GetDSA() { return dsa; } bool IsExpired() { return GetTime() - nTimeCreated > TIMEOUT; } friend bool operator==(const CPendingDsaRequest& a, const CPendingDsaRequest& b) @@ -75,23 +81,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; @@ -104,19 +98,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); @@ -125,73 +106,152 @@ 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 >& 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 >& vecPSInOutPairsIn, std::vector >& 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 >& vecPSInOutPairsIn, CConnman& connman); /// Get Masternode updates about the progress of mixing - bool CheckPoolStateUpdate(PoolState nStateNew, int nEntriesCountNew, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID, int nSessionIDNew=0); + 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); - void RelayIn(const CDarkSendEntry& entry, CConnman& connman); + void RelayIn(const CPrivateSendEntry& entry, CConnman& connman); 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; bool fEnablePrivateSend; bool fPrivateSendMultiSession; - int nCachedNumBlocks; //used for the overview screen + 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 GetStatus(); + std::string GetStatuses(); + std::string GetSessionDenoms(); - 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); + 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 UpdatedBlockTip(const CBlockIndex *pindex); -}; + void UpdatedSuccessBlock(); -void ThreadCheckPrivateSendClient(CConnman& connman); + void UpdatedBlockTip(const CBlockIndex* pindex); + + void DoMaintenance(CConnman& connman); +}; #endif diff --git a/src/privatesend-server.cpp b/src/privatesend-server.cpp index c77215a952dc..57f8267f67ea 100644 --- a/src/privatesend-server.cpp +++ b/src/privatesend-server.cpp @@ -1,6 +1,7 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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 "privatesend-server.h" #include "activemasternode.h" @@ -19,53 +20,63 @@ CPrivateSendServer privateSendServer; void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) { - if(!fMasternodeMode) return; - if(fLiteMode) return; // ignore all Dash related functionality - if(!masternodeSync.IsBlockchainSynced()) return; - - if(strCommand == NetMsgType::DSACCEPT) { + if (!fMasternodeMode) return; + if (fLiteMode) return; // ignore all Dash related functionality + if (!masternodeSync.IsBlockchainSynced()) return; - if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + if (strCommand == NetMsgType::DSACCEPT) { + if (pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { LogPrint("privatesend", "DSACCEPT -- 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_PRIVATESEND_PEER_PROTO_VERSION))); + connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PRIVATESEND_PEER_PROTO_VERSION))); PushStatus(pfrom, STATUS_REJECTED, ERR_VERSION, connman); return; } - if(IsSessionReady()) { + if (IsSessionReady()) { // too many users in this session already, reject new ones LogPrintf("DSACCEPT -- queue is already full!\n"); PushStatus(pfrom, STATUS_ACCEPTED, ERR_QUEUE_FULL, connman); return; } - CDarksendAccept dsa; + CPrivateSendAccept dsa; vRecv >> dsa; 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; } - if(vecSessionCollaterals.size() == 0 && mnInfo.nLastDsq != 0 && - mnInfo.nLastDsq + mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION)/5 > mnodeman.nDsqCount) - { - LogPrintf("DSACCEPT -- last dsq too recent, must wait: addr=%s\n", pfrom->addr.ToString()); - PushStatus(pfrom, STATUS_REJECTED, ERR_RECENT, connman); - return; + if (vecSessionCollaterals.size() == 0) { + { + TRY_LOCK(cs_vecqueue, lockRecv); + if (!lockRecv) return; + + for (const auto& q : vecPrivateSendQueue) { + if (q.masternodeOutpoint == activeMasternodeInfo.outpoint) { + // refuse to create another queue this often + LogPrint("privatesend", "DSACCEPT -- last dsq is still in queue, refuse to mix\n"); + PushStatus(pfrom, STATUS_REJECTED, ERR_RECENT, connman); + return; + } + } + } + + if (mnInfo.nLastDsq != 0 && mnInfo.nLastDsq + mnodeman.CountMasternodes() / 5 > mnodeman.nDsqCount) { + LogPrintf("DSACCEPT -- last dsq too recent, must wait: addr=%s\n", pfrom->addr.ToString()); + PushStatus(pfrom, STATUS_REJECTED, ERR_RECENT, connman); + return; + } } PoolMessage nMessageID = MSG_NOERR; - bool fResult = nSessionID == 0 ? CreateNewSession(dsa, nMessageID, connman) - : AddUserToExistingSession(dsa, nMessageID); - if(fResult) { + bool fResult = nSessionID == 0 ? CreateNewSession(dsa, nMessageID, connman) + : AddUserToExistingSession(dsa, nMessageID); + if (fResult) { LogPrintf("DSACCEPT -- is compatible, please submit!\n"); PushStatus(pfrom, STATUS_ACCEPTED, nMessageID, connman); return; @@ -75,23 +86,22 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm return; } - } else if(strCommand == NetMsgType::DSQUEUE) { - TRY_LOCK(cs_darksend, lockRecv); - if(!lockRecv) return; + } else if (strCommand == NetMsgType::DSQUEUE) { + TRY_LOCK(cs_vecqueue, lockRecv); + if (!lockRecv) return; - if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + 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, - strprintf("Version must be %d or greater", MIN_PRIVATESEND_PEER_PROTO_VERSION))); + connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PRIVATESEND_PEER_PROTO_VERSION))); return; } - CDarksendQueue dsq; + CPrivateSendQueue dsq; vRecv >> dsq; // process every dsq only once - for (const auto& q : vecDarksendQueue) { - if(q == dsq) { + for (const auto& q : vecPrivateSendQueue) { + if (q == dsq) { // LogPrint("privatesend", "DSQUEUE -- %s seen\n", dsq.ToString()); return; } @@ -99,89 +109,74 @@ 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; + if (dsq.IsExpired()) return; masternode_info_t mnInfo; - if(!mnodeman.GetMasternodeInfo(dsq.masternodeOutpoint, mnInfo)) return; + 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; } - if(!dsq.fReady) { - for (const auto& q : vecDarksendQueue) { - if(q.masternodeOutpoint == dsq.masternodeOutpoint) { + if (!dsq.fReady) { + for (const auto& q : vecPrivateSendQueue) { + if (q.masternodeOutpoint == dsq.masternodeOutpoint) { // no way same mn can send another "not yet ready" dsq this soon LogPrint("privatesend", "DSQUEUE -- Masternode %s is sending WAY too many dsq messages\n", mnInfo.addr.ToString()); return; } } - int nThreshold = mnInfo.nLastDsq + mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION)/5; + int nThreshold = mnInfo.nLastDsq + mnodeman.CountMasternodes() / 5; LogPrint("privatesend", "DSQUEUE -- nLastDsq: %d threshold: %d nDsqCount: %d\n", mnInfo.nLastDsq, nThreshold, mnodeman.nDsqCount); //don't allow a few nodes to dominate the queuing process - if(mnInfo.nLastDsq != 0 && nThreshold > mnodeman.nDsqCount) { + if (mnInfo.nLastDsq != 0 && nThreshold > mnodeman.nDsqCount) { LogPrint("privatesend", "DSQUEUE -- Masternode %s is sending too many dsq messages\n", mnInfo.addr.ToString()); return; } mnodeman.AllowMixing(dsq.masternodeOutpoint); LogPrint("privatesend", "DSQUEUE -- new PrivateSend queue (%s) from masternode %s\n", dsq.ToString(), mnInfo.addr.ToString()); - vecDarksendQueue.push_back(dsq); + vecPrivateSendQueue.push_back(dsq); dsq.Relay(connman); } - } else if(strCommand == NetMsgType::DSVIN) { - - if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + } else if (strCommand == NetMsgType::DSVIN) { + if (pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { LogPrint("privatesend", "DSVIN -- 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_PRIVATESEND_PEER_PROTO_VERSION))); + connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PRIVATESEND_PEER_PROTO_VERSION))); PushStatus(pfrom, STATUS_REJECTED, ERR_VERSION, connman); return; } //do we have enough users in the current session? - if(!IsSessionReady()) { + if (!IsSessionReady()) { LogPrintf("DSVIN -- session not complete!\n"); PushStatus(pfrom, STATUS_REJECTED, ERR_SESSION, connman); return; } - CDarkSendEntry entry; + CPrivateSendEntry entry; vRecv >> entry; LogPrint("privatesend", "DSVIN -- txCollateral %s", entry.txCollateral->ToString()); - if(entry.vecTxDSIn.size() > PRIVATESEND_ENTRY_MAX_SIZE) { + if (entry.vecTxDSIn.size() > PRIVATESEND_ENTRY_MAX_SIZE) { LogPrintf("DSVIN -- ERROR: too many inputs! %d/%d\n", entry.vecTxDSIn.size(), PRIVATESEND_ENTRY_MAX_SIZE); PushStatus(pfrom, STATUS_REJECTED, ERR_MAXIMUM, connman); return; } - if(entry.vecTxOut.size() > PRIVATESEND_ENTRY_MAX_SIZE) { + if (entry.vecTxOut.size() > PRIVATESEND_ENTRY_MAX_SIZE) { LogPrintf("DSVIN -- ERROR: too many outputs! %d/%d\n", entry.vecTxOut.size(), PRIVATESEND_ENTRY_MAX_SIZE); PushStatus(pfrom, STATUS_REJECTED, ERR_MAXIMUM, connman); 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)) { + if (!IsOutputsCompatibleWithSessionDenom(entry.vecTxOut)) { LogPrintf("DSVIN -- not compatible with existing transactions!\n"); PushStatus(pfrom, STATUS_REJECTED, ERR_EXISTING_TX, connman); return; @@ -198,12 +193,12 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm nValueOut += txout.nValue; tx.vout.push_back(txout); - if(txout.scriptPubKey.size() != 25) { + if (txout.scriptPubKey.size() != 25) { LogPrintf("DSVIN -- non-standard pubkey detected! scriptPubKey=%s\n", ScriptToAsmStr(txout.scriptPubKey)); PushStatus(pfrom, STATUS_REJECTED, ERR_NON_STANDARD_PUBKEY, connman); return; } - if(!txout.scriptPubKey.IsPayToPublicKeyHash()) { + if (!txout.scriptPubKey.IsPayToPublicKeyHash()) { LogPrintf("DSVIN -- invalid script! scriptPubKey=%s\n", ScriptToAsmStr(txout.scriptPubKey)); PushStatus(pfrom, STATUS_REJECTED, ERR_INVALID_SCRIPT, connman); return; @@ -216,7 +211,7 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm LogPrint("privatesend", "DSVIN -- txin=%s\n", txin.ToString()); Coin coin; - if(GetUTXOCoin(txin.prevout, coin)) { + if (GetUTXOCoin(txin.prevout, coin)) { nValueIn += coin.out.nValue; } else { LogPrintf("DSVIN -- missing input! txin=%s\n", txin.ToString()); @@ -227,7 +222,7 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm // There should be no fee in mixing tx CAmount nFee = nValueIn - nValueOut; - if(nFee != 0) { + if (nFee != 0) { LogPrintf("DSVIN -- there should be no fee in mixing tx! fees: %lld, tx=%s", nFee, tx.ToString()); PushStatus(pfrom, STATUS_REJECTED, ERR_FEES, connman); return; @@ -237,7 +232,7 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm PoolMessage nMessageID = MSG_NOERR; entry.addr = pfrom->addr; - if(AddEntry(entry, nMessageID)) { + if (AddEntry(entry, nMessageID)) { PushStatus(pfrom, STATUS_ACCEPTED, nMessageID, connman); CheckPool(connman); RelayStatus(STATUS_ACCEPTED, connman); @@ -246,12 +241,10 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm SetNull(); } - } else if(strCommand == NetMsgType::DSSIGNFINALTX) { - - if(pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { + } else if (strCommand == NetMsgType::DSSIGNFINALTX) { + if (pfrom->nVersion < MIN_PRIVATESEND_PEER_PROTO_VERSION) { LogPrint("privatesend", "DSSIGNFINALTX -- 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_PRIVATESEND_PEER_PROTO_VERSION))); + connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PRIVATESEND_PEER_PROTO_VERSION))); return; } @@ -265,7 +258,7 @@ void CPrivateSendServer::ProcessMessage(CNode* pfrom, const std::string& strComm for (const auto& txin : vecTxIn) { nTxInIndex++; - if(!AddScriptSig(txin)) { + if (!AddScriptSig(txin)) { LogPrint("privatesend", "DSSIGNFINALTX -- AddScriptSig() failed at %d/%d, session: %d\n", nTxInIndex, nTxInsCount, nSessionID); RelayStatus(STATUS_REJECTED, connman); return; @@ -282,7 +275,8 @@ void CPrivateSendServer::SetNull() // MN side vecSessionCollaterals.clear(); - CPrivateSendBase::SetNull(); + CPrivateSendBaseSession::SetNull(); + CPrivateSendBaseManager::SetNull(); } // @@ -316,7 +310,7 @@ void CPrivateSendServer::CreateFinalTransaction(CConnman& connman) CMutableTransaction txNew; // make our new transaction - for(int i = 0; i < GetEntriesCount(); i++) { + for (int i = 0; i < GetEntriesCount(); i++) { for (const auto& txout : vecEntries[i].vecTxOut) txNew.vout.push_back(txout); @@ -337,7 +331,7 @@ void CPrivateSendServer::CreateFinalTransaction(CConnman& connman) void CPrivateSendServer::CommitFinalTransaction(CConnman& connman) { - if(!fMasternodeMode) return; // check and relay final tx only on masternode + if (!fMasternodeMode) return; // check and relay final tx only on masternode CTransactionRef finalTransaction = MakeTransactionRef(finalMutableTransaction); uint256 hashTx = finalTransaction->GetHash(); @@ -348,9 +342,8 @@ void CPrivateSendServer::CommitFinalTransaction(CConnman& connman) // See if the transaction is valid 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)) - { + mempool.PrioritiseTransaction(hashTx, hashTx.ToString(), 1000, 0.1 * COIN); + if (!lockMain || !AcceptToMemoryPool(mempool, validationState, finalTransaction, false, NULL, false, maxTxFee, true)) { LogPrintf("CPrivateSendServer::CommitFinalTransaction -- AcceptToMemoryPool() error: Transaction not valid\n"); SetNull(); // not much we can do in this case, just notify clients @@ -362,8 +355,8 @@ void CPrivateSendServer::CommitFinalTransaction(CConnman& connman) LogPrintf("CPrivateSendServer::CommitFinalTransaction -- CREATING DSTX\n"); // create and sign masternode dstx transaction - if(!CPrivateSend::GetDSTX(hashTx)) { - CDarksendBroadcastTx dstxNew(finalTransaction, activeMasternode.outpoint, GetAdjustedTime()); + if (!CPrivateSend::GetDSTX(hashTx)) { + CPrivateSendBroadcastTx dstxNew(finalTransaction, activeMasternodeInfo.outpoint, GetAdjustedTime()); dstxNew.Sign(); CPrivateSend::AddDSTX(dstxNew); } @@ -398,33 +391,33 @@ void CPrivateSendServer::CommitFinalTransaction(CConnman& connman) // void CPrivateSendServer::ChargeFees(CConnman& connman) { - if(!fMasternodeMode) return; + if (!fMasternodeMode) return; //we don't need to charge collateral for every offence. - if(GetRandInt(100) > 33) return; + if (GetRandInt(100) > 33) return; std::vector vecOffendersCollaterals; - if(nState == POOL_STATE_ACCEPTING_ENTRIES) { + if (nState == POOL_STATE_ACCEPTING_ENTRIES) { for (const auto& txCollateral : vecSessionCollaterals) { bool fFound = false; for (const auto& entry : vecEntries) - if(*entry.txCollateral == *txCollateral) + if (*entry.txCollateral == *txCollateral) fFound = true; // This queue entry didn't send us the promised transaction - if(!fFound) { + if (!fFound) { LogPrintf("CPrivateSendServer::ChargeFees -- found uncooperative node (didn't send transaction), found offence\n"); vecOffendersCollaterals.push_back(txCollateral); } } } - if(nState == POOL_STATE_SIGNING) { + if (nState == POOL_STATE_SIGNING) { // who didn't sign? for (const auto& entry : vecEntries) { for (const auto& txdsin : entry.vecTxDSIn) { - if(!txdsin.fHasSig) { + if (!txdsin.fHasSig) { LogPrintf("CPrivateSendServer::ChargeFees -- found uncooperative node (didn't sign), found offence\n"); vecOffendersCollaterals.push_back(entry.txCollateral); } @@ -433,25 +426,25 @@ void CPrivateSendServer::ChargeFees(CConnman& connman) } // no offences found - if(vecOffendersCollaterals.empty()) return; + if (vecOffendersCollaterals.empty()) return; //mostly offending? Charge sometimes - if((int)vecOffendersCollaterals.size() >= Params().PoolMaxTransactions() - 1 && GetRandInt(100) > 33) return; + if ((int)vecOffendersCollaterals.size() >= Params().PoolMaxTransactions() - 1 && GetRandInt(100) > 33) return; //everyone is an offender? That's not right - if((int)vecOffendersCollaterals.size() >= Params().PoolMaxTransactions()) return; + if ((int)vecOffendersCollaterals.size() >= Params().PoolMaxTransactions()) return; //charge one of the offenders randomly 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", - (nState == POOL_STATE_SIGNING) ? "sign" : "send", vecOffendersCollaterals[0]->ToString()); + if (nState == POOL_STATE_ACCEPTING_ENTRIES || nState == POOL_STATE_SIGNING) { + 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 { @@ -474,16 +467,16 @@ void CPrivateSendServer::ChargeFees(CConnman& connman) */ void CPrivateSendServer::ChargeRandomFees(CConnman& connman) { - if(!fMasternodeMode) return; + if (!fMasternodeMode) return; LOCK(cs_main); for (const auto& txCollateral : vecSessionCollaterals) { - if(GetRandInt(100) > 10) return; + if (GetRandInt(100) > 10) return; 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 { @@ -497,16 +490,16 @@ void CPrivateSendServer::ChargeRandomFees(CConnman& connman) // void CPrivateSendServer::CheckTimeout(CConnman& connman) { - if(!fMasternodeMode) return; + if (!fMasternodeMode) return; CheckQueue(); int nTimeout = (nState == POOL_STATE_SIGNING) ? PRIVATESEND_SIGNING_TIMEOUT : PRIVATESEND_QUEUE_TIMEOUT; bool fTimeout = GetTime() - nTimeLastSuccessfulStep >= nTimeout; - if(nState != POOL_STATE_IDLE && fTimeout) { + if (nState != POOL_STATE_IDLE && fTimeout) { LogPrint("privatesend", "CPrivateSendServer::CheckTimeout -- %s timed out (%ds) -- resetting\n", - (nState == POOL_STATE_SIGNING) ? "Signing" : "Session", nTimeout); + (nState == POOL_STATE_SIGNING) ? "Signing" : "Session", nTimeout); ChargeFees(connman); SetNull(); } @@ -519,12 +512,12 @@ void CPrivateSendServer::CheckTimeout(CConnman& connman) */ void CPrivateSendServer::CheckForCompleteQueue(CConnman& connman) { - if(!fMasternodeMode) return; + if (!fMasternodeMode) return; - if(nState == POOL_STATE_QUEUE && IsSessionReady()) { + if (nState == POOL_STATE_QUEUE && IsSessionReady()) { SetState(POOL_STATE_ACCEPTING_ENTRIES); - CDarksendQueue dsq(nSessionDenom, nSessionInputCount, activeMasternode.outpoint, GetAdjustedTime(), true); + CPrivateSendQueue 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); @@ -543,14 +536,13 @@ bool CPrivateSendServer::IsInputScriptSigValid(const CTxIn& txin) CScript sigPubKey = CScript(); for (const auto& entry : vecEntries) { - for (const auto& txout : entry.vecTxOut) txNew.vout.push_back(txout); for (const auto& txdsin : entry.vecTxDSIn) { txNew.vin.push_back(txdsin); - if(txdsin.prevout == txin.prevout) { + if (txdsin.prevout == txin.prevout) { nTxInIndex = i; sigPubKey = txdsin.prevPubKey; } @@ -558,10 +550,10 @@ bool CPrivateSendServer::IsInputScriptSigValid(const CTxIn& txin) } } - if(nTxInIndex >= 0) { //might have to do this one input at a time? + if (nTxInIndex >= 0) { //might have to do this one input at a time? txNew.vin[nTxInIndex].scriptSig = txin.scriptSig; - LogPrint("privatesend", "CPrivateSendServer::IsInputScriptSigValid -- verifying scriptSig %s\n", ScriptToAsmStr(txin.scriptSig).substr(0,24)); - if(!VerifyScript(txNew.vin[nTxInIndex].scriptSig, sigPubKey, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, MutableTransactionSignatureChecker(&txNew, nTxInIndex))) { + LogPrint("privatesend", "CPrivateSendServer::IsInputScriptSigValid -- verifying scriptSig %s\n", ScriptToAsmStr(txin.scriptSig).substr(0, 24)); + if (!VerifyScript(txNew.vin[nTxInIndex].scriptSig, sigPubKey, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, MutableTransactionSignatureChecker(&txNew, nTxInIndex))) { LogPrint("privatesend", "CPrivateSendServer::IsInputScriptSigValid -- VerifyScript() failed on input %d\n", nTxInIndex); return false; } @@ -577,25 +569,25 @@ bool CPrivateSendServer::IsInputScriptSigValid(const CTxIn& txin) // // Add a clients transaction to the pool // -bool CPrivateSendServer::AddEntry(const CDarkSendEntry& entryNew, PoolMessage& nMessageIDRet) +bool CPrivateSendServer::AddEntry(const CPrivateSendEntry& entryNew, PoolMessage& nMessageIDRet) { - if(!fMasternodeMode) return false; + if (!fMasternodeMode) return false; for (const auto& txin : entryNew.vecTxDSIn) { - if(txin.prevout.IsNull()) { + if (txin.prevout.IsNull()) { LogPrint("privatesend", "CPrivateSendServer::AddEntry -- input not valid!\n"); nMessageIDRet = ERR_INVALID_INPUT; return false; } } - if(!CPrivateSend::IsCollateralValid(*entryNew.txCollateral)) { + if (!CPrivateSend::IsCollateralValid(*entryNew.txCollateral)) { LogPrint("privatesend", "CPrivateSendServer::AddEntry -- collateral not valid!\n"); nMessageIDRet = ERR_INVALID_COLLATERAL; return false; } - if(GetEntriesCount() >= CPrivateSend::GetMaxPoolTransactions()) { + if (GetEntriesCount() >= CPrivateSend::GetMaxPoolTransactions()) { LogPrint("privatesend", "CPrivateSendServer::AddEntry -- entries is full!\n"); nMessageIDRet = ERR_ENTRIES_FULL; return false; @@ -605,7 +597,7 @@ bool CPrivateSendServer::AddEntry(const CDarkSendEntry& entryNew, PoolMessage& n LogPrint("privatesend", "looking for txin -- %s\n", txin.ToString()); for (const auto& entry : vecEntries) { for (const auto& txdsin : entry.vecTxDSIn) { - if(txdsin.prevout == txin.prevout) { + if (txdsin.prevout == txin.prevout) { LogPrint("privatesend", "CPrivateSendServer::AddEntry -- found in txin\n"); nMessageIDRet = ERR_ALREADY_HAVE; return false; @@ -625,38 +617,38 @@ bool CPrivateSendServer::AddEntry(const CDarkSendEntry& entryNew, PoolMessage& n bool CPrivateSendServer::AddScriptSig(const CTxIn& txinNew) { - LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24)); + LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0, 24)); for (const auto& entry : vecEntries) { for (const auto& txdsin : entry.vecTxDSIn) { - if(txdsin.scriptSig == txinNew.scriptSig) { + if (txdsin.scriptSig == txinNew.scriptSig) { LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- already exists\n"); return false; } } } - if(!IsInputScriptSigValid(txinNew)) { + if (!IsInputScriptSigValid(txinNew)) { LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- Invalid scriptSig\n"); return false; } - LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- scriptSig=%s new\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24)); + LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- scriptSig=%s new\n", ScriptToAsmStr(txinNew.scriptSig).substr(0, 24)); for (auto& txin : finalMutableTransaction.vin) { - if(txin.prevout == txinNew.prevout && txin.nSequence == txinNew.nSequence) { + if (txin.prevout == txinNew.prevout && txin.nSequence == txinNew.nSequence) { txin.scriptSig = txinNew.scriptSig; - LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- adding to finalMutableTransaction, scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24)); + LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- adding to finalMutableTransaction, scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0, 24)); } } - for(int i = 0; i < GetEntriesCount(); i++) { - if(vecEntries[i].AddScriptSig(txinNew)) { - LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- adding to entries, scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0,24)); + for (int i = 0; i < GetEntriesCount(); i++) { + if (vecEntries[i].AddScriptSig(txinNew)) { + LogPrint("privatesend", "CPrivateSendServer::AddScriptSig -- adding to entries, scriptSig=%s\n", ScriptToAsmStr(txinNew.scriptSig).substr(0, 24)); return true; } } - LogPrintf("CPrivateSendServer::AddScriptSig -- Couldn't set sig!\n" ); + LogPrintf("CPrivateSendServer::AddScriptSig -- Couldn't set sig!\n"); return false; } @@ -665,131 +657,115 @@ bool CPrivateSendServer::IsSignaturesComplete() { for (const auto& entry : vecEntries) for (const auto& txdsin : entry.vecTxDSIn) - if(!txdsin.fHasSig) return false; + if (!txdsin.fHasSig) return false; return true; } bool CPrivateSendServer::IsOutputsCompatibleWithSessionDenom(const std::vector& vecTxOut) { - if(CPrivateSend::GetDenominations(vecTxOut) == 0) return false; + if (CPrivateSend::GetDenominations(vecTxOut) == 0) return false; for (const auto& entry : vecEntries) { LogPrintf("CPrivateSendServer::IsOutputsCompatibleWithSessionDenom -- vecTxOut denom %d, entry.vecTxOut denom %d\n", - CPrivateSend::GetDenominations(vecTxOut), CPrivateSend::GetDenominations(entry.vecTxOut)); - if(CPrivateSend::GetDenominations(vecTxOut) != CPrivateSend::GetDenominations(entry.vecTxOut)) return false; + CPrivateSend::GetDenominations(vecTxOut), CPrivateSend::GetDenominations(entry.vecTxOut)); + if (CPrivateSend::GetDenominations(vecTxOut) != CPrivateSend::GetDenominations(entry.vecTxOut)) return false; } return true; } -bool CPrivateSendServer::IsAcceptableDSA(const CDarksendAccept& dsa, PoolMessage& nMessageIDRet) +bool CPrivateSendServer::IsAcceptableDSA(const CPrivateSendAccept& dsa, PoolMessage& nMessageIDRet) { - if(!fMasternodeMode) return false; + if (!fMasternodeMode) return false; // is denom even smth legit? std::vector vecBits; - if(!CPrivateSend::GetDenominationsBits(dsa.nDenom, vecBits)) { + if (!CPrivateSend::GetDenominationsBits(dsa.nDenom, vecBits)) { LogPrint("privatesend", "CPrivateSendServer::%s -- denom not valid!\n", __func__); nMessageIDRet = ERR_DENOM; return false; } // check collateral - if(!fUnitTest && !CPrivateSend::IsCollateralValid(dsa.txCollateral)) { + if (!fUnitTest && !CPrivateSend::IsCollateralValid(dsa.txCollateral)) { LogPrint("privatesend", "CPrivateSendServer::%s -- collateral not valid!\n", __func__); nMessageIDRet = ERR_INVALID_COLLATERAL; 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; } -bool CPrivateSendServer::CreateNewSession(const CDarksendAccept& dsa, PoolMessage& nMessageIDRet, CConnman& connman) +bool CPrivateSendServer::CreateNewSession(const CPrivateSendAccept& dsa, PoolMessage& nMessageIDRet, CConnman& connman) { - if(!fMasternodeMode || nSessionID != 0) return false; + if (!fMasternodeMode || nSessionID != 0) return false; // new session can only be started in idle mode - if(nState != POOL_STATE_IDLE) { + if (nState != POOL_STATE_IDLE) { nMessageIDRet = ERR_MODE; LogPrintf("CPrivateSendServer::CreateNewSession -- incompatible mode: nState=%d\n", nState); return false; } - if(!IsAcceptableDSA(dsa, nMessageIDRet)) { + if (!IsAcceptableDSA(dsa, nMessageIDRet)) { return false; } // start new session nMessageIDRet = MSG_NOERR; - nSessionID = GetRandInt(999999)+1; + 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) { + 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); + CPrivateSendQueue dsq(nSessionDenom, activeMasternodeInfo.outpoint, GetAdjustedTime(), false); LogPrint("privatesend", "CPrivateSendServer::CreateNewSession -- signing and relaying new queue: %s\n", dsq.ToString()); dsq.Sign(); dsq.Relay(connman); - vecDarksendQueue.push_back(dsq); + vecPrivateSendQueue.push_back(dsq); } vecSessionCollaterals.push_back(MakeTransactionRef(dsa.txCollateral)); LogPrintf("CPrivateSendServer::CreateNewSession -- new session created, nSessionID: %d nSessionDenom: %d (%s) vecSessionCollaterals.size(): %d\n", - nSessionID, nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom), vecSessionCollaterals.size()); + nSessionID, nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom), vecSessionCollaterals.size()); return true; } -bool CPrivateSendServer::AddUserToExistingSession(const CDarksendAccept& dsa, PoolMessage& nMessageIDRet) +bool CPrivateSendServer::AddUserToExistingSession(const CPrivateSendAccept& dsa, PoolMessage& nMessageIDRet) { - if(!fMasternodeMode || nSessionID == 0 || IsSessionReady()) return false; + if (!fMasternodeMode || nSessionID == 0 || IsSessionReady()) return false; - if(!IsAcceptableDSA(dsa, nMessageIDRet)) { + if (!IsAcceptableDSA(dsa, nMessageIDRet)) { return false; } // we only add new users to an existing session when we are in queue mode - if(nState != POOL_STATE_QUEUE) { + if (nState != POOL_STATE_QUEUE) { nMessageIDRet = ERR_MODE; LogPrintf("CPrivateSendServer::AddUserToExistingSession -- incompatible mode: nState=%d\n", nState); return false; } - if(dsa.nDenom != nSessionDenom) { + if (dsa.nDenom != nSessionDenom) { LogPrintf("CPrivateSendServer::AddUserToExistingSession -- incompatible denom %d (%s) != nSessionDenom %d (%s)\n", - dsa.nDenom, CPrivateSend::GetDenominationsToString(dsa.nDenom), nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom)); + dsa.nDenom, CPrivateSend::GetDenominationsToString(dsa.nDenom), nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom)); nMessageIDRet = ERR_DENOM; 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; } @@ -797,7 +773,7 @@ bool CPrivateSendServer::AddUserToExistingSession(const CDarksendAccept& dsa, Po void CPrivateSendServer::RelayFinalTransaction(const CTransaction& txFinal, CConnman& connman) { LogPrint("privatesend", "CPrivateSendServer::%s -- nSessionID: %d nSessionDenom: %d (%s)\n", - __func__, nSessionID, nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom)); + __func__, nSessionID, nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom)); // final mixing tx with empty signatures should be relayed to mixing participants only for (const auto entry : vecEntries) { @@ -806,7 +782,7 @@ void CPrivateSendServer::RelayFinalTransaction(const CTransaction& txFinal, CCon connman.PushMessage(pnode, msgMaker.Make(NetMsgType::DSFINALTX, nSessionID, txFinal)); return true; }); - if(!fOk) { + if (!fOk) { // no such node? maybe this client disconnected or our own connection went down RelayStatus(STATUS_REJECTED, connman); break; @@ -816,7 +792,7 @@ void CPrivateSendServer::RelayFinalTransaction(const CTransaction& txFinal, CCon void CPrivateSendServer::PushStatus(CNode* pnode, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID, CConnman& connman) { - if(!pnode) return; + if (!pnode) return; CNetMsgMaker msgMaker(pnode->GetSendVersion()); connman.PushMessage(pnode, msgMaker.Make(NetMsgType::DSSTATUSUPDATE, nSessionID, (int)nState, (int)vecEntries.size(), (int)nStatusUpdate, (int)nMessageID)); } @@ -831,7 +807,7 @@ void CPrivateSendServer::RelayStatus(PoolStatusUpdate nStatusUpdate, CConnman& c PushStatus(pnode, nStatusUpdate, nMessageID, connman); return true; }); - if(!fOk) { + if (!fOk) { // no such node? maybe this client disconnected or our own connection went down ++nDisconnected; } @@ -840,7 +816,7 @@ void CPrivateSendServer::RelayStatus(PoolStatusUpdate nStatusUpdate, CConnman& c // smth went wrong LogPrintf("CPrivateSendServer::%s -- can't continue, %llu client(s) disconnected, nSessionID: %d nSessionDenom: %d (%s)\n", - __func__, nDisconnected, nSessionID, nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom)); + __func__, nDisconnected, nSessionID, nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom)); // notify everyone else that this session should be terminated for (const auto& entry : vecEntries) { @@ -850,7 +826,7 @@ void CPrivateSendServer::RelayStatus(PoolStatusUpdate nStatusUpdate, CConnman& c }); } - if(nDisconnected == vecEntries.size()) { + if (nDisconnected == vecEntries.size()) { // all clients disconnected, there is probably some issues with our own connection // do not charge any fees, just reset the pool SetNull(); @@ -860,7 +836,7 @@ void CPrivateSendServer::RelayStatus(PoolStatusUpdate nStatusUpdate, CConnman& c void CPrivateSendServer::RelayCompletedTransaction(PoolMessage nMessageID, CConnman& connman) { LogPrint("privatesend", "CPrivateSendServer::%s -- nSessionID: %d nSessionDenom: %d (%s)\n", - __func__, nSessionID, nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom)); + __func__, nSessionID, nSessionDenom, CPrivateSend::GetDenominationsToString(nSessionDenom)); // final mixing tx with empty signatures should be relayed to mixing participants only for (const auto& entry : vecEntries) { @@ -869,7 +845,7 @@ void CPrivateSendServer::RelayCompletedTransaction(PoolMessage nMessageID, CConn connman.PushMessage(pnode, msgMaker.Make(NetMsgType::DSCOMPLETE, nSessionID, (int)nMessageID)); return true; }); - if(!fOk) { + if (!fOk) { // no such node? maybe client disconnected or our own connection went down RelayStatus(STATUS_REJECTED, connman); break; @@ -879,9 +855,9 @@ void CPrivateSendServer::RelayCompletedTransaction(PoolMessage nMessageID, CConn void CPrivateSendServer::SetState(PoolState nStateNew) { - if(!fMasternodeMode) return; + if (!fMasternodeMode) return; - if(nStateNew == POOL_STATE_ERROR || nStateNew == POOL_STATE_SUCCESS) { + if (nStateNew == POOL_STATE_ERROR || nStateNew == POOL_STATE_SUCCESS) { LogPrint("privatesend", "CPrivateSendServer::SetState -- Can't set state to ERROR or SUCCESS as a Masternode. \n"); return; } @@ -890,29 +866,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(!fMasternodeMode) return; // only run on masternodes - - static bool fOneThread; - if(fOneThread) return; - fOneThread = true; - - // Make this thread recognisable as the PrivateSend thread - RenameThread("dash-ps-server"); + if (fLiteMode) return; // disable all Dash specific functionality + if (!fMasternodeMode) return; // only run on masternodes - 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 86af1e77b99a..c4deeefcaadf 100644 --- a/src/privatesend-server.h +++ b/src/privatesend-server.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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. @@ -15,7 +15,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 @@ -25,7 +25,7 @@ class CPrivateSendServer : public CPrivateSendBase bool fUnitTest; /// Add a clients entry to the pool - bool AddEntry(const CDarkSendEntry& entryNew, PoolMessage& nMessageIDRet); + bool AddEntry(const CPrivateSendEntry& entryNew, PoolMessage& nMessageIDRet); /// Add signature to a txin bool AddScriptSig(const CTxIn& txin); @@ -41,9 +41,9 @@ class CPrivateSendServer : public CPrivateSendBase void CommitFinalTransaction(CConnman& connman); /// Is this nDenom and txCollateral acceptable? - bool IsAcceptableDSA(const CDarksendAccept& dsa, PoolMessage &nMessageIDRet); - bool CreateNewSession(const CDarksendAccept& dsa, PoolMessage &nMessageIDRet, CConnman& connman); - bool AddUserToExistingSession(const CDarksendAccept& dsa, PoolMessage &nMessageIDRet); + bool IsAcceptableDSA(const CPrivateSendAccept& dsa, PoolMessage& nMessageIDRet); + bool CreateNewSession(const CPrivateSendAccept& dsa, PoolMessage& nMessageIDRet, CConnman& connman); + bool AddUserToExistingSession(const CPrivateSendAccept& dsa, PoolMessage& nMessageIDRet); /// Do we have enough users to take entries? bool IsSessionReady() { return (int)vecSessionCollaterals.size() >= CPrivateSend::GetMaxPoolTransactions(); } @@ -67,14 +67,14 @@ class CPrivateSendServer : public CPrivateSendBase public: CPrivateSendServer() : - fUnitTest(false) { SetNull(); } + 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 4fc59eaaab63..99571e0d866b 100644 --- a/src/privatesend-util.cpp +++ b/src/privatesend-util.cpp @@ -1,6 +1,7 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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 "privatesend-util.h" CKeyHolder::CKeyHolder(CWallet* pwallet) : @@ -38,7 +39,7 @@ CScript CKeyHolderStorage::AddKey(CWallet* pwallet) void CKeyHolderStorage::KeepAll() { - std::vector> tmp; + std::vector > tmp; { // don't hold cs_storage while calling KeepKey(), which might lock cs_wallet LOCK(cs_storage); @@ -46,7 +47,7 @@ void CKeyHolderStorage::KeepAll() } if (tmp.size() > 0) { - for (auto &key : tmp) { + for (auto& key : tmp) { key->KeepKey(); } LogPrintf("CKeyHolderStorage::%s -- %lld keys kept\n", __func__, tmp.size()); @@ -55,7 +56,7 @@ void CKeyHolderStorage::KeepAll() void CKeyHolderStorage::ReturnAll() { - std::vector> tmp; + std::vector > tmp; { // don't hold cs_storage while calling ReturnKey(), which might lock cs_wallet LOCK(cs_storage); @@ -63,7 +64,7 @@ void CKeyHolderStorage::ReturnAll() } if (tmp.size() > 0) { - for (auto &key : tmp) { + for (auto& key : tmp) { key->ReturnKey(); } LogPrintf("CKeyHolderStorage::%s -- %lld keys returned\n", __func__, tmp.size()); diff --git a/src/privatesend-util.h b/src/privatesend-util.h index 9040eb011a0f..348ce8502386 100644 --- a/src/privatesend-util.h +++ b/src/privatesend-util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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. @@ -12,6 +12,7 @@ class CKeyHolder private: CReserveKey reserveKey; CPubKey pubKey; + public: CKeyHolder(CWallet* pwalletIn); CKeyHolder(CKeyHolder&&) = default; @@ -20,7 +21,6 @@ class CKeyHolder void ReturnKey(); CScript GetScriptForDestination() const; - }; class CKeyHolderStorage @@ -33,6 +33,5 @@ class CKeyHolderStorage CScript AddKey(CWallet* pwalletIn); void KeepAll(); void ReturnAll(); - }; #endif //PRIVATESENDUTIL_H diff --git a/src/privatesend.cpp b/src/privatesend.cpp index ec16dead5d98..b6189e424813 100644 --- a/src/privatesend.cpp +++ b/src/privatesend.cpp @@ -1,31 +1,28 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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 "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) +bool CPrivateSendEntry::AddScriptSig(const CTxIn& txin) { for (auto& txdsin : vecTxDSIn) { - if(txdsin.prevout == txin.prevout && txdsin.nSequence == txin.nSequence) { - if(txdsin.fHasSig) return false; + if (txdsin.prevout == txin.prevout && txdsin.nSequence == txin.nSequence) { + if (txdsin.fHasSig) return false; txdsin.scriptSig = txin.scriptSig; txdsin.fHasSig = true; @@ -37,42 +34,56 @@ bool CDarkSendEntry::AddScriptSig(const CTxIn& txin) return false; } -uint256 CDarksendQueue::GetSignatureHash() const +uint256 CPrivateSendQueue::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() +bool CPrivateSendQueue::Sign() { - if(!fMasternodeMode) return false; + if (!fMasternodeMode) return false; std::string strError = ""; - if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + uint256 hash = GetSignatureHash(); + CBLSSignature sig = activeMasternodeInfo.blsKeyOperator->Sign(hash); + if (!sig.IsValid()) { + return false; + } + sig.GetBuf(vchSig); + } else if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash = GetSignatureHash(); - if (!CHashSigner::SignHash(hash, activeMasternode.keyMasternode, vchSig)) { - LogPrintf("CDarksendQueue::Sign -- SignHash() failed\n"); + if (!CHashSigner::SignHash(hash, activeMasternodeInfo.legacyKeyOperator, vchSig)) { + LogPrintf("CPrivateSendQueue::Sign -- SignHash() failed\n"); return false; } - if (!CHashSigner::VerifyHash(hash, activeMasternode.pubKeyMasternode, vchSig, strError)) { - LogPrintf("CDarksendQueue::Sign -- VerifyHash() failed, error: %s\n", strError); + if (!CHashSigner::VerifyHash(hash, activeMasternodeInfo.legacyKeyIDOperator, vchSig, strError)) { + LogPrintf("CPrivateSendQueue::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)) { - LogPrintf("CDarksendQueue::Sign -- SignMessage() failed, %s\n", ToString()); + if (!CMessageSigner::SignMessage(strMessage, vchSig, activeMasternodeInfo.legacyKeyOperator)) { + LogPrintf("CPrivateSendQueue::Sign -- SignMessage() failed, %s\n", ToString()); return false; } - if(!CMessageSigner::VerifyMessage(activeMasternode.pubKeyMasternode, vchSig, strMessage, strError)) { - LogPrintf("CDarksendQueue::Sign -- VerifyMessage() failed, error: %s\n", strError); + if (!CMessageSigner::VerifyMessage(activeMasternodeInfo.legacyKeyIDOperator, vchSig, strMessage, strError)) { + LogPrintf("CPrivateSendQueue::Sign -- VerifyMessage() failed, error: %s\n", strError); return false; } } @@ -80,26 +91,34 @@ bool CDarksendQueue::Sign() return true; } -bool CDarksendQueue::CheckSignature(const CPubKey& pubKeyMasternode) const +bool CPrivateSendQueue::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); + LogPrintf("CPrivateSendQueue::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)) { - LogPrintf("CDarksendQueue::CheckSignature -- Got bad Masternode queue signature: %s; error: %s\n", ToString(), strError); + if (!CMessageSigner::VerifyMessage(keyIDOperator, vchSig, strMessage, strError)) { + LogPrintf("CPrivateSendQueue::CheckSignature -- Got bad Masternode queue signature: %s; error: %s\n", ToString(), strError); return false; } } @@ -107,7 +126,7 @@ bool CDarksendQueue::CheckSignature(const CPubKey& pubKeyMasternode) const return true; } -bool CDarksendQueue::Relay(CConnman& connman) +bool CPrivateSendQueue::Relay(CConnman& connman) { connman.ForEachNode([&connman, this](CNode* pnode) { CNetMsgMaker msgMaker(pnode->GetSendVersion()); @@ -117,39 +136,47 @@ bool CDarksendQueue::Relay(CConnman& connman) return true; } -uint256 CDarksendBroadcastTx::GetSignatureHash() const +uint256 CPrivateSendBroadcastTx::GetSignatureHash() const { return SerializeHash(*this); } -bool CDarksendBroadcastTx::Sign() +bool CPrivateSendBroadcastTx::Sign() { - if(!fMasternodeMode) return false; + if (!fMasternodeMode) return false; std::string strError = ""; - if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + uint256 hash = GetSignatureHash(); + + CBLSSignature sig = activeMasternodeInfo.blsKeyOperator->Sign(hash); + if (!sig.IsValid()) { + return false; + } + sig.GetBuf(vchSig); + } else if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { uint256 hash = GetSignatureHash(); - if (!CHashSigner::SignHash(hash, activeMasternode.keyMasternode, vchSig)) { - LogPrintf("CDarksendBroadcastTx::Sign -- SignHash() failed\n"); + if (!CHashSigner::SignHash(hash, activeMasternodeInfo.legacyKeyOperator, vchSig)) { + LogPrintf("CPrivateSendBroadcastTx::Sign -- SignHash() failed\n"); return false; } - if (!CHashSigner::VerifyHash(hash, activeMasternode.pubKeyMasternode, vchSig, strError)) { - LogPrintf("CDarksendBroadcastTx::Sign -- VerifyHash() failed, error: %s\n", strError); + if (!CHashSigner::VerifyHash(hash, activeMasternodeInfo.legacyKeyIDOperator, vchSig, strError)) { + LogPrintf("CPrivateSendBroadcastTx::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)) { - LogPrintf("CDarksendBroadcastTx::Sign -- SignMessage() failed\n"); + if (!CMessageSigner::SignMessage(strMessage, vchSig, activeMasternodeInfo.legacyKeyOperator)) { + LogPrintf("CPrivateSendBroadcastTx::Sign -- SignMessage() failed\n"); return false; } - if(!CMessageSigner::VerifyMessage(activeMasternode.pubKeyMasternode, vchSig, strMessage, strError)) { - LogPrintf("CDarksendBroadcastTx::Sign -- VerifyMessage() failed, error: %s\n", strError); + if (!CMessageSigner::VerifyMessage(activeMasternodeInfo.legacyKeyIDOperator, vchSig, strMessage, strError)) { + LogPrintf("CPrivateSendBroadcastTx::Sign -- VerifyMessage() failed, error: %s\n", strError); return false; } } @@ -157,23 +184,32 @@ bool CDarksendBroadcastTx::Sign() return true; } -bool CDarksendBroadcastTx::CheckSignature(const CPubKey& pubKeyMasternode) const +bool CPrivateSendBroadcastTx::CheckSignature(const CKeyID& keyIDOperator, const CBLSPublicKey& blsPubKey) const { std::string strError = ""; - if (sporkManager.IsSporkActive(SPORK_6_NEW_SIGS)) { + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { uint256 hash = GetSignatureHash(); - if (!CHashSigner::VerifyHash(hash, pubKeyMasternode, vchSig, strError)) { + 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, keyIDOperator, vchSig, strError)) { // we don't care about dstxes with old signature format - LogPrintf("CDarksendBroadcastTx::CheckSignature -- VerifyHash() failed, error: %s\n", strError); + LogPrintf("CPrivateSendBroadcastTx::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)) { - LogPrintf("CDarksendBroadcastTx::CheckSignature -- Got bad dstx signature, error: %s\n", strError); + if (!CMessageSigner::VerifyMessage(keyIDOperator, vchSig, strMessage, strError)) { + LogPrintf("CPrivateSendBroadcastTx::CheckSignature -- Got bad dstx signature, error: %s\n", strError); return false; } } @@ -181,56 +217,86 @@ bool CDarksendBroadcastTx::CheckSignature(const CPubKey& pubKeyMasternode) const return true; } -bool CDarksendBroadcastTx::IsExpired(int nHeight) +bool CPrivateSendBroadcastTx::IsExpired(int nHeight) { // expire confirmed DSTXes after ~1h since confirmation return (nConfirmedHeight != -1) && (nHeight - nConfirmedHeight > 24); } -void CPrivateSendBase::SetNull() +void CPrivateSendBaseSession::SetNull() { // Both sides + LOCK(cs_privatesend); 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() { - TRY_LOCK(cs_darksend, lockDS); - if(!lockDS) return; // it's ok to fail here, we run this quite frequently + LOCK(cs_vecqueue); + vecPrivateSendQueue.clear(); +} + +void CPrivateSendBaseManager::CheckQueue() +{ + 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()); - it = vecDarksendQueue.erase(it); - } else ++it; + std::vector::iterator it = vecPrivateSendQueue.begin(); + while (it != vecPrivateSendQueue.end()) { + if ((*it).IsExpired()) { + LogPrint("privatesend", "CPrivateSendBaseManager::%s -- Removing expired queue (%s)\n", __func__, (*it).ToString()); + it = vecPrivateSendQueue.erase(it); + } else + ++it; } } -std::string CPrivateSendBase::GetStateString() const +bool CPrivateSendBaseManager::GetQueueItemAndTry(CPrivateSendQueue& dsqRet) { - switch(nState) { - case POOL_STATE_IDLE: return "IDLE"; - case POOL_STATE_QUEUE: return "QUEUE"; - case POOL_STATE_ACCEPTING_ENTRIES: return "ACCEPTING_ENTRIES"; - case POOL_STATE_SIGNING: return "SIGNING"; - case POOL_STATE_ERROR: return "ERROR"; - case POOL_STATE_SUCCESS: return "SUCCESS"; - default: return "UNKNOWN"; + TRY_LOCK(cs_vecqueue, lockDS); + if (!lockDS) return false; // it's ok to fail here, we run this quite frequently + + for (auto& dsq : vecPrivateSendQueue) { + // 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"; + case POOL_STATE_QUEUE: + return "QUEUE"; + case POOL_STATE_ACCEPTING_ENTRIES: + return "ACCEPTING_ENTRIES"; + case POOL_STATE_SIGNING: + return "SIGNING"; + case POOL_STATE_ERROR: + return "ERROR"; + case POOL_STATE_SUCCESS: + return "SUCCESS"; + default: + return "UNKNOWN"; } } // Definitions for static data members std::vector CPrivateSend::vecStandardDenominations; -std::map CPrivateSend::mapDSTX; +std::map CPrivateSend::mapDSTX; CCriticalSection CPrivateSend::cs_mapdstx; void CPrivateSend::InitStandardDenominations() @@ -248,20 +314,18 @@ void CPrivateSend::InitStandardDenominations() /* Disabled vecStandardDenominations.push_back( (100 * COIN)+100000 ); */ - vecStandardDenominations.push_back( (10 * COIN)+10000 ); - vecStandardDenominations.push_back( (1 * COIN)+1000 ); - vecStandardDenominations.push_back( (.1 * COIN)+100 ); - vecStandardDenominations.push_back( (.01 * COIN)+10 ); - /* Disabled till we need them - vecStandardDenominations.push_back( (.001 * COIN)+1 ); - */ + vecStandardDenominations.push_back((10 * COIN) + 10000); + vecStandardDenominations.push_back((1 * COIN) + 1000); + vecStandardDenominations.push_back((.1 * COIN) + 100); + vecStandardDenominations.push_back((.01 * COIN) + 10); + vecStandardDenominations.push_back((.001 * COIN) + 1); } // check to make sure the collateral provided by the client is valid bool CPrivateSend::IsCollateralValid(const CTransaction& txCollateral) { - if(txCollateral.vout.empty()) return false; - if(txCollateral.nLockTime != 0) return false; + if (txCollateral.vout.empty()) return false; + if (txCollateral.nLockTime != 0) return false; CAmount nValueIn = 0; CAmount nValueOut = 0; @@ -269,16 +333,15 @@ bool CPrivateSend::IsCollateralValid(const CTransaction& txCollateral) for (const auto& txout : txCollateral.vout) { nValueOut += txout.nValue; - bool fAllowData = mnpayments.GetMinMasternodePaymentsProto() > 70208; - if(!txout.scriptPubKey.IsPayToPublicKeyHash() && !(fAllowData && txout.scriptPubKey.IsUnspendable())) { - LogPrintf ("CPrivateSend::IsCollateralValid -- Invalid Script, txCollateral=%s", txCollateral.ToString()); + if (!txout.scriptPubKey.IsPayToPublicKeyHash() && !txout.scriptPubKey.IsUnspendable()) { + LogPrintf("CPrivateSend::IsCollateralValid -- Invalid Script, txCollateral=%s", txCollateral.ToString()); return false; } } for (const auto& txin : txCollateral.vin) { Coin coin; - if(!GetUTXOCoin(txin.prevout, coin)) { + if (!GetUTXOCoin(txin.prevout, coin)) { LogPrint("privatesend", "CPrivateSend::IsCollateralValid -- Unknown inputs in collateral transaction, txCollateral=%s", txCollateral.ToString()); return false; } @@ -286,7 +349,7 @@ bool CPrivateSend::IsCollateralValid(const CTransaction& txCollateral) } //collateral transactions are required to pay out a small fee to the miners - if(nValueIn - nValueOut < GetCollateralAmount()) { + if (nValueIn - nValueOut < GetCollateralAmount()) { LogPrint("privatesend", "CPrivateSend::IsCollateralValid -- did not include enough fees in transaction: fees: %d, txCollateral=%s", nValueOut - nValueIn, txCollateral.ToString()); return false; } @@ -296,7 +359,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; } @@ -307,13 +370,8 @@ bool CPrivateSend::IsCollateralValid(const CTransaction& txCollateral) bool CPrivateSend::IsCollateralAmount(CAmount nInputAmount) { - if (mnpayments.GetMinMasternodePaymentsProto() > 70208) { - // collateral input can be anything between 1x and "max" (including both) - return (nInputAmount >= GetCollateralAmount() && nInputAmount <= GetMaxCollateralAmount()); - } else { // <= 70208 - // collateral input can be anything between 2x and "max" (including both) - return (nInputAmount >= GetCollateralAmount() * 2 && nInputAmount <= GetMaxCollateralAmount()); - } + // collateral input can be anything between 1x and "max" (including both) + return (nInputAmount >= GetCollateralAmount() && nInputAmount <= GetMaxCollateralAmount()); } /* Create a nice string to show the denominations @@ -331,17 +389,17 @@ std::string CPrivateSend::GetDenominationsToString(int nDenom) std::string strDenom = ""; int nMaxDenoms = vecStandardDenominations.size(); - if(nDenom >= (1 << nMaxDenoms)) { + if (nDenom >= (1 << nMaxDenoms)) { return "out-of-bounds"; } for (int i = 0; i < nMaxDenoms; ++i) { - if(nDenom & (1 << i)) { + if (nDenom & (1 << i)) { strDenom += (strDenom.empty() ? "" : "+") + FormatMoney(vecStandardDenominations[i]); } } - if(strDenom.empty()) { + if (strDenom.empty()) { return "non-denom"; } @@ -369,12 +427,12 @@ int CPrivateSend::GetDenominations(const std::vector& vecTxOut, bool fSi for (const auto& txout : vecTxOut) { bool found = false; for (auto& s : vecDenomUsed) { - if(txout.nValue == s.first) { + if (txout.nValue == s.first) { s.second = 1; found = true; } } - if(!found) return 0; + if (!found) return 0; } int nDenom = 0; @@ -383,13 +441,13 @@ int CPrivateSend::GetDenominations(const std::vector& vecTxOut, bool fSi for (const auto& s : vecDenomUsed) { int bit = (fSingleRandomDenom ? GetRandInt(2) : 1) & s.second; nDenom |= bit << c++; - if(fSingleRandomDenom && bit) break; // use just one random denomination + if (fSingleRandomDenom && bit) break; // use just one random denomination } return nDenom; } -bool CPrivateSend::GetDenominationsBits(int nDenom, std::vector &vecBitsRet) +bool CPrivateSend::GetDenominationsBits(int nDenom, std::vector& vecBitsRet) { // ( bit on if present, 4 denominations example ) // bit 0 - 100DASH+1 @@ -399,12 +457,12 @@ bool CPrivateSend::GetDenominationsBits(int nDenom, std::vector &vecBitsRet int nMaxDenoms = vecStandardDenominations.size(); - if(nDenom >= (1 << nMaxDenoms)) return false; + if (nDenom >= (1 << nMaxDenoms)) return false; vecBitsRet.clear(); for (int i = 0; i < nMaxDenoms; ++i) { - if(nDenom & (1 << i)) { + if (nDenom & (1 << i)) { vecBitsRet.push_back(i); } } @@ -417,8 +475,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); } @@ -428,7 +486,7 @@ int CPrivateSend::GetDenominationsByAmounts(const std::vector& vecAmoun bool CPrivateSend::IsDenominatedAmount(CAmount nInputAmount) { for (const auto& nDenomValue : vecStandardDenominations) - if(nInputAmount == nDenomValue) + if (nInputAmount == nDenomValue) return true; return false; } @@ -436,51 +494,73 @@ bool CPrivateSend::IsDenominatedAmount(CAmount nInputAmount) std::string CPrivateSend::GetMessageByID(PoolMessage nMessageID) { switch (nMessageID) { - case ERR_ALREADY_HAVE: return _("Already have that input."); - case ERR_DENOM: return _("No matching denominations found for mixing."); - case ERR_ENTRIES_FULL: return _("Entries are full."); - case ERR_EXISTING_TX: return _("Not compatible with existing transactions."); - case ERR_FEES: return _("Transaction fees are too high."); - case ERR_INVALID_COLLATERAL: return _("Collateral not valid."); - case ERR_INVALID_INPUT: return _("Input is not valid."); - case ERR_INVALID_SCRIPT: return _("Invalid script detected."); - case ERR_INVALID_TX: return _("Transaction not valid."); - case ERR_MAXIMUM: return _("Entry exceeds maximum size."); - case ERR_MN_LIST: return _("Not in the Masternode list."); - case ERR_MODE: return _("Incompatible mode."); - case ERR_NON_STANDARD_PUBKEY: return _("Non-standard public key detected."); - case ERR_NOT_A_MN: return _("This is not a Masternode."); // not used - case ERR_QUEUE_FULL: return _("Masternode queue is full."); - case ERR_RECENT: return _("Last PrivateSend was too recent."); - case ERR_SESSION: return _("Session not complete!"); - case ERR_MISSING_TX: return _("Missing input transaction information."); - case ERR_VERSION: return _("Incompatible version."); - 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."); + case ERR_ALREADY_HAVE: + return _("Already have that input."); + case ERR_DENOM: + return _("No matching denominations found for mixing."); + case ERR_ENTRIES_FULL: + return _("Entries are full."); + case ERR_EXISTING_TX: + return _("Not compatible with existing transactions."); + case ERR_FEES: + return _("Transaction fees are too high."); + case ERR_INVALID_COLLATERAL: + return _("Collateral not valid."); + case ERR_INVALID_INPUT: + return _("Input is not valid."); + case ERR_INVALID_SCRIPT: + return _("Invalid script detected."); + case ERR_INVALID_TX: + return _("Transaction not valid."); + case ERR_MAXIMUM: + return _("Entry exceeds maximum size."); + case ERR_MN_LIST: + return _("Not in the Masternode list."); + case ERR_MODE: + return _("Incompatible mode."); + case ERR_NON_STANDARD_PUBKEY: + return _("Non-standard public key detected."); + case ERR_NOT_A_MN: + return _("This is not a Masternode."); // not used + case ERR_QUEUE_FULL: + return _("Masternode queue is full."); + case ERR_RECENT: + return _("Last PrivateSend was too recent."); + case ERR_SESSION: + return _("Session not complete!"); + case ERR_MISSING_TX: + return _("Missing input transaction information."); + case ERR_VERSION: + return _("Incompatible version."); + case MSG_NOERR: + return _("No errors detected."); + case MSG_SUCCESS: + return _("Transaction created successfully."); + case MSG_ENTRIES_ADDED: + return _("Your entries added successfully."); + default: + return _("Unknown response."); } } -void CPrivateSend::AddDSTX(const CDarksendBroadcastTx& dstx) +void CPrivateSend::AddDSTX(const CPrivateSendBroadcastTx& dstx) { LOCK(cs_mapdstx); mapDSTX.insert(std::make_pair(dstx.tx->GetHash(), dstx)); } -CDarksendBroadcastTx CPrivateSend::GetDSTX(const uint256& hash) +CPrivateSendBroadcastTx CPrivateSend::GetDSTX(const uint256& hash) { LOCK(cs_mapdstx); auto it = mapDSTX.find(hash); - return (it == mapDSTX.end()) ? CDarksendBroadcastTx() : it->second; + return (it == mapDSTX.end()) ? CPrivateSendBroadcastTx() : it->second; } void CPrivateSend::CheckDSTXes(int nHeight) { LOCK(cs_mapdstx); - std::map::iterator it = mapDSTX.begin(); - while(it != mapDSTX.end()) { + std::map::iterator it = mapDSTX.begin(); + while (it != mapDSTX.end()) { if (it->second.IsExpired(nHeight)) { mapDSTX.erase(it++); } else { @@ -490,14 +570,14 @@ void CPrivateSend::CheckDSTXes(int nHeight) LogPrint("privatesend", "CPrivateSend::CheckDSTXes -- mapDSTX.size()=%llu\n", mapDSTX.size()); } -void CPrivateSend::UpdatedBlockTip(const CBlockIndex *pindex) +void CPrivateSend::UpdatedBlockTip(const CBlockIndex* pindex) { - if(pindex && !fLiteMode && masternodeSync.IsMasternodeListSynced()) { + if (pindex && !fLiteMode && masternodeSync.IsMasternodeListSynced()) { CheckDSTXes(pindex->nHeight); } } -void CPrivateSend::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock) +void CPrivateSend::SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int posInBlock) { if (tx.IsCoinBase()) return; @@ -508,60 +588,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 Dash specific functionality - - static bool fOneThread; - if(fOneThread) return; - fOneThread = true; - - // Make this thread recognisable as the PrivateSend thread - RenameThread("dash-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 3530c0f70d06..440da1e7a084 100644 --- a/src/privatesend.h +++ b/src/privatesend.h @@ -1,31 +1,32 @@ -// Copyright (c) 2014-2017 The Dash Core developers +// Copyright (c) 2014-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 PRIVATESEND_H #define PRIVATESEND_H +#include "bls/bls.h" #include "chain.h" #include "chainparams.h" #include "primitives/transaction.h" #include "pubkey.h" #include "sync.h" -#include "tinyformat.h" #include "timedata.h" +#include "tinyformat.h" class CPrivateSend; class CConnman; // timeouts -static const int PRIVATESEND_AUTO_TIMEOUT_MIN = 5; -static const int PRIVATESEND_AUTO_TIMEOUT_MAX = 15; -static const int PRIVATESEND_QUEUE_TIMEOUT = 30; -static const int PRIVATESEND_SIGNING_TIMEOUT = 15; +static const int PRIVATESEND_AUTO_TIMEOUT_MIN = 5; +static const int PRIVATESEND_AUTO_TIMEOUT_MAX = 15; +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 = 70208; +static const int MIN_PRIVATESEND_PEER_PROTO_VERSION = 70213; -static const size_t PRIVATESEND_ENTRY_MAX_SIZE = 9; +static const size_t PRIVATESEND_ENTRY_MAX_SIZE = 9; // pool responses enum PoolMessage { @@ -51,7 +52,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 }; @@ -87,56 +87,48 @@ class CTxDSIn : public CTxIn CTxIn(txin), prevPubKey(script), fHasSig(false) - {} + { + } CTxDSIn() : CTxIn(), prevPubKey(), fHasSig(false) - {} + { + } }; -class CDarksendAccept +class CPrivateSendAccept { public: int nDenom; - int nInputCount; CMutableTransaction txCollateral; - CDarksendAccept() : + CPrivateSendAccept() : nDenom(0), - nInputCount(0), - txCollateral(CMutableTransaction()) - {}; + txCollateral(CMutableTransaction()){}; - CDarksendAccept(int nDenom, int nInputCount, const CMutableTransaction& txCollateral) : + CPrivateSendAccept(int nDenom, const CMutableTransaction& txCollateral) : nDenom(nDenom), - nInputCount(nInputCount), - txCollateral(txCollateral) - {}; + txCollateral(txCollateral){}; ADD_SERIALIZE_METHODS; template - inline void SerializationOp(Stream& s, Operation ser_action) { + inline void SerializationOp(Stream& s, Operation ser_action) + { READWRITE(nDenom); - int nVersion = s.GetVersion(); - if (nVersion > 70208) { - READWRITE(nInputCount); - } else if (ser_action.ForRead()) { - nInputCount = 0; - } READWRITE(txCollateral); } - friend bool operator==(const CDarksendAccept& a, const CDarksendAccept& b) + friend bool operator==(const CPrivateSendAccept& a, const CPrivateSendAccept& b) { return a.nDenom == b.nDenom && a.txCollateral == b.txCollateral; } }; // A clients transaction in the mixing pool -class CDarkSendEntry +class CPrivateSendEntry { public: std::vector vecTxDSIn; @@ -145,24 +137,27 @@ class CDarkSendEntry // memory only CService addr; - CDarkSendEntry() : + CPrivateSendEntry() : vecTxDSIn(std::vector()), vecTxOut(std::vector()), txCollateral(MakeTransactionRef()), addr(CService()) - {} + { + } - CDarkSendEntry(const std::vector& vecTxDSIn, const std::vector& vecTxOut, const CTransaction& txCollateral) : + CPrivateSendEntry(const std::vector& vecTxDSIn, const std::vector& vecTxOut, const CTransaction& txCollateral) : vecTxDSIn(vecTxDSIn), vecTxOut(vecTxOut), txCollateral(MakeTransactionRef(txCollateral)), addr(CService()) - {} + { + } ADD_SERIALIZE_METHODS; template - inline void SerializationOp(Stream& s, Operation ser_action) { + inline void SerializationOp(Stream& s, Operation ser_action) + { READWRITE(vecTxDSIn); READWRITE(txCollateral); READWRITE(vecTxOut); @@ -175,11 +170,10 @@ class CDarkSendEntry /** * A currently in progress mixing merge and denomination information */ -class CDarksendQueue +class CPrivateSendQueue { public: int nDenom; - int nInputCount; COutPoint masternodeOutpoint; int64_t nTime; bool fReady; //ready for submit @@ -187,51 +181,33 @@ class CDarksendQueue // memory only bool fTried; - CDarksendQueue() : + CPrivateSendQueue() : nDenom(0), - nInputCount(0), masternodeOutpoint(COutPoint()), nTime(0), fReady(false), vchSig(std::vector()), fTried(false) - {} + { + } - CDarksendQueue(int nDenom, int nInputCount, COutPoint outpoint, int64_t nTime, bool fReady) : + CPrivateSendQueue(int nDenom, COutPoint outpoint, int64_t nTime, bool fReady) : nDenom(nDenom), - nInputCount(nInputCount), masternodeOutpoint(outpoint), nTime(nTime), fReady(fReady), vchSig(std::vector()), fTried(false) - {} + { + } ADD_SERIALIZE_METHODS; template - inline void SerializationOp(Stream& s, Operation ser_action) { + inline void SerializationOp(Stream& s, Operation ser_action) + { READWRITE(nDenom); - int nVersion = s.GetVersion(); - if (nVersion > 70208) { - 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)) { @@ -249,28 +225,28 @@ 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); + bool Relay(CConnman& connman); /// Is this queue expired? bool IsExpired() { return GetAdjustedTime() - nTime > PRIVATESEND_QUEUE_TIMEOUT; } 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) + friend bool operator==(const CPrivateSendQueue& a, const CPrivateSendQueue& 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; } }; /** Helper class to store mixing transaction (tx) information. */ -class CDarksendBroadcastTx +class CPrivateSendBroadcastTx { private: // memory only @@ -283,82 +259,68 @@ class CDarksendBroadcastTx std::vector vchSig; int64_t sigTime; - CDarksendBroadcastTx() : + CPrivateSendBroadcastTx() : nConfirmedHeight(-1), tx(MakeTransactionRef()), masternodeOutpoint(), vchSig(), sigTime(0) - {} + { + } - CDarksendBroadcastTx(const CTransactionRef& _tx, COutPoint _outpoint, int64_t _sigTime) : + CPrivateSendBroadcastTx(const CTransactionRef& _tx, COutPoint _outpoint, int64_t _sigTime) : nConfirmedHeight(-1), tx(_tx), masternodeOutpoint(_outpoint), vchSig(), sigTime(_sigTime) - {} + { + } ADD_SERIALIZE_METHODS; template - inline void SerializationOp(Stream& s, Operation ser_action) { + 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); } READWRITE(sigTime); } - friend bool operator==(const CDarksendBroadcastTx& a, const CDarksendBroadcastTx& b) + friend bool operator==(const CPrivateSendBroadcastTx& a, const CPrivateSendBroadcastTx& b) { return *a.tx == *b.tx; } - friend bool operator!=(const CDarksendBroadcastTx& a, const CDarksendBroadcastTx& b) + friend bool operator!=(const CPrivateSendBroadcastTx& a, const CPrivateSendBroadcastTx& b) { return !(a == b); } explicit operator bool() const { - return *this != CDarksendBroadcastTx(); + return *this != CPrivateSendBroadcastTx(); } 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; + mutable CCriticalSection cs_privatesend; - std::vector vecEntries; // Masternode/clients entries + std::vector vecEntries; // Masternode/clients entries - PoolState nState; // should be one of the POOL_STATE_XXX values + PoolState nState; // should be one of the POOL_STATE_XXX values int64_t nTimeLastSuccessfulStep; // the time when last successful mixing step was performed int nSessionID; // 0 if no mixing session is active @@ -366,21 +328,46 @@ 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 vecPrivateSendQueue; + + void SetNull(); + void CheckQueue(); + +public: + CPrivateSendBaseManager() : + vecPrivateSendQueue() {} + + int GetQueueSize() const { return vecPrivateSendQueue.size(); } + bool GetQueueItemAndTry(CPrivateSendQueue& dsqRet); +}; + // helper class class CPrivateSend { @@ -389,13 +376,11 @@ class CPrivateSend CPrivateSend() {} ~CPrivateSend() {} CPrivateSend(CPrivateSend const&) = delete; - CPrivateSend& operator= (CPrivateSend const&) = delete; - - static const CAmount COLLATERAL = 0.001 * COIN; + CPrivateSend& operator=(CPrivateSend const&) = delete; // static members static std::vector vecStandardDenominations; - static std::map mapDSTX; + static std::map mapDSTX; static CCriticalSection cs_mapdstx; @@ -414,7 +399,7 @@ class CPrivateSend /// Get the denominations for a list of outputs (returns a bitshifted integer) static int GetDenominations(const std::vector& vecTxOut, bool fSingleRandomDenom = false); static std::string GetDenominationsToString(int nDenom); - static bool GetDenominationsBits(int nDenom, std::vector &vecBitsRet); + static bool GetDenominationsBits(int nDenom, std::vector& vecBitsRet); static std::string GetMessageByID(PoolMessage nMessageID); @@ -425,18 +410,16 @@ class CPrivateSend /// If the collateral is valid given by a client static bool IsCollateralValid(const CTransaction& txCollateral); - static CAmount GetCollateralAmount() { return COLLATERAL; } - static CAmount GetMaxCollateralAmount() { return COLLATERAL*4; } + static CAmount GetCollateralAmount() { return GetSmallestDenomination() / 10; } + static CAmount GetMaxCollateralAmount() { return GetCollateralAmount() * 4; } static bool IsCollateralAmount(CAmount nInputAmount); - static void AddDSTX(const CDarksendBroadcastTx& dstx); - static CDarksendBroadcastTx GetDSTX(const uint256& hash); + static void AddDSTX(const CPrivateSendBroadcastTx& dstx); + static CPrivateSendBroadcastTx GetDSTX(const uint256& hash); - static void UpdatedBlockTip(const CBlockIndex *pindex); - static void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock); + static void UpdatedBlockTip(const CBlockIndex* pindex); + 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 4cd85e24de88..fa1ccaf9ce77 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -69,6 +69,11 @@ const char *MNGOVERNANCESYNC="govsync"; const char *MNGOVERNANCEOBJECT="govobj"; const char *MNGOVERNANCEOBJECTVOTE="govobjvote"; const char *MNVERIFY="mnv"; +const char *GETMNLISTDIFF="getmnlistd"; +const char *MNLISTDIFF="mnlistdiff"; +const char *QFCOMMITMENT="qfcommit"; +const char *QDCOMMITMENT="qdcommit"; +const char *QCONTRIB="qcontrib"; }; static const char* ppszTypeName[] = @@ -96,6 +101,9 @@ static const char* ppszTypeName[] = NetMsgType::MNGOVERNANCEOBJECTVOTE, NetMsgType::MNVERIFY, "compact block", // Should never occur + NetMsgType::QFCOMMITMENT, + NetMsgType::QDCOMMITMENT, + NetMsgType::QCONTRIB, }; /** All known message types. Keep this in the same order as the list of @@ -153,6 +161,11 @@ const static std::string allNetMessageTypes[] = { NetMsgType::MNGOVERNANCEOBJECT, NetMsgType::MNGOVERNANCEOBJECTVOTE, NetMsgType::MNVERIFY, + NetMsgType::GETMNLISTDIFF, + NetMsgType::MNLISTDIFF, + NetMsgType::QFCOMMITMENT, + NetMsgType::QDCOMMITMENT, + NetMsgType::QCONTRIB, }; const static std::vector allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes)); diff --git a/src/protocol.h b/src/protocol.h index dc7389dc8878..ba1b25c45675 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -268,6 +268,11 @@ extern const char *MNGOVERNANCESYNC; extern const char *MNGOVERNANCEOBJECT; extern const char *MNGOVERNANCEOBJECTVOTE; extern const char *MNVERIFY; +extern const char *GETMNLISTDIFF; +extern const char *MNLISTDIFF; +extern const char *QFCOMMITMENT; +extern const char *QDCOMMITMENT; +extern const char *QCONTRIB; }; /* Get a vector of all valid message types (see above) */ @@ -369,6 +374,9 @@ enum GetDataMsg { // Nodes may always request a MSG_CMPCT_BLOCK in a getdata, however, // MSG_CMPCT_BLOCK should not appear in any invs except as a part of getdata. MSG_CMPCT_BLOCK = 20, //!< Defined in BIP152 + MSG_QUORUM_FINAL_COMMITMENT = 21, + MSG_QUORUM_DUMMY_COMMITMENT = 22, // only valid on testnet/devnet/regtest + MSG_QUORUM_DUMMY_CONTRIBUTION = 23, // not a valid contribution and only allowed on testnet/devnet/regtest. Will later be replaced with the real contribution }; /** inv message data */ diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 63a8a157bedd..07ff4ddcbb7a 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -84,7 +84,7 @@ QString ClientModel::getMasternodeCountString() const { // return tr("Total: %1 (PS compatible: %2 / Enabled: %3) (IPv4: %4, IPv6: %5, TOR: %6)").arg(QString::number((int)mnodeman.size())) return tr("Total: %1 (PS compatible: %2 / Enabled: %3)") - .arg(QString::number((int)mnodeman.size())) + .arg(QString::number((int)mnodeman.CountMasternodes(0))) .arg(QString::number((int)mnodeman.CountEnabled(MIN_PRIVATESEND_PEER_PROTO_VERSION))) .arg(QString::number((int)mnodeman.CountEnabled())); // .arg(QString::number((int)mnodeman.CountByIP(NET_IPV4))) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index ae07319657f6..85c0062ad6db 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -433,10 +433,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; } @@ -569,7 +569,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); @@ -781,7 +781,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.cpp b/src/qt/darksendconfig.cpp deleted file mode 100644 index ceb7ec3f79ac..000000000000 --- a/src/qt/darksendconfig.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "darksendconfig.h" -#include "ui_darksendconfig.h" - -#include "bitcoinunits.h" -#include "guiconstants.h" -#include "optionsmodel.h" -#include "privatesend-client.h" -#include "walletmodel.h" - -#include -#include -#include -#include - -DarksendConfig::DarksendConfig(QWidget *parent) : - QDialog(parent), - ui(new Ui::DarksendConfig), - model(0) -{ - ui->setupUi(this); - - connect(ui->buttonBasic, SIGNAL(clicked()), this, SLOT(clickBasic())); - connect(ui->buttonHigh, SIGNAL(clicked()), this, SLOT(clickHigh())); - connect(ui->buttonMax, SIGNAL(clicked()), this, SLOT(clickMax())); -} - -DarksendConfig::~DarksendConfig() -{ - delete ui; -} - -void DarksendConfig::setModel(WalletModel *model) -{ - this->model = model; -} - -void DarksendConfig::clickBasic() -{ - configure(true, 1000, 2); - - QString strAmount(BitcoinUnits::formatWithUnit( - model->getOptionsModel()->getDisplayUnit(), 1000 * COIN)); - QMessageBox::information(this, tr("PrivateSend Configuration"), - tr( - "PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen." - ).arg(strAmount) - ); - - close(); -} - -void DarksendConfig::clickHigh() -{ - configure(true, 1000, 8); - - QString strAmount(BitcoinUnits::formatWithUnit( - model->getOptionsModel()->getDisplayUnit(), 1000 * COIN)); - QMessageBox::information(this, tr("PrivateSend Configuration"), - tr( - "PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen." - ).arg(strAmount) - ); - - close(); -} - -void DarksendConfig::clickMax() -{ - configure(true, 1000, 16); - - QString strAmount(BitcoinUnits::formatWithUnit( - model->getOptionsModel()->getDisplayUnit(), 1000 * COIN)); - QMessageBox::information(this, tr("PrivateSend Configuration"), - tr( - "PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen." - ).arg(strAmount) - ); - - close(); -} - -void DarksendConfig::configure(bool enabled, int coins, int rounds) { - - QSettings settings; - - settings.setValue("nPrivateSendRounds", rounds); - settings.setValue("nPrivateSendAmount", coins); - - privateSendClient.nPrivateSendRounds = rounds; - privateSendClient.nPrivateSendAmount = coins; -} 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/dash_locale.qrc b/src/qt/dash_locale.qrc index 542d2059d9a9..91ccd5100092 100644 --- a/src/qt/dash_locale.qrc +++ b/src/qt/dash_locale.qrc @@ -1,5 +1,6 @@ + locale/dash_ar.qm locale/dash_bg.qm locale/dash_de.qm locale/dash_en.qm @@ -8,12 +9,15 @@ locale/dash_fr.qm locale/dash_it.qm locale/dash_ja.qm + locale/dash_ko.qm locale/dash_nl.qm locale/dash_pl.qm locale/dash_pt.qm locale/dash_ru.qm locale/dash_sk.qm locale/dash_sv.qm + locale/dash_th.qm + locale/dash_tr.qm locale/dash_vi.qm locale/dash_zh_CN.qm locale/dash_zh_TW.qm diff --git a/src/qt/dashstrings.cpp b/src/qt/dashstrings.cpp index ac3318ba33af..7fe00c06893d 100644 --- a/src/qt/dashstrings.cpp +++ b/src/qt/dashstrings.cpp @@ -12,6 +12,9 @@ static const char UNUSED *dash_strings[] = { QT_TRANSLATE_NOOP("dash-core", "Dash Core"), QT_TRANSLATE_NOOP("dash-core", "The %s developers"), QT_TRANSLATE_NOOP("dash-core", "" +"%s file contains all private keys from this wallet. Do not share it with " +"anyone!"), +QT_TRANSLATE_NOOP("dash-core", "" "(1 = keep tx meta data e.g. account owner and payment request information, 2 " "= drop tx meta data)"), QT_TRANSLATE_NOOP("dash-core", "" @@ -91,6 +94,9 @@ QT_TRANSLATE_NOOP("dash-core", "" "Execute command when the best block changes (%s in cmd is replaced by block " "hash)"), QT_TRANSLATE_NOOP("dash-core", "" +"Extra transactions to keep in memory for compact block reconstructions " +"(default: %u)"), +QT_TRANSLATE_NOOP("dash-core", "" "Failed to create backup, file already exists! This could happen if you " "restarted wallet in less than 60 seconds. You can continue if you are ok " "with this."), @@ -212,9 +218,6 @@ QT_TRANSLATE_NOOP("dash-core", "" "Set the number of script verification threads (%u to %d, 0 = auto, <0 = " "leave that many cores free, default: %d)"), QT_TRANSLATE_NOOP("dash-core", "" -"Show N confirmations for a successfully locked transaction (%u-%u, default: " -"%u)"), -QT_TRANSLATE_NOOP("dash-core", "" "Specify full path to directory for automatic wallet backups (must exist)"), QT_TRANSLATE_NOOP("dash-core", "" "Support filtering of blocks and transaction with bloom filters (default: %u)"), @@ -238,6 +241,9 @@ QT_TRANSLATE_NOOP("dash-core", "" "Total length of network version string (%i) exceeds maximum length (%i). " "Reduce the number or size of uacomments."), QT_TRANSLATE_NOOP("dash-core", "" +"Transaction index can't be disabled in full mode. Either start with -" +"litemode command line switch or enable transaction index."), +QT_TRANSLATE_NOOP("dash-core", "" "Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = " "no limit (default: %d)"), QT_TRANSLATE_NOOP("dash-core", "" @@ -312,6 +318,8 @@ QT_TRANSLATE_NOOP("dash-core", "" "Whitelisted peers cannot be DoS banned and their transactions are always " "relayed, even if they are already in the mempool, useful e.g. for a gateway"), QT_TRANSLATE_NOOP("dash-core", "" +"You are starting in lite mode, all Dash-specific functionality is disabled."), +QT_TRANSLATE_NOOP("dash-core", "" "You must specify a masternodeprivkey in the configuration. Please see " "documentation for help."), QT_TRANSLATE_NOOP("dash-core", "" @@ -366,6 +374,8 @@ QT_TRANSLATE_NOOP("dash-core", "Do you want to rebuild the block database now?") QT_TRANSLATE_NOOP("dash-core", "Done loading"), QT_TRANSLATE_NOOP("dash-core", "ERROR! Failed to create automatic backup"), QT_TRANSLATE_NOOP("dash-core", "Enable publish hash block in
"), +QT_TRANSLATE_NOOP("dash-core", "Enable publish hash of governance objects (like proposals) in
"), +QT_TRANSLATE_NOOP("dash-core", "Enable publish hash of governance votes in
"), QT_TRANSLATE_NOOP("dash-core", "Enable publish hash transaction (locked via InstantSend) in
"), QT_TRANSLATE_NOOP("dash-core", "Enable publish hash transaction in
"), QT_TRANSLATE_NOOP("dash-core", "Enable publish raw block in
"), @@ -393,10 +403,12 @@ QT_TRANSLATE_NOOP("dash-core", "Failed to create backup, error: %s"), QT_TRANSLATE_NOOP("dash-core", "Failed to delete backup, error: %s"), QT_TRANSLATE_NOOP("dash-core", "Failed to find mixing queue to join"), QT_TRANSLATE_NOOP("dash-core", "Failed to listen on any port. Use -listen=0 if you want this."), +QT_TRANSLATE_NOOP("dash-core", "Failed to load InstantSend data cache from"), QT_TRANSLATE_NOOP("dash-core", "Failed to load fulfilled requests cache from"), QT_TRANSLATE_NOOP("dash-core", "Failed to load governance cache from"), QT_TRANSLATE_NOOP("dash-core", "Failed to load masternode cache from"), QT_TRANSLATE_NOOP("dash-core", "Failed to load masternode payments cache from"), +QT_TRANSLATE_NOOP("dash-core", "Failed to load sporks cache from"), QT_TRANSLATE_NOOP("dash-core", "Failed to parse host:port string"), QT_TRANSLATE_NOOP("dash-core", "Failed to start a new mixing queue"), QT_TRANSLATE_NOOP("dash-core", "Fee (in %s/kB) to add to transactions you send (default: %s)"), @@ -419,6 +431,7 @@ QT_TRANSLATE_NOOP("dash-core", "Invalid -proxy address: '%s'"), QT_TRANSLATE_NOOP("dash-core", "Invalid amount for -%s=: '%s'"), QT_TRANSLATE_NOOP("dash-core", "Invalid amount for -fallbackfee=: '%s'"), QT_TRANSLATE_NOOP("dash-core", "Invalid amount for -paytxfee=: '%s' (must be at least %s)"), +QT_TRANSLATE_NOOP("dash-core", "Invalid input count."), QT_TRANSLATE_NOOP("dash-core", "Invalid masternodeprivkey. Please see documenation."), QT_TRANSLATE_NOOP("dash-core", "Invalid netmask specified in -whitelist: '%s'"), QT_TRANSLATE_NOOP("dash-core", "Invalid port detected in masternode.conf"), @@ -435,6 +448,7 @@ QT_TRANSLATE_NOOP("dash-core", "Last successful PrivateSend action was too recen QT_TRANSLATE_NOOP("dash-core", "Line: %d"), QT_TRANSLATE_NOOP("dash-core", "Listen for JSON-RPC connections on (default: %u or testnet: %u)"), QT_TRANSLATE_NOOP("dash-core", "Listen for connections on (default: %u or testnet: %u)"), +QT_TRANSLATE_NOOP("dash-core", "Loading InstantSend data cache..."), QT_TRANSLATE_NOOP("dash-core", "Loading addresses..."), QT_TRANSLATE_NOOP("dash-core", "Loading banlist..."), QT_TRANSLATE_NOOP("dash-core", "Loading block index..."), @@ -442,6 +456,7 @@ QT_TRANSLATE_NOOP("dash-core", "Loading fulfilled requests cache..."), QT_TRANSLATE_NOOP("dash-core", "Loading governance cache..."), QT_TRANSLATE_NOOP("dash-core", "Loading masternode cache..."), QT_TRANSLATE_NOOP("dash-core", "Loading masternode payment cache..."), +QT_TRANSLATE_NOOP("dash-core", "Loading sporks cache..."), QT_TRANSLATE_NOOP("dash-core", "Loading wallet... (%3.2f %%)"), QT_TRANSLATE_NOOP("dash-core", "Loading wallet..."), QT_TRANSLATE_NOOP("dash-core", "Location of the auth cookie (default: data dir)"), @@ -545,6 +560,7 @@ QT_TRANSLATE_NOOP("dash-core", "Transaction has too long of a mempool chain"), QT_TRANSLATE_NOOP("dash-core", "Transaction must have at least one recipient"), QT_TRANSLATE_NOOP("dash-core", "Transaction not valid."), QT_TRANSLATE_NOOP("dash-core", "Transaction too large for fee policy"), +QT_TRANSLATE_NOOP("dash-core", "Transaction too large"), QT_TRANSLATE_NOOP("dash-core", "Trying to connect..."), QT_TRANSLATE_NOOP("dash-core", "Unable to bind to %s on this computer (bind returned error %s)"), QT_TRANSLATE_NOOP("dash-core", "Unable to bind to %s on this computer. %s is probably already running."), @@ -570,6 +586,7 @@ QT_TRANSLATE_NOOP("dash-core", "Very low number of keys left: %d"), QT_TRANSLATE_NOOP("dash-core", "Wallet %s resides outside data directory %s"), QT_TRANSLATE_NOOP("dash-core", "Wallet debugging/testing options:"), QT_TRANSLATE_NOOP("dash-core", "Wallet is locked."), +QT_TRANSLATE_NOOP("dash-core", "Wallet is not initialized"), QT_TRANSLATE_NOOP("dash-core", "Wallet needed to be rewritten: restart %s to complete"), QT_TRANSLATE_NOOP("dash-core", "Wallet options:"), QT_TRANSLATE_NOOP("dash-core", "Wallet window title"), @@ -578,6 +595,7 @@ QT_TRANSLATE_NOOP("dash-core", "Warning: unknown new rules activated (versionbit QT_TRANSLATE_NOOP("dash-core", "Wasn't able to create wallet backup folder %s!"), QT_TRANSLATE_NOOP("dash-core", "Whether to operate in a blocks only mode (default: %u)"), QT_TRANSLATE_NOOP("dash-core", "Will retry..."), +QT_TRANSLATE_NOOP("dash-core", "You can not start a masternode in lite mode."), QT_TRANSLATE_NOOP("dash-core", "Your entries added successfully."), QT_TRANSLATE_NOOP("dash-core", "Your transaction was accepted into the pool!"), QT_TRANSLATE_NOOP("dash-core", "Zapping all transactions from wallet..."), diff --git a/src/qt/forms/darksendconfig.ui b/src/qt/forms/darksendconfig.ui deleted file mode 100644 index 4ac909fbf6e7..000000000000 --- a/src/qt/forms/darksendconfig.ui +++ /dev/null @@ -1,188 +0,0 @@ - - - DarksendConfig - - - - 0 - 0 - 630 - 307 - - - - Configure PrivateSend - - - - - 20 - 70 - 151 - 27 - - - - Basic Privacy - - - - - - 20 - 140 - 151 - 27 - - - - High Privacy - - - - - - 20 - 210 - 151 - 27 - - - - Maximum Privacy - - - - - - 30 - 20 - 571 - 31 - - - - Please select a privacy level. - - - - - - 190 - 70 - 421 - 21 - - - - Use 2 separate masternodes to mix funds up to 1000 DASH - - - - - - 190 - 140 - 411 - 21 - - - - Use 8 separate masternodes to mix funds up to 1000 DASH - - - - - - 190 - 210 - 421 - 21 - - - - Use 16 separate masternodes - - - - - - 40 - 100 - 561 - 21 - - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - - - - - - 40 - 170 - 561 - 21 - - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - - - - - - 40 - 240 - 561 - 21 - - - - This is the slowest and most secure option. Using maximum anonymity will cost - - - - - - 40 - 260 - 561 - 21 - - - - 0.1 DASH per 1000 DASH you anonymize. - - - - - - 10 - 120 - 601 - 16 - - - - Qt::Horizontal - - - - - - 10 - 190 - 601 - 16 - - - - Qt::Horizontal - - - - - - diff --git a/src/qt/forms/masternodelist.ui b/src/qt/forms/masternodelist.ui index 992d6c72d5af..f479a608d9e7 100644 --- a/src/qt/forms/masternodelist.ui +++ b/src/qt/forms/masternodelist.ui @@ -217,7 +217,55 @@ All Masternodes - + + + + 0 + + + + + Filter List: + + + + + + + Filter masternode list + + + + + + + Qt::Horizontal + + + + 10 + 20 + + + + + + + + Node Count: + + + + + + + 0 + + + + + + QAbstractItemView::NoEditTriggers @@ -270,26 +318,46 @@ - + + + <html><head/><body><p>Note: This list represents the legacy and non-deterministic masternode list. It is only active as long as DIP3 has not been fully activated. After SPORK15 activation, this list will be empty.</p></body></html> + + + Qt::AutoText + + + true + + + + + + + + DIP3 Masternodes + + + + 0 - + Filter List: - + Filter masternode list - + Qt::Horizontal @@ -302,14 +370,14 @@ - + Node Count: - + 0 @@ -317,6 +385,81 @@ + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + true + + + + Address + + + + + Status + + + + + PoSe Score + + + + + Registered + + + + + Last Paid + + + + + Next Payment + + + + + Payee + + + + + Operator Reward + + + + + + + + <html><head/><body><p>Note: This list is not active yet and only for informational purposes. The network is still running in compatibility mode, which means that the non-deterministic masternode list is still active. Only after SPORK15 activation, this list will become the active one. Also, the values in the payment related fields are not used at the moment, but still updated for every block. Please ignore this until SPORK15 activation.</p></body></html> + + + Qt::AutoText + + + true + + + diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index d4df3ba2fe0f..f0cf50166516 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -173,6 +173,16 @@ + + + + Show system popups for PrivateSend mixing transactions<br/>just like for all other transaction types. + + + Show popups for PrivateSend transactions + + + @@ -224,7 +234,7 @@ 2 - 8 + 16 4 diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index d1d6ae3c4221..e19910f2aecf 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -34,6 +34,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/locale/dash_ar.ts b/src/qt/locale/dash_ar.ts new file mode 100644 index 000000000000..5f588b8305f1 --- /dev/null +++ b/src/qt/locale/dash_ar.ts @@ -0,0 +1,5253 @@ + + + AddressBookPage + + Right-click to edit address or label + انقر بالزر الايمن لتعديل العنوان + + + Create a new address + انشاء عنوان جديد + + + &New + جديد + + + Copy the currently selected address to the system clipboard + قم بنسخ العنوان المختار لحافظة النظام + + + &Copy + نسخ + + + Delete the currently selected address from the list + حذف العنوان المحدد من القائمة + + + &Delete + حذف + + + Export the data in the current tab to a file + تحميل البيانات في علامة التبويب الحالية إلى ملف + + + &Export + تصدير + + + C&lose + غلق + + + Choose the address to send coins to + اختر العنوان الذي سترسل له العملات + + + Choose the address to receive coins with + اختر العنوان الذي تستقبل عليه العملات + + + C&hoose + اختر + + + Sending addresses + عناوين الإرسال + + + Receiving addresses + عناوين الاستقبال + + + These are your Dash addresses for sending payments. Always check the amount and the receiving address before sending coins. + هذه هي عناوين داش التابعة لك من أجل إرسال الدفعات. تحقق دائما من المبلغ و عنوان المرسل المستقبل قبل إرسال العملات + + + These are your Dash addresses for receiving payments. It is recommended to use a new receiving address for each transaction. + هذه هي عناوين داش التابعة لك من أجل إستقبال الدفعات. ينصح استخدام عنوان جديد من أجل كل صفقة + + + &Copy Address + انسخ العنوان + + + Copy &Label + نسخ الوصف + + + &Edit + تعديل + + + Export Address List + تصدير قائمة العناوين + + + Comma separated file (*.csv) + ملف مفصول بفواصل (*.csv) + + + Exporting Failed + فشل التصدير + + + There was an error trying to save the address list to %1. Please try again. + لقد حدث خطأ أثناء حفظ قائمة العناوين إلى %1. يرجى المحاولة مرة أخرى. + + + + AddressTableModel + + Label + وصف + + + Address + عنوان + + + (no label) + (لا وصف) + + + + AskPassphraseDialog + + Passphrase Dialog + حوار جملة السر + + + Enter passphrase + ادخل جملة السر + + + New passphrase + جملة السر جديدة + + + Repeat new passphrase + اعد كتابة جملة السر الجديدة + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>. + أدخل عبارة مرور جديدة إلى المحفظة. الرجاء استخدام عبارة مرور تتكون من10 حروف عشوائية على الاقل, أو ثمانية كلمات على الاقل + + + Encrypt wallet + تشفير المحفظة + + + This operation needs your wallet passphrase to unlock the wallet. + هذه العملية تحتاج كلمة مرور محفظتك لفتحها + + + Unlock wallet for mixing only + افتح المحفظة لدمج فقط + + + Unlock wallet + إفتح المحفظة + + + This operation needs your wallet passphrase to decrypt the wallet. + هذه العملية تحتاج كلمة مرور محفظتك لفك تشفيرها + + + Decrypt wallet + فك تشفير المحفظة + + + Change passphrase + تغيير كلمة المرور + + + Enter the old passphrase and new passphrase to the wallet. + أدخل كلمة المرور القديمة والجديدة للمحفظة + + + Confirm wallet encryption + تأكيد تشفير المحفظة + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR DASH</b>! + تحذير: إذا قمت بتشفير محفظتك وفقدت كلمة المرور الخاص بك, ستفقد كل عملات داش الخاصة بك + + + Are you sure you wish to encrypt your wallet? + هل أنت متأكد من رغبتك في تشفير محفظتك ؟ + + + Wallet encrypted + محفظة مشفرة + + + %1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your funds from being stolen by malware infecting your computer. + %1 سوف يغلق الآن لإنهاء عملية التشفير. تذكر أن تشفير محفظتك لا يمكنه حماية أموالك بالكامل من التعرض للسرقة بسبب البرامج الضارة التي تصيب جهاز الكمبيوتر الخاص بك. + + + IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. Previous backups of the unencrypted wallet file contain the same HD seed and still have full access to all your funds just like the new, encrypted wallet. + مهم:يجب استبدال أي نسخ احتياطية سابقة قمت بها من ملف المحفظة بملف المحفظة المشفر حديثًا. تحتوي النسخ الاحتياطية السابقة لملف المحفظة غير المشفرة على نفس البذرة عالية الدقة ، ولا تزال تتمتع بالوصول الكامل إلى جميع أموالك مثل المحفظة الجديدة المشفرة. + + + IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet. + هام: أي نسخة إحتياطية سابقة قمت بها لمحفظتك يجب استبدالها بأخرى حديثة، مشفرة. لأسباب أمنية، النسخ الاحتياطية السابقة لملفات المحفظة الغير مشفرة تصبح عديمة الفائدة مع بداية استخدام المحفظة المشفرة الجديدة + + + Wallet encryption failed + فشل تشفير المحفظة + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + فشل تشفير المحفظة بسبب خطأ داخلي. لم يتم تشفير محفظتك + + + The supplied passphrases do not match. + كلمتي المرور ليستا متطابقتان + + + Wallet unlock failed + فشل فتح المحفظة + + + The passphrase entered for the wallet decryption was incorrect. + كلمة المرور التي تم إدخالها لفك تشفير المحفظة غير صحيحة + + + Wallet decryption failed + فشل فك التشفير المحفظة + + + Wallet passphrase was successfully changed. + لقد تم تغير عبارة مرور المحفظة بنجاح + + + Warning: The Caps Lock key is on! + تحذير: مفتاح الحروف الكبيرة مفعل + + + + BanTableModel + + IP/Netmask + عنوان البروتوكول/مخفي + + + Banned Until + محظور حتى + + + + BitcoinGUI + + A fatal error occurred. Dash Core can no longer continue safely and will quit. + خطأ فادح حدث . لا يمكن اتمام داش بامان سيتم الخروج + + + Dash Core + جوهر الداش + + + Wallet + محفظة + + + Node + جهاز + + + &Overview + نظرة عامة + + + Show general overview of wallet + إظهار نظرة عامة على المحفظة + + + &Send + ارسل + + + Send coins to a Dash address + ارسل عملات الى عنوان داش + + + &Receive + استقبل + + + Request payments (generates QR codes and dash: URIs) + أطلب دفعات (يولد كودات الرمز المربع وبيت كوين: العناوين المعطاة) + + + &Transactions + المعاملات + + + Browse transaction history + تصفح سجل المعاملات + + + &Masternodes + العقد الرئيسية + + + Browse masternodes + تصفح العقد الرئيسية + + + E&xit + خروج + + + Quit application + الخروج من التطبيق + + + Show information about Dash Core + أظهر المعلومات حولة داش الأساسية + + + About &Qt + عن Qt + + + Show information about Qt + اظهر المعلومات حول Qt + + + &Options... + خيارات + + + &About %1 + حوالي %1 + + + Modify configuration options for %1 + تغيير خيارات الإعداد لأساس ل%1 + + + &Show / Hide + عرض / اخفاء + + + Show or hide the main Window + عرض او اخفاء النافذة الرئيسية + + + &Encrypt Wallet... + تشفير المحفظة + + + Encrypt the private keys that belong to your wallet + تشفير المفتاح الخاص بمحفظتك + + + &Backup Wallet... + احتياط للمحفظة + + + Backup wallet to another location + احفظ نسخة احتياطية للمحفظة في مكان آخر + + + &Change Passphrase... + تغيير كلمة المرور + + + Change the passphrase used for wallet encryption + تغيير كلمة المرور المستخدمة لتشفير المحفظة + + + &Unlock Wallet... + إفتح المحفظة + + + Unlock wallet + إفتح المحفظة + + + &Lock Wallet + غلق المحفظة + + + Sign &message... + توقيع الرسالة + + + Sign messages with your Dash addresses to prove you own them + وقَع الرسائل بواسطة اداش الخاص بك لإثبات امتلاكك لهم + + + &Verify message... + التحقق من الرسالة + + + Verify messages to ensure they were signed with specified Dash addresses + تحقق من الرسائل للتأكد من أنَها وُقعت برسائل داش محدَدة + + + &Information + معلومات + + + Show diagnostic information + عرض معلومات التشخيص + + + &Debug console + وحدة تصحيح الأخطاء + + + Open debugging console + فتح وحدة التحكم في التصحيح + + + &Network Monitor + مراقبة الشبكة + + + Show network monitor + إظهار شاشة الشبكة + + + &Peers list + قائمة الزملاء + + + Show peers info + عرض معلومات الزملاء + + + Wallet &Repair + المحفظة والإصلاح + + + Show wallet repair options + عرض خيارات إصلاح المحفظة + + + Open Wallet &Configuration File + فتح ملف المحفظة والتهيئة + + + Open configuration file + افتح ملف التهيئة + + + Open &Masternode Configuration File + فتح ملف تكوين العقدة الرئيسية + + + Open Masternode configuration file + افتح ملف تهيئة العقدة الرئيسية + + + Show Automatic &Backups + عرض النسخ الاحتياطية التلقائية + + + Show automatically created wallet backups + إظهار النسخ الاحتياطية المحفظة تلقائيا إنشاء + + + &Sending addresses... + عناوين الإرسال... + + + Show the list of used sending addresses and labels + عرض قائمة عناوين الإرسال المستخدمة والملصقات + + + &Receiving addresses... + عناوين الاستقبال... + + + Show the list of used receiving addresses and labels + عرض قائمة عناوين الإستقبال المستخدمة والملصقات + + + Open &URI... + فتح URI + + + Open a dash: URI or payment request + افتح شرطة: عنوان أوري أو طلب الدفع + + + &Command-line options + خيارات سطر الأوامر + + + Show the %1 help message to get a list with possible Dash command-line options + عرض رسالة المساعدة%1 للحصول على قائمة مع خيارات سطر أوامر داش المحتملة + + + %1 client + الزبون %1 + + + &PrivateSend information + المعلومات إرسال خاص  + + + Show the PrivateSend basic information + خاص إرسال المعلومات إظهار خاص إرسال المعلومات الأساسية + + + &File + ملف + + + &Settings + الاعدادات + + + &Tools + أدوات + + + &Help + مساعدة + + + Tabs toolbar + شريط أدوات علامات التبويب + + + %n active connection(s) to Dash network + %n اتصالات نشطة بشبكة داش%n اتصالات نشطة بشبكة داش%n اتصالات نشطة بشبكة داش%n اتصالات نشطة بشبكة داش%n اتصالات نشطة بشبكة داش%n اتصالات نشطة بشبكة داش + + + Network activity disabled + تم إلغاء تفعيل الشبكه + + + Syncing Headers (%1%)... + مزامنة الرؤوس (%1%) ... + + + Synchronizing with network... + جار المزامنة مع الشبكة ... + + + Indexing blocks on disk... + ترتيب الفهرسة الكتل على القرص... + + + Processing blocks on disk... + معالجة الكتل على القرص... + + + Reindexing blocks on disk... + إعادة الفهرسة الكتل على القرص ... + + + Connecting to peers... + اتصال إلي القرناء... + + + Processed %n block(s) of transaction history. + تمت معالجة %n من كتل سجل المعاملات.تمت معالجة %n من كتل سجل المعاملات.تمت معالجة %n من كتل سجل المعاملات.تمت معالجة %n من كتل سجل المعاملات.تمت معالجة %n من كتل سجل المعاملات.تمت معالجة %n من كتل سجل المعاملات. + + + %1 behind + %1 خلف + + + Catching up... + يمسك... + + + Last received block was generated %1 ago. + تم إنشاء آخر كتلة تم تلقيها منذ %1. + + + Transactions after this will not yet be visible. + لن تكون المعاملات بعد ذلك مرئية بعد. + + + Up to date + حتى الآن + + + Synchronizing additional data: %p% + مزامنة بيانات إضافية: %p% + + + Error + خطأ + + + Warning + تحذير + + + Information + معلومات + + + Date: %1 + + التاريخ:%1 + + + + Amount: %1 + + المبلغ:%1 + + + + Type: %1 + + النوع:%1 + + + + Label: %1 + + التصنيف:%1 + + + + Address: %1 + + العنوان:%1 + + + + Sent transaction + تم إرسال المعاملة + + + Incoming transaction + المعاملات الواردة + + + HD key generation is <b>enabled</b> + الجيل مفتاح هد هو 1 تمكين 1 + + + HD key generation is <b>disabled</b> + الجيل مفتاح هد هو 1 تعطيل 1 + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + المحفظة هي 1 مشفرة 1 وحاليا 2 مقفلة 2 + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> for mixing only + المحفظة هو 1 مشفرة 1 وحاليا 2 مقفلة 2 للخلط فقط + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + المحفظة هي 1 مشفرة 1 وحاليا 2 مؤمن 2 + + + + ClientModel + + Total: %1 (PS compatible: %2 / Enabled: %3) + الإجمالي: %1 (متوافق مع بس: %2 / ممكن: %3) + + + Network Alert + تنبيه الشبكة + + + + CoinControlDialog + + Quantity: + كمية: + + + Bytes: + بايت: + + + Amount: + كمية: + + + Fee: + رسوم الاشتراك: + + + Coin Selection + اختيار عملة + + + Dust: + غبار: + + + After Fee: + بعد الرسوم: + + + Change: + يتغيرون: + + + (un)select all + (إلغاء) تحديد الكل + + + toggle lock state + تبديل حالة قفل + + + Tree mode + وضع الشجرة + + + List mode + وضع القائمة + + + (1 locked) + (1 مقفل) + + + Amount + كمية + + + Received with label + تم استلامها مع التصنيف + + + Received with address + تلقى مع عنوان + + + PS Rounds + بس جولة + + + Date + تاريخ + + + Confirmations + تأكيدات + + + Confirmed + تم تأكيد + + + Copy address + نسخ عنوان + + + Copy label + نسخ التسمية + + + Copy amount + مبلغ النسخ + + + Copy transaction ID + نسخ معرف المعاملة + + + Lock unspent + قفل غير منفقة + + + Unlock unspent + إلغاء فتح + + + Copy quantity + نسخ الكمية + + + Copy fee + رسوم النسخ + + + Copy after fee + نسخ بعد رسوم + + + Copy bytes + نسخ وحدات البايت + + + Copy dust + نسخ الغبار + + + Copy change + نسخ التغيير + + + Please switch to "List mode" to use this function. + يرجى التبديل إلى "وضع قائمة" لاستخدام هذه الوظيفة. + + + (%1 locked) + (%1 مقفل) + + + yes + نعم + + + no + لا + + + This label turns red if any recipient receives an amount smaller than the current dust threshold. + يتحول هذا التصنيف إلى اللون الأحمر إذا تلقى أي مستلم مبلغا أصغر من عتبة الغبار الحالية. + + + Can vary +/- %1 duff(s) per input. + يمكن أن تختلف +/-%1 داف (ق) لكل مدخلات. + + + (no label) + (بدون تسمية) + + + change from %1 (%2) + تغيير من %1 (%2) + + + (change) + (يتغيرون) + + + n/a + غير معروف + + + + EditAddressDialog + + Edit Address + عدل العنوان + + + &Label + &وصف + + + The label associated with this address list entry + الملصق المرتبط بقائمة العناوين المدخلة + + + &Address + &العنوان + + + The address associated with this address list entry. This can only be modified for sending addresses. + العنوان المرتبط بقائمة العناوين المدخلة. و التي يمكن تعديلها فقط بواسطة ارسال العناوين + + + New receiving address + عنوان أستلام جديد + + + New sending address + عنوان إرسال جديد + + + Edit receiving address + تعديل عنوان الأستلام + + + Edit sending address + تعديل عنوان الارسال + + + The entered address "%1" is not a valid Dash address. + العنوان الذي تم إدخاله "%1" ليس عنوانًا صالحًا لداش. + + + The entered address "%1" is already in the address book. + هدا العنوان "%1" موجود مسبقا في دفتر العناوين + + + Could not unlock wallet. + يمكن فتح المحفظة. + + + New key generation failed. + فشل توليد مفتاح جديد. + + + + FreespaceChecker + + A new data directory will be created. + سيتم انشاء دليل بيانات جديد + + + name + الاسم + + + Directory already exists. Add %1 if you intend to create a new directory here. + الدليل موجوج بالفعل. أضف %1 لو نويت إنشاء دليل جديد هنا. + + + Path already exists, and is not a directory. + المسار موجود بالفعل، وهو ليس دليلاً. + + + Cannot create data directory here. + لا يمكن انشاء دليل بيانات هنا . + + + + HelpMessageDialog + + version + الإصدار + + + (%1-bit) + (%1-بت) + + + About %1 + حوالي %1 + + + Command-line options + خيارات سطر الأوامر + + + Usage: + المستخدم + + + command-line options + خيارات سطر الأوامر + + + UI Options: + خيارات واجهة المستخدم + + + Choose data directory on startup (default: %u) + اختر دليل البيانات عند بدء التشغير (افتراضي: %u) + + + Set language, for example "de_DE" (default: system locale) + أضع لغة, على سبيل المثال " de_DE " (افتراضي:- مكان النظام) + + + Start minimized + الدخول مصغر + + + Set SSL root certificates for payment request (default: -system-) + أضع شهادة بروتوكول الشبقة الأمنية لطلب المدفوع (افتراضي: -نظام-) + + + Show splash screen on startup (default: %u) + أظهر شاشة البداية عند بدء التشغيل (افتراضي: %u) + + + Reset all settings changed in the GUI + اعد تعديل جميع النظم المتغيرة في GUI + + + PrivateSend information + معلومات الإرسال المخفي + + + + Intro + + Welcome + أهلا + + + Welcome to %1. + اهلا بكم في %1 + + + As this is the first time the program is launched, you can choose where %1 will store its data. + بما انه هذه اول مرة لانطلاق هذا البرنامج, فيمكنك ان تختار اين سيخزن %1 بياناته + + + %1 will download and store a copy of the Dash block chain. At least %2GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory. + سيعمل%1 على تنزيل نسخة من سلسلة كتلة داش وتخزينها. سيتم تخزين ما لا يقل عن %2 جيغابايت من البيانات في هذا الدليل ، وسيزداد بمرور الوقت. سيتم أيضًا تخزين المحفظة في هذا الدليل. + + + Use the default data directory + استخدام دليل البانات الافتراضي + + + Use a custom data directory: + استخدام دليل بيانات مخصص: + + + Error: Specified data directory "%1" cannot be created. + خطأ: لا يمكن تكوين دليل بيانات مخصص ل %1 + + + Error + خطأ + + + %1 GB of free space available + يتوفر %1 جيغابايت من المساحة المتاحة + + + + MasternodeList + + Form + نمودج + + + My Masternodes + ماسترنود حقي + + + Note: Status of your masternodes in local wallet can potentially be slightly incorrect.<br />Always wait for wallet to sync additional data and then double check from another node<br />if your masternode should be running but you still do not see "ENABLED" in "Status" field. + ملاحظة: من المحتمل أن تكون حالة رموزك في المحفظة المحلية غير صحيحة بعض الشيء.<br />انتظر دائما للمحفظة لمزامنة بيانات إضافية ومن ثم تحقق مرة أخرى من عقدة أخرى<br /> إذا كان يجب تشغيل ماسترنود الخاص بك ولكنك لا تزال لا ترى "متاحة" في حقل "الحالة". + + + Alias + الاسم المستعار + + + Address + عنوان + + + Protocol + بروتوكول + + + Status + الحالة. + + + Active + نشيط + + + Last Seen + آخر زيارة + + + Payee + المستفيد + + + S&tart alias + بدأ الإسم المستعار + + + Start &all + بدأ الكل + + + Start &MISSING + بدأ المفقود + + + &Update status + تحديث الحالة + + + Show additional Masternode information + عرض معلومات ماسترنود إضافية + + + Show &Info... + إظهار المعلومات + + + Status will be updated automatically in (sec): + سيتم تحديث الحالة تلقائيا خلال (تواني): + + + 0 + 0 + + + All Masternodes + جميع ماسترنود + + + Filter List: + قائمة تصفية: + + + Filter masternode list + تصفية قائمة ماسترنود + + + Node Count: + عدد نود + + + Start alias + بدأ الإسم المستعار + + + Confirm masternode start + تأكيد بداية ماسترنود + + + Are you sure you want to start masternode %1? + هل أنت متأكد أنك تريد بدء ماسترنود %1؟ + + + Confirm all masternodes start + تأكيد لبدأ كل ماسترنود + + + Are you sure you want to start ALL masternodes? + هل أنت متأكد أنك تريد أن تبدأ جميع ماسترنود؟ + + + Command is not available right now + الأمر غير متوفر الآن + + + You can't use this command until masternode list is synced + لا يمكنك استخدام هذا الأمر حتى تتم مزامنة قائمة ماسترنود + + + Confirm missing masternodes start + تأكيد ماسترنود في عداد المفقودين تبدأ + + + Are you sure you want to start MISSING masternodes? + هل أنت متأكد أنك تريد أن تبدأ ماسترنود مفقود؟ + + + Additional information for Masternode %1 + معلومات إضافية عن ماسترنود %1 + + + Masternode Private Key + مفتاح الخاص لماسترنود + + + Private Key + مفتاح سري + + + Collateral + جانبية + + + IP + IP + + + Version + الإصدار + + + Unknown + غير معرف + + + Sentinel + الحارس + + + + ModalOverlay + + Form + نمودج + + + Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the Dash network, as detailed below. + قد لا تكون المعاملات الأخيرة مرئية بعد ، وبالتالي قد يكون رصيد محفظتك غير صحيح. ستكون هذه المعلومات صحيحة بمجرد انتهاء محفظتك من المزامنة مع شبكة داش ، كما هو مفصل أدناه. + + + Attempting to spend Dash that are affected by not-yet-displayed transactions will not be accepted by the network. + لن تقبل الشبكة محاولة إنفاق داش المتأثرة بالمعاملات التي لم يتم عرضها بعد. + + + Number of blocks left + عدد الكتل الفاضلة + + + Unknown... + غير معرف + + + Last block time + اخر وقت الكتلة + + + Progress + تقدم + + + Progress increase per hour + تقدم يزيد بلساعة + + + calculating... + تحسب الان... + + + Estimated time left until synced + الوقت المتبقي للمزامنة + + + Hide + إخفاء + + + Unknown. Syncing Headers (%1)... + غير معروف. مزامنة الرؤوس (%1) ... + + + + OpenURIDialog + + Open URI + افتح URL + + + Open payment request from URI or file + حدد طلب الدفع من ملف او URI + + + URI: + العنوان: + + + Select payment request file + حدد ملف طلب الدفع + + + Select payment request file to open + حدد ملف طلب الدفع لفتحه + + + + OptionsDialog + + Options + خيارات ... + + + &Main + &الرئيسي + + + Size of &database cache + حجم ذاكرة التخزين المؤقت لقاعدة البيانات + + + MB + م ب + + + Number of script &verification threads + عدد مؤشرات التحقق من البرنامج النصي + + + (0 = auto, <0 = leave that many cores free) + (0 = تلقائي، <0 = اترك ذلك العديد من النوى مجانا ) + + + Amount of Dash to keep anonymized + مبلغ من داش للحفاظ على مجهولة المصدر + + + W&allet + &محفظة + + + Show additional tab listing all your masternodes in first sub-tab<br/>and all masternodes on the network in second sub-tab. + عرض علامة تبويب إضافية تسرد جميع رموزك في أول علامة تبويب فرعية <br/>وجميع ماسترنود على الشبكة في علامة التبويب الفرعية الثانية. + + + Show Masternodes Tab + إظهار جدول ماسترنود + + + Show additional information and buttons for PrivateSend on overview screen. + عرض معلومات إضافية وأزرار لشاشة الإرسال الخاص في نظرة عامة. + + + Enable advanced PrivateSend interface + تمكين واجهة الإرسال المخفي المتقدمة + + + Show warning dialog when PrivateSend detects that wallet has very low number of keys left. + إظهار مربع حوار التحذير عندما يكتشف الإرسال المخفي أن هناك عددًا منخفضًا جدًا من المفاتيح في المحفظة. + + + Warn if PrivateSend is running out of keys + تحذير إذا نفدت مفاتيح الإرسال المخفي + + + Whether to use experimental PrivateSend mode with multiple mixing sessions per block.<br/>Note: You must use this feature carefully.<br/>Make sure you always have recent wallet (auto)backup in a safe place! + لتحديد ما إذا كنت تريد استخدام وضع الإرسال المخفي التجريبي مع جلسات خلط متعددة لكل مقطع.<br/>ملاحظة: يجب عليك استخدام هذه الميزة بعناية. <br/>تأكد من أن لديك دائمًا احتياطي محفظة (تلقائي) حديث في مكان آمن! + + + Enable PrivateSend &multi-session + تمكين الإرسال المخفي جلسة متعددة + + + If you disable the spending of unconfirmed change, the change from a transaction<br/>cannot be used until that transaction has at least one confirmation.<br/>This also affects how your balance is computed. + إذا قمت بتعطيل الإنفاق من التغيير غير المؤكد ، فإن التغيير من معاملة<br/> لا يمكن استخدامه حتى يكون لهذه المعاملة تأكيد واحد على الأقل. <br/> يؤثر هذا أيضًا على كيفية حساب رصيدك. + + + PrivateSend rounds to use + جولات لإستعمال الإرسال المخفي + + + This amount acts as a threshold to turn off PrivateSend once it's reached. + يعمل هذا المقدار كعتبة لإيقاف تشغيل الإرسال المخفي بمجرد الوصول إليه. + + + Automatically open the Dash Core client port on the router. This only works when your router supports UPnP and it is enabled. + فتح منفذ عميل داش كور تلقائيًا على جهاز التوجيه. هذا يعمل فقط عندما يدعم جهاز التوجيه الخاص بك UPnP وتمكينه. + + + Accept connections from outside + اقبل الاتصالات من الخارج + + + Allow incoming connections + السماح بالاتصالات الواردة + + + Connect to the Dash network through a SOCKS5 proxy. + الاتصال بشبكة داش من خلال وكيل SOCKS5. + + + &Connect through SOCKS5 proxy (default proxy): + الاتصال من خلال وكيل SOCKS5 (الوكيل الافتراضي): + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu. + التصغير بدلاً من الخروج من التطبيق عند إغلاق النافذة. عند تفعيل هذا الخيار، سيتم إغلاق التطبيق فقط بعد اختيار الخروج من القائمة. + + + Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items.<br/>%s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. + عناوين التابعة لجهات خارجية (مثل مستكشف كتلة) التي تظهر في علامة تبويب المعاملات كعناصر قائمة السياق <br/> يتم استبدال %s في عنوان بتجزئة المعاملة. عناوين متعددة مفصولة بشريط عمودي. + + + Expert + تصدير + + + This setting determines the amount of individual masternodes that an input will be anonymized through.<br/>More rounds of anonymization gives a higher degree of privacy, but also costs more in fees. + يحدد هذا الإعداد مقدار ماسترنود الفردية التي سيتم جعل مدخلات مجهولة المصدر.<br/>تعطي المزيد من جولات إخفاء الهوية درجة أعلى من الخصوصية ، ولكنها أيضًا تكلف أكثر في الرسوم. + + + Whether to show coin control features or not. + ما اذا أردت إظهار ميزات التحكم في العملة أم لا. + + + Automatically start %1 after logging in to the system. + ابدأ تلقائيًا %1 بعد تسجيل الدخول إلى النظام. + + + &Start %1 on system login + تشغيل %1 عند الدخول إلى النظام + + + Enable coin &control features + تفعيل ميزات التحكم في العملة + + + &Spend unconfirmed change + دفع الفكة غير المؤكدة + + + &Network + &الشبكة + + + Map port using &UPnP + ربط المنفذ باستخدام UPnP + + + Proxy &IP: + بروكسي &اي بي: + + + IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1) + عنوان IP للوكيل (e.g. IPv4: 127.0.0.1 / IPv6: ::1) + + + &Port: + &المنفذ: + + + Port of the proxy (e.g. 9050) + منفذ البروكسي (مثلا 9050) + + + Used for reaching peers via: + مستخدم للاتصال بالاصدقاء من خلال: + + + Shows, if the supplied default SOCKS5 proxy is used to reach peers via this network type. + يظهر ، إذا تم استخدام بروكسي SOCKS5 الافتراضي الموفر للوصول إلى الأقران عبر نوع الشبكة هذا. + + + IPv4 + IPv4 + + + IPv6 + IPv6 + + + Tor + تور + + + Connect to the Dash network through a separate SOCKS5 proxy for Tor hidden services. + اتصل بشبكة داش من خلال وكيل SOCKS5 منفصل لخدمات المخفية. + + + Use separate SOCKS5 proxy to reach peers via Tor hidden services: + استخدم بروكسي SOCKS5 منفصل للوصول إلى الأقران عبر خدمات المخفية: + + + &Window + نافذه + + + &Hide the icon from the system tray. + إخفاء الرمز من علبة النظام + + + Hide tray icon + رمز علبة اخفاء + + + Show only a tray icon after minimizing the window. + إظهار آيقونة الصينية فقط بعد تصغير النافذة. + + + &Minimize to the tray instead of the taskbar + التصغير إلى صينية النظام بدلاً من شريط المهام + + + M&inimize on close + تصغير عند الإغلاق + + + &Display + &عرض + + + User Interface &language: + واجهة المستخدم &اللغة: + + + The user interface language can be set here. This setting will take effect after restarting %1. + يمكن تعيين لغة واجهة المستخدم هنا. سيتم تفعيل هذا الإعداد بعد إعادة تشغيل%1. + + + Language missing or translation incomplete? Help contributing translations here: +https://www.transifex.com/projects/p/dash/ + لغة مفقودة أو ترجمة غير مكتملة؟ ساعد في المساهمة بالترجمات هنا: +https://www.transifex.com/projects/p/dash/ + + + User Interface Theme: + موضوع واجهة المستخدم: + + + &Unit to show amounts in: + الوحدة لإظهار المبالغ فيها: + + + Choose the default subdivision unit to show in the interface and when sending coins. + اختر وحدة التقسيم الفرعية الافتراضية للعرض في الواجهة وعند إرسال العملات. + + + Decimal digits + أرقام عشرية + + + Third party transaction URLs + عناوين لجهات خارجية + + + Active command-line options that override above options: + خيارات سطر الأوامر النشطة التي تتجاوز الخيارات أعلاه: + + + Reset all client options to default. + إعادة تعيين كل إعدادات العميل للحالة الإفتراضية. + + + &Reset Options + &استعادة الخيارات + + + &OK + تم + + + &Cancel + الغاء + + + default + الافتراضي + + + none + لا شيء + + + Confirm options reset + تأكيد استعادة الخيارات + + + Client restart required to activate changes. + يتطلب إعادة تشغيل العميل لتفعيل التغييرات. + + + Client will be shut down. Do you want to proceed? + سوف يتم إيقاف العميل تماماً. هل تريد الإستمرار؟ + + + This change would require a client restart. + هذا التغيير يتطلب إعادة تشغيل العميل بشكل كامل. + + + The supplied proxy address is invalid. + عنوان الوكيل توفيره غير صالح. + + + + OverviewPage + + Form + نمودج + + + The displayed information may be out of date. Your wallet automatically synchronizes with the Dash network after a connection is established, but this process has not completed yet. + قد تكون المعلومات المعروضة قديمة. تتزامن محفظتك تلقائيًا مع شبكة داش بعد إنشاء الاتصال ، ولكن هذه العملية لم تكتمل بعد. + + + Available: + متوفر + + + Your current spendable balance + رصيدك القابل للصرف + + + Pending: + معلق: + + + Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance + إجمالي المعاملات التي لم يتم تأكيدها بعد ولا تحتسب ضمن الرصيد القابل للانفاق + + + Immature: + غير ناضجة + + + Mined balance that has not yet matured + الرصيد المعدّن الذي لم ينضج بعد + + + Balances + الأرصدة + + + Unconfirmed transactions to watch-only addresses + معاملات غير مؤكدة للعناوين المشاهدة فقط + + + Mined balance in watch-only addresses that has not yet matured + الرصيد المعدّن في العناوين المشاهدة فقط التي لم تنضج بعد + + + Total: + المجموع: + + + Your current total balance + رصيدك الكلي الحالي + + + Current total balance in watch-only addresses + الرصيد الإجمالي الحالي في العناوين المشاهدة فقط + + + Watch-only: + مشاهدة فقط: + + + Your current balance in watch-only addresses + رصيدك الحالي في العناوين المشاهدة فقط + + + Spendable: + قابل للصرف: + + + PrivateSend + الإرسال المخفي + + + Status: + الحالة: + + + Enabled/Disabled + متاح/غير متاح + + + Completion: + إكمال: + + + Try to manually submit a PrivateSend request. + حاول تقديم طلب الإرسال المخفي يدويًا. + + + Reset the current status of PrivateSend (can interrupt PrivateSend if it's in the process of Mixing, which can cost you money!) + إعادة تعيين الحالة الحالية لـ الإرسال المخفي (يمكن مقاطعة الإرسال المخفي إذا كانت في عملية خلط ، مما قد يكلفك المال!) + + + Information about PrivateSend and Mixing + معلومات حول الإرسال المخفي والدمج + + + Info + معلومات + + + Amount and Rounds: + الكمية و الجولات + + + 0 DASH / 0 Rounds + 0 داش / 0 جولات + + + Submitted Denom: + المقدم denom : + + + n/a + غير معروف + + + Recent transactions + أحدث المعاملات + + + Start/Stop Mixing + بدأ/إنهاء/دمج + + + PrivateSend Balance: + رصيد الإرسال المخفي + + + The denominations you submitted to the Masternode.<br>To mix, other users must submit the exact same denominations. + الفئات التي قدمتها إلى ماسترنود <br> لدمج ، يجب على المستخدمين الآخرين تقديم نفس الفئات نفسها. + + + (Last Message) + ( آخر رسالة ) + + + Try Mix + محاولة الدمج + + + Reset + إعادة تعيين + + + out of sync + خارج المزامنه + + + Automatic backups are disabled, no mixing available! + تم تعطيل النسخ الاحتياطية التلقائية ، ولا يوجد دمج متاح! + + + Start Mixing + بدأ دمج + + + Stop Mixing + إنهاء دمج + + + No inputs detected + لم يتم اكتشاف أي مدخلات + + + %n Rounds + %n جولات%n جولات%n جولات%n جولات%n جولات%n جولات + + + Found enough compatible inputs to anonymize %1 + تم العثور على مدخلات متوافقة كافية لإخفاء الهوية%1 + + + Not enough compatible inputs to anonymize <span style='color:red;'>%1</span>,<br>will anonymize <span style='color:red;'>%2</span> instead + لا توجد مدخلات متوافقة كافية لإخفاء الهوية <span style='color:red;'> %1 </span>. <br>سوف مجهول <span style='color:red;'>%2</span> بدلا من + + + Overall progress + التقدم العام + + + Denominated + المقومة + + + Mixed + دمج + + + Anonymized + مجهول + + + Denominated inputs have %5 of %n rounds on average + تبلغ قيمة المدخلات المقومة %5 من %n من الجولات في المتوسطتبلغ قيمة المدخلات المقومة %5 من %n من الجولات في المتوسطتبلغ قيمة المدخلات المقومة %5 من %n من الجولات في المتوسطتبلغ قيمة المدخلات المقومة %5 من %n من الجولات في المتوسطتبلغ قيمة المدخلات المقومة %5 من %n من الجولات في المتوسطتبلغ قيمة المدخلات المقومة %5 من %n من الجولات في المتوسط + + + keys left: %1 + المفاتيح المتبقية:%1 + + + Disabled + غير متاح + + + Very low number of keys left since last automatic backup! + عدد قليل جدا من مفاتيح اليسار منذ آخر النسخ الاحتياطي التلقائي! + + + We are about to create a new automatic backup for you, however <span style='color:red;'> you should always make sure you have backups saved in some safe place</span>! + نحن على وشك إنشاء نسخة احتياطية تلقائية جديدة لك <span style='color:red;'>يجب عليك دائمًا التأكد من الاحتفاظ بنسخ احتياطية في مكان آمن </span>! + + + WARNING! Something went wrong on automatic backup + تحذير! حدث خطأ ما في النسخ الاحتياطي التلقائي + + + ERROR! Failed to create automatic backup + خطأ! فشل إنشاء نسخة احتياطية تلقائية + + + Mixing is disabled, please close your wallet and fix the issue! + تم تعطيل المزج ، يرجى إغلاق محفظتك وإصلاح المشكلة! + + + Enabled + متاح + + + see debug.log for details. + انظر debug.log لمزيد من المعلومات + + + WARNING! Failed to replenish keypool, please unlock your wallet to do so. + تحذير! تعذّر تجديد مفتاح ، يرجى إلغاء قفل محفظتك للقيام بذلك. + + + Last PrivateSend message: + + آخر رسالة ابإرسال المخفي + + + + N/A + غير معروف + + + PrivateSend was successfully reset. + تم اعادة ضبط الإرسال المخفي بنجاح .. + + + If you don't want to see internal PrivateSend fees/transactions select "Most Common" as Type on the "Transactions" tab. + إذا كنت لا ترغب في رؤية رسوم / معاملات الإرسال المخفي الداخلي حدد "الأكثر شيوعًا" كأنواع في علامة التبويب "المعاملات". + + + PrivateSend requires at least %1 to use. + يتطلب الإرسال المخفي على الأقل%1 لاستخدامه. + + + Wallet is locked and user declined to unlock. Disabling PrivateSend. + تم قفل المحفظة ورفض المستخدم إلغاء القفل. تعطيل الإرسال المخفي + + + + PaymentServer + + Payment request error + خطأ في طلب الدفع + + + Cannot start dash: click-to-pay handler + لا يمكن بدء داش: معالج الدفع مقابل الدفع + + + URI handling + التعامل مع العنوان + + + Payment request fetch URL is invalid: %1 + عنوان الخاص بجلب طلب الدفع غير صالح:%1 + + + Invalid payment address %1 + عنوان الدفع غير صالح %1 + + + URI cannot be parsed! This can be caused by an invalid Dash address or malformed URI parameters. + لا يمكن تحليل العنوان! يمكن أن يكون ذلك بسبب عنوان داش غير صالح أو معلمات العنوان غير صحيحة. + + + Payment request file handling + التعامل مع ملف طلب الدفع + + + Payment request file cannot be read! This can be caused by an invalid payment request file. + لا يمكن قراءة ملف طلب الدفع! يمكن أن يحدث هذا بسبب ملف لطلب الدفع غير صالح. + + + Payment request rejected + تم رفض طلب الدفع + + + Payment request network doesn't match client network. + لا تتطابق شبكة طلب الدفع مع شبكة العميل. + + + Payment request expired. + انتهاء صلاحية طلب الدفع. + + + Payment request is not initialized. + لم يتم تهيئة طلب الدفع. + + + Unverified payment requests to custom payment scripts are unsupported. + طلبات الدفع غير المؤكدة إلى نصوص الدفع المخصصة غير مدعومة. + + + Invalid payment request. + طلب دفع غير صالح. + + + Requested payment amount of %1 is too small (considered dust). + المبلغ المطلوب للدفع %1 صغير جداً (يعتبر غبار). + + + Refund from %1 + إعادة مال من %1 + + + Payment request %1 is too large (%2 bytes, allowed %3 bytes). + طلب الدفع %1 كبير جداً (%2 بايت، المسموح به %3 بايت). + + + Error communicating with %1: %2 + حدث خطأ في الاتصال بـ %1: %2 + + + Payment request cannot be parsed! + لا يمكن تحليل طلب الدفع! + + + Bad response from server %1 + استجابة سيئة من الملقم %1 + + + Network request error + خطأ في طلب الشبكة + + + Payment acknowledged + اعتراف بالدفع + + + + PeerTableModel + + NodeId + رقم العقدة + + + Node/Service + عقدة/خدمة + + + User Agent + وكيل المستخدم + + + Ping + رنين + + + + QObject + + Dash Core + جوهر الداش + + + %1 didn't yet exit safely... + %1 لم يخرج بعد بأمان... + + + Error reading masternode configuration file: %1 + خطأ في قراءة ملف تهيئة ماسترنود :%1 + + + Amount + مبلغ + + + Enter a Dash address (e.g. %1) + أدخل عنوان داش (على سبيل المثال%1) + + + %1 d + %1 يوم + + + %1 h + %1 ساعة + + + %1 m + %1 دقيقة + + + %1 s + %1 ثانية + + + None + لا شيء + + + N/A + غير معروف + + + %1 ms + %1 جزء من الثانية + + + %n second(s) + %n تواني%n تواني%n تواني%n تواني%n تواني%n تواني + + + %n minute(s) + %n دقائق%n دقائق%n دقائق%n دقائق%n دقائق%n دقائق + + + %n hour(s) + %n ساعات%n ساعات%n ساعات%n ساعات%n ساعات%n ساعات + + + %n day(s) + %n أيام%n أيام%n أيام%n أيام%n أيام%n أيام + + + %n week(s) + %n أسابيع%n أسابيع%n أسابيع%n أسابيع%n أسابيع%n أسابيع + + + %n year(s) + %n أعوام%n أعوام%n أعوام%n أعوام%n أعوام%n أعوام + + + %1 and %2 + %1 و %2 + + + unknown + غير معروف + + + + QObject::QObject + + Error: Specified data directory "%1" does not exist. + خطأ: دليل البيانات المحدد "%1" غير موجود. + + + Error: Cannot parse configuration file: %1. Only use key=value syntax. + خطأ: لا يمكن تحليل ملف التهيئة:%1. استخدم فقط بناء الجملة = القيمة الأساسية. + + + Error: %1 + خطأ: %1 + + + + QRDialog + + QR-Code Title + عنوان QR-Code + + + QR Code + رمز كيو ار + + + &Save Image... + &حفظ الصورة + + + Error creating QR Code. + خطأ في إنشاء رمز الاستجابة السريعة. + + + + QRGeneralImageWidget + + &Save Image... + &حفظ الصورة + + + &Copy Image + &نسخ الصورة + + + Save QR Code + حفظ رمز الاستجابة السريعة QR + + + PNG Image (*.png) + صورة PNG (*.png) + + + + QRImageWidget + + &Save Image... + &حفظ الصورة + + + &Copy Image + &نسخ الصورة + + + Save QR Code + حفظ رمز الاستجابة السريعة QR + + + PNG Image (*.png) + صورة PNG (*.png) + + + + RPCConsole + + Tools window + نافذة الأدوات + + + &Information + المعلومات + + + General + عام + + + Name + الاسم + + + N/A + غير معروف + + + Number of connections + عدد الاتصالات + + + &Open + الفتح + + + Startup time + وقت البدء + + + Network + الشبكه + + + Last block time + اخر وقت الكتلة + + + Debug log file + تصحيح ملف السجل + + + Current number of blocks + عدد الكتل الحالي + + + Client version + نسخه العميل + + + Using BerkeleyDB version + باستخدام BerkeleyDB إصدار + + + Block chain + سلسلة الكتل + + + Number of Masternodes + ارقام ماسترنود + + + Memory Pool + تجمع الذاكرة + + + Current number of transactions + عدد المعاملات الحالي + + + Memory usage + استخدام الذاكرة + + + &Console + وحدة التحكم + + + Clear console + مسح وحدة التحكم + + + &Network Traffic + &حركة مرور الشبكة + + + &Clear + &مسح + + + Totals + المجاميع + + + Received + إستقبل + + + Sent + تم الإرسال + + + &Peers + &اصدقاء + + + Banned peers + الأقران الممنوعين + + + Select a peer to view detailed information. + حدد نظير لعرض معلومات مفصلة. + + + Whitelisted + اللائحة البيضاء + + + Direction + جهة + + + Version + الإصدار + + + Starting Block + كتلة البداية + + + Synced Headers + رؤوس متزامنة + + + Synced Blocks + كتل متزامنة + + + Wallet Path + محفظة path + + + User Agent + وكيل المستخدم + + + Datadir + دليل البيانات + + + Open the %1 debug log file from the current data directory. This can take a few seconds for large log files. + افتح ملف سجل تصحيح %1 من دليل البيانات الحالي. قد يستغرق هذا بضع ثوانٍ لملفات السجل الكبيرة. + + + Decrease font size + تصغير حجم الخط + + + Increase font size + تكبير حجم الخط + + + Services + خدمات + + + Ban Score + نقاط الحظر + + + Connection Time + مدة الاتصال + + + Last Send + آخر استقبال + + + Last Receive + آخر إرسال + + + Ping Time + وقت الرنين + + + The duration of a currently outstanding ping. + مدة الرنين المعلقة حالياً. + + + Ping Wait + انتظار الرنين + + + Min Ping + أقل رنين + + + Time Offset + إزاحة الوقت + + + &Wallet Repair + إصلاح المحفظة + + + Salvage wallet + محفظة الإنقاذ + + + Rescan blockchain files + إعادة فحص ملفات بلوكشين + + + Recover transactions 1 + استرداد المعاملات 1 + + + Recover transactions 2 + استرداد المعاملات 2 + + + Upgrade wallet format + ترقية تنسيق المحفظة + + + The buttons below will restart the wallet with command-line options to repair the wallet, fix issues with corrupt blockhain files or missing/obsolete transactions. + ستقوم الأزرار الموجودة أدناه بإعادة تشغيل المحفظة باستخدام خيارات سطر الأوامر لإصلاح المحفظة ، وإصلاح المشكلات المتعلقة بملفات بلوكشين الفاسدة أو معاملات مفقودة / قديمة. + + + -salvagewallet: Attempt to recover private keys from a corrupt wallet.dat. + محفظة النقود: محاولة استرداد المفاتيح الخاصة من wallet.dat فاسدة. + + + -rescan: Rescan the block chain for missing wallet transactions. + إعادة المسح: إعادة فحص سلسلة الكتل الخاصة بمعاملات المحفظة المفقودة. + + + -zapwallettxes=1: Recover transactions from blockchain (keep meta-data, e.g. account owner). + zapwallettxes = 1: استرداد المعاملات من بلوكشين (الاحتفاظ ببيانات التعريف ، على سبيل المثال ، مالك الحساب). + + + -zapwallettxes=2: Recover transactions from blockchain (drop meta-data). + zapwallettxes = 2: استرداد المعاملات من بلوكشين (إسقاط بيانات التعريف). + + + -upgradewallet: Upgrade wallet to latest format on startup. (Note: this is NOT an update of the wallet itself!) + ترقية المحفظة: ترقية المحفظة إلى أحدث تنسيق عند بدء التشغيل. (ملاحظة: هذا ليس تحديثًا للمحفظة نفسها!) + + + Wallet repair options. + خيارات إصلاح المحفظة. + + + Rebuild index + إعادة بناء الفهرس + + + -reindex: Rebuild block chain index from current blk000??.dat files. + إعادة بناء فهرس سلسلة كتلة من الملفات الحالية blk000 ؟؟. + + + &Disconnect + قطع الاتصال + + + Ban for + حظر ل + + + 1 &hour + 1 &ساعة + + + 1 &day + 1 & يوم + + + 1 &week + 1 & اسبوع + + + 1 &year + 1 & سنة + + + &Unban + رفع الحظر + + + Welcome to the %1 RPC console. + مرحبًا بك في وحدة التحكم %1 RPC. + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + استخدم اسهم الاعلى و الاسفل للتنقل بين السجلات و <b>Ctrl-L</b> لمسح الشاشة + + + Type <b>help</b> for an overview of available commands. + نوع <b>مساعدة</b>للحصول على نظرة عامة على الأوامر المتاحة. + + + WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramification of a command. + تحذير: المخادعون نشطون ، ويطلبون من المستخدمين كتابة الأوامر هنا ، وسرقة محتويات محفظتهم. لا تستخدم وحدة التحكم هذه بدون فهم تباين أمر ما تمامًا + + + In: + داخل: + + + Out: + خارج: + + + Network activity disabled + تم إلغاء تفعيل الشبكه + + + %1 B + %1 بايت + + + %1 KB + %1 كيلو بايت + + + %1 MB + %1 ميقا بايت + + + %1 GB + %1 قيقا بايت + + + (node id: %1) + (معرف العقدة: %1) + + + via %1 + خلال %1 + + + never + ابدا + + + Inbound + داخل + + + Outbound + خارجي + + + Yes + نعم + + + No + لا + + + Unknown + غير معروف + + + + ReceiveCoinsDialog + + Reuse one of the previously used receiving addresses.<br>Reusing addresses has security and privacy issues.<br>Do not use this unless re-generating a payment request made before. + إعادة استخدام أحد عناوين الاستلام المستخدمة مسبقًا.<br> إعادة استخدام العناوين لديها قضايا الأمن والخصوصية.<br> لا تستخدم هذا إلا إذا أعدت إنشاء طلب دفع مقدم من قبل. + + + R&euse an existing receiving address (not recommended) + إعادة استخدام عنوان استلام موجود (غير مستحسن) + + + An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Dash network. + رسالة اختيارية لإرفاقها بطلب الدفع ، والتي سيتم عرضها عند فتح الطلب. ملاحظة: لن يتم إرسال الرسالة مع الدفع عبر شبكة داش. + + + &Message: + &رسالة: + + + An optional label to associate with the new receiving address. + تسمية اختيارية لربطها بعنوان المستلم الجديد. + + + An optional message to attach to the payment request, which will be displayed when the request is opened.<br>Note: The message will not be sent with the payment over the Dash network. + رسالة اختيارية لإرفاقها بطلب الدفع ، والتي سيتم عرضها عند فتح الطلب.<br> ملاحظة: لن يتم إرسال الرسالة مع الدفع عبر شبكة داش. + + + Use this form to request payments. All fields are <b>optional</b>. + استخدم هذا النموذج لطلب الدفعات. جميع الحقول <b>اختيارية</b>. + + + &Label: + &وصف : + + + An optional amount to request. Leave this empty or zero to not request a specific amount. + مبلغ اختياري للطلب. اترك هذا فارغًا أو صفراً لعدم طلب مبلغ محدد. + + + &Amount: + &القيمة + + + &Request payment + &طلب دفعة + + + Clear all fields of the form. + مسح كل حقول النموذج المطلوبة + + + Clear + مسح + + + Request InstantSend + طلب الإرسال الفوري + + + Requested payments history + سجل طلبات الدفع + + + Show the selected request (does the same as double clicking an entry) + إظهار الطلب المحدد (يقوم بنفس نتيجة النقر المزدوج على أي إدخال) + + + Show + عرض + + + Remove the selected entries from the list + قم بإزالة الإدخالات المحددة من القائمة + + + Remove + ازل + + + Copy URI + نسخ العنوان + + + Copy label + انسخ التسمية + + + Copy message + انسخ الرسالة + + + Copy amount + نسخ الكمية + + + + ReceiveRequestDialog + + QR Code + رمز كيو ار + + + Copy &URI + نسخ &URI + + + Copy &Address + نسخ &العنوان + + + &Save Image... + &حفظ الصورة + + + Request payment to %1 + طلب الدفعة إلى %1 + + + Payment information + معلومات الدفع + + + URI + URI + + + Address + عنوان + + + Amount + مبلغ + + + Label + وصف + + + Message + رسالة + + + InstantSend + الإرسال الفوري + + + Yes + نعم + + + No + لا + + + Resulting URI too long, try to reduce the text for label / message. + العنوان المستخدم طويل جدًا، حاول أن تقوم بتقليل نص التسمية / الرسالة. + + + Error encoding URI into QR Code. + خطأ في ترميز العنوان إلى الرمز المربع. + + + + RecentRequestsTableModel + + Date + تاريخ + + + Label + وصف + + + Message + رسالة + + + (no label) + (لا وصف) + + + (no message) + ( لا رسائل ) + + + (no amount requested) + (لا يوجد مبلغ مطلوب) + + + Requested + تم الطلب + + + + SendCoinsDialog + + Send Coins + إرسال Coins + + + Coin Control Features + ميزات التحكم بالعملة + + + Inputs... + المدخلات... + + + automatically selected + اختيار تلقائيا + + + Insufficient funds! + الرصيد غير كافي! + + + Quantity: + الكمية : + + + Bytes: + بايت + + + Amount: + القيمة : + + + Fee: + رسوم : + + + Dust: + غبار: + + + After Fee: + بعد الرسوم : + + + Change: + تعديل : + + + If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address. + إذا تم تنشيط هذا، ولكن عنوان الفكة فارغ أو غير صالح، فسيتم إرسال الفكة إلى عنوان تم إنشاؤه حديثًا. + + + Custom change address + تغيير عنوان الفكة + + + Transaction Fee: + رسوم المعاملة: + + + Choose... + إختر … + + + Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until your have validated the complete chain. + قد يؤدي استخدام fallbackfee إلى إرسال معاملة تستغرق عدة ساعات أو أيام (أو أبداً) للتأكيد. فكر في اختيار الرسوم يدويًا أو انتظر حتى يتم التحقق من صحة السلسلة الكاملة. + + + Warning: Fee estimation is currently not possible. + تحذير: تقدير الرسوم غير ممكن في الوقت الحالي. + + + collapse fee-settings + خفض اعدادات الرسوم + + + Confirmation time target: + هدف وقت التأكيد: + + + PrivateSend + الإرسال المخفي + + + InstantSend + الإسال الفوري + + + If the custom fee is set to 1000 duffs and the transaction is only 250 bytes, then "per kilobyte" only pays 250 duffs in fee,<br />while "at least" pays 1000 duffs. For transactions bigger than a kilobyte both pay by kilobyte. + إذا تم تعيين الرسوم الجمركية على 1000 دافع وكانت المعاملة 250 بايتًا فقط ، فإن "كل كيلوبايت" يدفع 250 جنيهًا فقط رسومًا ،<br /> بينما "على الأقل" يدفع 1000 دفين. لمعاملات أكبر من كيلوبايت تدفع كل من كيلوبايت. + + + If the custom fee is set to 1000 duffs and the transaction is only 250 bytes, then "per kilobyte" only pays 250 duffs in fee,<br />while "total at least" pays 1000 duffs. For transactions bigger than a kilobyte both pay by kilobyte. + إذا تم تعيين الرسوم الجمركية على 1000 دافع وكانت المعاملة 250 بايتًا فقط ، فإن "كل كيلوبايت" يدفع 250 جنيهًا فقط رسومًا ، <br /> بينما "الإجمالي على الأقل" يدفع 1000 دفين. لمعاملات أكبر من كيلوبايت تدفع كل من كيلوبايت. + + + Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks.<br />But be aware that this can end up in a never confirming transaction once there is more demand for dash transactions than the network can process. + إن دفع الحد الأدنى فقط من الرسوم يكون على ما يرام طالما أن حجم المعاملات أقل من المساحة الموجودة في الكتل.<br /> ولكن كن على علم بأن هذا يمكن أن ينتهي في معاملة تؤكد أبدا عندما يكون هناك المزيد من الطلب على معاملات اندفاعة مما يمكن أن تعالجه الشبكة. + + + per kilobyte + لكل كيلوبايت + + + Hide + إخفاء + + + total at least + مجموع على الأقل + + + (read the tooltip) + (اقرأ تلميح الأدوات) + + + Recommended: + موصى به: + + + Custom: + تخصيص: + + + (Smart fee not initialized yet. This usually takes a few blocks...) + (الرسوم الذكية لم يتم تهيئتها بعد. عادة ما يستغرق ذلك بضع كتل ...) + + + normal + طبيعي + + + fast + سريع + + + Confirm the send action + تأكيد الإرسال + + + S&end + &ارسال + + + Clear all fields of the form. + مسح كل حقول النموذج المطلوبة + + + Clear &All + مسح الكل + + + Send to multiple recipients at once + إرسال إلى عدة مستلمين في وقت واحد + + + Add &Recipient + أضافة &مستلم + + + Balance: + الرصيد: + + + Copy quantity + نسخ الكمية + + + Copy amount + نسخ الكمية + + + Copy fee + نسخ الرسوم + + + Copy after fee + نسخ بعد الرسوم + + + Copy bytes + نسخ البايتات + + + Copy dust + نسخ الغبار + + + Copy change + نسخ التعديل + + + using + إستخدام + + + anonymous funds + مدفوعات مجهولة + + + (privatesend requires this amount to be rounded up to the nearest %1). + (يتطلب الإرسال المخفي تقريب هذا المبلغ إلى أقرب%1). + + + any available funds (not anonymous) + أي أموال متاحة (غير مجهولة) + + + and InstantSend + و الإرسال الفوري + + + %1 to %2 + %1 الى %2 + + + Are you sure you want to send? + هل أنت متأكد من أنك تريد أن ترسل؟ + + + are added as transaction fee + تضاف كرسوم المعاملة + + + Total Amount = <b>%1</b><br />= %2 + مجمود المبلغ = <b> %1</b> <br /> = %2 + + + <b>(%1 of %2 entries displayed)</b> + <b>( %1 على %2 المداخلات المعروضة)</b> + + + Confirm send coins + تأكيد الإرسال Coins + + + The recipient address is not valid. Please recheck. + عنوان المستلم غير صالح. يرجى إعادة الفحص. + + + The amount to pay must be larger than 0. + المبلغ المدفوع يجب ان يكون اكبر من 0 + + + The amount exceeds your balance. + القيمة تتجاوز رصيدك + + + The total exceeds your balance when the %1 transaction fee is included. + المجموع يتجاوز رصيدك عندما يتم اضافة %1 رسوم العملية + + + Duplicate address found: addresses should only be used once each. + تم العثور على عنوان مكرر: يجب استخدام العناوين مرة واحدة فقط. + + + Transaction creation failed! + فشل في إنشاء المعاملة! + + + The transaction was rejected with the following reason: %1 + تم رفض المعاملة للسبب التالي: %1 + + + A fee higher than %1 is considered an absurdly high fee. + تعتبر الرسوم الأعلى من %1 رسوماً باهظة. + + + Payment request expired. + انتهاء صلاحية طلب الدفع. + + + %n block(s) + %n كتل%n كتل%n كتل%n كتل%n كتل%n كتل + + + Pay only the required fee of %1 + دفع فقط الرسوم المطلوبة ل %1 + + + Estimated to begin confirmation within %n block(s). + يقدر أن يبدأ التأكيد ضمن %n من الكتل.يقدر أن يبدأ التأكيد ضمن %n من الكتل.يقدر أن يبدأ التأكيد ضمن %n من الكتل.يقدر أن يبدأ التأكيد ضمن %n من الكتل.يقدر أن يبدأ التأكيد ضمن %n من الكتل.يقدر أن يبدأ التأكيد ضمن %n من الكتل. + + + Warning: Invalid Dash address + تحذير: عنوان داش غير صالح + + + Warning: Unknown change address + تحذير: عنوان الفكة غير معروف + + + Confirm custom change address + تأكيد تغيير العنوان الفكة + + + The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure? + العنوان الذي حددته للتغيير ليس جزءًا من هذه المحفظة. يمكن إرسال أي أو كل الأموال في محفظتك إلى هذا العنوان. هل أنت واثق؟ + + + (no label) + (لا وصف) + + + + SendCoinsEntry + + This is a normal payment. + هذا دفع اعتيادي + + + Pay &To: + ادفع &الى : + + + The Dash address to send the payment to + عنوان داش لإرسال الدفعة إلى + + + Choose previously used address + اختر عنوانا مستخدم سابقا + + + Alt+A + Alt+A + + + Paste address from clipboard + انسخ العنوان من لوحة المفاتيح + + + Alt+P + Alt+P + + + Remove this entry + ازل هذه المداخله + + + &Label: + &وصف : + + + Enter a label for this address to add it to the list of used addresses + أدخل تسمية لهذا العنوان لإضافته إلى قائمة العناوين المستخدمة + + + A&mount: + &القيمة + + + The fee will be deducted from the amount being sent. The recipient will receive a lower amount of Dash than you enter in the amount field. If multiple recipients are selected, the fee is split equally. + سيتم خصم الرسوم من المبلغ الذي يتم إرساله. سوف يتلقى المستلم كمية أقل من الشرطة من إدخالها في حقل المبلغ. في حالة تحديد عدة مستلمين ، يتم تقسيم الرسوم بالتساوي. + + + S&ubtract fee from amount + طرح الرسوم من المبلغ + + + Message: + الرسائل + + + A message that was attached to the dash: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Dash network. + رسالة تم إرفاقها بخط العطف: عنوان الذي سيتم تخزينه مع المعاملة كمرجع لك. ملاحظة: لن يتم إرسال هذه الرسالة عبر شبكة داش. + + + This is an unauthenticated payment request. + هذا طلب دفع لم يتم مصادقته. + + + This is an authenticated payment request. + هذا طلب دفع تمت مصادقته. + + + Pay To: + ادفع &الى : + + + Memo: + مذكرة: + + + Enter a label for this address to add it to your address book + إدخال تسمية لهذا العنوان لإضافته إلى دفتر العناوين الخاص بك + + + + SendConfirmationDialog + + Yes + نعم + + + + ShutdownWindow + + %1 is shutting down... + اتمام إيقاف %1... + + + Do not shut down the computer until this window disappears. + لا توقف عمل الكمبيوتر حتى تختفي هذه النافذة + + + + SignVerifyMessageDialog + + Signatures - Sign / Verify a Message + التواقيع - التوقيع /تأكيد الرسالة + + + &Sign Message + &توقيع الرسالة + + + You can sign messages/agreements with your addresses to prove you can receive Dash sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + يمكنك توقيع الرسائل / الاتفاقيات مع العناوين الخاصة بك لإثبات أنك يمكن أن تتلقى داش المرسلة إليهم. احرص على عدم التوقيع على أي شيء غامض أو عشوائي ، حيث إن هجمات التصيد الاحتيالي قد تحاول خداعك لتوقيع هويتك عليها. توقيع فقط عبارات تفصيلية كاملة توافق على. + + + The Dash address to sign the message with + عنوان داش للتوقيع على الرسالة + + + Choose previously used address + اختر عنوانا مستخدم سابقا + + + Alt+A + Alt+A + + + Paste address from clipboard + انسخ العنوان من لوحة المفاتيح + + + Alt+P + Alt+P + + + Enter the message you want to sign here + ادخل الرسالة التي تريد توقيعها هنا + + + Signature + التوقيع + + + Copy the current signature to the system clipboard + نسخ التوقيع الحالي إلى حافظة النظام + + + Sign the message to prove you own this Dash address + وقع على الرسالة لإثبات أنك تملك عنوان داش هذا + + + Sign &Message + توقيع $الرسالة + + + Reset all sign message fields + إعادة تعيين كافة حقول رسالة التوقيع + + + Clear &All + مسح الكل + + + &Verify Message + &تحقق رسالة + + + Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction! + أدخل عنوان المتلقي ، رسالة (تأكد من نسخ فواصل الأسطر والمسافات وعلامات التبويب وما إلى ذلك تمامًا) والتوقيع أدناه للتحقق من الرسالة. يجب الحرص على عدم قراءة المزيد في التوقيع أكثر مما هو موجود في الرسالة الموقّعة نفسها ، لتجنب التعرض للخداع من خلال هجوم رجل في الوسط. لاحظ أن هذا يثبت فقط أن الطرف المتلقي يتلقى العنوان ، فلا يمكنه إثبات إرسال أي معاملة! + + + The Dash address the message was signed with + عنوان داش الذي تم توقيع الرسالة به + + + Verify the message to ensure it was signed with the specified Dash address + تحقق من الرسالة للتأكد من توقيعها باستخدام عنوان داش المحدد + + + Verify &Message + تحقق &الرسالة + + + Reset all verify message fields + إعادة تعيين جميع حقول التحقق من الرسالة + + + Click "Sign Message" to generate signature + اضغط "توقيع الرسالة" لتوليد التوقيع + + + The entered address is invalid. + العنوان المدخل غير صالح + + + Please check the address and try again. + الرجاء التأكد من العنوان والمحاولة مرة اخرى + + + The entered address does not refer to a key. + العنوان المدخل لا يشير الى مفتاح + + + Wallet unlock was cancelled. + تم الغاء عملية فتح المحفظة + + + Private key for the entered address is not available. + المفتاح الخاص للعنوان المدخل غير موجود. + + + Message signing failed. + فشل توقيع الرسالة. + + + Message signed. + الرسالة موقعة. + + + The signature could not be decoded. + لا يمكن فك تشفير التوقيع. + + + Please check the signature and try again. + فضلا تاكد من التوقيع وحاول مرة اخرى + + + The signature did not match the message digest. + لم يتطابق التوقيع مع ملخص الرسالة. + + + Message verification failed. + فشلت عملية التأكد من الرسالة. + + + Message verified. + تم تأكيد الرسالة. + + + + SplashScreen + + [testnet] + [testnet] + + + Version %1 + نسخة %1 + + + + TrafficGraphWidget + + KB/s + كيلوبايت/ث + + + + TransactionDesc + + Open for %n more block(s) + فتح لـ %n كتل إضافيةفتح لـ %n كتل إضافيةفتح لـ %n كتل إضافيةفتح لـ %n كتل إضافيةفتح لـ %n كتل إضافيةفتح لـ %n كتل إضافية + + + Open until %1 + مفتوح حتى %1 + + + conflicted + يتعارض + + + %1/offline + %1 غير متواجد + + + 0/unconfirmed, %1 + 0/غير مؤكد ،%1 + + + in memory pool + في تجمع الذاكرة + + + not in memory pool + ليس في تجمع الذاكرة + + + abandoned + مهجور + + + %1/unconfirmed + غير مؤكدة/%1 + + + %1 confirmations + تأكيد %1 + + + verified via InstantSend + محقق من فبل الإرسال الفوري + + + InstantSend verification in progress - %1 of %2 signatures + الإرسال الغوري التحقق قيد التقدم - %1 على %2 إمضاءات + + + InstantSend verification failed + فشل التحقق من الإرسال الفوري + + + Status + الحالة. + + + , has not been successfully broadcast yet + , لم يتم حتى الآن البث بنجاح + + + , broadcast through %n node(s) + البث من خلال عقد %nالبث من خلال عقد %nالبث من خلال عقد %nالبث من خلال عقد %nالبث من خلال عقد %nالبث من خلال عقد %n + + + Date + تاريخ + + + Source + المصدر + + + Generated + تم اصداره. + + + From + من + + + unknown + غير معروف + + + To + الى + + + own address + عنوانه + + + watch-only + مشاهدة فقط + + + label + وصف + + + Credit + رصيد + + + matures in %n more block(s) + ينضج في %n كتل إضافيةينضج في %n كتل إضافيةينضج في %n كتل إضافيةينضج في %n كتل إضافيةينضج في %n كتل إضافيةينضج في %n كتل إضافية + + + not accepted + غير مقبولة + + + Debit + دين + + + Total debit + إجمالي الخصم + + + Total credit + إجمالي الرصيد + + + Transaction fee + رسوم المعاملة + + + Net amount + صافي المبلغ + + + Message + رسالة + + + Comment + تعليق + + + Transaction ID + رقم المعاملة + + + Output index + مؤشر المخرجات + + + Transaction total size + الحجم الكلي للمعاملات + + + Merchant + تاجر + + + Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + يجب أن تنضج العملات المولدة%1 قبل أن يتم إنفاقها. عند إنشاء هذا الحظر ، تم بثه إلى الشبكة ليتم إضافته إلى سلسلة الحظر. إذا فشلت في الدخول إلى السلسلة ، ستتغير حالتها إلى "غير مقبولة" ولن تكون قابلة للانفاق. قد يحدث هذا أحيانًا إذا أدت عقدة أخرى إلى إنشاء كتلة في غضون ثوانٍ قليلة من حسابك. + + + Debug information + معلومات التصحيح + + + Transaction + معاملة + + + Inputs + المدخلات + + + Amount + مبلغ + + + true + صحيح + + + false + خاطئ + + + + TransactionDescDialog + + This pane shows a detailed description of the transaction + يبين هذا الجزء وصفا مفصلا لهده المعاملة + + + Details for %1 + تفاصيل عن %1 + + + + TransactionTableModel + + Date + تاريخ + + + Type + النوع + + + Address / Label + تسمية / عنوان + + + Open for %n more block(s) + فتح لـ %n كتل إضافيةفتح لـ %n كتل إضافيةفتح لـ %n كتل إضافيةفتح لـ %n كتل إضافيةفتح لـ %n كتل إضافيةفتح لـ %n كتل إضافية + + + Open until %1 + مفتوح حتى %1 + + + Offline + غير متصل + + + Unconfirmed + غير مؤكد + + + Abandoned + مهجور + + + Confirming (%1 of %2 recommended confirmations) + قيد التأكيد (%1 من %2 تأكيد موصى به) + + + Confirmed (%1 confirmations) + تأكيد %1 الإرسال + + + Conflicted + يتعارض + + + Immature (%1 confirmations, will be available after %2) + غير ناضجة (تأكيدات %1 ، ستكون متوفرة بعد %2) + + + This block was not received by any other nodes and will probably not be accepted! + لم يتم تلقى هذه الكتلة (Block) من قبل أي العقد الأخرى وربما لن تكون مقبولة! + + + Generated but not accepted + ولدت ولكن لم تقبل + + + Received with + استقبل مع + + + Received from + استقبل من + + + Received via PrivateSend + تلقى عن طريق الإرسال المخفي + + + Sent to + أرسل إلى + + + Payment to yourself + دفع لنفسك + + + Mined + Mined + + + PrivateSend Denominate + الإرسال المخفي المقوم + + + PrivateSend Collateral Payment + الإرسال المخفي الدفع الجانبي + + + PrivateSend Make Collateral Inputs + الإرسال المخفي جعل المدخلات الجانبية + + + PrivateSend Create Denominations + الإرسال المخفي إنشاء الطوائف + + + PrivateSend + لإرسال المخفي + + + watch-only + مشاهدة فقط + + + (n/a) + غير متوفر + + + (no label) + (لا وصف) + + + Transaction status. Hover over this field to show number of confirmations. + حالة التحويل. مرر فوق هذا الحقل لعرض عدد التأكيدات. + + + Date and time that the transaction was received. + التاريخ والوقت الذي تم فيه تلقي المعاملة. + + + Type of transaction. + نوع المعاملات + + + Whether or not a watch-only address is involved in this transaction. + ما إذا كان العنوان المشاهدة فقط متضمنًا في هذه المعاملة أم لا. + + + User-defined intent/purpose of the transaction. + هدف / غرض محدد من قبل المستخدم للمعاملات. + + + Amount removed from or added to balance. + المبلغ الذي أزيل أو أضيف الى الرصيد + + + + TransactionView + + All + الكل + + + Today + اليوم + + + This week + هدا الاسبوع + + + This month + هدا الشهر + + + Last month + الشهر الماضي + + + This year + هدا العام + + + Range... + المدى... + + + Most Common + الاكثر انتشارا + + + Received with + استقبل مع + + + Sent to + أرسل إلى + + + PrivateSend + الإرسال المخفي + + + PrivateSend Make Collateral Inputs + الإرسال المخفي جعل المدخلات الجانبية + + + PrivateSend Create Denominations + الإرسال المخفي إنشاء الطوائف + + + PrivateSend Denominate + الإرسال المخفي المقوم + + + PrivateSend Collateral Payment + الإرسال المخفي الدفع الجانبي + + + To yourself + إليك + + + Mined + Mined + + + Other + اخرى + + + Enter address or label to search + ادخل عنوان أووصف للبحث + + + Min amount + الحد الأدنى + + + Abandon transaction + التخلي عن المعاملة + + + Copy address + انسخ عنوان + + + Copy label + انسخ التسمية + + + Copy amount + نسخ الكمية + + + Copy transaction ID + نسخ رقم العملية + + + Copy raw transaction + نسخ المعاملة الخام + + + Copy full transaction details + نسخ كامل تفاصيل المعاملة + + + Edit label + عدل الوصف + + + Show transaction details + عرض تفاصيل المعاملة + + + Export Transaction History + تصدير تفاصيل المعاملات + + + Comma separated file (*.csv) + ملف مفصول بفواصل (*.csv) + + + Confirmed + تأكيد + + + Watch-only + مشاهدة فقط + + + Date + تاريخ + + + Type + النوع + + + Label + وصف + + + Address + عنوان + + + ID + العنوان + + + Exporting Failed + فشل التصدير + + + There was an error trying to save the transaction history to %1. + حدث خطأ أثناء محاولة حفظ محفوظات المعاملة إلى %1. + + + Exporting Successful + نجح التصدير + + + The transaction history was successfully saved to %1. + تم حفظ محفوظات المعاملة بنجاح إلى %1. + + + Range: + المدى: + + + to + الى + + + + UnitDisplayStatusBarControl + + Unit to show amounts in. Click to select another unit. + الوحدة لإظهار المبالغ فيها. انقر لتحديد وحدة أخرى. + + + + WalletFrame + + No wallet has been loaded. + لا يوجد محفظة تم تحميلها. + + + + WalletModel + + Send Coins + إرسال Coins + + + InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH. + لا يدعم الإرسال الفوري إرسال قيم عالية حتى الآن. تقتصر المعاملات حاليًا على%1 داش . + + + Used way too many inputs (>%1) for this InstantSend transaction, fees could be huge. + تستخدم طريقة إدخال كثيرة (>%1) لهذه المعاملة الإرسال الفوري ، قد تكون الرسوم كبيرة + + + + WalletView + + &Export + &تصدير + + + Export the data in the current tab to a file + تحميل البيانات في علامة التبويب الحالية إلى ملف. + + + Selected amount: + المبلغ المحدد: + + + Backup Wallet + نسخ احتياط للمحفظة + + + Wallet Data (*.dat) + بيانات المحفظة (*.dat) + + + Backup Failed + فشل النسخ الاحتياطي + + + There was an error trying to save the wallet data to %1. + حدث خطأ أثناء محاولة حفظ بيانات المحفظة إلى%1. + + + Backup Successful + نجاح النسخ الاحتياطي + + + The wallet data was successfully saved to %1. + تم حفظ بيانات المحفظة بنجاح إلى %1. + + + + dash-core + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + ربط عنوان معين والاستماع دائما على ذلك. استخدم [host]: تدوين المنفذ لـ IPv6 + + + Error: Listening for incoming connections failed (listen returned error %s) + خطأ: فشل الاستماع إلى الاتصالات الواردة (تم إرجاع الخطأ %s) + + + Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message) + تنفيذ الأمر عند تلقي تنبيه ذي صلة أو ظهور شوكة طويلة بالفعل (يتم استبدال %s في cmd بالرسالة) + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + تنفيذ الأمر عند تغيير معاملة المحفظة (يتم استبدال %s في cmd بـ TxID) + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + تنفيذ الأمر عند تغيير أفضل كتلة (يتم استبدال %s في cmd بتجزئة التجميع) + + + Name to construct url for KeePass entry that stores the wallet passphrase + اسم لإنشاء عنوان لإدخال KeePass الذي يخزن عبارة مرور المحفظة + + + Set maximum size of high-priority/low-fee transactions in bytes (default: %d) + تعيين الحد الأقصى لحجم المعاملات عالية الأولوية / منخفضة الرسوم بالبايت (الافتراضي: %d) + + + Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d) + تعيين عدد سلاسل عمليات التحقق من البرنامج النصي (%u إلى %d ، 0 = auto ، <0 = ترك العديد من النوى خالية ، الافتراضي: %d) + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + + + Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues. + تحذير: لا يبدو أن الشبكة توافق تمامًا! يبدو أن بعض المشتغلين بالتعدين يواجهون مشكلات. + + + Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade. + تحذير: لا يبدو أننا نتفق تمامًا مع نظرائنا! قد تحتاج إلى الترقية ، أو قد تحتاج العقد الأخرى إلى الترقية. + + + You must specify a masternodeprivkey in the configuration. Please see documentation for help. + يجب عليك تحديد masternodeprivkey في التكوين. يرجى الاطلاع على الوثائق للمساعدة. + + + Accept command line and JSON-RPC commands + قبول أوامر وحدة التحكم وأوامر JSON-RPC + + + Add a node to connect to and attempt to keep the connection open + أضف عقدة للاتصال بها ومحاولة للحفاظ على اتصال مفتوح + + + Allow DNS lookups for -addnode, -seednode and -connect + السماح بعمليات البحث عن DNS لـ -ddnode و -seednode و -connect + + + Already have that input. + بالفعل هذا الإدخال. + + + Block creation options: + خيارات صناعة الكتل + + + Cannot downgrade wallet + لا يمكن تخفيض قيمة المحفظة + + + Cannot write default address + لا يمكن كتابة العنوان الافتراضي + + + Collateral not valid. + ضمانات غير صالحة. + + + Connect to a node to retrieve peer addresses, and disconnect + الاتصال بعقدة لاسترداد عناوين النظير ، وقطع الاتصال + + + Connection options: + خيارات الاتصال: + + + Corrupted block database detected + تم الكشف عن قاعدة بيانات كتل تالفة + + + Debugging/Testing options: + خيارات التصحيح / الاختبار: + + + Do not load the wallet and disable wallet RPC calls + لا تقم بتحميل المحفظة وتعطيل استدعاءات RPC للمحفظة + + + Do you want to rebuild the block database now? + هل تريد إعادة بناء قاعدة بيانات الكتل الآن؟ + + + Done loading + انتهاء التحميل + + + Entries are full. + إدخالات ممتلئة. + + + Error initializing block database + خطأ في تهيئة قاعدة البيانات + + + Error initializing wallet database environment %s! + حدث خطأ أثناء تهيئة بيئة قاعدة بيانات المحفظة %s! + + + Error loading block database + خطأ في تحميل قاعدة بيانات الكتل + + + Error opening block database + خطأ في فتح قاعدة بيانات الكتل + + + Error reading from database, shutting down. + خطأ في القراءة من قاعدة البيانات ، والتوقف. + + + Error + خطأ + + + Error: Disk space is low! + تحذير: مساحة القرص منخفضة + + + Failed to listen on any port. Use -listen=0 if you want this. + فشل في الاستماع على أي منفذ. استخدام الاستماع = 0 إذا كنت تريد هذا. + + + (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data) + (1 = الاحتفاظ ببيانات التعريف tx ، على سبيل المثال ، مالك الحساب ومعلومات طلب الدفع ، 2 = بيانات تعريف tx drop) + + + -maxtxfee is set very high! Fees this large could be paid on a single transaction. + يتم تعيين maxtxfee عالية جدا! يمكن دفع هذه الرسوم في معاملة واحدة. + + + A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s) + سعر الرسوم (بالكيلو %s / kB) الذي سيتم استخدامه عندما يكون تقدير الرسوم غير كافٍ للبيانات (الافتراضي: %s) + + + Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d) + قبول المعاملات المرحّلة التي تم استلامها من أقران القائمة البيضاء حتى عند عدم ترحيل المعاملات (القيمة الافتراضية: %d) + + + Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times + السماح لاتصالات JSON-RPC من المصدر المحدد. صالحة ل<ip> عبارة عن عنوان IP فردي (على سبيل المثال ، 1.2.3.4) ، وشبكة / قناع شبكة (على سبيل المثال 1.2.3.4/255.255.255.0) أو شبكة / CIDR (على سبيل المثال 1.2.3.4/24). يمكن تحديد هذا الخيار عدة مرات + + + Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6 + ربط العنوان المعطى وأقرانه في القائمة البيضاء بالاتصال به. استخدم [host]: تدوين المنفذ لـ IPv6 + + + Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces) + ربط عنوان معين للاستماع إلى اتصالات JSON-RPC. استخدم [host]: تدوين المنفذ لـ IPv6. يمكن تحديد هذا الخيار عدة مرات (افتراضي: ربط لجميع الواجهات) + + + Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality) + إنشاء ملفات جديدة بأذونات النظام الافتراضية ، بدلاً من umask 077 (فعال فقط مع وظيفة المحفظة المعطلة) + + + Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup + حذف جميع معاملات المحفظة واسترداد فقط تلك الأجزاء من سلسلة الكتل عبر -escan عند بدء التشغيل + + + Error loading %s: You can't enable HD on a already existing non-HD wallet + حدث خطأ أثناء تحميل %s: لا يمكنك تمكين HD في محفظة غير عالية موجودة بالفعل + + + Found unconfirmed denominated outputs, will wait till they confirm to continue. + العثور على نتائج مقومة غير مؤكدة ، سوف تنتظر حتى تؤكد استمرارها. + + + How thorough the block verification of -checkblocks is (0-4, default: %u) + مدى دقة التحقق من كتلة -checkblocks (0-4 ، الافتراضي: %u) + + + If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u) + إذا لم يتم تعيين paytxfee ، قم بتضمين رسوم كافية حتى تبدأ المعاملات في التأكيد في المتوسط داخل كتل n (الافتراضي: %u) + + + 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: %s, testnet: %s) + إذا كانت هذه المجموعة في السلسلة تفترض أنها وأصولها صالحة ، ومن المحتمل أن تتخطى التحقق من البرنامج النصي (0 للتحقق من الكل ، الافتراضي: %s ، testnet: %s) + + + Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions) + كمية غير صالحة لـ -maxtxfee = : '%s' <amount> (يجب أن تكون على الأقل رسوم minrelay %s لمنع المعاملات عالقة) + + + Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u) + احتفظ بفهرس للمعاملات كامل ، يستخدم من قبل استدعاء getrawtransaction (الافتراضي: %u) + + + Make sure to encrypt your wallet and delete all non-encrypted backups after you verified that wallet works! + تأكد من تشفير محفظتك وحذف جميع النسخ الاحتياطية غير المشفرة بعد التحقق من أن هذه المحفظة تعمل! + + + Maximum size of data in data carrier transactions we relay and mine (default: %u) + الحد الأقصى لحجم البيانات في معاملات شركات نقل البيانات التي نقوم بترحيلها وبياناتي (الافتراضي: %u) + + + Number of seconds to keep misbehaving peers from reconnecting (default: %u) + عدد الثواني للاحتفاظ بسوء تصرف الأقران من إعادة الاتصال (الإعداد الافتراضي: %u) + + + Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s) + استخدام وكيل SOCKS5 منفصل للوصول إلى الأقران عبر خدمات تور المخفية (القيمة الافتراضية: %s) + + + User defined mnemonic for HD wallet (bip39). Only has effect during wallet creation/first start (default: randomly generated) + تعريف المستخدم ذاكري لمحرك HD (bip39). يكون تأثيره فقط أثناء إنشاء المحفظة / البدء الأول (افتراضي: يتم إنشاؤه عشوائيًا) + + + User defined seed for HD wallet (should be in hex). Only has effect during wallet creation/first start (default: randomly generated) + تعريف المستخدم البذور لمحفظ HD (يجب أن يكون في ست عشري). يكون تأثيره فقط أثناء إنشاء المحفظة / البدء الأول (افتراضي: يتم إنشاؤه عشوائيًا) + + + Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway + لا يمكن حظر الزملاء في القائمة البيضاء في DoS ويتم ترحيل معاملاتهم دائمًا ، حتى إذا كانت موجودة بالفعل في mempool ، على سبيل المثال ، على سبيل المثال ، لبوابة + + + You need to rebuild the database using -reindex-chainstate to change -txindex + تحتاج إلى إعادة بناء قاعدة البيانات باستخدام -reindex-chainstate لتغيير -txindex + + + (default: %s) + (القيمة الافتراضية: %s) + + + Accept public REST requests (default: %u) + قبول طلبات REST العامة (القيمة الافتراضية: %u) + + + Always query for peer addresses via DNS lookup (default: %u) + استعلم دومًا عن عناوين النظير عبر البحث عن DNS (الافتراضي: %u) + + + Can't mix: no compatible inputs found! + لا يمكن الجمع: لم يتم العثور على مدخلات متوافقة! + + + Chain selection options: + خيارات اختيار السلسلة: + + + Connect through SOCKS5 proxy + الاتصال من خلال وكيل SOCKS5 + + + Connect to KeePassHttp on port <port> (default: %u) + الاتصال KeePassHttp على المنفذ<port> ( الافتراضي: %u ) + + + Enable the client to act as a masternode (0-1, default: %u) + تمكين العميل ليكون بمثابة ماسترنود (0-1 ، الافتراضي: %u) + + + Entry exceeds maximum size. + يتجاوز الدخول الحد الأقصى للحجم. + + + Error loading %s: You can't disable HD on a already existing HD wallet + حدث خطأ أثناء تحميل %s: لا يمكنك تعطيل HD في محفظة HD موجودة بالفعل + + + Failed to load fulfilled requests cache from + فشل في تحميل ذاكرة التخزين المؤقت للطلبات التي تم تنفيذها. + + + Failed to load governance cache from + فشل تحميل ذاكرة التخزين المؤقتة من + + + Failed to load masternode cache from + فشل تحميل ذاكرة التخزين المؤقتة من + + + Failed to load masternode payments cache from + أخفق تحميل ذاكرة التخزين المؤقت لمدفوعات ماسترنود من + + + Found enough users, signing ( waiting %s ) + العثور على عدد كافٍ من المستخدمين ، والتوقيع (الانتظار %s) + + + Found enough users, signing ... + العثور على عدد كافٍ من المستخدمين ، والتوقيع ... + + + How many blocks to check at startup (default: %u, 0 = all) + كم عدد الكتل المطلوب فحصها عند بدء التشغيل (الافتراضي: %u، 0 =) + + + Importing... + استيراد... + + + Include IP addresses in debug output (default: %u) + تضمين عناوين IP في مخرجات التصحيح (القيمة الافتراضية: %u) + + + Incompatible mode. + وضع غير متوافق. + + + Incompatible version. + إصدار غير متوافق. + + + Incorrect or no genesis block found. Wrong datadir for network? + لم يتم العثور على كتلة تكوين أو لم تكون صحيحة. datadir خاطئة للشبكة؟ + + + Information + معلومات + + + Input is not valid. + الإدخال غير صالح .. + + + Insufficient funds. + رصيد غير كاف. + + + Invalid -onion address: '%s' + عنوان - صالح غير صالح: '%s' + + + Invalid -proxy address: '%s' + عنوان البروكسي غير صحيح : '%s' + + + Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s) + مبلغ غير صالح لـ -paytxfee = : '%s' <amount> (يجب أن يكون على الأقل %s) + + + Keypool ran out, please call keypoolrefill first + ركض خارج ، يرجى الاتصال الأولى + + + Listen for JSON-RPC connections on <port> (default: %u or testnet: %u) + استمع لاتصالات JSON-RPC<port> (الإعداد الافتراضي: %u أو testnet: %u) + + + Listen for connections on <port> (default: %u or testnet: %u) + استمع لاتصالات<port>(الإعداد الافتراضي: %u أو testnet: %u) + + + Loading banlist... + جاري تحميل قائمة الحظر... + + + Loading fulfilled requests cache... + جارٍ تحميل ذاكرة التخزين المؤقت للطلبات المكتملة ... + + + Loading masternode cache... + جارٍ تحميل ذاكرة التخزين المؤقت في ماسترنود ... + + + Loading masternode payment cache... + جارٍ تحميل ذاكرة التخزين المؤقت للدفع ماسترنود ... + + + Lock is already in place. + قفل بالفعل في المكان. + + + Lock masternodes from masternode configuration file (default: %u) + قفل ماسترنود من ملف تهيئة ماسترنود (الإعداد الافتراضي: %u) + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: %u) + الحد الأقصى لاستقبال كل مخزن مؤقت ، <n>* 1000 بايت (افتراضيًا: %u) + + + Maximum per-connection send buffer, <n>*1000 bytes (default: %u) + الحد الأقصى لكل اتصال الإرسال المؤقت ، <n>* 1000 بايت (افتراضيًا: %u) + + + Mixing in progress... + الدمج قيد التقدم ... + + + Mnemonic passphrase is too long, must be at most 256 characters + عبارة المرور الطويلة طويلة جدًا ويجب أن تكون 256 حرفًا على الأكثر + + + Need to specify a port with -whitebind: '%s' + تحتاج إلى تحديد منفذ مع -whitebind: '%s' + + + No Masternodes detected. + لم يتم اكتشاف ماسترنود. + + + No compatible Masternode found. + لم يتم العثور على متسترنود متوافق. + + + Not in the Masternode list. + ليس في قائمة ماسترنود. + + + Only connect to nodes in network <net> (ipv4, ipv6 or onion) + فقط الاتصال بالعقد في الشبكة <net> (ipv4, ipv6 or onion) + + + Port: %d + Port: %d + + + Prepend debug output with timestamp (default: %u) + إعداد إخراج debug مع الطابع الزمني (الافتراضي: %u) + + + Rebuild chain state and block index from the blk*.dat files on disk + إعادة إنشاء حالة سلسلة وكتلة فهرس من ملفات blk * .dat على القرص + + + Rebuild chain state from the currently indexed blocks + إعادة بناء سلسلة السلسلة من الكتل المفهرسة حاليًا + + + Send trace/debug info to debug.log file (default: %u) + إرسال معلومات التتبع / التصحيح إلى ملف debug.log (الافتراضي: %u) + + + Send transactions as zero-fee transactions if possible (default: %u) + إرسال المعاملات بصفقات صفرية إن أمكن (الافتراضي: %u) + + + Set key pool size to <n> (default: %u) + تعيين حجم تجمع مفتاح <n> (افتراضي: %u) + + + Set the number of threads to service RPC calls (default: %d) + تعيين عدد مؤشرات الترابط لخدمة المكالمات RPC (الافتراضي: %d) + + + Specify configuration file (default: %s) + تحديد ملف التهيئة (افتراضي: %s) + + + Specify connection timeout in milliseconds (minimum: 1, default: %d) + تحديد مهلة الاتصال بالمللي ثانية (الحد الأدنى: 1 ، القيمة الافتراضية: %d) + + + Specify masternode configuration file (default: %s) + تحديد ملف تهيئة ماسترنود (الإعداد الافتراضي: %s) + + + Specify pid file (default: %s) + تحديد ملف pid (الافتراضي: %s) + + + Spend unconfirmed change when sending transactions (default: %u) + اقضي على تغيير غير مؤكد عند إرسال المعاملات (القيمة الافتراضية: %u) + + + Submitted following entries to masternode: %u / %d + يتم إرسال الإدخالات التالية إلى ماسترنود : %u / %d + + + Submitted to masternode, waiting for more entries ( %u / %d ) %s + تم إرساله إلى ماسترنود ، في انتظار المزيد من الإدخالات ( %u / %d) %s + + + Submitted to masternode, waiting in queue %s + تم إرساله إلى ماسترنود ، في الانتظار في قائمة الانتظار %s + + + Synchroning blockchain... + تزامن بلوكشين ... + + + Synchronization failed + فشل التزامن + + + Synchronization finished + انتهى التزامن + + + Synchronizing masternodes... + تزامن ماسترنود... + + + This is not a Masternode. + هذه ليست ماسترنود. + + + Threshold for disconnecting misbehaving peers (default: %u) + الحد الأدنى لفصل أقران سوء السلوك (الافتراضي: %u) + + + Too many %f denominations, removing. + عدد كبير جدًا من %f فئة ، إزالة. + + + Tor control port password (default: empty) + كلمة مرور منفذ تحكم تور (القيمة الافتراضية: فارغة) + + + Tor control port to use if onion listening enabled (default: %s) + منفذ تحكم Tor لاستخدامه في حالة تفعيل الاستماع بالبصل (افتراضي: %s) + + + Unable to start HTTP server. See debug log for details. + غير قادر على بدء خادم ال HTTP. راجع سجل تصحيح الأخطاء للحصول على التفاصيل. + + + Unknown response. + استجابة غير معروفة. + + + Unsupported argument -benchmark ignored, use -debug=bench. + الوسيطة غير مدعومة - بامتياز تجاهلها ، استخدم -debug = bench. + + + Unsupported argument -debugnet ignored, use -debug=net. + تم تجاهل الوسيطة غير المدعومة -debugnet ، استخدم -debug = net. + + + Unsupported argument -tor found, use -onion. + تم العثور على وسيطة غير مدعومة -tor ، استخدم -onion. + + + Upgrade wallet to latest format on startup + ترقية المحفظة إلى التنسيق الأحدث عند بدء التشغيل + + + Use KeePass 2 integration using KeePassHttp plugin (default: %u) + استخدام تكامل KeePass 2 باستخدام المكون الإضافي KeePassHttp (الإعداد الافتراضي: %u) + + + Use UPnP to map the listening port (default: %u) + استخدام UPnP لتعيين منفذ الاستماع (الافتراضي: %u) + + + Use the test chain + إستخدم السلسلة التجريبية + + + User Agent comment (%s) contains unsafe characters. + يحتوي تعليق وكيل المستخدم (%s) على أحرف غير آمنة. + + + Will retry... + سيعيد المحاولة ... + + + Invalid masternodeprivkey. Please see documenation. + ماسترنود غير صحيح. يرجى الاطلاع على الوثائق + + + Can't find random Masternode. + لا يمكن العثور على ماسترنود عشوائي. + + + Can't mix while sync in progress. + لا يمكن الدمج أثناء المزامنة قيد التقدم. + + + Could not parse masternode.conf + لا يمكن تحليل masternode.conf + + + Invalid netmask specified in -whitelist: '%s' + قناع الشبكة غير صالح محدد في-القائمة البيضاء: '%s' + + + Invalid port detected in masternode.conf + تم اكتشاف منفذ غير صالح في masternode.conf + + + Invalid script detected. + تم اكتشاف نص برمجي غير صالح. + + + KeePassHttp id for the established association + معرف KeePassHttp للرابطة المنشأة + + + KeePassHttp key for AES encrypted communication with KeePass + مفتاح KeePassHttp للتواصل المشفر مع AES KeePass + + + Keep at most <n> unconnectable transactions in memory (default: %u) + ابقى على اقصى حد <n> معاملات غير قابلة للاتصال في الذاكرة (القيمة الافتراضية: %u) + + + Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) + تعطيل جميع وظائف داش المحددة (ماسترنود ، الإرسال المخفي ، الإرسال الفوري ، الحوكمة) (0-1 ، القيمة الافتراضية: %u) + + + Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) + اكتشاف عناوين IP الخاصة (الافتراضي: 1 عند الاستماع وعدم وجود - أو "خضوع") + + + Do not keep transactions in the mempool longer than <n> hours (default: %u) + لا تحتفظ بالمعاملات في الذاكرة أكثر من <n> ساعات (الافتراضي: %u) + + + Enable InstantSend, show confirmations for locked transactions (0-1, default: %u) + تمكين الإرسال الفوري ، عرض التأكيدات للمعاملات المقفلة (0-1 ، القيمة الافتراضية: %u) + + + Enable multiple PrivateSend mixing sessions per block, experimental (0-1, default: %u) + تمكين جلسات دمج الإرسال المحمي متعددة لكل كتلة ، تجريبية (0-1 ، القيمة الافتراضية: %u) + + + Enable use of automated PrivateSend for funds stored in this wallet (0-1, default: %u) + تمكين استخدام الإرسال المخفي التلقائي للأموال المخزنة في هذه المحفظة (0-1 ، القيمة الافتراضية: %u) + + + Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID) + تنفيذ الأمر عند قفل معاملة الإرسال الفوري في المحفظة بنجاح (يتم استبدال %s في cmd بـ TxID) + + + Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. + فشل إنشاء نسخة احتياطية ، يوجد ملف بالفعل! قد يحدث هذا إذا أعدت تشغيل المحفظة في أقل من 60 ثانية. يمكنك الاستمرار إذا كنت على ما يرام مع هذا. + + + Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s) + الرسوم (بالكيلو %s / kB) الأصغر من ذلك تعتبر رسومًا صفرًا في حالة الترحيل والتعدين وإنشاء المعاملات (الافتراضي: %s) + + + Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s) + الرسوم (بـ %s / kB) الأصغر من ذلك تعتبر رسومًا صفرًا لإنشاء المعاملات (الافتراضي: %s) + + + If <category> is not supplied or if <category> = 1, output all debugging information. + إذا <category> لا يتم توفير أو إذا <category> = 1 ، إخراج كافة معلومات التصحيح + + + InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH. + لا يدعم الإرسال الغوري إرسال قيم عالية حتى الآن. تقتصر المعاملات حاليًا على%1 داش. + + + InstantSend requires inputs with at least %d confirmations, you might need to wait a few minutes and try again. + يتطلب الإرسال الفوري مدخلات مع %d تأكيدات على الأقل ، قد تحتاج إلى الانتظار بضع دقائق وإعادة المحاولة. + + + Maintain a full address index, used to query for the balance, txids and unspent outputs for addresses (default: %u) + الاحتفاظ بفهرس كامل للعنوان ، يُستخدم للاستعلام عن الرصيد ، و txids والمخرجات غير المنفقة للعناوين (القيمة الافتراضية: %u) + + + Maintain a full spent index, used to query the spending txid and input index for an outpoint (default: %u) + الاحتفاظ بفهرس كامل مستنفد ، يُستخدم للاستعلام عن إنفاق txid ومُدخل فهرس نقطة خارجية (افتراضي: %u) + + + Maintain a timestamp index for block hashes, used to query blocks hashes by a range of timestamps (default: %u) + الحفاظ على فهرس الطابع الزمني لكتل التجزئات ، المستخدمة في الاستعلام عن تجزئة القطع بواسطة مجموعة من الطوابع الزمنية (الإعداد الافتراضي: %u) + + + Maintain at most <n> connections to peers (temporary service connections excluded) (default: %u) + الحفاظ على الأكثر <n> اتصالات للأقران (باستثناء اتصالات الخدمة المؤقتة) (الافتراضي: %u) + + + PrivateSend uses exact denominated amounts to send funds, you might simply need to anonymize some more coins. + تستخدم الإرسال المخفي مبالغ محددة بدقة لإرسال الأموال ، قد تحتاج ببساطة إلى إخفاء بعض العملات الأخرى. + + + Prune configured below the minimum of %d MiB. Please use a higher number. + تم تكوين Prune أسفل الحد الأدنى من %d MiB. يرجى استخدام عدد أكبر. + + + Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node) + تقليم: آخر تزامن المحفظة يتجاوز البيانات المحصورة. تحتاج إلى -reindex (قم بتنزيل بلوكشين بأكمله مرة أخرى في حالة عقدة مقشرة) + + + Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u) + عشوائية وثائق الاعتماد لكل اتصال عبر وكيل. يتيح ذلك عزل تيار تور (القيمة الافتراضية: %u) + + + Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again. + إعادة المسح ليست ممكنة في وضع مجسم. سوف تحتاج إلى استخدام -reindex التي ستقوم بتنزيل بلوكشين كله مرة أخرى. + + + Specify full path to directory for automatic wallet backups (must exist) + تحديد المسار الكامل للدليل للنسخ الاحتياطية للمحفظة التلقائية (يجب أن تكون موجودة) + + + Support filtering of blocks and transaction with bloom filters (default: %u) + دعم تصفية الكتل والمعاملات مع مرشحات bloom (الافتراضي: %u) + + + The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct + تحتوي قاعدة البيانات كتلة على كتلة يبدو من المستقبل. قد يكون هذا بسبب تعيين تاريخ ووقت الكمبيوتر الخاص بك بشكل غير صحيح. فقط إعادة إنشاء قاعدة البيانات كتلة إذا كنت متأكداً من صحة التاريخ والوقت للكمبيوتر الخاص بك + + + The transaction amount is too small to send after the fee has been deducted + قيمة المعاملة صغيرة جدًا ولا يمكن إرسالها بعد خصم الرسوم + + + Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. + إجمالي طول سلسلة إصدار الشبكة (%i) يتجاوز الحد الأقصى للطول (%i). تقليل عدد أو حجم + + + Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) + يحاول الحفاظ على حركة المرور الصادرة تحت الهدف المحدد (في MiB لكل 24 ساعة) ، 0 = بلا حدود (الافتراضي: %d) + + + Unable to locate enough PrivateSend denominated funds for this transaction. + غير قادر على تحديد ما يكفي من الأموال المخصصة الإرسال المحمي لهذه الصفقة .. + + + Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported. + تم العثور على وسيطة غير مدعومة. إعداد SOCKS غير ممكن بعد الآن ، يتم دعم بروكسيات SOCKS5 فقط. + + + Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay. + الوسيطة غير المدعومة -whitelistalwaysrelay تم تجاهلها واستخدامها - و / أو whitelistforcerelay و / أو -whitelistforcerelay. + + + Use UPnP to map the listening port (default: 1 when listening and no -proxy) + استخدام UPnP لتعيين منفذ الاستماع (الافتراضي: 1 عند الاستماع و no -proxy) + + + WARNING! Failed to replenish keypool, please unlock your wallet to do so. + تحذير! تعذّر تجديد مفتاح keypool ، يرجى إلغاء قفل محفظتك للقيام بذلك. + + + Wallet is locked, can't replenish keypool! Automatic backups and mixing are disabled, please unlock your wallet to replenish keypool. + المحفظة مؤمّنة ، ولا يمكن تجديد مفتاح keypool! تم تعطيل النسخ الاحتياطي التلقائي والاختلاط ، يرجى فتح محفظتك لتجديد keypool + + + Warning: Unknown block versions being mined! It's possible unknown rules are in effect + تحذير: يتم نسخ إصدارات الحظر غير المعروفة! من المحتمل أن تكون قواعد غير معروفة سارية المفعول + + + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain + تحتاج إلى إعادة إنشاء قاعدة البيانات باستخدام -reindex للعودة إلى الوضعية الغير مجردة. هذا سوف يعيد تحميل سلسلة الكتل بأكملها + + + (%d could be used only on mainnet) + (%d يمكن استخدامها فقط على mainnet) + + + (default: %u) + (القيمة الافتراضية: %u) + + + (must be %d for mainnet) + (يجب أن يكون %d لـ mainnet) + + + -maxmempool must be at least %d MB + يجب أن يكون maxmempool على الأقل %d ميغابايت + + + <category> can be: + <category> يمكن أن يكون + + + Append comment to the user agent string + إلحاق تعليق إلى سلسلة وكيل المستخدم + + + Automatic backups disabled + النسخ الاحتياطي التلقائي تعطيل + + + Automatically create Tor hidden service (default: %d) + إنشاء خدمة تور المخفية تلقائيًا (القيمة الافتراضية: %d) + + + ERROR! Failed to create automatic backup + خطأ! فشل إنشاء نسخة احتياطية تلقائية + + + Enable publish hash block in <address> + تمكين حظر التجزئة في <address> + + + Enable publish hash transaction (locked via InstantSend) in <address> + تمكين عملية التجزئة المنشورة (مؤمنة عبر الإرسال الفوري) في <address> + + + Enable publish hash transaction in <address> + تمكين معالجة التجزئة في <address> + + + Enable publish raw block in <address> + تمكين نشر كتلة الخام في <address> + + + Enable publish raw transaction (locked via InstantSend) in <address> + تمكين نشر المعاملة الأولية (مؤمّنة عبر InstantSend) في <address> + + + Enable publish raw transaction in <address> + تمكين نشر المعاملة الأولية في <address> + + + Enable transaction replacement in the memory pool (default: %u) + تمكين استبدال المعاملة في تجمع الذاكرة (الافتراضي: %u) + + + Error: A fatal internal error occurred, see debug.log for details + خطأ: حدث خطأ داخلي فادح، راجع debug.log للحصول على التفاصيل + + + Failed to create backup %s! + فشل إنشاء النسخة الاحتياطية %s! + + + Failed to create backup, error: %s + أخفق إنشاء نسخة احتياطية ، خطأ: %s + + + Failed to delete backup, error: %s + أخفق حذف النسخة الاحتياطية ، الخطأ: %s + + + Failed to parse host:port string + فشل تحليل المضيف: سلسلة المنفذ + + + Fee (in %s/kB) to add to transactions you send (default: %s) + الرسوم (في %s / kB) لإضافتها إلى المعاملات التي ترسلها (الافتراضي: %s) + + + Imports blocks from external blk000??.dat file on startup + استيراد كتل من ملف blk000??.dat الخارجي عند بدء التشغيل + + + InstantSend options: + خيارات الإرسال الفوري: + + + Invalid amount for -fallbackfee=<amount>: '%s' + المبلغ غير صالح forfallbackfee = <amount> :'%s + + + Keep the transaction memory pool below <n> megabytes (default: %u) + إبقاء تجمع ذاكرة المعاملة أدناه <n> ميغابايت (افتراضي: %u) + + + Last PrivateSend was too recent. + اخر إرسال مخفي كان حديث للغاية + + + Last successful PrivateSend action was too recent. + آخر إجراء الإرسال المخفي ناجح كان حديث للغاية. + + + Line: %d + سطر: %d + + + Loading addresses... + تحميل العنوان + + + Loading block index... + تحميل مؤشر الكتلة + + + Loading governance cache... + جارٍ تحميل ذاكرة التخزين المؤقتة ... + + + Loading wallet... (%3.2f %%) + جارٍ تحميل المحفظة ... (%3.2f %%) + + + Loading wallet... + تحميل المحفظه + + + Location of the auth cookie (default: data dir) + موقع ملف تعريف ارتباط المصادقة (القيمة الافتراضية: دليل البيانات) + + + Make the wallet broadcast transactions + إنتاج معاملات بث المحفظة + + + Masternode cache is empty, skipping payments and governance cache... + ذاكرة التخزين المؤقت ماسترنود فارغة ، وتخطي الدفعات وذاكرة التخزين المؤقت للحوكمة ... + + + Masternode options: + خيارات ماسترنود + + + Masternode queue is full. + طابور ماسترنود ممتلئ. + + + Masternode: + ماسترنود + + + Minimum bytes per sigop in transactions we relay and mine (default: %u) + .الحد الأدنى من البايت لكل sigop في المعاملات التي نقوم بترحيلها وبياناتي (الافتراضي: %u) + + + Missing input transaction information. + معلومات حول معاملات الإدخال مفقودة. + + + No errors detected. + لم يتم اكتشاف أخطاء. + + + No matching denominations found for mixing. + لم يتم العثور على فئات مطابقة للاختلاط. + + + Node relay options: + خيارات ترحيل العقد: + + + Non-standard public key detected. + تم اكتشاف مفتاح عام غير قياسي. + + + Not compatible with existing transactions. + غير متوافق مع المعاملات الحالية. + + + Not enough file descriptors available. + لا تتوفر واصفات ملفات كافية. + + + Not enough funds to anonymize. + لا توجد أموال كافية لإخفاء الهوية. + + + Number of automatic wallet backups (default: %u) + عدد النسخ الاحتياطية للمحفظة التلقائية (الافتراضي: %u) + + + Options: + خيارات: + + + Password for JSON-RPC connections + كلمة المرور لاتصالات JSON-RPC + + + Print version and exit + طباعة النسخة ومن ثم الخروج + + + PrivateSend is idle. + الإرسال المخفي غير نشط. + + + PrivateSend options: + خيارات الإرسال المخفي: + + + PrivateSend request complete: + اكتمل طلب الإرسال المخفي: + + + PrivateSend request incomplete: + طلب الإرسال المخفي غير مكتمل: + + + Prune cannot be configured with a negative value. + لا يمكن تهيئة التجريد بقيمة سالبة. + + + Prune mode is incompatible with -txindex. + وضع التجريد غير متوافق مع -txindex. + + + Pruning blockstore... + تجريد مخزن الكتل... + + + Rescan the block chain for missing wallet transactions on startup + إعادة فحص سلسلة كتلة معاملات المحفظة المفقودة عند بدء التشغيل + + + The wallet will avoid paying less than the minimum relay fee. + سوف تتجنب المحفظة دفع أقل من الحد الأدنى لرسوم التتابع. + + + This is the minimum transaction fee you pay on every transaction. + هذا هو الحد الأدنى من رسوم المعاملة التي تدفعها في كل معاملة. + + + This is the transaction fee you will pay if you send a transaction. + هذه هي رسوم المعاملة التي ستدفعها إذا أرسلت معاملة. + + + Transaction amounts must not be negative + يجب ألا تكون قيمة المعاملة سلبية + + + Transaction has too long of a mempool chain + المعاملة طويلة جدًا من سلسلة + + + Transaction must have at least one recipient + يجب أن تحتوي المعاملة على مستلم واحد على الأقل + + + Trying to connect... + تحاول الاتصال... + + + Unable to bind to %s on this computer. %s is probably already running. + يتعذر الربط مع %s على هذا الكمبيوتر. من المحتمل أن %s قيد التشغيل بالفعل. + + + Upgrading UTXO database + ترقية قاعدة بيانات UTXO + + + Use devnet chain with provided name + استخدم سلسلة devnet بالاسم المقدم + + + Wallet debugging/testing options: + خيارات تصحيح / اختبار المحفظة: + + + Wallet needed to be rewritten: restart %s to complete + يلزم إعادة كتابة المحفظة: إعادة تشغيل %s لإكمال العملية + + + Warning: unknown new rules activated (versionbit %i) + تحذير: قواعد جديدة غير معروفة مفعلة (versionbit %i) + + + Wasn't able to create wallet backup folder %s! + لم يكن قادراً على إنشاء مجلد النسخ الاحتياطي المحفظة %s! + + + Whether to operate in a blocks only mode (default: %u) + ما إذا كان سيتم العمل في وضع الكتل فقط (الإعداد الافتراضي: %u) + + + ZeroMQ notification options: + خيارات إعلام ZeroMQ: + + + no mixing available. + لا خلط المتاحة. + + + see debug.log for details. + انظر debug.log للحصول على التفاصيل. + + + RPC server options: + خيارات خادم RPC: + + + Dash Core + جوهر الداش + + + The %s developers + %s المبرمجون + + + Accept connections from outside (default: 1 if no -proxy or -connect/-noconnect) + قبول الاتصالات من الخارج (الافتراضي: 1 إذا لم يكن هناك أي -proxy أو -connect / -noconnect) + + + Cannot obtain a lock on data directory %s. %s is probably already running. + لا يمكن الحصول على قفل على دليل البيانات %s. من المحتمل أن %s يعمل بالفعل. + + + Connect only to the specified node(s); -noconnect or -connect=0 alone to disable automatic connections + الاتصال فقط (بالعقدة) المحددة ؛ -اتصال أو اتصال = 0 فقط لتعطيل الاتصالات التلقائية + + + Distributed under the MIT software license, see the accompanying file %s or %s + موزعة تحت ترخيص برنامج MIT ، راجع الملف المرافق %s أو %s + + + Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + خطأ في قراءة %s! قراءة كافة المفاتيح بشكل صحيح ، ولكن قد تكون بيانات المعاملة أو إدخالات دفتر العناوين مفقودة أو غير صحيحة. + + + Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d) + فرض ترحيل المعاملات من أقران القائمة البيضاء حتى إذا كانت تنتهك سياسة الترحيل المحلي (افتراضي: %d) + + + Incorrect or no devnet genesis block found. Wrong datadir for devnet specified? + تم العثور على كتلة تكوين devnet غير صحيحة أو لا. datadir خاطئة ل devnet المحدد؟ + + + Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds) + الحد الأقصى المسموح به من متوسط الوقت لتعديل الأقران. قد يتأثر المنظور المحلي للوقت بالأقران إلى الأمام أو إلى الخلف بهذا المقدار. (افتراضي: %u ثانية) + + + Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s) + الحد الأقصى للرسوم الإجمالية (في %s) لاستخدامها في معاملة محفظة واحدة أو معاملة أولية ؛ قد يؤدي تعيين هذا منخفض جدًا إلى إحباط المعاملات الكبيرة (الإعداد الافتراضي: %s) + + + Override spork address. Only useful for regtest and devnet. Using this on mainnet or testnet will ban you. + تجاوز عنوان السكة. مفيدة فقط ل regtest و devnet. استخدام هذا على mainnet أو testnet سيحظر لك. + + + Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly. + يرجى التحقق من صحة تاريخ ووقت جهازك! إذا كانت ساعتك خاطئة ، فلن يعمل %s بشكل صحيح. + + + Please contribute if you find %s useful. Visit %s for further information about the software. + يرجى المساهمة إذا وجدت %s مفيداً. تفضل بزيارة %s لمزيد من المعلومات حول البرنامج. + + + 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) + توفير السيولة إلى الإرسال المخفي عن طريق خلط العملات المعدنية بشكل متكرر على أساس مستمر (%u-%u ، القيمة الافتراضية: %u ، 1 = متكررة للغاية ، رسوم عالية ، %u = نادر جداً ، رسوم منخفضة) + + + Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect/-noconnect) + الاستعلام عن عناوين النظير عبر البحث عن DNS ، إذا كان منخفضًا في العناوين (افتراضي: 1 ، ما لم يتم الاتصال / الاتصال) + + + Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB) + تقليل متطلبات التخزين عن طريق تمكين التقليم (حذف) الكتل القديمة. يسمح هذا باستدعاء RUN pruneblockchain لحذف كتل معينة ، وتمكين التقليم التلقائي للكتل القديمة إذا تم توفير حجم الهدف في MiB. هذا الوضع غير متوافق مع -txindex و -rescan. تحذير: يتطلب إعادة هذا الإعداد إعادة تنزيل blockchain بالكامل. (افتراضي: 0 = تعطيل كتل التشذيب ، 1 = السماح بالتقليم اليدوي عن طريق RPC ،> %u = ملفات التجميع تلقائياً للتخفيض تحت حجم الهدف المحدد في MiB) + + + Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) + تعيين أدنى معدل رسوم (في %s / kB) للمعاملات ليتم تضمينها في إنشاء كتلة. (افتراضي: %s) + + + This is the transaction fee you may pay when fee estimates are not available. + هذه هي رسوم المعاملة التي قد تدفعها عندما لا تكون تقديرات الرسوم متوفرة. + + + This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit %s and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard. + يتضمن هذا المنتج برنامجًا تم تطويره بواسطة OpenSSL Project للاستخدام في OpenSSL Toolkit %s وبرنامج التشفير الذي كتبه Eric Young و UPnP الذي كتبه Thomas Bernard. + + + Unable to locate enough PrivateSend non-denominated funds for this transaction. + غير قادر على تحديد ما يكفي من الأموال المخصصة غير المقومة الإرسال المخفي لهذه الصفقة. + + + Use N separate masternodes for each denominated input to mix funds (%u-%u, default: %u) + استخدم N ماسترنود منفصلة لكل إدخال مقنن لخلط الصناديق (%u-%u ، القيمة الافتراضية: %u) + + + Use hierarchical deterministic key generation (HD) after BIP39/BIP44. Only has effect during wallet creation/first start + استخدام جيل المفتاح الحتمي الهرمي (HD) بعد BIP39 / BIP44. يكون تأثيره فقط أثناء إنشاء المحفظة / البدء الأول + + + User defined mnemonic passphrase for HD wallet (BIP39). Only has effect during wallet creation/first start (default: empty string) + تعريف المستخدم عبارة مرور ذاكري لمحفظة HD (BIP39). يكون تأثيره فقط أثناء إنشاء المحفظة / البدء الأول (افتراضي: سلسلة فارغة) + + + Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times + اسم المستخدم وكلمة المرور المجزأة لاتصالات JSON-RPC. الميدان<userpw> يأتي في الشكل:<USERNAME> : <SALT> $<HASH> يتم تضمين النصي بيثون الكنسي في المشاركة / rpcuser. ثم يتصل العميل عادة باستخدام rpcuser = <USERNAME> /rpcpassword<PASSWORD> زوج من الحجج. يمكن تحديد هذا الخيار عدة مرات + + + Wallet will not create transactions that violate mempool chain limits (default: %u) + لن تنشئ المحفظة المعاملات التي تنتهك حدود سلسلة mempool (القيمة الافتراضية: %u) + + + Warning: At least %d of %d masternodes are running on a newer software version. Please check latest releases, you might need to update too. + تحذير: يتم تشغيل %d من %d ماسترنود على إصدار برنامج أحدث. يرجى التحقق من أحدث الإصدارات ، فقد تحتاج إلى التحديث أيضًا. + + + Warning: Every masternode (out of %d known ones) is running on a newer software version. Please check latest releases, it's very likely that you missed a major/critical update. + تحذير: يتم تشغيل كل ماسترنود (خارج %d المعروفة) على إصدار برنامج أحدث. يرجى التحقق من أحدث الإصدارات ، من المحتمل جدًا أنك فاتتك تحديثًا رئيسيًا / هامًا. + + + Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup. + تحذير: ملف المحفظة فاسد ، تم انقاذ البيانات! تم حفظ %s الأصلي ك %s في %s؛ إذا كان رصيدك أو كانت معاملاتك غير صحيحة، فيجب عليك الإستعادة من نسخة احتياطية. + + + Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times. + أقران القائمة البيضاء المتصلين من عنوان IP المحدد (مثل 1.2.3.4) أو شبكة CIDR Notated (على سبيل المثال 1.2.3.0/24). يمكن تحديده عدة مرات. + + + %s corrupt, salvage failed + %s فاسدة ، فشلت عملية الإنقاذ + + + %s is not a valid backup folder! + %s ليس مجلد نسخ احتياطي صالح! + + + %s is set very high! + %s عالٍ جداً + + + (press q to shutdown and continue later) + (اضغط على q للإيقاف والاستمرار في وقت لاحق) + + + -devnet can only be specified once + لا يمكن تحديد -devnet إلا مرة واحدة + + + -port must be specified when -devnet and -listen are specified + يجب تحديد المنفذ عند تحديد -devnet و -listen + + + -rpcport must be specified when -devnet and -server are specified + يجب تحديد -ppcport عند تحديد -devnet و -server + + + Allow RFC1918 addresses to be relayed and connected to (default: %u) + السماح بعناوين RFC1918 والتواصل معها (الإعداد الافتراضي:%u) + + + Attempt to recover private keys from a corrupt wallet on startup + محاولة استرداد المفاتيح الخاصة من المحفظة في حالة تلفها عند بدء التشغيل + + + Cannot resolve -%s address: '%s' + لا يمكن حل - عنوان%s: '%s' + + + Change index out of range + فهرس الفكة خارج النطاق + + + Copyright (C) + حقوق الطبع والنشر (C) + + + Error loading %s + خطأ في تحميل %s + + + Error loading %s: Wallet corrupted + حدث خطأ أثناء تحميل%s: Wallet corrupted + + + Error loading %s: Wallet requires newer version of %s + حدث خطأ أثناء تحميل%s: تتطلب المحفظة إصدارًا أحدث من%s + + + Error upgrading chainstate database + خطأ في ترقية قاعدة بيانات chainstate + + + Failed to find mixing queue to join + فشل في العثور على قائمة انتظار الخلط للانضمام + + + Failed to start a new mixing queue + فشل في بدء صف مختلط جديد + + + Initialization sanity check failed. %s is shutting down. + فشل بالتحقق في اختبار التعقل. تم إيقاف %s. + + + Invalid amount for -%s=<amount>: '%s' + مبلغ غير صالح لـ -%s =<amount>: '%s' + + + Invalid spork address specified with -sporkaddr + عنوان spork غير صالح محدد بـ -sporkaddr + + + Keep N DASH anonymized (%u-%u, default: %u) + الاحتفاظ بـ N داش مجهولة المصدر (%u-%u ، القيمة الافتراضية: %u) + + + Print this help message and exit + اطبع رسالة المساعدة هذه واخرج منها + + + Receive and display P2P network alerts (default: %u) + تلقي تنبيهات شبكة P2P وعرضها (الافتراضي: %u) + + + Reducing -maxconnections from %d to %d, because of system limitations. + تقليل -maxconnections من %d إلى %d ، بسبب قيود النظام. + + + Relay and mine data carrier transactions (default: %u) + معاملات ترحيل البيانات وبيانات الألغام (الافتراضي: %u) + + + Relay non-P2SH multisig (default: %u) + Relay non-P2SH multisig (default: %u) + + + Rescanning... + إعادة مسح + + + Run in the background as a daemon and accept commands + تشغيل في الخلفية بشكل مخفي لقبول الأوامر + + + Send trace/debug info to console instead of debug.log file + إرسال معلومات التتبع / التصحيح إلى وحدة التحكم بدلاً من ملف debug.log + + + Session not complete! + الجلسة غير كاملة! + + + Session timed out. + انتهت الجلسة. + + + Set database cache size in megabytes (%d to %d, default: %d) + تعيين حجم ذاكرة التخزين المؤقت لقاعدة البيانات بالميغابايت (%d إلى %d ، القيمة الافتراضية: %d) + + + Set maximum block size in bytes (default: %d) + تعيين الحد الأقصى لحجم الفدرة بالبايت (الافتراضي: %d) + + + Set the masternode private key + تعيين مفتاح خاص ماسترنود + + + Show all debugging options (usage: --help -help-debug) + عرض جميع خيارات التصحيح (الاستخدام: - مساعدة - تصحيح الأخطاء - تصحيح الأخطاء) + + + Shrink debug.log file on client startup (default: 1 when no -debug) + تقليص ملف debug.log على بدء تشغيل العميل (الافتراضي: 1 عند عدم - debug) + + + Signing transaction failed + فشل توقيع المعاملة + + + Specify data directory + حدد مجلد المعلومات + + + Specify wallet file (within data directory) + تحديد ملف المحفظة (داخل دليل البيانات) + + + Specify your own public address + حدد عنوانك العام هنا + + + Starting network threads... + بدء مؤشرات شبكة الاتصال... + + + Synchronization pending... + التزامن في انتظار ... + + + Synchronizing governance objects... + مزامنة كائنات الحوكمة ... + + + Synchronizing masternode payments... + تزامن المدفوعات ماسترنود ... + + + The source code is available from %s. + شفرة المصدر متاحة من %s. + + + The transaction amount is too small to pay the fee + قيمة المعاملة صغيرة جدا لدفع الأجر + + + This is experimental software. + هذا برنامج تجريبي. + + + Transaction amount too small + قيمة العملية صغيره جدا + + + Transaction created successfully. + تم إنشاء المعاملة بنجاح. + + + Transaction fees are too high. + رسوم المعاملات مرتفعة للغاية. + + + Transaction not valid. + المعاملة غير صالحة. + + + Transaction too large for fee policy + قيمة المعاملة كبيرة جدا لسياسة الأجر + + + Unable to bind to %s on this computer (bind returned error %s) + يتعذر الربط مع %s على هذا الكمبيوتر (الربط انتج خطأ %s) + + + Unable to sign spork message, wrong key? + غير قادر على التوقيع على رسالة ورش ، مفتاح خاطئ؟ + + + Unknown network specified in -onlynet: '%s' + شبكة غير معروفة محددة في -onlynet: '%s' + + + Unknown state: id = %u + حالة غير معروفة: id = %u + + + Username for JSON-RPC connections + اسم المستخدم لاتصالات JSON-RPC + + + Verifying blocks... + التحقق من الكتل... + + + Verifying wallet... + التحقق من المحفظة ... + + + Very low number of keys left: %d + عدد منخفض جدًا من المفاتيح المتبقية: %d + + + Wallet %s resides outside data directory %s + تتواجد المحفظة %s خارج دليل البيانات %s + + + Wallet is locked. + المحفظة مؤمنة. + + + Wallet options: + خيارات المحفظة : + + + Wallet window title + عنوان نافذة المحفظة + + + Warning + تحذير + + + Your entries added successfully. + تمت إضافة إدخالاتك بنجاح. + + + Your transaction was accepted into the pool! + تم قبول معاملتك! + + + Zapping all transactions from wallet... + إزالة جميع المعاملات من المحفظة... + + + \ No newline at end of file diff --git a/src/qt/locale/dash_bg.ts b/src/qt/locale/dash_bg.ts index 609c494c6655..1d417f656b16 100644 --- a/src/qt/locale/dash_bg.ts +++ b/src/qt/locale/dash_bg.ts @@ -794,7 +794,7 @@ Моля преминете към "Режим Списък" за да използвате тази функция. - Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again. + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. Избран е не анонимизиран вход. <b>PrivateSend ще бъде изключен.</b><br><br>Ако все още желаете да използвате PrivateSend, моля отмаркирайте всички не анонимизирани входове и след това изберете PrivateSend опцията отново. @@ -834,73 +834,6 @@ няма такъв - - DarksendConfig - - Configure PrivateSend - Конфигуриране на PrivateSend - - - Basic Privacy - Нормална сигурност - - - High Privacy - Висока сигурност - - - Maximum Privacy - Максимална сигурност - - - Please select a privacy level. - Моля изберете ниво на сигурност. - - - Use 2 separate masternodes to mix funds up to 1000 DASH - Използване на 2 различни masternodes за смесване на средства до 1000 DASH - - - Use 8 separate masternodes to mix funds up to 1000 DASH - Използване на 8 различни masternodes за смесване на средства до 1000 DASH - - - Use 16 separate masternodes - Използване на 16 различни masternodes за смесване на средства до 1000 DASH - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - Това е най-бързият вариант, анонимизиране на 1000 DASH ще ви струва около 0.025 DASH - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - Това е средно бърз вариант, анонимизиране на 1000 DASH ще ви струва около 0.05 DASH - - - 0.1 DASH per 1000 DASH you anonymize. - 0.1 DASH за всеки 1000 DASH които анонимизирате. - - - This is the slowest and most secure option. Using maximum anonymity will cost - Това е най-бавния и най-сигурен начин. Използването на максимална анонимност ще ви коства - - - PrivateSend Configuration - PrivateSend конфигурация - - - PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend беше успешно настроен до основно ниво (%1 2 цикъла). Може да промените това по всяко време, като отворите екрана за конфигурация на Dash. - - - PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend беше успешно настроен до високо ниво (%1 и 8 цикъла). Може да промените това по всяко време, като отворите екрана за конфигурация на Dash. - - - PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend беше успешно настроен до максимално ниво (%1 и 16 цикъла). Може да промените това по всяко време, като отворите екрана за конфигурация на Dash. - - EditAddressDialog @@ -1038,8 +971,8 @@ PrivateSend информация - <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more info see <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> - <h3>PrivateSend основни понятия</h3> PrivateSend ви дава истинска финансова независимост чрез скриване произхода на средствата ви. Всички Dash монети в портфейла ви са съставени от различни "входове" които можете да приемете като отделни дискретни монети<br> PrivateSend използва иновативен процес като смесва вашите входове с входовете на други двама души без монетите да напускат изобщо портфейла ви. Имате пълен контрол върху парите си през цялото време. <hr> <b> PrivateSend процесът работи по следния начин:</b><ol type="1"> <li>PrivateSend започва чрез разделяне на транзакцията ви до стандартни деноминации. Тези деноминации са 0.01 DASH, 0.1 DASH, 1 DASH и 10 DASH --подобно на стандартните пари, които ползваме ежедневно.</li> <li>Вашия портфейл изпраща запитване към специално конфигурираните софтуерни възли в мрежата, наречени "masternodes." Тези masternodes са информирани, че се интересувате от смесване на определена деноминация. Никаква идентификационна информация не се изпраща към masternodes, така, че те никога не знаят "кой" сте.</li> <li>Когато други двама души изпратят подобни съобщения, обозначаващи, че искат да смесят същата деноминация, смесващата сесия започва. Masternode смесват входовете и инструктират портфейлите на тримата потребители да платят новотрансформираните входове обратно към притежателите им. Вашият портфейл плаща деноминацията директно на себе си, но с различен адрес (наричан сменен адрес). </li> <li>С цел напълно скриване на доходите ви, вашият портфейл трябва да повтори този процес няколко пъти във всяка деноминация. Всеки път завършеният процес се нарича "цикъл." Всеки цикъл на PrivateSend прави откриването на първоначалния източник на вашите средства напълно непроследим.</li> <li>Този смесващ процес се случва като процес на заден фон без никаква намеса от ваша страна. Когато искате да направите транзакция, вашите средства са винаги анонимни. Не е необходимо допълнително изчакване. </li> </ol> <hr><b>ВАЖНО:</b> Вашият портфейл съдържа само 1000 от тези "сменяеми адреси". Всеки път когато се случва смесването до 9 от вашите адреси се ползват. Това означава, че тези 1000 адреси се миксират в около 100 смесващи събития. Когато 900 се използват, вашия портфейл трябва да създаде повече адреси. Това може да стане само ако имате автоматично архивиране.<br> Следователно, потребителите, на които е изключено автоматичното архивиране, нямат и PrivateSend включено. <hr>За повече информация вижте<a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. + <h3>PrivateSend основни понятия</h3> PrivateSend ви дава истинска финансова независимост чрез скриване произхода на средствата ви. Всички Dash монети в портфейла ви са съставени от различни "входове" които можете да приемете като отделни дискретни монети<br> PrivateSend използва иновативен процес като смесва вашите входове с входовете на други двама души без монетите да напускат изобщо портфейла ви. Имате пълен контрол върху парите си през цялото време. <hr> <b> PrivateSend процесът работи по следния начин:</b><ol type="1"> <li>PrivateSend започва чрез разделяне на транзакцията ви до стандартни деноминации. Тези деноминации са 0.01 DASH, 0.1 DASH, 1 DASH и 10 DASH --подобно на стандартните пари, които ползваме ежедневно.</li> <li>Вашия портфейл изпраща запитване към специално конфигурираните софтуерни възли в мрежата, наречени "masternodes." Тези masternodes са информирани, че се интересувате от смесване на определена деноминация. Никаква идентификационна информация не се изпраща към masternodes, така, че те никога не знаят "кой" сте.</li> <li>Когато други двама души изпратят подобни съобщения, обозначаващи, че искат да смесят същата деноминация, смесващата сесия започва. Masternode смесват входовете и инструктират портфейлите на тримата потребители да платят новотрансформираните входове обратно към притежателите им. Вашият портфейл плаща деноминацията директно на себе си, но с различен адрес (наричан сменен адрес). </li> <li>С цел напълно скриване на доходите ви, вашият портфейл трябва да повтори този процес няколко пъти във всяка деноминация. Всеки път завършеният процес се нарича "цикъл." Всеки цикъл на PrivateSend прави откриването на първоначалния източник на вашите средства напълно непроследим.</li> <li>Този смесващ процес се случва като процес на заден фон без никаква намеса от ваша страна. Когато искате да направите транзакция, вашите средства са винаги анонимни. Не е необходимо допълнително изчакване. </li> </ol> <hr><b>ВАЖНО:</b> Вашият портфейл съдържа само 1000 от тези "сменяеми адреси". Всеки път когато се случва смесването до 9 от вашите адреси се ползват. Това означава, че тези 1000 адреси се миксират в около 100 смесващи събития. Когато 900 се използват, вашия портфейл трябва да създаде повече адреси. Това може да стане само ако имате автоматично архивиране.<br> Следователно, потребителите, на които е изключено автоматичното архивиране, нямат и PrivateSend включено. <hr>За повече информация вижте <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">документацията за PrivateSend</a>. @@ -4480,6 +4413,10 @@ https://www.transifex.com/projects/p/dash/ Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) Изключване на всички специфични Dash функции (PrivateSend, InstantSend, Governance) (0-1, по подразбиране: %u) + + %s file contains all private keys from this wallet. Do not share it with anyone! + Файл %s съдържа всички частни ключове от този портфейл. Не го споделяйте с никого! + Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) Открий собствения IP адрес (по подразбиране: 1, когато слуша и няма -externalip или -proxy) @@ -4504,6 +4441,10 @@ https://www.transifex.com/projects/p/dash/ Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID) Изпълнена команда когато транзакцията в InstantSend портфейла е успешно заключена (%s в cmd е заместен от TxID) + + Extra transactions to keep in memory for compact block reconstructions (default: %u) + Допълнителни транзакции поддържани в паметта за реконструкции на компактни блокове (по подразбиране: %u) + Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. Не може да се създаде копие, файлът вече съществува! Това може да се случи, ако сте рестартирали портфейла си преди по-малко от 60 секунди. Може да продължите, ако сте разбрали това. @@ -4584,6 +4525,10 @@ https://www.transifex.com/projects/p/dash/ Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. Общата дължина на мрежовата версия на низ (%i) надвишава максималната дължина (%i). Намалете броя или размера на коментарите. + + Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index. + Индексът на транзакциите не може да бъде деактивиран в пълен режим. Или започнете с превключване -litemode от командния ред или активирайте индексиране на транзакциите. + Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) Опитва се да поддържа изходящият трафик по зададената цел (в MiB за 24h), 0 = без лимит (по подразбиране: %d) @@ -4616,6 +4561,10 @@ https://www.transifex.com/projects/p/dash/ Warning: Unknown block versions being mined! It's possible unknown rules are in effect ВНИМАНИЕ: Непозната версия на блока!Възможно е да са активирани неизвестни правила + + You are starting in lite mode, all Dash-specific functionality is disabled. + Стартирали сте в олекотен режим, всички Dash- специфични функционалности са изключени. + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain Необходимо е повторно изграждане на базата данни използвайки -reindex за да се върнете в неизчистен режим.Това ще изтегли отново съществуващата блок-верига @@ -4660,6 +4609,14 @@ https://www.transifex.com/projects/p/dash/ Enable publish hash block in <address> Включва публикуване на блок хеш в <address> + + Enable publish hash of governance objects (like proposals) in <address> + Активиране публикуването на хеш от обектите за управление (като предложения) в <address> + + + Enable publish hash of governance votes in <address> + Активиране публикуването на хеш от управляващите гласове в <address> + Enable publish hash transaction (locked via InstantSend) in <address> Включва публикуване хеша на транзакцията (заключен чрез InstantSend) в <address> @@ -4700,6 +4657,14 @@ https://www.transifex.com/projects/p/dash/ Failed to delete backup, error: %s Неуспешно изтриване на архив, грешка: %s + + Failed to load InstantSend data cache from + Неуспешно зареждане на InstantSend кеша за данни от + + + Failed to load sporks cache from + Неуспешно зареждане на кеша за sporks от + Failed to parse host:port string Неуспешен разбор на host:port стринг @@ -4720,6 +4685,10 @@ https://www.transifex.com/projects/p/dash/ Invalid amount for -fallbackfee=<amount>: '%s' Невалидно количество за -fallbackfee=<amount>: '%s' + + Invalid input count. + Невалиден брой входове. + Keep the transaction memory pool below <n> megabytes (default: %u) Дръж мемпула за транзакциите под <n> мегабайта (по подразбиране: %u) @@ -4736,6 +4705,10 @@ https://www.transifex.com/projects/p/dash/ Line: %d Линия: %d + + Loading InstantSend data cache... + Зареждане на InstantSend кеш данни... + Loading addresses... Зареждане на адреси... @@ -4748,6 +4721,10 @@ https://www.transifex.com/projects/p/dash/ Loading governance cache... Зарежда се masternode кеш... + + Loading sporks cache... + Зареждане на sporks кеш... + Loading wallet... (%3.2f %%) Зареждане на портфейла... (%3.2f %%) @@ -4888,6 +4865,10 @@ https://www.transifex.com/projects/p/dash/ Transaction must have at least one recipient Транзакцията трябва да има поне един получател. + + Transaction too large + Транзакцията е твърде голяма + Trying to connect... Опит за свързване... @@ -4908,6 +4889,10 @@ https://www.transifex.com/projects/p/dash/ Wallet debugging/testing options: Опции за Откриване на грешки/Тестване на портфейла: + + Wallet is not initialized + Портфейлът не е инициализиран + Wallet needed to be rewritten: restart %s to complete Портфейлът трябва да бъде презаписан: рестартирайте %s за да завършите @@ -4924,6 +4909,10 @@ https://www.transifex.com/projects/p/dash/ Whether to operate in a blocks only mode (default: %u) Дали да работи само в блоков режим (по подразбиране: %u) + + You can not start a masternode in lite mode. + Не може да стартирате masternode в олекотен режим. + ZeroMQ notification options: ZeroMQ опции за уведомяване: @@ -5012,10 +5001,6 @@ https://www.transifex.com/projects/p/dash/ Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) Задайте най-ниска такса (в %s / kB) за транзакции, които да бъдат включени в създаването на блок. (по подразбиране:%s) - - Show N confirmations for a successfully locked transaction (%u-%u, default: %u) - Покажи N потвърждения при успешно заключена транзакция (%u-%u, по подразбиране: %u) - This is the transaction fee you may pay when fee estimates are not available. Това е таксата за транзакция, която можете да платите, когато не са налице оценки на таксите. diff --git a/src/qt/locale/dash_de.ts b/src/qt/locale/dash_de.ts index d15b9cbbc106..dea83685e6b6 100644 --- a/src/qt/locale/dash_de.ts +++ b/src/qt/locale/dash_de.ts @@ -794,8 +794,8 @@ Wechseln Sie bitte zum "Listenmodus" um die Funktion zu benutzen. - Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again. - Nicht-anonymisierter Input ausgewählt. <b>PrivateSend wird deaktiviert.</b><br><br>Sollten Sie trotzdem PrivateSend verwenden wollen, müssen Sie zuerst alle nicht-anonymisierten Inputs entmarkieren und das Ankreuzfeld "PrivateSend" erneut auswählen. + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. + Nicht-anonymisierter Input ausgewählt. <b>PrivateSend wird deaktiviert.</b><br><br>Sollten Sie trotzdem PrivateSend verwenden wollen, müssen Sie zuerst alle nicht-anonymisierten Inputs entmarkieren und das Ankreuzfeld "PrivateSend" erneut auswählen. (%1 locked) @@ -834,73 +834,6 @@ k.A. - - DarksendConfig - - Configure PrivateSend - PrivateSend konfigurieren - - - Basic Privacy - Einfacher Datenschutz - - - High Privacy - Hoher Datenschutz - - - Maximum Privacy - Maximaler Datenschutz - - - Please select a privacy level. - Bitten wählen Sie eine Datenschutz-Stufe. - - - Use 2 separate masternodes to mix funds up to 1000 DASH - Benutze 2 separate Masternodes um bis zu 1000 DASH zu mixen - - - Use 8 separate masternodes to mix funds up to 1000 DASH - Benutze 8 separate Masternodes um bis zu 1000 DASH zu mixen - - - Use 16 separate masternodes - Benutze 16 separate Masternodes - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - Diese Option ist am Schnellsten und kostet ungefähr 0,025 DASH, um 1000 DASH zu anonymisieren - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - Diese Option ist einigermaßen schnell und kostet ungefähr 0,05 DASH, um 1000 DASH zu anonymisieren - - - 0.1 DASH per 1000 DASH you anonymize. - 0,1 DASH pro 1000 zu anonymisierende Dash. - - - This is the slowest and most secure option. Using maximum anonymity will cost - Dies ist die langsamste und sicherste Option. Maximale Anonymität kostet - - - PrivateSend Configuration - PrivateSend Konfiguration - - - PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend wurde erfolgreich auf einfachen Datenschutz (%1 und 2 Runden) gesetzt. Sie können dies jederzeit im Konfigurationsfenster von Dash ändern. - - - PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend wurde erfolgreich auf hohen Datenschutz (%1 und 8 Runden) gesetzt. Sie können dies jederzeit im Konfigurationsfenster von Dash ändern. - - - PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend wurde erfolgreich auf maximalen Datenschutz (%1 und 16 Runden) gesetzt. Sie können dies jederzeit im Konfigurationsfenster von Dash ändern. - - EditAddressDialog @@ -1038,9 +971,9 @@ PrivateSend Informationen - <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more info see <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. <h3>Basisinformation zu PrivateSend</h3> PrivateSend bietet finanziellen Datenschutz durch geschickte Verschleierung der Herkunft Ihres Geldes. Die Dash in Ihrer Geldbörse setzen sich aus völlig unterschiedlichen eigenständigen "Inputs" zusammen.<br> PrivateSend benutzt einen innovativen Prozeß, um Ihre "Inputs" mit denen von 2 anderen Personen zu mischen, ohne das Ihr Geld jemals Ihre Geldbörse verlassen muß, d.h. Sie haben während dieses Prozesses immer die Kontrolle über Ihr Geld.<hr><b>Der PrivateSend-Prozess funktioniert folgendermaßen:</b><ol type="1"><li>PrivateSend stückelt Ihre "Inputs" in kleinere Teile. Diese sind 0,01 DASH, 0,1 DASH, 1DASH und 10 DASH -- im Prinzip wie Münzen oder Scheine, wie wir sie jeden Tag benutzen.</li> <li>Ihre Geldbörse sendet dann eine entsprechende Mixing Anforderungen an spezielle Dash-Server im Internet, die sogenannten "Masternodes". Damit werden diese Masternodes darüber informiert, daß Sie Ihre gestückelten DASH gerne mixen würden. Dabei wird keinerlei Information über Sie versendet, d.h. die Masternodes wissen nie, wer genau mixen möchte.</li> <li> Sobald zwei andere Personen eine gleiche Mixing-Anforderung mit der gleichen Stückelung senden beginnt der Mixing-Prozeß. Der Masternode vermischt (daher das Wort "Mixing") die gestückelten Inputs und weist das Ergebnis wieder den Geldbörsen zu, allerdings mit neuen Empfängeradressen (die natürlich zu Ihrer Wallet gehören), so daß man sie nicht mehr den ursprünglichen Adressen zuordnen kann.. Man kann sich das so vorstellen wie wenn 3 Personen jeweils 100 Euro in der gleichen Anzahl von 10 Euro Scheinen, 5 Euro Scheinen, 2- und 1-Euro Münzen auf einen Tisch legen, alles einmal gut durchmischen, und sich anschießend ohne hinzusehen wieder 100 Euro vom Tischen nehmen. Jeder hat genau so viele Euro wie vorher, aber keiner weiß, wessen Scheine oder Münzen er letztendlich in seiner Geldbörse hat, oder wo sie herkommen. Und das Gute dabei ist, im Dash Mixing Prozeß verlassen die Scheine oder Münzen niemals die Geldbörse ihres Besitzers, daher kann kein Betrug stattfinden.</li> <li>Um die Herkunft Ihres Guthabens vollständig zu verschleiern muß dieser Prozeß mehrmals wiederholt werden, d.h. es gibt mehrere "Runden" des Mixing-Prozesses. Die Anzahl der Runden ist einstellbar, je mehr, desto besser ist die Herkunft Ihres Guthabens verschleiert, aber um so länger dauert der Prozeß.</li> <li> -Der Mixing-Prozeß läuft nach dem Start vollständig im Hintergrund, d.h. es ist keine Benutzerinteraktion mehr erforderlich. Das Wallet informiert Sie über den Fortschrittsbalken über den aktuellen Status des Prozesses.</li> </ol> <hr><b>WICHTIG:</b> beim Mischen der Inputs werden die einzelnen Stückelungen einer NEUEN Empfängeradresse zugeordnet (siehe Oben). Ihre Geldbörse hat bereits beim ersten Start 1000 dieser "Wechseladressen" auf Vorrat erzeugt. Bei jedem Mischen werden 9 dieser Wechseladressen verbraucht, d.h. nach ungefähr 100 Mischvorgänge sind diese 1000 Wechseladressen aufgebraucht. Die Geldbörse ist so eingestellt, daß sie bei Erreichen von 900 benutzen Wechseladressen wieder genug neue Wechseladressen erzeugt, damit man auch in der Zukunft wieder mischen kann. Die neuen Wechseladressen werden aber nur dann erzeugt, wenn man in den Einstellungen "Automatische Datensicherungen" aktiviert hat.<br> Daher ist bei Benutzern, die "Automatische Datensicherungen" deaktiviert haben, automatisch auch PrivateSend dekativiert.<hr> Weitere Information hierzu finden Sie auf <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> +Der Mixing-Prozeß läuft nach dem Start vollständig im Hintergrund, d.h. es ist keine Benutzerinteraktion mehr erforderlich. Das Wallet informiert Sie über den Fortschrittsbalken über den aktuellen Status des Prozesses.</li> </ol> <hr><b>WICHTIG:</b> beim Mischen der Inputs werden die einzelnen Stückelungen einer NEUEN Empfängeradresse zugeordnet (siehe Oben). Ihre Geldbörse hat bereits beim ersten Start 1000 dieser "Wechseladressen" auf Vorrat erzeugt. Bei jedem Mischen werden 9 dieser Wechseladressen verbraucht, d.h. nach ungefähr 100 Mischvorgänge sind diese 1000 Wechseladressen aufgebraucht. Die Geldbörse ist so eingestellt, daß sie bei Erreichen von 900 benutzen Wechseladressen wieder genug neue Wechseladressen erzeugt, damit man auch in der Zukunft wieder mischen kann. Die neuen Wechseladressen werden aber nur dann erzeugt, wenn man in den Einstellungen "Automatische Datensicherungen" aktiviert hat.<br> Daher ist bei Benutzern, die "Automatische Datensicherungen" deaktiviert haben, automatisch auch PrivateSend dekativiert.<hr> Weitere Information hierzu finden Sie in der <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend-Dokumentation</a>. @@ -1832,7 +1765,7 @@ https://www.transifex.com/projects/p/dash/ Note: You can turn this message off in options. - Hinweis: diese Nachricht kann man unter "Einstellungen" abschalten. + Hinweis: Diese Nachricht kann unter "Einstellungen" deaktiviert werden. WARNING! Something went wrong on automatic backup @@ -4482,6 +4415,10 @@ https://www.transifex.com/projects/p/dash/ Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) Deaktiviere all Dash-spezifischen Funktionen (Masternodes, PrivateSend, InstantSend, Governance) (0-1, Standard: %u) + + %s file contains all private keys from this wallet. Do not share it with anyone! + Die Datei %s beinhaltet alle privaten Schlüssel der Wallet. Diese sollten niemals weitergegeben werden! + Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) Eigene IP-Adresse erkennen (Standard: 1, wenn -listen aktiv ist und nicht -externalip) @@ -4506,6 +4443,10 @@ https://www.transifex.com/projects/p/dash/ Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID) Befehl ausführen wenn eine Wallet-Transaktion erfolgreich gesperrt wurde (%s im Befehl wird durch die TxID ersetzt) + + Extra transactions to keep in memory for compact block reconstructions (default: %u) + Zusätzliche Transaktionen, die im Speicher bleiben, damit kompakte Blöcke ermöglicht werden können (Standard: %u) + Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. Datensicherung fehlgeschlagen, da diese Datei bereits existiert. Dies kann vorkommen, wenn das Wallet innerhalb von 60 Sekunden neu gestartet wurde. Sie können ohne Probleme weiterarbeiten, falls das so von Ihnen gewollt war. @@ -4586,6 +4527,10 @@ https://www.transifex.com/projects/p/dash/ Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. Die Gesamtlänge des Versions-Namens (%i) überschreitet die erlaubte Maximallänge (%i). Bitte verringern Sie Anzahl oder Größe der Eingaben für die Kommandozeilenoption -uacomments. + + Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index. + Der Transaktionsindex kann bei einer Fullnode nicht deaktiviert werden. Der Transaktionsindex muss entweder aktiviert sein oder die Node muss mit dem Befehl -litemode gestartet werden. + Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) Versucht die Menge des Ausgehenden Datenverkehrs unterhalb der angegebenen Maximalmenge (in MiB pro Tag) zu halten, 0 = keine Maximalmenge (Standard: %d) @@ -4618,6 +4563,10 @@ https://www.transifex.com/projects/p/dash/ Warning: Unknown block versions being mined! It's possible unknown rules are in effect Warning: es wird eine unbekannt Block-Version gemined. Es werden unbekannte/ungültige Blockregeln angewandt. + + You are starting in lite mode, all Dash-specific functionality is disabled. + Der Start findet im Lite-Modus statt, weswegen alle Dash-spezifischen Funktionen deaktiviert wurden. + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain Sie müssen die Datenbank mit Hilfe von -reindex neu aufbauen, um zurück in den nicht abgeschnittenen/pruned Modus zu gehen. Dies wird die gesamte Blockchain downloaden @@ -4662,6 +4611,14 @@ https://www.transifex.com/projects/p/dash/ Enable publish hash block in <address> Veröffentlichung des Blockhash in <Adresse> ermöglichen + + Enable publish hash of governance objects (like proposals) in <address> + Veröffentlichung des Hash eines Governance Objects (z.B. ein Proposal) in <address> ermöglichen + + + Enable publish hash of governance votes in <address> + Veröffentlichung des Hash einer Governance Stimme in <address> ermöglichen + Enable publish hash transaction (locked via InstantSend) in <address> Veröffentlichung des Transaktionshash (blockiert via InstantSend) in <Adresse> ermöglichen @@ -4702,6 +4659,14 @@ https://www.transifex.com/projects/p/dash/ Failed to delete backup, error: %s Löschen einer alten Datensicherung ist fehlgeschlagen, Fehler: %s + + Failed to load InstantSend data cache from + Data Cache für InstantSend konnte nicht geladen werden aus + + + Failed to load sporks cache from + Sporks Cache konnte nicht geladen werden aus + Failed to parse host:port string Die Eingabe für Rechnername:Port wird nicht verstanden @@ -4722,6 +4687,10 @@ https://www.transifex.com/projects/p/dash/ Invalid amount for -fallbackfee=<amount>: '%s' Ungültiger Betrag für -fallbackfee=<amount>: '%s' + + Invalid input count. + Ungültige Input-Zahl + Keep the transaction memory pool below <n> megabytes (default: %u) Transaktionen im "Mempool" unterhalb von <n> halten (Standard: %u) @@ -4738,6 +4707,10 @@ https://www.transifex.com/projects/p/dash/ Line: %d Zeile: %d + + Loading InstantSend data cache... + Lade InstantSend Data Cache... + Loading addresses... Lade Adressen... @@ -4750,6 +4723,10 @@ https://www.transifex.com/projects/p/dash/ Loading governance cache... Lade Governance-Cache... + + Loading sporks cache... + Lade Sporks Cache... + Loading wallet... (%3.2f %%) Lade Wallet... (%3.2f %%) @@ -4890,6 +4867,10 @@ https://www.transifex.com/projects/p/dash/ Transaction must have at least one recipient Die Transaktion muss mindestens einen Empfänger enthalten. + + Transaction too large + Transaktion zu groß + Trying to connect... Verbindungsaufbau... @@ -4910,6 +4891,10 @@ https://www.transifex.com/projects/p/dash/ Wallet debugging/testing options: Wallet Debugging-/Testoptionen: + + Wallet is not initialized + Wallet wurde nicht initialisiert + Wallet needed to be rewritten: restart %s to complete Wallet musste neu geschrieben werden: starten Sie %s zur Fertigstellung neu @@ -4926,6 +4911,10 @@ https://www.transifex.com/projects/p/dash/ Whether to operate in a blocks only mode (default: %u) Arbeite im Block-Only-Modus (Standard: %u) + + You can not start a masternode in lite mode. + Eine Masternode kann nicht im Lite-Modus gestartet werden, + ZeroMQ notification options: ZeroMQ Benachrichtigung-Optionen: @@ -5014,10 +5003,6 @@ https://www.transifex.com/projects/p/dash/ Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) Niedrigste Gebühr (in %s/kB) für Transaktionen einstellen, die bei der Blockerzeugung berücksichtigt werden sollen. (default: %s) - - Show N confirmations for a successfully locked transaction (%u-%u, default: %u) - Anzahl Bestätigungen für eine erfolgreich gesperrte Transaktion (%u-%u, voreingestellt: %u) - This is the transaction fee you may pay when fee estimates are not available. Das ist die Transaktionsgebühr, welche du zahlen müsstest, wenn die Gebührenschätzungen nicht verfügbar sind. diff --git a/src/qt/locale/dash_en.ts b/src/qt/locale/dash_en.ts index 76d8e0b12f79..85b199ef5976 100644 --- a/src/qt/locale/dash_en.ts +++ b/src/qt/locale/dash_en.ts @@ -317,12 +317,12 @@ A fatal error occurred. Dash Core can no longer continue safely and will quit. - + Dash Core Dash Core - + Wallet Wallet @@ -728,7 +728,7 @@ Transactions after this will not yet be visible. - + Up to date Up to date @@ -1006,8 +1006,8 @@ - Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again. - Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again. + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. + @@ -1056,91 +1056,6 @@ n/a - - DarksendConfig - - - Configure PrivateSend - Configure PrivateSend - - - - Basic Privacy - Basic Privacy - - - - High Privacy - High Privacy - - - - Maximum Privacy - Maximum Privacy - - - - Please select a privacy level. - Please select a privacy level. - - - - Use 2 separate masternodes to mix funds up to 1000 DASH - Use 2 separate masternodes to mix funds up to 1000 DASH - - - - Use 8 separate masternodes to mix funds up to 1000 DASH - Use 8 separate masternodes to mix funds up to 1000 DASH - - - - Use 16 separate masternodes - Use 16 separate masternodes - - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - - - - 0.1 DASH per 1000 DASH you anonymize. - 0.1 DASH per 1000 DASH you anonymize. - - - - This is the slowest and most secure option. Using maximum anonymity will cost - This is the slowest and most secure option. Using maximum anonymity will cost - - - - - - PrivateSend Configuration - PrivateSend Configuration - - - - PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - - - - PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - - - - PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - - EditAddressDialog @@ -1312,8 +1227,8 @@ - <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more info see <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> - <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more info see <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. + @@ -1388,53 +1303,53 @@ - + Alias Alias - + Address Address - - + + Protocol Protocol - - + + Status Status - - + + Active Active - - + + Last Seen Last Seen - - + + Payee Payee - + S&tart alias S&tart alias @@ -1470,17 +1385,17 @@ - + 0 0 - + All Masternodes All Masternodes - + Filter List: Filter List: @@ -1495,12 +1410,12 @@ Node Count: - + Start alias Start alias - + Confirm masternode start Confirm masternode start @@ -2017,7 +1932,7 @@ https://www.transifex.com/projects/p/dash/ &Cancel - + default default @@ -2144,7 +2059,7 @@ https://www.transifex.com/projects/p/dash/ - + @@ -2245,7 +2160,7 @@ https://www.transifex.com/projects/p/dash/ Reset - + out of sync @@ -2258,14 +2173,14 @@ https://www.transifex.com/projects/p/dash/ - + Start Mixing Start Mixing - - + + Stop Mixing Stop Mixing @@ -2331,7 +2246,7 @@ https://www.transifex.com/projects/p/dash/ - + @@ -2339,7 +2254,7 @@ https://www.transifex.com/projects/p/dash/ Disabled - + Very low number of keys left since last automatic backup! Very low number of keys left since last automatic backup! @@ -4661,7 +4576,7 @@ https://www.transifex.com/projects/p/dash/ (n/a) - + (no label) (no label) @@ -5009,7 +4924,7 @@ https://www.transifex.com/projects/p/dash/ dash-core - + Bind to given address and always listen on it. Use [host]:port notation for IPv6 Bind to given address and always listen on it. Use [host]:port notation for IPv6 @@ -5034,7 +4949,7 @@ https://www.transifex.com/projects/p/dash/ Execute command when the best block changes (%s in cmd is replaced by block hash) - + Name to construct url for KeePass entry that stores the wallet passphrase Name to construct url for KeePass entry that stores the wallet passphrase @@ -5049,12 +4964,12 @@ https://www.transifex.com/projects/p/dash/ Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d) - + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications This is a pre-release test build - use at your own risk - do not use for mining or merchant applications - + Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues. Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues. @@ -5064,7 +4979,7 @@ https://www.transifex.com/projects/p/dash/ Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade. - + You must specify a masternodeprivkey in the configuration. Please see documentation for help. You must specify a masternodeprivkey in the configuration. Please see documentation for help. @@ -5144,7 +5059,7 @@ https://www.transifex.com/projects/p/dash/ Done loading - + Entries are full. Entries are full. @@ -5189,7 +5104,7 @@ https://www.transifex.com/projects/p/dash/ Failed to listen on any port. Use -listen=0 if you want this. - + (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data) (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data) @@ -5239,7 +5154,7 @@ https://www.transifex.com/projects/p/dash/ Error loading %s: You can't enable HD on a already existing non-HD wallet - + Found unconfirmed denominated outputs, will wait till they confirm to continue. Found unconfirmed denominated outputs, will wait till they confirm to continue. @@ -5309,7 +5224,7 @@ https://www.transifex.com/projects/p/dash/ Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway - + You need to rebuild the database using -reindex-chainstate to change -txindex You need to rebuild the database using -reindex-chainstate to change -txindex @@ -5349,7 +5264,7 @@ https://www.transifex.com/projects/p/dash/ Connect to KeePassHttp on port <port> (default: %u) - + Enable the client to act as a masternode (0-1, default: %u) Enable the client to act as a masternode (0-1, default: %u) @@ -5364,7 +5279,7 @@ https://www.transifex.com/projects/p/dash/ Error loading %s: You can't disable HD on a already existing HD wallet - + Failed to load fulfilled requests cache from Failed to load fulfilled requests cache from @@ -5384,7 +5299,7 @@ https://www.transifex.com/projects/p/dash/ Failed to load masternode payments cache from - + Found enough users, signing ( waiting %s ) Found enough users, signing ( waiting %s ) @@ -5454,7 +5369,7 @@ https://www.transifex.com/projects/p/dash/ Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s) - + Keypool ran out, please call keypoolrefill first Keypool ran out, please call keypoolrefill first @@ -5469,7 +5384,7 @@ https://www.transifex.com/projects/p/dash/ Listen for connections on <port> (default: %u or testnet: %u) - + Loading banlist... Loading banlist... @@ -5489,7 +5404,7 @@ https://www.transifex.com/projects/p/dash/ Loading masternode payment cache... - + Lock is already in place. Lock is already in place. @@ -5669,7 +5584,7 @@ https://www.transifex.com/projects/p/dash/ Tor control port to use if onion listening enabled (default: %s) - + Unable to start HTTP server. See debug log for details. Unable to start HTTP server. See debug log for details. @@ -5719,17 +5634,17 @@ https://www.transifex.com/projects/p/dash/ User Agent comment (%s) contains unsafe characters. - + Will retry... Will retry... - + Invalid masternodeprivkey. Please see documenation. Invalid masternodeprivkey. Please see documenation. - + Can't find random Masternode. Can't find random Masternode. @@ -5744,7 +5659,7 @@ https://www.transifex.com/projects/p/dash/ Could not parse masternode.conf - + Invalid netmask specified in -whitelist: '%s' Invalid netmask specified in -whitelist: '%s' @@ -5774,12 +5689,17 @@ https://www.transifex.com/projects/p/dash/ Keep at most <n> unconnectable transactions in memory (default: %u) - + Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) - + + %s file contains all private keys from this wallet. Do not share it with anyone! + + + + Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) @@ -5810,6 +5730,11 @@ https://www.transifex.com/projects/p/dash/ + Extra transactions to keep in memory for compact block reconstructions (default: %u) + + + + Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. @@ -5884,7 +5809,7 @@ https://www.transifex.com/projects/p/dash/ Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again. - + Specify full path to directory for automatic wallet backups (must exist) Specify full path to directory for automatic wallet backups (must exist) @@ -5908,6 +5833,11 @@ https://www.transifex.com/projects/p/dash/ Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. + + + Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index. + + Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) @@ -5949,7 +5879,12 @@ https://www.transifex.com/projects/p/dash/ Warning: Unknown block versions being mined! It's possible unknown rules are in effect - + + You are starting in lite mode, all Dash-specific functionality is disabled. + + + + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain @@ -6003,6 +5938,16 @@ https://www.transifex.com/projects/p/dash/ Enable publish hash block in <address> Enable publish hash block in <address> + + + Enable publish hash of governance objects (like proposals) in <address> + + + + + Enable publish hash of governance votes in <address> + + Enable publish hash transaction (locked via InstantSend) in <address> @@ -6054,7 +5999,17 @@ https://www.transifex.com/projects/p/dash/ Failed to delete backup, error: %s - + + Failed to load InstantSend data cache from + + + + + Failed to load sporks cache from + + + + Failed to parse host:port string Failed to parse host:port string @@ -6079,7 +6034,12 @@ https://www.transifex.com/projects/p/dash/ Invalid amount for -fallbackfee=<amount>: '%s' - + + Invalid input count. + + + + Keep the transaction memory pool below <n> megabytes (default: %u) Keep the transaction memory pool below <n> megabytes (default: %u) @@ -6100,6 +6060,11 @@ https://www.transifex.com/projects/p/dash/ + Loading InstantSend data cache... + + + + Loading addresses... Loading addresses... @@ -6115,6 +6080,11 @@ https://www.transifex.com/projects/p/dash/ + Loading sporks cache... + + + + Loading wallet... (%3.2f %%) Loading wallet... (%3.2f %%) @@ -6290,6 +6260,11 @@ https://www.transifex.com/projects/p/dash/ + Transaction too large + + + + Trying to connect... Trying to connect... @@ -6315,6 +6290,11 @@ https://www.transifex.com/projects/p/dash/ + Wallet is not initialized + + + + Wallet needed to be rewritten: restart %s to complete Wallet needed to be rewritten: restart %s to complete @@ -6334,7 +6314,12 @@ https://www.transifex.com/projects/p/dash/ Whether to operate in a blocks only mode (default: %u) - + + You can not start a masternode in lite mode. + + + + ZeroMQ notification options: ZeroMQ notification options: @@ -6349,12 +6334,12 @@ https://www.transifex.com/projects/p/dash/ see debug.log for details. - + RPC server options: RPC server options: - + Dash Core Dash Core @@ -6364,7 +6349,7 @@ https://www.transifex.com/projects/p/dash/ The %s developers - + Accept connections from outside (default: 1 if no -proxy or -connect/-noconnect) Accept connections from outside (default: 1 if no -proxy or -connect/-noconnect) @@ -6389,7 +6374,7 @@ https://www.transifex.com/projects/p/dash/ Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect. - + Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d) Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d) @@ -6444,12 +6429,7 @@ https://www.transifex.com/projects/p/dash/ Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) - - Show N confirmations for a successfully locked transaction (%u-%u, default: %u) - Show N confirmations for a successfully locked transaction (%u-%u, default: %u) - - - + This is the transaction fee you may pay when fee estimates are not available. This is the transaction fee you may pay when fee estimates are not available. @@ -6459,7 +6439,7 @@ https://www.transifex.com/projects/p/dash/ This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit %s and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard. - + Unable to locate enough PrivateSend non-denominated funds for this transaction. Unable to locate enough PrivateSend non-denominated funds for this transaction. @@ -6509,7 +6489,7 @@ https://www.transifex.com/projects/p/dash/ Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times. - + %s corrupt, salvage failed %s corrupt, salvage failed @@ -6569,7 +6549,7 @@ https://www.transifex.com/projects/p/dash/ Copyright (C) - + Error loading %s Error loading %s @@ -6594,7 +6574,7 @@ https://www.transifex.com/projects/p/dash/ Failed to find mixing queue to join - + Failed to start a new mixing queue Failed to start a new mixing queue @@ -6609,7 +6589,7 @@ https://www.transifex.com/projects/p/dash/ Invalid amount for -%s=<amount>: '%s' - + Invalid spork address specified with -sporkaddr Invalid spork address specified with -sporkaddr @@ -6619,7 +6599,7 @@ https://www.transifex.com/projects/p/dash/ Keep N DASH anonymized (%u-%u, default: %u) - + Print this help message and exit Print this help message and exit @@ -6774,7 +6754,7 @@ https://www.transifex.com/projects/p/dash/ Transaction too large for fee policy - + Unable to bind to %s on this computer (bind returned error %s) Unable to bind to %s on this computer (bind returned error %s) @@ -6824,7 +6804,7 @@ https://www.transifex.com/projects/p/dash/ Wallet is locked. - + Wallet options: Wallet options: @@ -6839,7 +6819,7 @@ https://www.transifex.com/projects/p/dash/ Warning - + Your entries added successfully. Your entries added successfully. diff --git a/src/qt/locale/dash_es.ts b/src/qt/locale/dash_es.ts index cbbb1eb6895f..52363b975a28 100644 --- a/src/qt/locale/dash_es.ts +++ b/src/qt/locale/dash_es.ts @@ -11,7 +11,7 @@ &New - Nuevo + &Nuevo Copy the currently selected address to the system clipboard @@ -19,7 +19,7 @@ &Copy - Copiar + &Copiar Delete the currently selected address from the list @@ -27,7 +27,7 @@ &Delete - Eliminar + &Eliminar Export the data in the current tab to a file @@ -35,11 +35,11 @@ &Export - Exportar + &Exportar C&lose - Cerrar + C&errar Choose the address to send coins to @@ -51,7 +51,7 @@ C&hoose - Escoger + E&scoger Sending addresses @@ -71,15 +71,15 @@ &Copy Address - Copiar Dirección + &Copiar Dirección Copy &Label - Copiar Etiqueta + Copiar &Etiqueta &Edit - Editar + &Editar Export Address List @@ -133,31 +133,31 @@ Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>. - Introduzca la nueva contraseña del monedero.<br/>Por favor, use una contraseña con <b>diez o más caracteres aleatorios</b>, u <b>ocho o más palabras</b>. + Introduzca la nueva contraseña de la billetera.<br/>Por favor, use una contraseña con <b>diez o más caracteres aleatorios</b>, u <b>ocho o más palabras</b>. Encrypt wallet - Cifrar el monedero + Cifrar la billetera This operation needs your wallet passphrase to unlock the wallet. - Esta operación requiere su contraseña para desbloquear el monedero. + Esta operación requiere su contraseña para desbloquear la billetera. Unlock wallet for mixing only - Desbloquear monedero solo para mezcla + Desbloquear la billetera solo para mezcla Unlock wallet - Desbloquear monedero + Desbloquear la billetera This operation needs your wallet passphrase to decrypt the wallet. - Esta operación requiere su contraseña para descifrar el monedero. + Esta operación requiere su contraseña para descifrar la billetera. Decrypt wallet - Descifrar el monedero + Descifrar la billetera Change passphrase @@ -165,43 +165,43 @@ Enter the old passphrase and new passphrase to the wallet. - Ingresa la antigua frase de acceso y la nueva contraseña del monedero. + Ingresa la antigua frase de acceso y la nueva contraseña de la billetera. Confirm wallet encryption - Confirmar cifrado del monedero + Confirmar cifrado de la billetera Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR DASH</b>! - Advertencia: Si cifra su monedero y pierde su contraseña, ¡<b>PERDERÁ TODOS SUS DASH</b>! + Advertencia: Si cifra su billetera y pierde su contraseña, ¡<b>PERDERÁ TODOS SUS DASH</b>! Are you sure you wish to encrypt your wallet? - ¿Esta seguro que desea cifrar su monedero? + ¿Esta seguro que desea cifrar su billetera? Wallet encrypted - Monedero cifrado + Billetera cifrado %1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your funds from being stolen by malware infecting your computer. - %1 se cerrará al finalizar el proceso de encriptación. Recuerda que el cifrado de tu monedero no protege totalmente tus fondos de ser robados por infección de malware en tu computador. + %1 se cerrará al finalizar el proceso de encriptación. Recuerda que el cifrado de tu billetera no protege totalmente tus fondos de ser robados por infección de malware en tu computador. IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. Previous backups of the unencrypted wallet file contain the same HD seed and still have full access to all your funds just like the new, encrypted wallet. - IMPORTANTE: Todas las copias de seguridad anteriores que haya realizado de su archivo de monedero se deben reemplazar con el archivo del monedero cifrado recién generado. Las copias de seguridad anteriores del archivo de monedero sin cifrar contienen la misma semilla HD y aun contienen el acceso completo a todos tus fondos al igual que el nuevo monedero cifrado. + IMPORTANTE: Todas las copias de seguridad anteriores que haya realizado de su archivo de la billetera se deben reemplazar con el archivo de la billetera cifrado recién generado. Las copias de seguridad anteriores del archivo de la billetera sin cifrar contienen la misma semilla HD y aun contienen el acceso completo a todos tus fondos al igual que la nueva billetera cifrada. IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet. - IMPORTANTE: Cualquier copia de seguridad que haya realizado previamente de su archivo del monedero debe reemplazarse con el nuevo archivo de monedero cifrado. Por razones de seguridad, las copias de seguridad previas del archivo del monedero no cifradas serán inservibles en cuanto comience a usar el nuevo monedero cifrado. + IMPORTANTE: Cualquier copia de seguridad que haya realizado previamente de su archivo de la billetera debe reemplazarse con el nuevo archivo de la billetera cifrado. Por razones de seguridad, las copias de seguridad previas del archivo de la billetera no cifradas serán inservibles en cuanto comience a usar la nueva billetera cifrado. Wallet encryption failed - Ha fallado el cifrado del monedero + Ha fallado el cifrado de la billetera Wallet encryption failed due to an internal error. Your wallet was not encrypted. - Ha fallado el cifrado del monedero debido a un error interno. Su monedero no fue cifrado. + Ha fallado el cifrado de la billetera debido a un error interno. Su billetera no fue cifrada. The supplied passphrases do not match. @@ -209,19 +209,19 @@ Wallet unlock failed - Ha fallado el desbloqueo del monedero + Ha fallado el desbloqueo de la billetera The passphrase entered for the wallet decryption was incorrect. - La contraseña introducida para descifrar el monedero es incorrecta. + La contraseña introducida para descifrar la billetera es incorrecta. Wallet decryption failed - Ha fallado el descifrado del monedero + Ha fallado el descifrado de la billetera Wallet passphrase was successfully changed. - Se ha cambiado correctamente la contraseña del monedero. + Se ha cambiado correctamente la contraseña de la billetera Warning: The Caps Lock key is on! @@ -251,7 +251,7 @@ Wallet - Monedero + Billetera Node @@ -259,15 +259,15 @@ &Overview - Vista general + &Vista general Show general overview of wallet - Mostrar vista general del monedero + Mostrar vista general de la billetera &Send - Enviar + &Enviar Send coins to a Dash address @@ -275,7 +275,7 @@ &Receive - Recibir + &Recibir Request payments (generates QR codes and dash: URIs) @@ -283,7 +283,7 @@ &Transactions - Transacciones + &Transacciones Browse transaction history @@ -291,7 +291,7 @@ &Masternodes - Masternodes + &Masternodes Browse masternodes @@ -299,7 +299,7 @@ E&xit - Salir + S&alir Quit application @@ -311,7 +311,7 @@ About &Qt - Acerca de Qt + Acerca de &Qt Show information about Qt @@ -319,11 +319,11 @@ &Options... - Opciones... + &Opciones... &About %1 - Acerca de %1 + &Acerca de %1 Modify configuration options for %1 @@ -331,7 +331,7 @@ &Show / Hide - Mostrar / ocultar + &Mostrar / ocultar Show or hide the main Window @@ -339,43 +339,43 @@ &Encrypt Wallet... - Cifrar monedero… + &Cifrar billetera… Encrypt the private keys that belong to your wallet - Cifrar las llaves privadas que pertenezcan a su monedero + Cifrar las llaves privadas que pertenezcan a su billetera &Backup Wallet... - Copia de seguridad del Monedero... + &Copia de seguridad de la billetera... Backup wallet to another location - Crear copia de seguridad del monedero en otra ubicación + Crear copia de seguridad de la billetera en otra ubicación &Change Passphrase... - Cambiar contraseña… + &Cambiar contraseña… Change the passphrase used for wallet encryption - Cambiar la contraseña utilizada para el cifrado del monedero + Cambiar la contraseña utilizada para el cifrado de la billetera &Unlock Wallet... - Desbloquear Monedero + &Desbloquear billetera Unlock wallet - Desbloquear monedero + Desbloquear billetera &Lock Wallet - Bloquear Monedero + &Bloquear billetera Sign &message... - Firmar mensaje... + Firmar &mensaje... Sign messages with your Dash addresses to prove you own them @@ -383,7 +383,7 @@ &Verify message... - Verificar mensaje... + &Verificar mensaje... Verify messages to ensure they were signed with specified Dash addresses @@ -391,7 +391,7 @@ &Information - Información + &Información Show diagnostic information @@ -399,7 +399,7 @@ &Debug console - Consola de depuración + &Consola de depuración Open debugging console @@ -407,7 +407,7 @@ &Network Monitor - Monitor de Red + &Monitor de Red Show network monitor @@ -415,7 +415,7 @@ &Peers list - Lista de Pares + &Lista de Pares Show peers info @@ -423,15 +423,15 @@ Wallet &Repair - Reparar Monedero + Reparar &billetera Show wallet repair options - Mostrar opciones para reparar monedero + Mostrar opciones para reparar billetera Open Wallet &Configuration File - Abrir Archivo de Configuración del Monedero + Abrir Archivo de &Configuración de la billetera Open configuration file @@ -439,7 +439,7 @@ Open &Masternode Configuration File - Abrir Archivo de Configuración de Masternode + Abrir Archivo de Configuración de &Masternode Open Masternode configuration file @@ -447,15 +447,15 @@ Show Automatic &Backups - Mostrar Copias de Seguridad Automáticas + Mostrar &Copias de Seguridad Automáticas Show automatically created wallet backups - Mostrar las copias de seguridad del monedero creadas automáticamente + Mostrar las copias de seguridad de la billetera creadas automáticamente &Sending addresses... - Direcciones de envío... + &Direcciones de envío... Show the list of used sending addresses and labels @@ -463,7 +463,7 @@ &Receiving addresses... - Direcciones de recepción... + &Direcciones de recepción... Show the list of used receiving addresses and labels @@ -471,7 +471,7 @@ Open &URI... - Abrir URI... + Abrir &URI... Open a dash: URI or payment request @@ -479,7 +479,7 @@ &Command-line options - Opciones de consola de comandos + &Opciones de consola de comandos Show the %1 help message to get a list with possible Dash command-line options @@ -491,7 +491,7 @@ &PrivateSend information - Información de PrivateSend + &Información de PrivateSend Show the PrivateSend basic information @@ -499,19 +499,19 @@ &File - Archivo + &Archivo &Settings - Configuración + &Configuración &Tools - Herramientas + &Herramientas &Help - Ayuda + &Ayuda Tabs toolbar @@ -637,15 +637,15 @@ Wallet is <b>encrypted</b> and currently <b>unlocked</b> - El monedero está <b>cifrado</b> y actualmente <b>desbloqueado</b> + La billetera está <b>cifrada</b> y actualmente <b>desbloqueada</b> Wallet is <b>encrypted</b> and currently <b>unlocked</b> for mixing only - El monedero está <b>encriptado</b>y actualmente<b>desbloqueado</b>solo para el mezclado + La billetera está <b>encriptada</b>y actualmente<b>desbloqueada</b>solo para el mezclado Wallet is <b>encrypted</b> and currently <b>locked</b> - El monedero está <b>cifrado</b> y actualmente <b>bloqueado</b> + La billetera está <b>cifrada</b> y actualmente <b>bloqueada</b> @@ -794,7 +794,7 @@ Por favor, cambie a "Modo Lista" para poder usar esta función. - Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again. + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. Entrada no-anónima seleccionada.<b>PrivateSend será deshabilitado.</b><br><br>Si aún quiere usar PrivateSend, por favor, deseleccione todas las entradas no-anónimas primero y luego marque la casilla de verificación PrivateSend de nuevo. @@ -811,7 +811,7 @@ This label turns red if any recipient receives an amount smaller than the current dust threshold. - Esta etiqueta se vuelve roja si cualquier receptor recibe una cantidad mas pequeña que el límite mínimo actual de polvo. + Esta etiqueta se vuelve roja si cualquier receptor recibe una cantidad mas pequeña que el límite, mínimo actual de polvo. Can vary +/- %1 duff(s) per input. @@ -834,73 +834,6 @@ n/d - - DarksendConfig - - Configure PrivateSend - Configurar PrivateSend - - - Basic Privacy - Privacidad Básica - - - High Privacy - Privacidad Alta - - - Maximum Privacy - Privacidad Máxima - - - Please select a privacy level. - Por favor, seleccione un nivel de privacidad. - - - Use 2 separate masternodes to mix funds up to 1000 DASH - Usar 2 masternodes distintos para mezclar fondos hasta 1000 DASH - - - Use 8 separate masternodes to mix funds up to 1000 DASH - Usar 8 masternodes diferentes para mezclar fondos hasta 1000 DASH - - - Use 16 separate masternodes - Usar 16 masternodes diferentes - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - Esta es la opción más rápida y anonimizar 1000 DASH costará alrededor de 0.025 DASH - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - Esta opción es moderadamente rápida y anonimizar 1000 DASH costará alrededor de 0.05 DASH - - - 0.1 DASH per 1000 DASH you anonymize. - 0.1 DASH por cada 1000 DASH que anonimice. - - - This is the slowest and most secure option. Using maximum anonymity will cost - Esta es la opción más lenta y segura de todas. Usar la anonimización máxima costará - - - PrivateSend Configuration - Configuración de PrivateSend - - - PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend se configuró correctamente a básico (%1 y 2 rondas). Puedes canbiar esto en cualquier momento, abriendo la ventana de configuración de Dash. - - - PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend se configuró correctamente a alto (%1 y 8 rondas). Puedes cambiar esto en cualquier momento abriendo la ventana de configuración de Dash. - - - PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend se configuró correctamente a máximo (%1 y 16 rondas). Puedes cambiar esto en cualquier momento, abriendo la ventana de configuración de Dash. - - EditAddressDialog @@ -909,7 +842,7 @@ &Label - Etiqueta + &Etiqueta The label associated with this address list entry @@ -917,7 +850,7 @@ &Address - Dirección + &Dirección The address associated with this address list entry. This can only be modified for sending addresses. @@ -949,7 +882,7 @@ Could not unlock wallet. - No se pudo desbloquear el monedero. + No se pudo desbloquear la billetera. New key generation failed. @@ -1038,8 +971,8 @@ Información de PrivateSend - <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more info see <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> - <h3>Conceptos básico de PrivateSend</h3>PrivateSend te da verdadera privacidad financiera al ocultar los orígenes de tus fondos. Todos los Dash en su monedero están compuestos por diferentes "depositos" en las que puede pensar como monedas separadas, discretas.<br>PrivateSend utiliza un proceso innovador que mezcla sus depósitos con las depósitos de otras dos personas, sin que sus monedas salgan de su monedero. Usted retiene el control de su dinero en todo momento.<hr><b>El proceso de PrivateSend funciona así:</b><ol type="1"><li>PrivateSend comienza dividiendo los depósitos de sus transacciones en denominaciones estándar. Éstas denominaciones son 0.01 DASH, 0.1 DASH, 1 DASH y 10 DASH - algo así como el papel moneda que usas todos los días.</li><li>Entonces, su monedero envía solicitudes a nodos de software especialmente configurados en la red, llamados "masternodes". Estos masternodes son informados que usted está interesado en mezclar una cierta denominación. Información no identificable es enviada a los masternodes, por lo que nunca saben "quién" es usted.</li><li>Cuando otras dos personas envían mensajes similares, indicando que desean mezclar la misma denominación, comienza una sesión de mezclado. El masternode mezcla los depósitos e instruye a los monederos de los tres usuarios para que paguen el depósito ahora transformado a si mismos. Su monedero paga esa denominación directamente a sí misma, pero en una dirección diferente (llamada dirección de cambio). En orden de ocultar totalmente sus fondos, su monedero debe repetir este proceso varias veces con cada denominación. Cada vez que se completa el proceso, se denomina "ronda". Cada ronda de PrivateSend hace que sea exponencialmente más difícil determinar de dónde provienen los fondos.</li><li>Este proceso de mezclado ocurre en segundo plano sin ninguna intervención de su parte. Cuando desee realizar una transacción, sus fondos ya serán anónimos. No se requiere tiempo de espera adicional.</li></ol><hr><b>IMPORTATE:</b>Su mondero solo contiene 1000 de estas "direcciones de cambio". Cada vez que ocurre un evento de mezclado, hasta 9 de sus direcciones son usadas. Esto significa que esas 1000 direcciones duran alrededor de 100 eventos de mezclado. Cuando 900 de ellas sean usadas, su monedero debe crear más direcciones. Sin embargo, solo podra hacer esto si tiene las copias de seguridad automáticas habilitadas.<br>En consecuencia, los usuarios que tengan las copias de seguridad deshabilitadas también tendrán PrivateSend deshabilitado.<hr>Para mas información visite<a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. + <h3>Conceptos básico de PrivateSend</h3>PrivateSend te da verdadera privacidad financiera al ocultar los orígenes de tus fondos. Todos los Dash en su billetera están compuestos por diferentes "depositos" en las que puede pensar como monedas separadas, discretas.<br>PrivateSend utiliza un proceso innovador que mezcla sus depósitos con las depósitos de otras dos personas, sin que sus monedas salgan de billetera. Usted retiene el control de su dinero en todo momento ..<hr><b>El proceso de PrivateSend funciona así:</b><ol type="1"><li>PrivateSend comienza dividiendo los depósitos de sus transacciones en denominaciones estándar. Éstas denominaciones son 0.01 DASH, 0.1 DASH, 1 DASH y 10 DASH - algo así como el papel moneda que usas todos los días.</li><li>Entonces, su billetera envía solicitudes a nodos de software especialmente configurados en la red, llamados "masternodes". Estos masternodes son informados que usted está interesado en mezclar una cierta denominación. Información no identificable es enviada a los masternodes, por lo que nunca saben "quién" es usted.</li><li>Cuando otras dos personas envían mensajes similares, indicando que desean mezclar la misma denominación, comienza una sesión de mezclado. El masternode mezcla los depósitos e instruye a las billeteras de los tres usuarios para que paguen el depósito ahora transformado a si mismos. Su monedero paga esa denominación directamente a sí misma, pero en una dirección diferente (llamada dirección de cambio).</li> <li>En orden de ocultar totalmente sus fondos, su billetera debe repetir este proceso varias veces con cada denominación. Cada vez que se completa el proceso, se denomina "ronda". Cada ronda de PrivateSend hace que sea exponencialmente más difícil determinar de dónde provienen los fondos.</li><li>Este proceso de mezclado ocurre en segundo plano sin ninguna intervención de su parte. Cuando desee realizar una transacción, sus fondos ya serán anónimos. No se requiere tiempo de espera adicional.</li></ol><hr><b>IMPORTATE:</b>Su billetera solo contiene 1000 de estas "direcciones de cambio". Cada vez que ocurre un evento de mezclado, hasta 9 de sus direcciones son usadas. Esto significa que esas 1000 direcciones duran alrededor de 100 eventos de mezclado. Cuando 900 de ellas sean usadas, su billetera debe crear más direcciones. Sin embargo, solo podra hacer esto si tiene las copias de seguridad automáticas habilitadas.<br>En consecuencia, los usuarios que tengan las copias de seguridad deshabilitadas también tendrán PrivateSend deshabilitado.<hr>Para mas información, consulte la <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">documentación de PrivateSend</a>. @@ -1058,7 +991,7 @@ %1 will download and store a copy of the Dash block chain. At least %2GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory. - %1 se descargara y almacenara una copia de la cadena de bloques de Dash. Por lo menos %2GB de datos se almacenaran en este directorio, y crecerá con el tiempo. El monedero también se almacenara en este directorio. + %1 se descargara y almacenara una copia de la cadena de bloques de Dash. Por lo menos %2GB de datos se almacenaran en este directorio, y crecerá con el tiempo. La billetera también se almacenara en este directorio. Use the default data directory @@ -1097,7 +1030,7 @@ Note: Status of your masternodes in local wallet can potentially be slightly incorrect.<br />Always wait for wallet to sync additional data and then double check from another node<br />if your masternode should be running but you still do not see "ENABLED" in "Status" field. - Nota: El estado de sus masternodes en el monedero local puede estar ligeramente incorrecto.<br />Siempre espere a que el monedero sincronice los datos adicionales y luego vuelva a verificar desde otro nodo<br />Si su masternode debería estar ejecutándose pero aún no ve "HABILITADO" en el campo "Estado". + Nota: El estado de sus masternodes en la billetera local puede estar ligeramente incorrecto.<br />Siempre espere a que la billetera sincronice los datos adicionales y luego vuelva a verificar desde otro nodo<br />Si su masternode debería estar ejecutándose pero aún no ve "HABILITADO" en el campo "Estado". Alias @@ -1129,19 +1062,19 @@ S&tart alias - Iniciar alias + I&niciar alias Start &all - Iniciar todo + Iniciar &todo Start &MISSING - Iniciar FALTANTE + Iniciar &FALTANTE &Update status - Actualizar estado + &Actualizar estado Show additional Masternode information @@ -1149,7 +1082,7 @@ Show &Info... - Mostrar Información + Mostrar &Información Status will be updated automatically in (sec): @@ -1252,7 +1185,7 @@ Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the Dash network, as detailed below. - Es posible que las transacciones recientes aún no estén visibles y, por lo tanto, el saldo de su monedero podría ser incorrecto. Esta información será corregida una vez que su monedero haya terminado de sincronizarse con la red de Dash, como se detalla a continuación. + Es posible que las transacciones recientes aún no estén visibles y, por lo tanto, el saldo de su billetera podría ser incorrecto. Esta información será corregida una vez que su billetera haya terminado de sincronizarse con la red de Dash, como se detalla a continuación. Attempting to spend Dash that are affected by not-yet-displayed transactions will not be accepted by the network. @@ -1326,11 +1259,11 @@ &Main - Principal + &Principal Size of &database cache - Tamaño de la memoria caché en la base de datos + Tamaño de la &memoria caché en la base de datos MB @@ -1338,7 +1271,7 @@ Number of script &verification threads - Número de hilos de verificación de instrucciones + Número de hilos de &verificación de instrucciones (0 = auto, <0 = leave that many cores free) @@ -1350,7 +1283,7 @@ W&allet - Monedero + B&illetera Show additional tab listing all your masternodes in first sub-tab<br/>and all masternodes on the network in second sub-tab. @@ -1370,7 +1303,7 @@ Show warning dialog when PrivateSend detects that wallet has very low number of keys left. - Muestra el cuadro de diálogo de advertencia cuando PrivateSend detecta que el monedero tiene muy pocas llaves restantes. + Muestra el cuadro de diálogo de advertencia cuando PrivateSend detecta que la billetera tiene muy pocas llaves restantes. Warn if PrivateSend is running out of keys @@ -1378,11 +1311,11 @@ Whether to use experimental PrivateSend mode with multiple mixing sessions per block.<br/>Note: You must use this feature carefully.<br/>Make sure you always have recent wallet (auto)backup in a safe place! - Ya sea para usar el modo experimental PrivateSend con múltiples sesiones de mezclado por bloque.<br/>Nota: debe usar esta función con cuidado.<br/>¡Asegúrese de tener siempre una copia de seguridad (automática) del monedero en un lugar seguro! + Ya sea para usar el modo experimental PrivateSend con múltiples sesiones de mezclado por bloque.<br/>Nota: debe usar esta función con cuidado.<br/>¡Asegúrese de tener siempre una copia de seguridad (automática) de la billetera en un lugar seguro! Enable PrivateSend &multi-session - Habilitar multisesión de PrivateSend + Habilitar &multisesión de PrivateSend If you disable the spending of unconfirmed change, the change from a transaction<br/>cannot be used until that transaction has at least one confirmation.<br/>This also affects how your balance is computed. @@ -1414,7 +1347,7 @@ &Connect through SOCKS5 proxy (default proxy): - Conectarse a través de proxy SOCKS5 (proxy predeterminado): + &Conectarse a través de proxy SOCKS5 (proxy predeterminado): Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu. @@ -1442,27 +1375,27 @@ &Start %1 on system login - Iniciar %1 al ingresar al sistema + &Iniciar %1 al ingresar al sistema Enable coin &control features - Activar característica coin control + Activar característica coin &control &Spend unconfirmed change - Gastar cambio no confirmado + &Gastar cambio no confirmado &Network - Red + &Red Map port using &UPnP - Mapear puerto usando UPnP + Mapear puerto usando &UPnP Proxy &IP: - Dirección IP del proxy: + Dirección &IP del proxy: IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1) @@ -1470,7 +1403,7 @@ &Port: - Puerto: + &Puerto: Port of the proxy (e.g. 9050) @@ -1506,11 +1439,11 @@ &Window - Ventana + &Ventana &Hide the icon from the system tray. - Ocultar el icono de la bandeja del sistema. + &Ocultar el icono de la bandeja del sistema. Hide tray icon @@ -1522,19 +1455,19 @@ &Minimize to the tray instead of the taskbar - Minimizar en la bandeja en vez de en la barra de tareas + &Minimizar en la bandeja en vez de en la barra de tareas M&inimize on close - Minimizar al cerrar + M&inimizar al cerrar &Display - Interfaz + &Interfaz User Interface &language: - Idioma de la interfaz de usuario + &Idioma de la interfaz de usuario The user interface language can be set here. This setting will take effect after restarting %1. @@ -1552,7 +1485,7 @@ https://www.transifex.com/projects/p/dash/ &Unit to show amounts in: - Unidad para mostrar cantidades: + &Unidad para mostrar cantidades: Choose the default subdivision unit to show in the interface and when sending coins. @@ -1576,15 +1509,15 @@ https://www.transifex.com/projects/p/dash/ &Reset Options - Restablecer opciones + &Restablecer opciones &OK - Aceptar + &Aceptar &Cancel - Cancelar + &Cancelar default @@ -1623,7 +1556,7 @@ https://www.transifex.com/projects/p/dash/ The displayed information may be out of date. Your wallet automatically synchronizes with the Dash network after a connection is established, but this process has not completed yet. - La información mostrada puede estar desactualizada. Su monedero se sincroniza automáticamente con la red Dash después de que se haya establecido una conexión, pero este proceso aún no se ha completado. + La información mostrada puede estar desactualizada. Su billetera se sincroniza automáticamente con la red Dash después de que se haya establecido una conexión, pero este proceso aún no se ha completado. Available: @@ -1831,7 +1764,7 @@ https://www.transifex.com/projects/p/dash/ Note: You can turn this message off in options. - Nota: desactiva este mensaje en las opciones. + Nota: Puedes desactivar este mensaje en opciones. WARNING! Something went wrong on automatic backup @@ -1843,7 +1776,7 @@ https://www.transifex.com/projects/p/dash/ Mixing is disabled, please close your wallet and fix the issue! - El Mezclado está deshabilitado, ¡por favor, cierre su monedero y solucione el problema! + El Mezclado está deshabilitado, ¡por favor, cierre su billetera y solucione el problema! Enabled @@ -1855,7 +1788,7 @@ https://www.transifex.com/projects/p/dash/ WARNING! Failed to replenish keypool, please unlock your wallet to do so. - ¡ADVERTENCIA! Falla al reponer keypool, para hacerlo, por favor desbloquee su monedero. + ¡ADVERTENCIA! Falla al reponer keypool, para hacerlo, por favor desbloquee su billetera. Last PrivateSend message: @@ -1881,7 +1814,7 @@ https://www.transifex.com/projects/p/dash/ Wallet is locked and user declined to unlock. Disabling PrivateSend. - El monedero está bloqueado y el usuario se negó a desbloquearlo. Desactivando PrivateSend. + La billetera está bloqueado y el usuario se negó a desbloquearlo. Desactivando PrivateSend. @@ -2104,7 +2037,7 @@ https://www.transifex.com/projects/p/dash/ &Save Image... - Guardar Imagen... + &Guardar Imagen... Error creating QR Code. @@ -2115,11 +2048,11 @@ https://www.transifex.com/projects/p/dash/ QRGeneralImageWidget &Save Image... - Guardar Imagen... + &Guardar Imagen... &Copy Image - Copiar imagen + &Copiar imagen Save QR Code @@ -2134,11 +2067,11 @@ https://www.transifex.com/projects/p/dash/ QRImageWidget &Save Image... - Guardar Imagen... + &Guardar Imagen... &Copy Image - Copiar imagen + &Copiar imagen Save QR Code @@ -2157,7 +2090,7 @@ https://www.transifex.com/projects/p/dash/ &Information - Información + &Información General @@ -2177,7 +2110,7 @@ https://www.transifex.com/projects/p/dash/ &Open - Abrir + &Abrir Startup time @@ -2229,7 +2162,7 @@ https://www.transifex.com/projects/p/dash/ &Console - Consola + &Consola Clear console @@ -2237,11 +2170,11 @@ https://www.transifex.com/projects/p/dash/ &Network Traffic - Tráfico de Red + &Tráfico de Red &Clear - Limpiar + &Limpiar Totals @@ -2257,7 +2190,7 @@ https://www.transifex.com/projects/p/dash/ &Peers - Pares + &Pares Banned peers @@ -2293,7 +2226,7 @@ https://www.transifex.com/projects/p/dash/ Wallet Path - Ruta del Monedero + Ruta de la billetera User Agent @@ -2357,11 +2290,11 @@ https://www.transifex.com/projects/p/dash/ &Wallet Repair - Reparar Monedero + &Reparar Billetera Salvage wallet - Rescatar monedero + Rescatar billetera Rescan blockchain files @@ -2377,11 +2310,11 @@ https://www.transifex.com/projects/p/dash/ Upgrade wallet format - Actualizar formato del monedero + Actualizar formato de la billetera The buttons below will restart the wallet with command-line options to repair the wallet, fix issues with corrupt blockhain files or missing/obsolete transactions. - Los botones de abajo reiniciarán el monedero con las opciones de la línea de comandos para reparar el monedero, arreglar problemas con archivos corruptos de la cadena de bloques o transacciones perdidas/obsoletas. + Los botones de abajo reiniciarán la billetera con las opciones de la línea de comandos para reparar la billetera, arreglar problemas con archivos corruptos de la cadena de bloques o transacciones perdidas/obsoletas. -salvagewallet: Attempt to recover private keys from a corrupt wallet.dat. @@ -2389,7 +2322,7 @@ https://www.transifex.com/projects/p/dash/ -rescan: Rescan the block chain for missing wallet transactions. - -rescan: Volver a examinar la cadena de bloques en busca de transacciones perdidas. + -rescan: Volver a examinar la cadena de bloques en busca de transacciones perdidas en la billetera. -zapwallettxes=1: Recover transactions from blockchain (keep meta-data, e.g. account owner). @@ -2401,11 +2334,11 @@ https://www.transifex.com/projects/p/dash/ -upgradewallet: Upgrade wallet to latest format on startup. (Note: this is NOT an update of the wallet itself!) - -upgradewallet: Actualizar el monedero al último formato al iniciar. (Nota: ¡esto NO es una actualización del propio monedero!) + -upgradewallet: Actualizar la billetera al último formato al iniciar. (Nota: ¡esto NO es una actualización de la propia billetera!) Wallet repair options. - Opciones de reparación del monedero. + Opciones de reparación de la billetera Rebuild index @@ -2417,7 +2350,7 @@ https://www.transifex.com/projects/p/dash/ &Disconnect - Desconectar + &Desconectar Ban for @@ -2425,23 +2358,23 @@ https://www.transifex.com/projects/p/dash/ 1 &hour - 1 hora + 1 &hora 1 &day - 1 día + 1 &día 1 &week - 1 semana + 1 &semana 1 &year - 1 año + 1 &año &Unban - Levantar prohibición + &Levantar prohibición Welcome to the %1 RPC console. @@ -2457,7 +2390,7 @@ https://www.transifex.com/projects/p/dash/ WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramification of a command. - ADVERTENCIA: Estafadores han estado activos, diciendo a los usuarios que escriban comandos aquí, robando el contenido de sus monederos. No use esta consola sin un completo entendimiento de la ramificación de un comando. + ADVERTENCIA: Estafadores han estado activos, diciendo a los usuarios que escriban comandos aquí, robando el contenido de su billetera. No use esta consola sin un completo entendimiento de la ramificación de un comando. In: @@ -2528,7 +2461,7 @@ https://www.transifex.com/projects/p/dash/ R&euse an existing receiving address (not recommended) - Reutilizar una dirección receptora existente (no recomendado) + R&eutilizar una dirección receptora existente (no recomendado) An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Dash network. @@ -2536,7 +2469,7 @@ https://www.transifex.com/projects/p/dash/ &Message: - Mensaje: + &Mensaje: An optional label to associate with the new receiving address. @@ -2552,7 +2485,7 @@ https://www.transifex.com/projects/p/dash/ &Label: - Etiqueta: + &Etiqueta: An optional amount to request. Leave this empty or zero to not request a specific amount. @@ -2560,11 +2493,11 @@ https://www.transifex.com/projects/p/dash/ &Amount: - Cantidad: + &Cantidad: &Request payment - Solicitar pago + &Solicitar pago Clear all fields of the form. @@ -2623,15 +2556,15 @@ https://www.transifex.com/projects/p/dash/ Copy &URI - Copiar URI + Copiar &URI Copy &Address - Copiar Dirección + Copiar &Dirección &Save Image... - Guardar Imagen... + &Guardar Imagen... Request payment to %1 @@ -2857,7 +2790,7 @@ https://www.transifex.com/projects/p/dash/ S&end - Enviar + E&nviar Clear all fields of the form. @@ -2865,7 +2798,7 @@ https://www.transifex.com/projects/p/dash/ Clear &All - Limpiar Todo + Limpiar &Todo Send to multiple recipients at once @@ -2873,7 +2806,7 @@ https://www.transifex.com/projects/p/dash/ Add &Recipient - Añadir Destinatario + Añadir &Destinatario Balance: @@ -3013,7 +2946,7 @@ https://www.transifex.com/projects/p/dash/ The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure? - La dirección que ha seleccionado para cambiar no es parte de este monedero. Ninguno o todos los fondos en su monedero pueden ser enviados a esta dirección. ¿Está seguro? + La dirección que ha seleccionado para cambiar no es parte de esta billetera. Ninguno o todos los fondos en su billetera pueden ser enviados a esta dirección. ¿Está seguro? (no label) @@ -3028,7 +2961,7 @@ https://www.transifex.com/projects/p/dash/ Pay &To: - Pagar a: + Pagar &a: The Dash address to send the payment to @@ -3056,7 +2989,7 @@ https://www.transifex.com/projects/p/dash/ &Label: - Etiqueta: + &Etiqueta: Enter a label for this address to add it to the list of used addresses @@ -3064,7 +2997,7 @@ https://www.transifex.com/projects/p/dash/ A&mount: - Cantidad: + C&antidad: The fee will be deducted from the amount being sent. The recipient will receive a lower amount of Dash than you enter in the amount field. If multiple recipients are selected, the fee is split equally. @@ -3072,7 +3005,7 @@ https://www.transifex.com/projects/p/dash/ S&ubtract fee from amount - Extraer comisión de la cantidad + E&xtraer comisión de la cantidad Message: @@ -3129,7 +3062,7 @@ https://www.transifex.com/projects/p/dash/ &Sign Message - Firmar Mensaje + &Firmar Mensaje You can sign messages/agreements with your addresses to prove you can receive Dash sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. @@ -3173,7 +3106,7 @@ https://www.transifex.com/projects/p/dash/ Sign &Message - Firmar Mensaje + Firmar &Mensaje Reset all sign message fields @@ -3181,11 +3114,11 @@ https://www.transifex.com/projects/p/dash/ Clear &All - Limpiar Todo + Limpiar &Todo &Verify Message - Verificar Mensaje + &Verificar Mensaje Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction! @@ -3201,7 +3134,7 @@ https://www.transifex.com/projects/p/dash/ Verify &Message - Verificar Mensaje + Verificar &Mensaje Reset all verify message fields @@ -3225,7 +3158,7 @@ https://www.transifex.com/projects/p/dash/ Wallet unlock was cancelled. - Se ha cancelado el desbloqueo del monedero. + Se ha cancelado el desbloqueo de la billetera. Private key for the entered address is not available. @@ -3805,7 +3738,7 @@ https://www.transifex.com/projects/p/dash/ WalletFrame No wallet has been loaded. - No se ha cargado ningún monedero. + No se ha cargado ninguna billetera. @@ -3827,7 +3760,7 @@ https://www.transifex.com/projects/p/dash/ WalletView &Export - Exportar + &Exportar Export the data in the current tab to a file @@ -3839,11 +3772,11 @@ https://www.transifex.com/projects/p/dash/ Backup Wallet - Copia de Seguridad del Monedero + Copia de Seguridad de la billetera Wallet Data (*.dat) - Datos del Monedero (*.dat) + Datos de la billetera (*.dat) Backup Failed @@ -3851,7 +3784,7 @@ https://www.transifex.com/projects/p/dash/ There was an error trying to save the wallet data to %1. - Ha habido un error al intentar guardar los datos del monedero en %1. + Ha habido un error al intentar guardar los datos de la billetera en %1. Backup Successful @@ -3859,7 +3792,7 @@ https://www.transifex.com/projects/p/dash/ The wallet data was successfully saved to %1. - Los datos del monedero se han guardado con éxito en %1. + Los datos de la billetera se han guardado con éxito en %1. @@ -3878,7 +3811,7 @@ https://www.transifex.com/projects/p/dash/ Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) - Ejecutar comando cuando una transacción del monedero cambie (%s en cmd es reemplazado por TxID) + Ejecutar comando cuando una transacción de la billetera cambie (%s en cmd es reemplazado por TxID) Execute command when the best block changes (%s in cmd is replaced by block hash) @@ -3886,7 +3819,7 @@ https://www.transifex.com/projects/p/dash/ Name to construct url for KeePass entry that stores the wallet passphrase - Nombre para construir la url para la entrada de KeePass que almacena la contraseña del monedero + Nombre para construir la url para la entrada de KeePass que almacena la contraseña de la billetera Set maximum size of high-priority/low-fee transactions in bytes (default: %d) @@ -3934,7 +3867,7 @@ https://www.transifex.com/projects/p/dash/ Cannot downgrade wallet - No se puede regresar a una versión anterior del monedero + No se puede regresar a una versión anterior de la billetera Cannot write default address @@ -3962,7 +3895,7 @@ https://www.transifex.com/projects/p/dash/ Do not load the wallet and disable wallet RPC calls - No cargue el monedero y desactive las llamadas RPC + No cargue la billetera y desactive las llamadas RPC Do you want to rebuild the block database now? @@ -3982,7 +3915,7 @@ https://www.transifex.com/projects/p/dash/ Error initializing wallet database environment %s! - Error al inicializar el entorno de la base de datos del monedero %s + Error al inicializar el entorno de la base de datos de la billetera %s Error loading block database @@ -4038,15 +3971,15 @@ https://www.transifex.com/projects/p/dash/ Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality) - Crear archivos nuevos con los permisos por defecto del sistema, en lugar de umask 077 (sólo será efectivo con la funcionalidad del monedero desactivada) + Crear archivos nuevos con los permisos por defecto del sistema, en lugar de umask 077 (sólo será efectivo con la funcionalidad de la billetera desactivada) Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup - Borrar todas las transacciones del monedero y recuperar únicamente aquellas partes de la cadena de bloques a través de -rescan al iniciar + Borrar todas las transacciones de la billetera y recuperar únicamente aquellas partes de la cadena de bloques a través de -rescan al iniciar Error loading %s: You can't enable HD on a already existing non-HD wallet - Error cargando %s: No puede habilitar HD en un monedero non-HD existente. + Error cargando %s: No puede habilitar HD en una billetera non-HD existente. Found unconfirmed denominated outputs, will wait till they confirm to continue. @@ -4074,7 +4007,7 @@ https://www.transifex.com/projects/p/dash/ Make sure to encrypt your wallet and delete all non-encrypted backups after you verified that wallet works! - ¡Asegúrese de encriptar su monedero y borrar todas las copias de seguridad no encriptadas después de verificar que su monedero funciona! + ¡Asegúrese de encriptar su billetera y borrar todas las copias de seguridad no encriptadas después de verificar que su billetera funciona! Maximum size of data in data carrier transactions we relay and mine (default: %u) @@ -4094,11 +4027,11 @@ https://www.transifex.com/projects/p/dash/ User defined mnemonic for HD wallet (bip39). Only has effect during wallet creation/first start (default: randomly generated) - Mnemonic definida por el usuario para monedero HD (bip39). Solo tomara efecto durante la creación/primer inicio de su monedero (predeterminado: generado aleatoriamente) + Mnemonic definida por el usuario para la billetera HD (bip39). Solo tomara efecto durante la creación/primer inicio de su billetera (predeterminado: generado aleatoriamente) User defined seed for HD wallet (should be in hex). Only has effect during wallet creation/first start (default: randomly generated) - Semilla definida por el usuario para monedero HD (debe estar en hexadecimal). Solo tomara efecto durante la creación/primer inicio de su monedero (predeterminado: generado aleatoriamente) + Semilla definida por el usuario para la billetera HD (debe estar en hexadecimal). Solo tomara efecto durante la creación/primer inicio de su billetera (predeterminado: generado aleatoriamente) Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway @@ -4146,7 +4079,7 @@ https://www.transifex.com/projects/p/dash/ Error loading %s: You can't disable HD on a already existing HD wallet - Error cargando %s: No puede deshabilitar HD en un monedero HD existente + Error cargando %s: No puede deshabilitar HD en una billetera HD existente Failed to load fulfilled requests cache from @@ -4414,7 +4347,7 @@ https://www.transifex.com/projects/p/dash/ Upgrade wallet to latest format on startup - Actualizar el monedero al último formato al iniciar + Actualizar la billetera al último formato al iniciar Use KeePass 2 integration using KeePassHttp plugin (default: %u) @@ -4480,6 +4413,10 @@ https://www.transifex.com/projects/p/dash/ Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) Deshabilitar todas las funcionalidades especificas de Dash (Masternodes, PrivateSend, InstantSend, Governanza) (0-1, predeterminado: %u) + + %s file contains all private keys from this wallet. Do not share it with anyone! + %s archivo contiene todas las llaves privadas de esta billetera. ¡No lo compartas con nadie! + Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) Descubra direcciones IP propias (predeterminado: 1 cuando se escucha y nadie -externalip o -proxy) @@ -4498,15 +4435,19 @@ https://www.transifex.com/projects/p/dash/ Enable use of automated PrivateSend for funds stored in this wallet (0-1, default: %u) - Habilitar uso de PrivateSend automatizado para almacenar fundos en este monedero (0-1, predeterminado: %u) + Habilitar uso de PrivateSend automatizado para almacenar fundos en esta billetera (0-1, predeterminado: %u) Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID) - Ejecutar comando en el monedero cuando la transacción InstantSend este totalmente bloqueada (%s en cmd es reemplazada por TxID) + Ejecutar comando en la billetera cuando la transacción InstantSend este totalmente bloqueada (%s en cmd es reemplazada por TxID) + + + Extra transactions to keep in memory for compact block reconstructions (default: %u) + Transacciones extra para guardar en memoria para reconstrucciones de bloques compactos (predeterminado: %u) Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. - ¡Error al crear copia de seguridad, el archivo ya existe! Esto puede ocurrir si reinicio el monedero en menos de 60 segundos. Puede continuar si esta de acuerdo con esto. + ¡Error al crear copia de seguridad, el archivo ya existe! Esto puede ocurrir si reinicio la billetera en menos de 60 segundos. Puede continuar si esta de acuerdo con esto. Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s) @@ -4554,7 +4495,7 @@ https://www.transifex.com/projects/p/dash/ Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node) - Poda: la ultima sincronizacion del monedero sobrepasa los datos podados. Necesitas reindexar con -reindex (o descargar la cadena de bloques de nuevo en el caso de un nodo podado) + Poda: la ultima sincronizacion de la billetera sobrepasa los datos podados. Necesitas reindexar con -reindex (o descargar la cadena de bloques de nuevo en el caso de un nodo podado) Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u) @@ -4566,7 +4507,7 @@ https://www.transifex.com/projects/p/dash/ Specify full path to directory for automatic wallet backups (must exist) - Especificar ruta completa del directorio para copias de seguridad completas del monedero (debe existir) + Especificar ruta completa del directorio para copias de seguridad completas de la billetera (debe existir) Support filtering of blocks and transaction with bloom filters (default: %u) @@ -4584,6 +4525,10 @@ https://www.transifex.com/projects/p/dash/ Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. La longitud total de la cadena de versión de red ( %i ) supera la longitud máxima ( %i ) . Reducir el número o tamaño de uacomments . + + Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index. + El índice de transacción no se puede desactivar en modo completo. Inicia con el interruptor de línea de comando -litemode o habilita el índice de transacción. + Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) Intentar mantener el tráfico de salida bajo el objetivo determinado (en MiB por 24h), 0 = sin limite (predeterminado :%d ) @@ -4606,16 +4551,20 @@ https://www.transifex.com/projects/p/dash/ WARNING! Failed to replenish keypool, please unlock your wallet to do so. - ¡ADVERTENCIA! Error al reponer la keypool, por favor desbloquee su monedero para hacerlo. + ¡ADVERTENCIA! Error al reponer la keypool, por favor desbloquee su billetera para hacerlo. Wallet is locked, can't replenish keypool! Automatic backups and mixing are disabled, please unlock your wallet to replenish keypool. - ¡El monedero esta bloqueado, no se puede reponer keypool! Copias de seguridad automáticas y mezclado están deshabilitados, por favor desbloquee su monedero para reponer keypool. + ¡La billetera esta bloqueado, no se puede reponer keypool! Copias de seguridad automáticas y mezclado están deshabilitados, por favor desbloquee su billetera para reponer keypool. Warning: Unknown block versions being mined! It's possible unknown rules are in effect Advertencia: Se están minando versiones de bloques desconocidas! Es posible que normas desconocidas estén activas + + You are starting in lite mode, all Dash-specific functionality is disabled. + Estás iniciando en modo lite, toda la funcionalidad Dash-specific está deshabilitada. + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain Necesitas reconstruir la base de datos utilizando -reindex para volver al modo sin poda. Esto volverá a descargar toda la cadena de bloques @@ -4660,6 +4609,14 @@ https://www.transifex.com/projects/p/dash/ Enable publish hash block in <address> Habilitar la publicación del hash del bloque en <address> + + Enable publish hash of governance objects (like proposals) in <address> + Habilitar la publicación de hash de objetos de gobernanza (como propuestas) en <address> + + + Enable publish hash of governance votes in <address> + Habilitar la publicación de hash de los votos de gobernanza en <address> + Enable publish hash transaction (locked via InstantSend) in <address> Habilitar la publicación del hash de transacción (bloqueado a través de InstantSend) en <address> @@ -4700,6 +4657,14 @@ https://www.transifex.com/projects/p/dash/ Failed to delete backup, error: %s Error al borrar copia de seguridad, error: %s + + Failed to load InstantSend data cache from + Error al cargar el caché de datos de InstantSend desde + + + Failed to load sporks cache from + Error al cargar el caché de sporks desde + Failed to parse host:port string Error al interpretar linea host:port @@ -4720,6 +4685,10 @@ https://www.transifex.com/projects/p/dash/ Invalid amount for -fallbackfee=<amount>: '%s' Cantidad inválida para -fallbackfee=<amount>: '%s' + + Invalid input count. + Cuenta de entrada inválida. + Keep the transaction memory pool below <n> megabytes (default: %u) Mantener el pool de la memoria de transacciones por debajo de <n> megabytes (predeterminado: %u) @@ -4736,6 +4705,10 @@ https://www.transifex.com/projects/p/dash/ Line: %d Línea: %d + + Loading InstantSend data cache... + Cargando el caché de datos de InstantSend... + Loading addresses... Cargando direcciones... @@ -4748,13 +4721,17 @@ https://www.transifex.com/projects/p/dash/ Loading governance cache... Cargando memoria cache de gobernanza... + + Loading sporks cache... + Cargando el caché de sporks... + Loading wallet... (%3.2f %%) - Cargando monedero... (%3.2f %%) + Cargando billetera... (%3.2f %%) Loading wallet... - Cargando monedero... + Cargando billetera... Location of the auth cookie (default: data dir) @@ -4762,7 +4739,7 @@ https://www.transifex.com/projects/p/dash/ Make the wallet broadcast transactions - Realiza las operaciones de difusión del monedero + Realiza las operaciones de difusión de la billetera Masternode cache is empty, skipping payments and governance cache... @@ -4818,7 +4795,7 @@ https://www.transifex.com/projects/p/dash/ Number of automatic wallet backups (default: %u) - Numero de copias de seguridad automáticas del monedero (predeterminado: %u) + Numero de copias de seguridad automáticas de la billetera (predeterminado: %u) Options: @@ -4862,11 +4839,11 @@ https://www.transifex.com/projects/p/dash/ Rescan the block chain for missing wallet transactions on startup - Reescanear la cadena de bloques para buscar transacciones perdidas en el monedero al iniciar + Reescanear la cadena de bloques para buscar transacciones perdidas en la billetera al iniciar The wallet will avoid paying less than the minimum relay fee. - El monedero evitará pagar menos que la comisión mínima de transmisión. + La billetera evitará pagar menos que la comisión mínima de transmisión. This is the minimum transaction fee you pay on every transaction. @@ -4888,6 +4865,10 @@ https://www.transifex.com/projects/p/dash/ Transaction must have at least one recipient La transacción debe de tener al menos un receptor + + Transaction too large + Transacción demasiado grande + Trying to connect... Intentando conectar... @@ -4906,11 +4887,15 @@ https://www.transifex.com/projects/p/dash/ Wallet debugging/testing options: - Opciones de depuración/pruebas de monedero: + Opciones de depuración/pruebas de billetera: + + + Wallet is not initialized + Billetera no inicializada Wallet needed to be rewritten: restart %s to complete - Es necesario reescribir el monedero: reiniciar %s para completar + Es necesario reescribir la billetera: reiniciar %s para completar Warning: unknown new rules activated (versionbit %i) @@ -4918,12 +4903,16 @@ https://www.transifex.com/projects/p/dash/ Wasn't able to create wallet backup folder %s! - ¡No es posible crear carpeta de copia de seguridad del monedero %s! + ¡No es posible crear carpeta de copia de seguridad de la billetera %s! Whether to operate in a blocks only mode (default: %u) Ya sea para operar en modo solo bloques (predeterminado: %u) + + You can not start a masternode in lite mode. + No puedes iniciar un masternode en modo lite. + ZeroMQ notification options: Opciones de notificación ZeroQM: @@ -4982,7 +4971,7 @@ https://www.transifex.com/projects/p/dash/ Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s) - Máximas comisiones totales (en %s) para utilizar en una sola transacción del monedero o transacciones sin procesar; establecer esto demasiado bajo puede abortar grandes transacciones (predeterminado: %s) + Máximas comisiones totales (en %s) para utilizar en una sola transacción de la billetera o transacciones sin procesar; establecer esto demasiado bajo puede abortar grandes transacciones (predeterminado: %s) Override spork address. Only useful for regtest and devnet. Using this on mainnet or testnet will ban you. @@ -5012,10 +5001,6 @@ https://www.transifex.com/projects/p/dash/ Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) Establecer la tasa más baja (en %s/kB) por transacciones para incluirse en la creación de bloque. (predeterminado: %s) - - Show N confirmations for a successfully locked transaction (%u-%u, default: %u) - Mostrar N confirmaciones para un bloqueo de transacciones (%u-%u, predeterminado: %u) - This is the transaction fee you may pay when fee estimates are not available. Esta es la tarifa de cuota que debe pagar cuando las estimaciones de tarifas no estén disponibles. @@ -5034,11 +5019,11 @@ https://www.transifex.com/projects/p/dash/ Use hierarchical deterministic key generation (HD) after BIP39/BIP44. Only has effect during wallet creation/first start - Usar generación de llave jerárquica determinista (HD) después de BIP39/BIP44. Solo tomara efecto durante la creación/primer inicio del monedero + Usar generación de llave jerárquica determinista (HD) después de BIP39/BIP44. Solo tomara efecto durante la creación/primer inicio de la billetera User defined mnemonic passphrase for HD wallet (BIP39). Only has effect during wallet creation/first start (default: empty string) - Contraseña mnemonic definida por el usuario para monedero HD (BIP39). Solo tomara efecto durante la creación/primer inicio del monedero (predeterminado: linea vacía) + Contraseña mnemonic definida por el usuario para la billetera HD (BIP39). Solo tomara efecto durante la creación/primer inicio de la billetera (predeterminado: linea vacía) Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times @@ -5046,7 +5031,7 @@ https://www.transifex.com/projects/p/dash/ Wallet will not create transactions that violate mempool chain limits (default: %u) - El monedero no creará transacciones que violen los límites de la cadena mempool (predeterminado: %u) + La billetera no creará transacciones que violen los límites de la cadena mempool (predeterminado: %u) Warning: At least %d of %d masternodes are running on a newer software version. Please check latest releases, you might need to update too. @@ -5058,7 +5043,7 @@ https://www.transifex.com/projects/p/dash/ Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup. - Advertencia: archivo monedero esta corrupto, datos recuperados! Original %s guardado como %s en %s; si su balance o transacciones es incorrecto, debe restaurar desde una copia de seguridad. + Advertencia: archivo Billetera esta corrupto, datos recuperados! Original %s guardado como %s en %s; si su balance o transacciones es incorrecto, debe restaurar desde una copia de seguridad. Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times. @@ -5098,7 +5083,7 @@ https://www.transifex.com/projects/p/dash/ Attempt to recover private keys from a corrupt wallet on startup - Intentar recuperar llaves privadas de un monedero corrupto al iniciar + Intentar recuperar llaves privadas de una billetera corrupta al iniciar Cannot resolve -%s address: '%s' @@ -5118,11 +5103,11 @@ https://www.transifex.com/projects/p/dash/ Error loading %s: Wallet corrupted - Error cargando %s: Monedero corrupto + Error cargando %s: Billetera corrupta Error loading %s: Wallet requires newer version of %s - Error cargando %s: Monedero requiere una versión mas reciente de %s + Error cargando %s: La billetera requiere una versión mas reciente de %s Error upgrading chainstate database @@ -5222,7 +5207,7 @@ https://www.transifex.com/projects/p/dash/ Specify wallet file (within data directory) - Especificar archivo de monedero (dentro del directorio de datos) + Especificar archivo de billetera (dentro del directorio de datos) Specify your own public address @@ -5302,7 +5287,7 @@ https://www.transifex.com/projects/p/dash/ Verifying wallet... - Verificando monedero... + Verificando billetera... Very low number of keys left: %d @@ -5310,19 +5295,19 @@ https://www.transifex.com/projects/p/dash/ Wallet %s resides outside data directory %s - El monedero %s se encuentra fuera del directorio de datos %s + La billetera %s se encuentra fuera del directorio de datos %s Wallet is locked. - El monedero está bloqueado. + La billetera está bloqueada. Wallet options: - Opciones de monedero: + Opciones de billetera: Wallet window title - Título de la ventana del monedero + Título de la ventana de la billetera Warning @@ -5338,7 +5323,7 @@ https://www.transifex.com/projects/p/dash/ Zapping all transactions from wallet... - Eliminando todas las transacciones del monedero... + Eliminando todas las transacciones de la billetera... \ No newline at end of file diff --git a/src/qt/locale/dash_fi.ts b/src/qt/locale/dash_fi.ts index 4a0f43e4ef13..8393905ed04b 100644 --- a/src/qt/locale/dash_fi.ts +++ b/src/qt/locale/dash_fi.ts @@ -794,7 +794,7 @@ Vaihda "Lista tilaan" käyttääksesi tätä toimintoa. - Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again. + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. Ei anonyymeja syötteitä valittu. <b>PrivateSend poistetaan käytöstä.</b><br><br>Jos silti haluat käyttää PrivateSend:iä, poista ei anonyymit valinnat ensin ja valitse uudelleen PrivateSend optio. @@ -834,73 +834,6 @@ e/s - - DarksendConfig - - Configure PrivateSend - PrivateSend Asetukset - - - Basic Privacy - Perustason Yksityisyys - - - High Privacy - Korkean tason Yksityisyys - - - Maximum Privacy - Maksimaalinen Yksityisyys - - - Please select a privacy level. - Valitse yksityisyyden taso. - - - Use 2 separate masternodes to mix funds up to 1000 DASH - Käytä 2 erillistä masternodea sekoittaaksesi varoja - - - Use 8 separate masternodes to mix funds up to 1000 DASH - Käytä 8 erillistä masternodea sekoittaaksesi varoja - - - Use 16 separate masternodes - Käytä 16 erillistä masternodea - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - Tämä vaihtoehto on nopein ja maksaa noin ~0.025 DASH kun anonymisoidaan 1000 DASH - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - Tämä vaihtoehto on keskinopea ja maksaa noin ~0.05 DASH kun anonymisoidaan 1000 DASH - - - 0.1 DASH per 1000 DASH you anonymize. - 0.1 DASH maksu per 1000 DASH jonka anonymisoit. - - - This is the slowest and most secure option. Using maximum anonymity will cost - Tämä vaihtoehto on hitain ja kaikkein anonyymi. Suurimman yksityisyyden käyttö maksaa - - - PrivateSend Configuration - PrivateSend Asetukset - - - PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend on asetettu perusasetuksiin (%1 and 2 kierrosta). Voit muuttaa asetuksia milloin vain Dash asetuksista. - - - PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend on asetettu korkean tason asetuksiin (%1 and 8 kierrosta). Voit muuttaa asetuksia milloin vain Dash asetuksista. - - - PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend on asetettu maksimitason asetuksiin (%1 and 16 kierrosta). Voit muuttaa asetuksia milloin vain Dash asetuksista. - - EditAddressDialog @@ -1038,8 +971,8 @@ PrivateSend tietoja - <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more info see <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> - <h3>PrivateSend perusteet</h3> PrivateSend tarjoaa yksityisyyden hämärtämällä varojesi alkuperäisen osoitteen. Kaikki Dash:it lompakossasi muodostuvat erillisistä "syötteistä", joita voit ajatella erillisinä kolikkoina.<br> PrivateSend käyttää innovatiivista prosessia sekoittaakseen lompakkosi syötteet kahden muun ihmisen syötteisiin, siirtämättä varoja pois lompakostasi. Varojesi kontrolli pysyy aina sinulla.<hr> <b> PrivateSend prosessi toimii seuraavasti:</b><ol type="1"> <li>PrivateSend aloittaa pilkkomalla siirtotapahtumiesi syötteet pienemmiksi standardi arvoiksi. Nämä arvot ovat 0.01 DASH, 0.1 DASH, 1 DASH ja 10 DASH.</li> <li>Sitten lompakkosi lähettää pyynnön verkon palvelimille, joita kutsutaan "masternode:iksi". Masternodet saavat tiedon että haluat sekoittaa pilkottuja standardi arvoja. Mitään tunnistetietoja ei lähetetä masternode:ille, ne eivät koskaan tiedä "kuka" olet.</li> <li>Kun 2 muuta käyttäjää ilmoittaa että he haluavat myös sekoittaa varoja, alkaa sekoitus-sessio. Masternodet sekoittavat standardi arvot ja ilmoittavat kaikille 3:lle käyttäjän lompakoille että maksavat sekoitetut arvot takaisin itselleen. Lompakkosi maksaa nuo sekoitetut arvot suoraan itselleen, mutta eri osoitteeseen (vaihto-osoite).</li> <li>Jotta varojesi alkuperäinen lähde hämärretään, lompakkosi suorittaa tämän prosessin useita kertoja kaikilla standardi arvoilla. Aina kun prosessi on valmis, sitä kutsutaan "kierrokseksi". Jokainen PrivateSend kierros tekee eksponentiaalisesti vaikeammaksi löytää varojesi alkuperäisen osoitteen.</li> <li>Tämä sekoitusprosessi tapahtuu taustalla ilman käyttäjän toimenpiteitä. Kun haluat myöhemmin tehdä anonyymin varojen siirron, on varasi valmiiksi sekoitettu (anonymisoitu). Erillistä sekoitusta/odotusta ei tarvita.</li> </ol> <hr><b>TÄRKEÄÄ:</b> Lompakkosi sisältää vain 1000 "vaihto-osoitetta". Aina kun sekoitustapahtuma tehdään, max 9 osoitetta käytetään. Tämä tarkoittaa sitä että nuo 1000 osoitetta kestää noin 100 sekoitustapahtumaa. Kun 900 osoitetta on käytetty, lompakkosi täytyy tehdä lisää osoitteita. Se voi tehdä niitä vain jos automaattinen varmistus on käytössä.<br> Tästä seuraa että jos varmistus ei ole käytössä, myös PrivateSend on pois käytöstä. <hr>Lisätietoja <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. + <h3>PrivateSend perusteet</h3> PrivateSend tarjoaa yksityisyyden hämärtämällä varojesi alkuperäisen osoitteen. Kaikki Dash:it lompakossasi muodostuvat erillisistä "syötteistä", joita voit ajatella erillisinä kolikkoina.<br> PrivateSend käyttää innovatiivista prosessia sekoittaakseen lompakkosi syötteet kahden muun ihmisen syötteisiin, siirtämättä varoja pois lompakostasi. Varojesi kontrolli pysyy aina sinulla.<hr> <b> PrivateSend prosessi toimii seuraavasti:</b><ol type="1"> <li>PrivateSend aloittaa pilkkomalla siirtotapahtumiesi syötteet pienemmiksi standardi arvoiksi. Nämä arvot ovat 0.01 DASH, 0.1 DASH, 1 DASH ja 10 DASH.</li> <li>Sitten lompakkosi lähettää pyynnön verkon palvelimille, joita kutsutaan "masternode:iksi". Masternodet saavat tiedon että haluat sekoittaa pilkottuja standardi arvoja. Mitään tunnistetietoja ei lähetetä masternode:ille, ne eivät koskaan tiedä "kuka" olet.</li> <li>Kun 2 muuta käyttäjää ilmoittaa että he haluavat myös sekoittaa varoja, alkaa sekoitus-sessio. Masternodet sekoittavat standardi arvot ja ilmoittavat kaikille 3:lle käyttäjän lompakoille että maksavat sekoitetut arvot takaisin itselleen. Lompakkosi maksaa nuo sekoitetut arvot suoraan itselleen, mutta eri osoitteeseen (vaihto-osoite).</li> <li>Jotta varojesi alkuperäinen lähde hämärretään, lompakkosi suorittaa tämän prosessin useita kertoja kaikilla standardi arvoilla. Aina kun prosessi on valmis, sitä kutsutaan "kierrokseksi". Jokainen PrivateSend kierros tekee eksponentiaalisesti vaikeammaksi löytää varojesi alkuperäisen osoitteen.</li> <li>Tämä sekoitusprosessi tapahtuu taustalla ilman käyttäjän toimenpiteitä. Kun haluat myöhemmin tehdä anonyymin varojen siirron, on varasi valmiiksi sekoitettu eli anonymisoitu. Erillistä sekoitusta/odotusta ei tarvita.</li> </ol> <hr><b>TÄRKEÄÄ:</b> Lompakkosi sisältää vain 1000 "vaihto-osoitetta". Aina kun sekoitustapahtuma tehdään, max 9 osoitetta käytetään. Tämä tarkoittaa sitä että nuo 1000 osoitetta kestää noin 100 sekoitustapahtumaa. Kun 900 osoitetta on käytetty, lompakkosi täytyy tehdä lisää osoitteita. Se voi tehdä niitä vain jos automaattinen varmistus on käytössä.<br> Tästä seuraa että jos varmistus ei ole käytössä, myös PrivateSend on pois käytöstä. <hr>Katso lisätietoja <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend dokumentaatiosta</a>. @@ -1422,7 +1355,7 @@ Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items.<br/>%s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. - Ulkopuoliset URL-osoitteet (esim. lohkoselain) jotka esiintyvät tapahtumat-välilehdellä valikossa.<br/>%s URL osoitteessa korvataan siirtotunnuksella. Useampi URL osoite on eroteltu pystyviivalla |. + Ulkopuoliset URL-osoitteet (esim. lohkoselain) jotka esiintyvät tapahtumat-välilehdellä valikossa.<br/>%s URL osoitteessa korvataan siirtotunnuksen tarkisteella. Useampi URL osoite on eroteltu pystyviivalla |. Expert @@ -1831,7 +1764,7 @@ https://www.transifex.com/projects/p/dash/ Note: You can turn this message off in options. - Huom: Voit kytkeä tämän viestin pois asetuksissa. + Huom: Voit kytkeä tämän viestin pois päältä asetuksissa. WARNING! Something went wrong on automatic backup @@ -4158,7 +4091,7 @@ Näillä toiminnoilla voit korjata korruptoituneen lohkoketjun tai puuttuvat/van Failed to load governance cache from - Budjetti välimuistin lataaminen epäonnistui + Hallinnon välimuistin lataaminen epäonnistui Failed to load masternode cache from @@ -4482,7 +4415,11 @@ Näillä toiminnoilla voit korjata korruptoituneen lohkoketjun tai puuttuvat/van Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) - Kytke pois käytöstä kaikki Dash:in erityistoiminnot (Masternodet, PrivateSend, InstantSend, Budjetointi) (0-1, oletus: %u) + Kytke pois käytöstä kaikki Dash:in erityistoiminnot (Masternodet, PrivateSend, InstantSend, Hallinto) (0-1, oletus: %u) + + + %s file contains all private keys from this wallet. Do not share it with anyone! + %s tiedosto sisältää kaikki yksityisavaimet tähän lompakkoon. Älä luovuta sitä kenellekkän! Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) @@ -4508,6 +4445,10 @@ Näillä toiminnoilla voit korjata korruptoituneen lohkoketjun tai puuttuvat/van Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID) Suorita käsky kun lompakon InstantSend siirtotapahtuma on hyväksytysti lukittu (%s cmd on korvattu TxID:llä) + + Extra transactions to keep in memory for compact block reconstructions (default: %u) + Ekstra siirtotapahtumat pidetään muistivarannossa kompaktia lohkon rekonstruktiota varten (oletus: %u) + Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. Varmistus epäonnistui, tiedosto on jo olemassa! Tämä voi tapahtua jos olet käynnistänyt lompakon uudelleen alle 60 sek aikana. Voit jatkaa jos hyväksyt tämän. @@ -4589,6 +4530,10 @@ Näillä toiminnoilla voit korjata korruptoituneen lohkoketjun tai puuttuvat/van Verkon version merkkijonon (%i) kokonaispituus ylittää maksimi pituuden (%i). Vähennä uakommenttien määrää tai kokoa. + + Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index. + Siirtotapahtumaindeksiä ei voi poistaa käytöstä normaalitilassa. Joko käynnistä -litemode komentorivin valinnalla tai ota käyttöön siirtotapahtumaindeksi. + Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) Yrittää pitää ulos menevän liikenteen alle annetun tavoitteen (MiB per 24h), 0 = ei rajaa (oletus: %d) @@ -4621,6 +4566,10 @@ Vähennä uakommenttien määrää tai kokoa. Warning: Unknown block versions being mined! It's possible unknown rules are in effect Varoitus: Tuntemattomia lohkoversioita louhitaan! On mahdollista että tuntemattomia sääntöjä on käytössä + + You are starting in lite mode, all Dash-specific functionality is disabled. + Olet käynnistämässä lite tilassa, kaikki Dash-toiminnot ovat pois käytöstä. + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain Sinun tulee uudelleen rakentaa tietokanta käyttäen -reindex palataksesi takaisin 'ei karsita' tilaan. Tämä aiheuttaa koko lohkoketjun uudelleen lataamisen @@ -4665,6 +4614,14 @@ Vähennä uakommenttien määrää tai kokoa. Enable publish hash block in <address> Ota käyttöön julkaise tarkistelohko osoitteessa <address> + + Enable publish hash of governance objects (like proposals) in <address> + Ota käyttöön julkaise hallinnon objektien tarkiste (esim ehdotus) <address> + + + Enable publish hash of governance votes in <address> + Ota käyttöön julkaise hallinnon äänten tarkiste <address> + Enable publish hash transaction (locked via InstantSend) in <address> Ota käyttöön julkaise siirtotapahtuman tarkiste (lukittu InstantSend kautta) osoitteessa <address> @@ -4705,6 +4662,14 @@ Vähennä uakommenttien määrää tai kokoa. Failed to delete backup, error: %s Varmistuksen poisto epäonnistui, virhe: %s + + Failed to load InstantSend data cache from + InstantSend välimuistin lataaminen epäonnistui + + + Failed to load sporks cache from + Spork välimuistin lataaminen epäonnistui + Failed to parse host:port string Isäntä:portti merkkijonon jäsennys epäonnistui @@ -4725,6 +4690,10 @@ Vähennä uakommenttien määrää tai kokoa. Invalid amount for -fallbackfee=<amount>: '%s' Virheellinen määrä -fallbackfee=<amount>: '%s' + + Invalid input count. + Virheellinen syötemäärä. + Keep the transaction memory pool below <n> megabytes (default: %u) Pidä siirtotapahtuma muistivaranto alle <n> megatavun (oletus: %u) @@ -4741,6 +4710,10 @@ Vähennä uakommenttien määrää tai kokoa. Line: %d Rivi: %d + + Loading InstantSend data cache... + Ladataan InstantSend välimuistia... + Loading addresses... Ladataan osoitteita... @@ -4751,7 +4724,11 @@ Vähennä uakommenttien määrää tai kokoa. Loading governance cache... - Ladataan budjetti välimuistia... + Ladataan hallinnon välimuistia... + + + Loading sporks cache... + Ladataan spork välimuistia... Loading wallet... (%3.2f %%) @@ -4771,7 +4748,7 @@ Vähennä uakommenttien määrää tai kokoa. Masternode cache is empty, skipping payments and governance cache... - Masternode välimuisti on tyhjä, ohitetaan maksujen ja budjetin välimuisti... + Masternode välimuisti on tyhjä, ohitetaan maksujen ja hallinnon välimuisti... Masternode options: @@ -4893,6 +4870,10 @@ Vähennä uakommenttien määrää tai kokoa. Transaction must have at least one recipient Lähetyksessä tulee olla ainakin yksi vastaanottaja + + Transaction too large + Siirtotapahtuma on liian iso + Trying to connect... Yritetään kytkeytyä... @@ -4913,6 +4894,10 @@ Vähennä uakommenttien määrää tai kokoa. Wallet debugging/testing options: Lompakon debug ja testausvalinnat: + + Wallet is not initialized + Lompakko ei ole alustettu + Wallet needed to be rewritten: restart %s to complete Lompakko tarvitsee uudelleenkirjoittaa: käynnistä %s uudelleen @@ -4929,6 +4914,10 @@ Vähennä uakommenttien määrää tai kokoa. Whether to operate in a blocks only mode (default: %u) Käytä vain lohkoja moodia (default: %u) + + You can not start a masternode in lite mode. + Et voi käynnistää masternodea lite tilassa. + ZeroMQ notification options: ZeroMQ ilmoitus asetukset: @@ -5017,10 +5006,6 @@ Vähennä uakommenttien määrää tai kokoa. Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) Aseta lohkon luonnissa lisättävien siirtojen vähimmäispalkkion suhde (%s/kt) (oletus: %s) - - Show N confirmations for a successfully locked transaction (%u-%u, default: %u) - Näytä N vahvistusta onnistuneesti lukitulle siirtotapahtumalle (%u-%u, oletus: %u) - This is the transaction fee you may pay when fee estimates are not available. Tämä on siirtomaksu jonka saatat maksaa jos siirtomaksun arviota ei ole mahdollista saada. @@ -5243,7 +5228,7 @@ Vähennä uakommenttien määrää tai kokoa. Synchronizing governance objects... - Ladataan budjetti objekteja... + Ladataan hallinnon objekteja... Synchronizing masternode payments... diff --git a/src/qt/locale/dash_fr.ts b/src/qt/locale/dash_fr.ts index 4ba090283468..f5def6a97e4f 100644 --- a/src/qt/locale/dash_fr.ts +++ b/src/qt/locale/dash_fr.ts @@ -793,10 +793,6 @@ Please switch to "List mode" to use this function. Veuillez passer en “mode liste” pour utiliser cette fonction. - - Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again. - Éléments non-anonymisés sélectionnés. <b>PrivateSend sera désactivé.</b><br><br>Si vous voulez quand même utiliser PrivateSend, veuillez d'abord désélectionner tous les éléments non-anonymisés, puis recochez la case PrivateSend. - (%1 locked) (%1 verrouillés) @@ -834,73 +830,6 @@ n/a - - DarksendConfig - - Configure PrivateSend - Configurer PrivateSend - - - Basic Privacy - Confidentialité normale - - - High Privacy - Confidentialité élevée - - - Maximum Privacy - Confidentialité maximale - - - Please select a privacy level. - Veuillez choisir un niveau de confidentialité. - - - Use 2 separate masternodes to mix funds up to 1000 DASH - Utiliser 2 masternodes distincts pour mélanger jusqu'à 1000 DASH - - - Use 8 separate masternodes to mix funds up to 1000 DASH - Utiliser 8 masternodes distincts pour mélanger jusqu'à 1000 DASH - - - Use 16 separate masternodes - Utiliser 16 masternodes distincts - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - Cette option est la plus rapide et coûtera environ 0,025 DASH pour anonymiser 1000 DASH - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - Cette option est un peu moins rapide et coûtera environ 0,05 DASH pour anonymiser 1000 DASH - - - 0.1 DASH per 1000 DASH you anonymize. - 0,1 DASH par 1000 DASH anonymisés. - - - This is the slowest and most secure option. Using maximum anonymity will cost - Cette option est le plus lente et la plus sécurisée. Utiliser l'anonymisation maximale coûtera - - - PrivateSend Configuration - Configuration PrivateSend - - - PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend a bien été réglé sur “normale” (%1 et 2 cycles). Vous pouvez modifier cela à tout moment dans la fenêtre de configuration de Dash. - - - PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend a bien été réglé sur “élevée” (%1 et 8 cycles). Vous pouvez modifier cela à tout moment dans la fenêtre de configuration de Dash. - - - PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend a bien été réglé sur “maximale” (%1 et 16 cycles). Vous pouvez modifier cela à tout moment dans la fenêtre de configuration de Dash. - - EditAddressDialog @@ -1037,11 +966,7 @@ PrivateSend information Information PrivateSend - - <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more info see <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> - <h3>Ce qu'il faut savoir sur PrivateSend</h3> PrivateSend vous offre une véritable confidentialité financière en obscurcissant l'origine de vos fonds. Tous les dashs de votre portefeuille sont répartis en différentes "entrées", qu'on peut se représenter comme des pièces distinctes.<br> PrivateSend est un procédé innovant qui mélange vos entrées avec les entrées de deux autres personnes, sans que vos fonds ne quittent jamais votre portefeuille. Vous gardez le contrôle de votre argent à tout moment.<hr> <b>La procédure PrivateSend fonctionne comme ceci :</b><ol type="1"> <li>PrivateSend commence par diviser vos entrées de transaction en coupures standard. Ces coupures sont de 0.01 DASH, 0.1 DASH, 1 DASH et 10 DASH -- un peu comme pour les billets de banque que vous utilisez tous les jours.</li> <li>Votre portefeuille envoie alors des requêtes à des serveurs spécifiques sur le réseau, appelés "masternodes". Ces masternodes sont informés que vous souhaiteriez mélanger certaines coupures. Aucune information permettant de vous identifier n'est envoyée aux masternodes, qui ne savent donc jamais "qui" vous êtes.</li> <li>Quand deux autres personnes envoient des requêtes similaires, indiquant qu'elles veulent mélanger les mêmes coupures, alors commence une session de mélange. Le masternode mélange les entrées et demande aux portefeuilles des trois utilisateurs de payer l'entrée, désormais transformée, à eux-mêmes. Votre portefeuille paie cette coupure directement à lui-même, mais à une adresse différente (appelée adresse de monnaie rendue).</li> <li>Afin d'obscurcir complètement vos fonds, votre portefeuille doit répéter cette procédure un certain nombre de fois avec chaque coupure. Une procédure terminée s'appelle un "cycle". Chaque cycle PrivateSend rend exponentiellement plus difficile de savoir d'où viennent vos fonds.</li> <li>Cette procédure de mélange intervient en arrière-plan, sans aucune intervention de votre part. Quand vous souhaiterez faire une transaction, vos fonds seront déjà anonymisés. Aucune autre attente ne sera nécessaire.</li> </ol> <hr><b>IMPORTANT :</b> Votre portefeuille ne contient que 1000 de ces "adresses de monnaie rendue". À chaque opération de mélange, jusqu'à 9 de ces adresses sont utilisées. Cela signifie que ces 1000 adresses couvrent environ 100 opérations de mélange. Quand 900 d'entre elles sont utilisées, votre portefeuille doit créer de nouvelles adresses. Cependant il ne peut le faire que si vous avez activé les sauvegardes automatiques.<br> En conséquence, les utilisateurs qui ont désactivé les sauvegardes ont aussi PrivateSend désactivé. <hr>Pour en savoir plus, voir <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> - - + Intro @@ -1346,7 +1271,7 @@ Amount of Dash to keep anonymized - Nombre de Dash à conserver anonymisés + Nombre de dashs à conserver anonymisés W&allet @@ -1829,10 +1754,6 @@ https://www.transifex.com/projects/p/dash/ We are about to create a new automatic backup for you, however <span style='color:red;'> you should always make sure you have backups saved in some safe place</span>! Une nouvelle sauvegarde automatique est sur le point d'être créée pour vous, cependant <span style='color:red;'>assurez-vous toujours d'avoir des sauvegardes en lieu sûr</span> ! - - Note: You can turn this message off in options. - Note : masquez ce message dans Options. - WARNING! Something went wrong on automatic backup ATTENTION ! Quelque chose a mal tourné lors de la sauvegarde automatique @@ -4594,7 +4515,7 @@ https://www.transifex.com/projects/p/dash/ Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported. - Paramètre obsolète -socks utilisé. Il n'est plus possible d'indiquer la version SOCKS, seul les proxy SOCKS5 sont supportés. + Paramètre obsolète -socks utilisé. Il n'est plus possible d'indiquer la version SOCKS, seuls les proxy SOCKS5 sont supportés. Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay. @@ -4762,7 +4683,7 @@ https://www.transifex.com/projects/p/dash/ Make the wallet broadcast transactions - Faire transmettre les transactions par le portefeuille + Diffuser les transactions via le portefeuille Masternode cache is empty, skipping payments and governance cache... @@ -4930,7 +4851,7 @@ https://www.transifex.com/projects/p/dash/ no mixing available. - pas de mélangeur disponible. + pas de mélange disponible. see debug.log for details. @@ -5012,10 +4933,6 @@ https://www.transifex.com/projects/p/dash/ Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) Définir le taux minimal de frais (en %s/Ko) pour les transactions à inclure dans la création de blocs (par défaut : %s) - - Show N confirmations for a successfully locked transaction (%u-%u, default: %u) - Afficher N confirmations pour une transaction verrouillée avec succès (%u-%u, par défaut : %u) - This is the transaction fee you may pay when fee estimates are not available. Il s’agit des frais de transaction que vous pourriez payer si aucune estimation de frais n’est proposée. @@ -5290,7 +5207,7 @@ https://www.transifex.com/projects/p/dash/ Unknown state: id = %u - État inconnu: id = %u + État inconnu : id = %u Username for JSON-RPC connections diff --git a/src/qt/locale/dash_it.ts b/src/qt/locale/dash_it.ts index 5bee5e4392e1..0b26b3de7797 100644 --- a/src/qt/locale/dash_it.ts +++ b/src/qt/locale/dash_it.ts @@ -794,7 +794,7 @@ Passare a "Modalità elenco" per utilizzare questa funzione. - Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again. + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. Selezionato input non anonimo. <b>PrivateSend sarà disabilitato.</b><br><br>Se vuoi ancora utilizzare PrivateSend, deseleziona prima tutti gli input non anonimi e poi controlla nuovamente la checkbox di PrivateSend. @@ -834,73 +834,6 @@ n/a - - DarksendConfig - - Configure PrivateSend - Configura il PrivateSend - - - Basic Privacy - Privacy base - - - High Privacy - Privacy alta - - - Maximum Privacy - Privacy massima - - - Please select a privacy level. - Selezionare il livello di privacy - - - Use 2 separate masternodes to mix funds up to 1000 DASH - Usa 2 masternode separati per mixare fino al 1000 DASH - - - Use 8 separate masternodes to mix funds up to 1000 DASH - Usa 8 masternode separati per mixare fino al 1000 DASH - - - Use 16 separate masternodes - Usa 16 masternode separati - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - Questa opzione è la più veloce e ti costerà ~0.025DASH circa per anonimizzare 1000DASH - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - Questa opzione è moderatamente veloce e costerà 0.05DASH circa per anonimizzare 1000DASH - - - 0.1 DASH per 1000 DASH you anonymize. - 0.1 DASH ogni 1000 DASH che anonimizzi. - - - This is the slowest and most secure option. Using maximum anonymity will cost - Questa è l'opzione più lenta ma più sicura . Usare il massimo grado di anonimizzazione costerà - - - PrivateSend Configuration - Configurazione del PrivateSend - - - PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend è stato impostato correttamente sul basic (%1 e 2 round). Puoi cambiarlo in qualsiasi momento aprendo la schermata di configurazione di Dash. - - - PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend è stato impostato correttamente su high (%1 e 8 round). Puoi cambiarlo in qualsiasi momento aprendo la schermata di configurazione di Dash. - - - PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend è stato impostato correttamente su maximum (%1 e 16 round). Puoi cambiarlo in qualsiasi momento aprendo la schermata di configurazione di Dash. - - EditAddressDialog @@ -1038,8 +971,8 @@ Informazioni di Private Send - <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more info see <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> - <h3>Nozioni di base di PrivateSend</h3> PrivateSend ti offre una vera privacy finanziaria oscurando le origini dei tuoi fondi. Tutti i Dash nel tuo portafoglio sono costituiti da diversi "input" che puoi pensare come monete separate e discrete. <br>PrivateSend utilizza un processo innovativo per mescolare i tuoi input con gli input di due altre persone, senza che le tue monete lascino mai il tuo portafoglio. Si mantiene il controllo del proprio denaro in ogni momento. <hr><b>Il processo di PrivateSend funziona in questo modo: </b><ol type="1"><li>PrivateSend inizia dividendo gli input della transazioni in denominazioni standard. Queste denominazioni sono 0,01 DASH, 0,1 DASH, 1 DASH e 10 DASH: una specie di carta moneta che utilizzi ogni giorno.</li><li> Il tuo portafoglio quindi invia richieste ai nodi software appositamente configurati sulla rete, chiamati "masternode". Questi masternode vengono informati che sei interessato a mescolare una certa denominazione. Nessuna informazione identificabile viene inviata ai masternodes, quindi non sanno mai "chi" tu sia. </li><li>Quando altre due persone inviano messaggi simili, indicando che desiderano mescolare la stessa denominazione, inizia una sessione di mixaggio. Il masternode mescola gli input e incarica i portafogli di tutti e tre gli utenti di pagare a loro stessi l'input trasformato. Il tuo portafoglio paga quella denominazione direttamente a se stessa, ma in un indirizzo diverso (chiamato indirizzo di modifica). </li><li>Per oscurare completamente i tuoi fondi, il tuo portafoglio deve ripetere questo processo un certo numero di volte con ogni denominazione. Ogni processo completato, è chiamato "round". Ogni ciclo di PrivateSend rende esponenzialmente più difficile determinare la provenienza dei fondi. </li><li>Questo processo di miscelazione avviene in background senza alcun intervento da parte dell'utente. Quando desideri effettuare una transazione, i tuoi fondi saranno già resi anonimi. Non è richiesta alcuna attesa aggiuntiva.</li></ol><hr><b> IMPORTANTE: </b>il tuo portafoglio contiene solo 1000 di questi "indirizzi di modifica". Ogni volta che si verifica un evento di mixaggio, vengono usati fino a 9 dei tuoi indirizzi. Ciò significa che i 1000 indirizzi durano per circa 100 eventi di mixaggio. Quando vengono utilizzati 900 di questi, il tuo portafoglio deve creare altri indirizzi. Può farlo solo se si dispone dei backup automatici abilitati. <br>Di conseguenza, gli utenti che hanno disabilitato i backup avranno anche PrivateSend disabilitato. <hr>Per maggiori informazioni consulta <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. + <h3>Nozioni di base di PrivateSend</h3> PrivateSend ti offre una vera privacy finanziaria oscurando le origini dei tuoi fondi. Tutti i Dash nel tuo portafoglio sono costituiti da diversi "input" che puoi pensare come monete separate e discrete. <br>PrivateSend utilizza un processo innovativo per mescolare i tuoi input con gli input di due altre persone, senza che le tue monete lascino mai il tuo portafoglio. Si mantiene il controllo del proprio denaro in ogni momento. <hr><b>Il processo di PrivateSend funziona in questo modo: </b><ol type="1"><li>PrivateSend inizia dividendo gli input della transazioni in denominazioni standard. Queste denominazioni sono 0,01 DASH, 0,1 DASH, 1 DASH e 10 DASH: una specie di carta moneta che utilizzi ogni giorno.</li><li> Il tuo portafoglio quindi invia richieste ai nodi software appositamente configurati sulla rete, chiamati "masternode". Questi masternode vengono informati che sei interessato a mescolare una certa denominazione. Nessuna informazione identificabile viene inviata ai masternodes, quindi non sanno mai "chi" tu sia. </li><li>Quando altre due persone inviano messaggi simili, indicando che desiderano mescolare la stessa denominazione, inizia una sessione di mixaggio. Il masternode mescola gli input e incarica i portafogli di tutti e tre gli utenti di pagare a loro stessi l'input trasformato. Il tuo portafoglio paga quella denominazione direttamente a se stessa, ma in un indirizzo diverso (chiamato indirizzo di modifica). </li><li>Per oscurare completamente i tuoi fondi, il tuo portafoglio deve ripetere questo processo un certo numero di volte con ogni denominazione. Ogni processo completato, è chiamato "round". Ogni ciclo di PrivateSend rende esponenzialmente più difficile determinare la provenienza dei fondi. </li><li>Questo processo di miscelazione avviene in background senza alcun intervento da parte dell'utente. Quando desideri effettuare una transazione, i tuoi fondi saranno già resi anonimi. Non è richiesta alcuna attesa aggiuntiva.</li></ol><hr><b> IMPORTANTE: </b>il tuo portafoglio contiene solo 1000 di questi "indirizzi di modifica". Ogni volta che si verifica un evento di mixaggio, vengono usati fino a 9 dei tuoi indirizzi. Ciò significa che i 1000 indirizzi durano per circa 100 eventi di mixaggio. Quando vengono utilizzati 900 di questi, il tuo portafoglio deve creare altri indirizzi. Può farlo solo se si dispone dei backup automatici abilitati. <br>Di conseguenza, gli utenti che hanno disabilitato i backup avranno anche PrivateSend disabilitato. <hr>Per maggiori informazioni consulta la <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">documentazione di PrivateSend</a>. @@ -1831,7 +1764,7 @@ https://www.transifex.com/projects/p/dash/ Note: You can turn this message off in options. - Nota: puoi disattivare questo messaggio nelle opzioni. + Nota: è possibile disattivare questo messaggio nelle opzioni. WARNING! Something went wrong on automatic backup @@ -4480,6 +4413,10 @@ https://www.transifex.com/projects/p/dash/ Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) Disabilitare tutte le funzionalità specifiche di Dash (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) + + %s file contains all private keys from this wallet. Do not share it with anyone! + %s il file contiene tutte le chiavi private di questo portafoglio. Non condividerlo con nessuno! + Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) Scopre i propri indirizzi IP (predefinito: 1 se in ascolto ed -externalip o -proxy non sono specificati) @@ -4504,6 +4441,10 @@ https://www.transifex.com/projects/p/dash/ Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID) Esegui comando quando una transazione InstantSend del portafoglio è bloccata correttamente (%s in cmd è sostituito da TxID) + + Extra transactions to keep in memory for compact block reconstructions (default: %u) + Transazioni aggiuntive da tenere in memoria per ricostruzioni di blocchi compatti (default: %u) + Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. Impossibile creare il backup, il file esiste già! Questo può accadere quando riavvii il portafoglio in meno di 60 secondi. Se per te va bene puoi continuare. @@ -4584,6 +4525,10 @@ https://www.transifex.com/projects/p/dash/ Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. La lunghezza totale della stringa di network version (%i) eccede la lunghezza massima (%i). Ridurre il numero o la dimensione di uacomments. + + Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index. + L'indice di transazione non può essere disabilitato in modalità completa. Iniziare con l'opzione della riga di comando -litemode o abilitare l'indice delle transazioni. + Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) Cerca di mantenere il traffico in uscita al di sotto della soglia scelta (in MiB ogni 24h), 0 = nessun limite (default: %d) @@ -4616,6 +4561,10 @@ https://www.transifex.com/projects/p/dash/ Warning: Unknown block versions being mined! It's possible unknown rules are in effect Attenzione: si stanno minando versioni sconocsiute di blocchi! E' possibile che siano attive regole sconosciute + + You are starting in lite mode, all Dash-specific functionality is disabled. + Stai iniziando in modalità lite, tutte le funzionalità specifiche di Dash sono disabilitate. + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain Per ritornare alla modalità unpruned sarà necessario ricostruire il database utilizzando l'opzione -reindex. L'intera blockchain sarà riscaricata. @@ -4660,6 +4609,14 @@ https://www.transifex.com/projects/p/dash/ Enable publish hash block in <address> Abilita pubblicazione hash blocco in <address> + + Enable publish hash of governance objects (like proposals) in <address> + Abilita l'hash di pubblicazione degli oggetti di governance (come le proposte) in <address> + + + Enable publish hash of governance votes in <address> + Abilita l'hash di pubblicazione dei voti di governance in <address> + Enable publish hash transaction (locked via InstantSend) in <address> Abilita la pubblicazione della hash della transazione (bloccata tramite InstantSend) in <address> @@ -4700,6 +4657,14 @@ https://www.transifex.com/projects/p/dash/ Failed to delete backup, error: %s Impossibile eliminare il backup, errore: %s + + Failed to load InstantSend data cache from + Impossibile caricare la cache di dati di InstantSend da + + + Failed to load sporks cache from + Impossibile caricare la cache degli sporks da + Failed to parse host:port string Impossibile analizzare l'host: port string @@ -4720,6 +4685,10 @@ https://www.transifex.com/projects/p/dash/ Invalid amount for -fallbackfee=<amount>: '%s' Importo non valido per -fallbackfee=<amount>: '%s' + + Invalid input count. + Conteggio di input non valido. + Keep the transaction memory pool below <n> megabytes (default: %u) Mantieni la memory pool delle transazioni al di sotto di <n> megabyte (default: %u) @@ -4736,6 +4705,10 @@ https://www.transifex.com/projects/p/dash/ Line: %d Riga: %d + + Loading InstantSend data cache... + Caricamento della cache di dati InstantSend ... + Loading addresses... Caricamento indirizzi... @@ -4748,6 +4721,10 @@ https://www.transifex.com/projects/p/dash/ Loading governance cache... Caricamento della cache di governance ... + + Loading sporks cache... + Caricamento della cache degli sporks ... + Loading wallet... (%3.2f %%) Caricando portafoglio... (%3.2f %%) @@ -4889,6 +4866,10 @@ https://www.transifex.com/projects/p/dash/ Transaction must have at least one recipient La transazione deve avere almeno un destinatario + + Transaction too large + Transazione troppo grande + Trying to connect... cercando di connettersi ... @@ -4909,6 +4890,10 @@ https://www.transifex.com/projects/p/dash/ Wallet debugging/testing options: Opzioni di Debug/Test del portafoglio: + + Wallet is not initialized + Wallet non è stato inizializzato + Wallet needed to be rewritten: restart %s to complete Il portamonete necessita di essere riscritto: riavviare %s per completare @@ -4925,6 +4910,11 @@ https://www.transifex.com/projects/p/dash/ Whether to operate in a blocks only mode (default: %u) Imposta se operare in modalità solo blocchi (default: %u) + + You can not start a masternode in lite mode. + +Non è possibile avviare un masternode in modalità lite + ZeroMQ notification options: Opzioni di notifica ZeroMQ @@ -5013,10 +5003,6 @@ https://www.transifex.com/projects/p/dash/ Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) Imposta la tariffa di commissione più bassa (in %s/kB) per transazioni da includere nella creazione del blocco. (default: %s) - - Show N confirmations for a successfully locked transaction (%u-%u, default: %u) - Mostra N conferme per una transazione bloccata con successo (%u-%u, default: %u) - This is the transaction fee you may pay when fee estimates are not available. Questo è il costo di transazione che potresti pagare quando le stime della fee non sono disponibili. diff --git a/src/qt/locale/dash_ja.ts b/src/qt/locale/dash_ja.ts index 1e0d322412a2..375e8e5fee1c 100644 --- a/src/qt/locale/dash_ja.ts +++ b/src/qt/locale/dash_ja.ts @@ -1,9 +1,9 @@ - + AddressBookPage Right-click to edit address or label - 右クリックでアドレスまたはラベルを編集します + 右クリックでアドレスまたはラベルを編集 Create a new address @@ -15,7 +15,7 @@ Copy the currently selected address to the system clipboard - 現在選択されているアドレスをシステムのクリップボードにコピーする + 現在選択中のアドレスをクリップボードにコピー &Copy @@ -23,7 +23,7 @@ Delete the currently selected address from the list - 選択されたアドレスを一覧から削除する + 選択中のアドレスを一覧から削除 &Delete @@ -31,7 +31,7 @@ Export the data in the current tab to a file - ファイルに現在のタブのデータをエクスポート + ファイルに現在のタブにあるデータをエクスポート &Export @@ -43,23 +43,31 @@ Choose the address to send coins to - 送信先のアドレスを選択 + 送金先のアドレスを選択 Choose the address to receive coins with - 支払いを受け取るアドレスを指定する + 受取先のアドレスを選択 C&hoose - 選択(&C) + 選択(&H) Sending addresses - アドレス送信中 + 送金アドレス Receiving addresses - アドレス受信中 + 受取アドレス + + + These are your Dash addresses for sending payments. Always check the amount and the receiving address before sending coins. + これらは送金するためのあなたの Dash アドレスです。送金する前に常に金額と受取アドレスを確認してください。 + + + These are your Dash addresses for receiving payments. It is recommended to use a new receiving address for each transaction. + これらは送金を受取用のあなたのDashアドレスです。トランザクションごとに新しい受け取り用アドレスの作成を推奨します。 &Copy Address @@ -75,7 +83,7 @@ Export Address List - アドレス帳をエクスポート + アドレスのリストをエクスポート Comma separated file (*.csv) @@ -87,7 +95,7 @@ There was an error trying to save the address list to %1. Please try again. - トランザクション履歴を %1 へ保存する際にエラーが発生しました。再試行してください。 + アドレスのリストを %1 へ保存する際にエラーが発生しました。再試行してください。 @@ -109,7 +117,7 @@ AskPassphraseDialog Passphrase Dialog - パスフレーズ ダイアログ + パスフレーズのダイアログ Enter passphrase @@ -121,39 +129,51 @@ Repeat new passphrase - 新しいパスフレーズをもう一度 + 新しいパスフレーズを再入力 Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>. - ウォレットの新しいパスフレーズを入力してください。<br/><b>10文字以上のランダムな文字</b>で構成されたものか、<b>8単語以上の単語</b>で構成されたパスフレーズを使用してください。 + ウォレット用の新しいパスフレーズを入力してください。<br/><b>10文字以上のランダムな文字</b>か、または<b>8単語以上</b>で構成されたパスフレーズを使用してください。 Encrypt wallet - ウォレットを暗号化する + ウォレットを暗号化 This operation needs your wallet passphrase to unlock the wallet. - この操作はウォレットをアンロックするためにパスフレーズが必要です。 + この操作にはウォレットをアンロックするためのパスフレーズが必要です。 + + + Unlock wallet for mixing only + ミキシングのためだけにウォレットをアンロック Unlock wallet - ウォレットをアンロックする + ウォレットをアンロック This operation needs your wallet passphrase to decrypt the wallet. - この操作はウォレットの暗号化解除のためにパスフレーズが必要です。 + この操作にはウォレットの暗号化解除のためのパスフレーズが必要です。 Decrypt wallet - ウォレットの暗号化を解除する + ウォレットの暗号化を解除 Change passphrase パスフレーズの変更 + + Enter the old passphrase and new passphrase to the wallet. + ウォレットの古いパスフレーズと新しいパスフレーズを入力してください。 + Confirm wallet encryption - ウォレットの暗号化を確認する + ウォレットの暗号化を確認 + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR DASH</b>! + 警告: ウォレットを暗号化しパスフレーズを紛失した場合、あなたは <b>すべてのDash</b>を失います! Are you sure you wish to encrypt your wallet? @@ -163,9 +183,17 @@ Wallet encrypted ウォレットは暗号化されました + + %1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your funds from being stolen by malware infecting your computer. + %1 は暗号化プロセスを終了するために閉じます。ウォレットの暗号化だけでは充分ではなく、使用しているコンピューターがマルウェアに感染して盗難される可能性があります。 + + + IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. Previous backups of the unencrypted wallet file contain the same HD seed and still have full access to all your funds just like the new, encrypted wallet. + 重要: 過去のウォレットファイルのバックアップは、暗号化された新しいウォレットファイルに取り替える必要があります。暗号化されていないウォレットファイルの過去のバックアップは同じHDウォレットのシードを含むので、新しい暗号化ウォレットと同様にすべての資産にアクセスできます。 + IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet. - 重要: 過去のウォレット ファイルのバックアップは、暗号化された新しいウォレット ファイルに取り替える必要があります。セキュリティ上の理由により、暗号化された新しいウォレットを使い始めると、暗号化されていないウォレット ファイルのバックアップはすぐに使えなくなります。 + 重要: 過去のウォレットファイルのバックアップは、暗号化された新しいウォレットファイルに取り替える必要があります。セキュリティ上の理由により、暗号化された新しいウォレットを使い始めると、暗号化されていないウォレットファイルのバックアップはすぐに使えなくなります。 Wallet encryption failed @@ -173,35 +201,54 @@ Wallet encryption failed due to an internal error. Your wallet was not encrypted. - 内部エラーによりウォレットの暗号化が失敗しました。ウォレットは暗号化されませんでした。 + 内部エラーによりウォレットの暗号化が失敗しました。ウォレットは暗号化されていません。 The supplied passphrases do not match. - パスフレーズが同じではありません。 + 入力したパスフレーズが正しくありません。 Wallet unlock failed - ウォレットのアンロックに失敗しました + ウォレットのアンロックに失敗しました。 The passphrase entered for the wallet decryption was incorrect. - ウォレットの暗号化解除のパスフレーズが正しくありません。 + 入力されたウォレットの暗号化解除のパスフレーズが正しくありません。 Wallet decryption failed - ウォレットの暗号化解除に失敗しました + ウォレットの暗号化解除に失敗しました。 Wallet passphrase was successfully changed. - ウォレットのパスフレーズの変更が成功しました。 + ウォレットのパスフレーズが変更されました。 Warning: The Caps Lock key is on! - 警告: キャプスロック・キーがオンになっています! + 警告:Caps Lockキーがオンになっています! + + + + BanTableModel + + IP/Netmask + IPアドレス/ネットマスク + + + Banned Until + 以下の時間までbanする: BitcoinGUI + + A fatal error occurred. Dash Core can no longer continue safely and will quit. + 重大なエラーが発生しました。 Dash Coreは安全に続行できなくなり終了します。 + + + Dash Core + Dash Core + Wallet ウォレット @@ -216,51 +263,79 @@ Show general overview of wallet - ウォレットの概要を見る + ウォレットの概要を表示 &Send - 送る (&S) + 送金 (&S) + + + Send coins to a Dash address + Dash アドレスに送金 &Receive - 受信 (&R) + 受取 (&R) + + + Request payments (generates QR codes and dash: URIs) + 送金を要求 (QRコードとdash:URIを生成) &Transactions - 取引(&T) + トランザクション(&T) Browse transaction history - 取引履歴を閲覧 + トランザクション履歴を閲覧 + + + &Masternodes + マスターノード(&M) + + + Browse masternodes + マスターノードを閲覧 E&xit - 終了(&E) + 終了(&X) Quit application アプリケーションを終了 + + Show information about Dash Core + Dash Coreについての情報を表示 + About &Qt - Qt について(&Q) + Qtについて(&Q) Show information about Qt - Qt の情報を表示 + Qt についての情報を表示 &Options... オプション… (&O) + + &About %1 + %1 について (&A) + + + Modify configuration options for %1 + %1 の設定を変更 + &Show / Hide - 見る/隠す (&S) + 表示/非表示 (&S) Show or hide the main Window - メイン ウインドウを表示または非表示 + メインウインドウを表示または非表示 &Encrypt Wallet... @@ -268,7 +343,7 @@ Encrypt the private keys that belong to your wallet - あなたのウォレットの秘密鍵を暗号化します + あなたのウォレットの秘密鍵を暗号化 &Backup Wallet... @@ -284,48 +359,144 @@ Change the passphrase used for wallet encryption - ウォレット暗号化用パスフレーズの変更 + ウォレット暗号化のためのパスフレーズを変更 + + + &Unlock Wallet... + ウォレットをアンロック...(&U) Unlock wallet - ウォレットをアンロックする + ウォレットをアンロック + + + &Lock Wallet + ウォレットをロック(&L) Sign &message... - メッセージの署名… (&m) + メッセージの署名… (&M) + + + Sign messages with your Dash addresses to prove you own them + あなたがDash アドレスを所有していることを証明するために、あなたのDashアドレスでメッセージに署名してください。 &Verify message... メッセージの検証… (&V) + + Verify messages to ensure they were signed with specified Dash addresses + 指定されたDashアドレスで署名されたことを確認するためにメッセージを検証してください。 + &Information 情報 (&I) + + Show diagnostic information + 診断情報を表示 + + + &Debug console + デバッグコンソール(&D) + + + Open debugging console + デバッグコンソールを開く + + + &Network Monitor + ネットワークモニタ(&N) + + + Show network monitor + ネットワークモニタを表示 + + + &Peers list + ネットワークのピアのリスト(&P) + + + Show peers info + 接続しているピアの情報を表示 + + + Wallet &Repair + ウォレットの修復(&R) + + + Show wallet repair options + ウォレットの修復オプションを表示 + + + Open Wallet &Configuration File + ウォレット設定ファイルを開く(&C) + + + Open configuration file + 設定ファイルを開く + + + Open &Masternode Configuration File + マスターノード設定ファイルを開く(&M) + + + Open Masternode configuration file + マスターノード設定ファイルを開く + + + Show Automatic &Backups + 自動バックアップを表示(&B) + + + Show automatically created wallet backups + 自動で生成されたウォレットのバックアップを表示 + &Sending addresses... - 送金先アドレス一覧… (&S) + 送金先アドレス… (&S) Show the list of used sending addresses and labels - 使用済みの送金用アドレスとラベルの一覧を表示する + 過去に使用した送金先アドレスとラベルの一覧を表示 &Receiving addresses... - 受け取り用アドレス一覧… (&R) + 受取用アドレス… (&R) Show the list of used receiving addresses and labels - 支払いを受け取るアドレスとラベルのリストを表示する + 過去に使用した受取先アドレスとラベルの一覧を表示 Open &URI... - URI を開く… (&U) + URIを開く… (&U) + + + Open a dash: URI or payment request + dash: URIまたは送金リクエストを開く &Command-line options コマンドラインオプション (&C) + + Show the %1 help message to get a list with possible Dash command-line options + 有効なDashのコマンドラインオプションのリストを見るために %1 のヘルプメッセージを表示 + + + %1 client + %1 クライアント + + + &PrivateSend information + プライベートセンドの情報(&P) + + + Show the PrivateSend basic information + プライベートセンドの基本的な情報を表示 + &File ファイル(&F) @@ -334,6 +505,10 @@ &Settings 設定(&S) + + &Tools + ツール(&T) + &Help ヘルプ(&H) @@ -342,45 +517,41 @@ Tabs toolbar タブツールバー - - Synchronizing with network... - ネットワークに同期中… + + %n active connection(s) to Dash network + %n アクティブコネクション - Importing blocks from disk... - ディスクからブロックをインポートしています… + Network activity disabled + ネットワークアクティビティは無効化されました - Reindexing blocks on disk... - ディスク上のブロックのインデックスを再作成中… + Syncing Headers (%1%)... + ヘッダーを同期しています (%1%)... - No block source available... - 利用可能なブロックがありません… + Synchronizing with network... + ネットワークに同期中… - Up to date - バージョンは最新です - - - %n hour(s) - %n 時間 + Indexing blocks on disk... + ディスク上のブロックのインデックスを作成中... - - %n day(s) - %n 日 + + Processing blocks on disk... + ディスク上のブロックを処理中... - - %n week(s) - %n 週間 + + Reindexing blocks on disk... + ディスク上のブロックのインデックスを再作成中… - %1 and %2 - %1 と %2 + Connecting to peers... + ピアに接続中... - %n year(s) - %n 年 + Processed %n block(s) of transaction history. + %n ブロックのトランザクション履歴を処理 %1 behind @@ -392,11 +563,19 @@ Last received block was generated %1 ago. - 最後に受信されたブロックは %1 前に生成されました。 + 最後に受信したブロックは %1 前に生成されました。 Transactions after this will not yet be visible. - この後の取引はまだ表示されません。 + この後のトランザクションはまだ表示されません。 + + + Up to date + バージョンは最新です + + + Synchronizing additional data: %p% + 追加データを同期中: %p% Error @@ -410,25 +589,71 @@ Information 情報 + + Date: %1 + + 日付: %1 + + + + Amount: %1 + + 金額: %1 + + + + Type: %1 + + タイプ: %1 + + + + Label: %1 + + ラベル: %1 + + + + Address: %1 + + アドレス: %1 + + Sent transaction - 送金取引 + 送金トランザクション Incoming transaction - 着金取引 + 入金トランザクション + + + HD key generation is <b>enabled</b> + HDウォレット鍵生成は<b>有効</b>です。 + + + HD key generation is <b>disabled</b> + HDウォレット鍵生成は<b>無効</b>です。 Wallet is <b>encrypted</b> and currently <b>unlocked</b> - ウォレットは<b>暗号化されて、アンロックされています</b> + ウォレットは <b>暗号化</b>され、現在<b>アンロック</b>されています。 + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> for mixing only + ウォレットは <b>暗号化</b>され、現在ミキシングのために<b>アンロック</b>されています。 Wallet is <b>encrypted</b> and currently <b>locked</b> - ウォレットは<b>暗号化されて、ロックされています</b> + ウォレットは<b>暗号化</b>され、現在 <b>ロック</b>されています。 ClientModel + + Total: %1 (PS compatible: %2 / Enabled: %3) + 合計: %1 (PS 互換: %2 / 有効: %3) + Network Alert ネットワーク警告 @@ -448,10 +673,6 @@ Amount: 総額: - - Priority: - 優先度: - Fee: 手数料: @@ -470,12 +691,16 @@ Change: - 釣り銭: + チェンジアドレス: (un)select all すべて選択/選択解除 + + toggle lock state + トグルロック状態 + Tree mode ツリーモード @@ -484,9 +709,13 @@ List mode リストモード + + (1 locked) + (1 ロック中) + Amount - 総額 + 金額 Received with label @@ -496,6 +725,10 @@ Received with address アドレスに対する入金一覧 + + PS Rounds + PSラウンド + Date 日付 @@ -508,21 +741,17 @@ Confirmed 検証済み - - Priority - 優先度 - Copy address - アドレスをコピーする + アドレスをコピー Copy label - ラベルをコピーする + ラベルをコピー Copy amount - 総額のコピー + 金額をコピー Copy transaction ID @@ -530,83 +759,47 @@ Lock unspent - 未使用トランザクションをロックする + 未送金トランザクションをロック Unlock unspent - 未使用トランザクションをアンロックする + 未送金トランザクションをアンロック Copy quantity - 数量をコピーする + 数量をコピー Copy fee - 手数料をコピーする + 手数料をコピー Copy after fee - 手数料差引後の値をコピーする + 手数料差引後の金額をコピー Copy bytes - バイト数をコピーする - - - Copy priority - 優先度をコピーする + バイト数をコピー Copy dust - ダストをコピーする + ダストをコピー Copy change - 釣り銭をコピー + チェンジアドレスをコピー - highest - 最高 + Please switch to "List mode" to use this function. + この機能を使うにはリストモードにスイッチしてください。 - higher - 非常に高 - - - high - - - - medium-high - 中〜高 - - - medium - - - - low-medium - 低〜中 - - - low - - - - lower - 非常に低 - - - lowest - 最低 + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. + 匿名化されてないインプットが選択されました。 <b>プライベートセンドは無効になります。</b><br><br>プライベートセンドを使用したい場合は、すべての匿名化されてないインプットの選択を解除して、プライベートセンドのチェックボックスを再びチェックしてください。 (%1 locked) - (%1 がロック済み) - - - none - なし + (%1 がロック中) yes @@ -617,16 +810,12 @@ いいえ - This means a fee of at least %1 per kB is required. - これは少なくとも1kBあたり %1 の手数料が必要であることを意味します。 - - - Can vary +/- 1 byte per input. - ひとつの入力につき1バイト程度ずれることがあります。 + This label turns red if any recipient receives an amount smaller than the current dust threshold. + 受取額が現在のダスト閾値を下回る場合にはこのラベルは赤くなります。 - Transactions with higher priority are more likely to get included into a block. - より高い優先度を持つトランザクションの方がブロックに取り込まれやすくなります。 + Can vary +/- %1 duff(s) per input. + インプットごとに +/- %1 duff(s) が変更可能 (no label) @@ -634,16 +823,17 @@ change from %1 (%2) - %1 (%2) からのおつり + %1 (%2) からの変更 (change) - (おつり) + (チェンジ) + + + n/a + n/a - - DarksendConfig - EditAddressDialog @@ -656,31 +846,35 @@ The label associated with this address list entry - このアドレス帳項目に結びつけられているラベル + このアドレスに関連しているラベル &Address - アドレス帳 (&A) + アドレス (&A) The address associated with this address list entry. This can only be modified for sending addresses. - このアドレス帳項目に結びつけられているアドレス。この項目は送金用アドレスの場合のみ編集することができます。 + このアドレスに関連しているアドレス。これは送金アドレス用にのみ変更できます。 New receiving address - 新しい受信アドレス + 新しい入金アドレス New sending address - 新しい送信アドレス + 新しい送金アドレス Edit receiving address - 受信アドレスを編集 + 入金アドレスを編集 Edit sending address - 送信アドレスを編集 + 送金アドレスを編集 + + + The entered address "%1" is not a valid Dash address. + 入力されたアドレス "%1" は有効なDashアドレスではありません。 The entered address "%1" is already in the address book. @@ -703,19 +897,19 @@ name - name + 名前 Directory already exists. Add %1 if you intend to create a new directory here. - ディレクトリがもうあります。 新しいのディレクトリを作るつもりなら%1を書いてください。 + ディレクトリがすでに存在します。 新しいディレクトリを作成する場合%1を追加してください。 Path already exists, and is not a directory. - パスが存在しますがディレクトリではありません。 + パスが存在します。ディレクトリではありません。 Cannot create data directory here. - ここにデータ ディレクトリを作成することはできません。 + ここにデータディレクトリを作成することはできません。 @@ -726,11 +920,15 @@ (%1-bit) - (%1ビット) + (%1-ビット) + + + About %1 + %1 について Command-line options - コマンドライン オプション + コマンドラインオプション Usage: @@ -738,31 +936,43 @@ command-line options - コマンドライン オプション + コマンドラインオプション - UI options - ユーザインターフェイスオプション + UI Options: + UIオプション: - Choose data directory on startup (default: 0) - 起動時にデータ ディレクトリを選ぶ ( デフォルト設定:0) + Choose data directory on startup (default: %u) + 起動時にデータディレクトリを選ぶ (初期設定: %u) Set language, for example "de_DE" (default: system locale) - 言語設定 例: "de_DE" (デフォルト設定: システムの言語) + 言語を設定 例: "de_DE" (デフォルト設定: システムの言語) Start minimized - 最小化された状態で起動する + 最小化された状態で起動 Set SSL root certificates for payment request (default: -system-) - 支払いリクエスト用にSSLルート証明書を設定する(デフォルト設定:-system-) + 送金リクエスト用にSSLルート証明書を設定(初期設定:-system-) + + + Show splash screen on startup (default: %u) + 起動時にスプラッシュ画面を表示 (初期設定: %u) + + + Reset all settings changed in the GUI + GUI で行われた設定の変更を全てリセット - Show splash screen on startup (default: 1) - 起動時にスプラッシュ画面を表示する (デフォルト設定: 1) + PrivateSend information + プライベートセンドの情報 + + + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. + <h3>プライベートセンドの基本</h3> プライベートセンドはあなたのファンドの起源を不明瞭にすることによって真のファイナンシャルプライバシーを提供します。あなたのウォレットにあるDashは別々の異なるコインとして考えられる”インプット”から構成されています。<br> プライベートセンドは画期的なプロセスを使用し、ウォレットからあなたのコインが移動させられることなく、他の二人の人々のインプットとあなたのインプットをミックスします。あなたは常にあなたのファンドをコントロールしています。<hr> <b>プライベートセンドのプロセスは以下のように機能します:</b><ol type="1"> <li>プライベートセンドは、あなたのトランザクションインプットをスタンダードな通貨単位に分割します。これらの通貨単位は0.01 DASH, 0.1 DASH, 1 DASH そして 10 DASH -- 日常的に使われる紙幣のようなものです。</li> <li>あなたのウォレットは次に、 "マスターノード"と呼ばれるネットワーク上に特別に設定されたソフトウェアノードにリクエストを送信します。これらのマスターノードは、特定の通貨単位をミキシングしたいというあなたの要望を知らされます。送信者を特定できる情報は送信されないので、マスターノードはあなたが誰かわかりません。 </li> <li>二人の別の人たちが同じようなメッセージを送信して、同じ通貨単位をミキシングしたいという要望を伝え、ミキシングセッションが開始されます。マスターノードはインプットをミックスし、すべての三人のユーザーのウォレットに命じて変更されたインプットをもとのウォレットに戻させます。あなたのウォレットはその通貨単位を直接もとのウォレットに支払いますが、アドレスは異なっています(チェンジアドレスといいます)</li> <li>完全にあなたのファンドを匿名化するには、あなたのウォレットはこのプロセスをそれぞれの通貨単位で何回もリピートする必要があります。このプロセスは "ラウンド"と呼びます。プライベートセンドのラウンドが進めば、あなたのファンドの起源を遡ることは指数関数的に困難になっていきます。</li> <li>このミキシングのプロセスはあなたになにも干渉することなくバックグラウンドで実行されます。あなたがトランザクションを実行したいとき、あなたのファンドはすでに匿名化されています。追加の待ち時間は必要ありません。</li> </ol> <hr><b>重要:</b> あなたのウォレットは、 1000個の "チェンジアドレス"しか保有できません。ミキシングが実行されるたびに、9個のアドレスが使用されます。これは1000個のアドレスを使い切るまでに約100回のミキシングがきることを意味します。900個のアドレスが使用されると、あなたのウォレットは追加のアドレスを生成しなくてはなりません。しかし、あなたが自動バックアップを有効にしていれば可能です。<br> つまりバックアップが無効なユーザーはプライベートセンドが無効になります。 <hr>詳細は <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">プライベートセンドドキュメンテーション</a>参照. @@ -772,13 +982,25 @@ ようこそ! + + Welcome to %1. + %1 へようこそ。 + + + As this is the first time the program is launched, you can choose where %1 will store its data. + これは本プログラムの最初の起動です。%1 がデータを保存する場所を選択して下さい。 + + + %1 will download and store a copy of the Dash block chain. At least %2GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory. + %1はDashブロックチェーンのデータのコピーをダウンロードして保存します。 少なくとも%2GBのデータがこのディレクトリに保存され、時間の経過とともに容量が増加します。 ウォレットもこのディレクトリに保存されます。 + Use the default data directory - 初期値のデータ ディレクトリを使用 + 初期設定のデータディレクトリを使用 Use a custom data directory: - 任意のデータ ディレクトリを使用: + 任意のデータディレクトリを使用: Error: Specified data directory "%1" cannot be created. @@ -790,2218 +1012,4151 @@ %1 GB of free space available - 使用可能な空き領域の%1ギガバイト + 使用可能な空き領域の%1 GB - + + (of %1 GB needed) + (%1 GB 必要) + + - OpenURIDialog + MasternodeList - Open URI - URI を開く + Form + フォーム - Open payment request from URI or file - URI またはファイルから支払いリクエストを開く + My Masternodes + 私のマスターノード - URI: - URI: + Note: Status of your masternodes in local wallet can potentially be slightly incorrect.<br />Always wait for wallet to sync additional data and then double check from another node<br />if your masternode should be running but you still do not see "ENABLED" in "Status" field. + 注:ローカルウォレットのマスターノードのステータスは、若干正しくない可能性があります。<br />ウォレットが新しいデータを同期するのを常に待って、他のノードからダブルチェックを行います。<br />あなたのマスターノードが動作していても、"ステータス"フィールドに "ENABLED"が表示されていない場合があります。 - Select payment request file - 支払いリクエストファイルを選択してください + Alias + エイリアス - Select payment request file to open - 開きたい支払いリクエストファイルを選択してください + Address + アドレス - - - OptionsDialog - Options - 設定 + Protocol + プロトコル - &Main - メイン (&M) + Status + ステータス - Size of &database cache - データベースキャッシュのサイズ (&D) + Active + アクティブ - MB - MB + Last Seen + 最終更新 - Number of script &verification threads - スクリプト検証用スレッド数 (&V) + Payee + 受取人 - (0 = auto, <0 = leave that many cores free) - (0 = 自動、0以上 = 指定した数のコアをフリーにする) + S&tart alias + エイリアスの開始(&T) - W&allet - ウォレット (&A) + Start &all + すべて開始(&A) - Accept connections from outside - 外部からの接続を許可する + Start &MISSING + ミッシングマスターノードの開始(&M) - Allow incoming connections - 外部からの接続を許可する + &Update status + ステータスアップデート(&U) - &Connect through SOCKS5 proxy (default proxy): - SOCKS5 プロキシ経由で接続する (デフォルトプロキシ): (&C) + Show additional Masternode information + 追加のマスターノード情報を表示 - Expert - エクスポート + Show &Info... + 情報の表示(&I) - Whether to show coin control features or not. - コインコントロール機能を表示するかどうか。 + Status will be updated automatically in (sec): + ステータスは自動でアップデートされます(秒): - Enable coin &control features - コインコントロール機能を有効化する (&C) + 0 + 0 - &Spend unconfirmed change - 未検証のおつりを使用する (&S) + All Masternodes + すべてのマスターノード - &Network - ネットワーク (&N) + Filter List: + フィルターリスト: - Map port using &UPnP - UPnP を使ってポートを割り当てる (&U) + Filter masternode list + マスターノードのリストをフィルター - Proxy &IP: - プロキシの IP (&I) : + Node Count: + ノード数: - IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1) - プロキシのIPアドレス (例えば IPv4: 127.0.0.1 / IPv6: ::1) + Start alias + エイリアス開始 - &Port: - ポート (&P): + Confirm masternode start + マスターノードの開始を確認 - Port of the proxy (e.g. 9050) - プロキシのポート番号 (例 9050) + Are you sure you want to start masternode %1? + 本当にマスターノード %1 を開始したいですか? - &Window - ウインドウ (&W) + Confirm all masternodes start + すべてのマスターノードの開始を確認 - Show only a tray icon after minimizing the window. - ウインドウを最小化したあとトレイ アイコンだけを表示する。 + Are you sure you want to start ALL masternodes? + 本当にすべてのマスターノードを開始したいですか? - &Minimize to the tray instead of the taskbar - タスクバーの代わりにトレイに最小化 (&M) + Command is not available right now + コマンドは現在利用できません。 - M&inimize on close - 閉じる時に最小化 (&i) + You can't use this command until masternode list is synced + このコマンドはマスターノードのリストが同期されるまで使用できません。 - &Display - 表示 (&D) + Confirm missing masternodes start + ミッシングマスターノードの開始を確認 - User Interface &language: - ユーザインターフェースの言語 (&l): + Are you sure you want to start MISSING masternodes? + 本当に不明なマスターノードを開始したいですか? - &Unit to show amounts in: - 額を表示する単位 (&U): + Additional information for Masternode %1 + マスターノード %1 の追加情報 - Choose the default subdivision unit to show in the interface and when sending coins. - インターフェース上の表示とコインの送信で使用する単位を選択します。 + Masternode Private Key + マスターノードプライベートキー - Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. - トランザクションタブのコンテキストメニュー項目に表示する、サードパーティURL (例えばブロックエクスプローラ)。URL中の%sはトランザクションのハッシュ値に置き換えられます。垂直バー | で区切ることで、複数のURLを指定できます。 + Private Key + プライベートキー - Third party transaction URLs - サードパーティのトランザクションURL + Collateral + コラテラル - Active command-line options that override above options: - 上のオプションを置き換えることのできる、有効なコマンドラインオプションの一覧: + IP + IPアドレス - Reset all client options to default. - すべてのオプションを初期値に戻します。 + Version + バージョン - &Reset Options - オプションをリセット (&R) + Unknown + 不明 - &OK - &OK + Sentinel + センチネル + + + ModalOverlay - &Cancel - キャンセル (&C) + Form + フォーム - default - デフォルト設定 + Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the Dash network, as detailed below. + 確認できない最新のトランザクションがあるかもしれません。これによりウォレットの残高は不正確なものである可能性があります。この情報はウォレットが一度Dashのネットワークへの同期が完了すると正確なものとなります。詳細は下記を参照してください。 - none - なし + Attempting to spend Dash that are affected by not-yet-displayed transactions will not be accepted by the network. + 表示されてないトランザクションに影響を受けたDashを使用しようとするとネットワークに承認されません。 - Confirm options reset - オプションのリセットの確認 + Number of blocks left + 残りのブロック数 - Client restart required to activate changes. - 変更を有効化するにはクライアントを再起動する必要があります。 + Unknown... + 不明... - This change would require a client restart. - この変更はクライアントの再起動が必要です。 + Last block time + 最終ブロックの日時 - The supplied proxy address is invalid. - プロキシアドレスが無効です。 + Progress + 進捗 - - - OverviewPage - Form - フォーム + Progress increase per hour + 一時間毎の進捗の変化 - Available: - 利用可能: + calculating... + 計算中... - Your current spendable balance - あなたの利用可能残高 + Estimated time left until synced + 同期が完了するまでの推定残り時間 - Pending: - 検証待ち: + Hide + 非表示 - Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance - 未検証の取引で利用可能残高に反映されていない数 + Unknown. Syncing Headers (%1)... + 不明。ヘッダーを同期中 (%1)... + + + OpenURIDialog - Immature: - 未完成: + Open URI + URI を開く - Mined balance that has not yet matured - 完成していない採掘された残高 + Open payment request from URI or file + URI またはファイルから送金リクエストを開く - Balances - 残高 + URI: + URI: - Unconfirmed transactions to watch-only addresses - 監視限定アドレスに対する未検証のトランザクション + Select payment request file + 送金リクエストファイルを選択してください - Mined balance in watch-only addresses that has not yet matured - ウォッチオンリーアドレスの採掘された残高のうち、成熟していないもの + Select payment request file to open + 開きたい送金リクエストファイルを選択してください + + + OptionsDialog - Total: - 合計: + Options + オプション - Your current total balance - あなたの現在の残高 + &Main + メイン (&M) - Current total balance in watch-only addresses - 監視限定アドレス内の現在の全残高 + Size of &database cache + データベースキャッシュのサイズ (&D) - Watch-only: - 監視限定: + MB + MB - Your current balance in watch-only addresses - 監視限定アドレス内の現在の残高 + Number of script &verification threads + スクリプト検証用スレッド数 (&V) - Spendable: - 使用可能: + (0 = auto, <0 = leave that many cores free) + (0 = 自動、0以上 = 指定した数のコアをフリーにする) - Status: - 状態: + Amount of Dash to keep anonymized + 匿名化されたDashの総額 - Recent transactions - 最近のトランザクション + W&allet + ウォレット (&A) - N/A - N/A + Show additional tab listing all your masternodes in first sub-tab<br/>and all masternodes on the network in second sub-tab. + あなたのすべてのマスターノードをリストしている追加のタブを最初のサブタブに表示し<br/>すべてのマスターノードを次のサブタブに表示 - - - PaymentServer - Payment request error - 支払いのリクエストのエラーです + Show Masternodes Tab + マスターノードタブを表示する - URI handling - URI の操作 + Show additional information and buttons for PrivateSend on overview screen. + プライベートセンドの追加の情報とボタンを概要に表示 - Payment request fetch URL is invalid: %1 - 支払い要求の取得先URLが無効です: %1 + Enable advanced PrivateSend interface + 高度なプライベートセンドのインターフェイスを有効にする - Payment request file handling - 支払いリクエストファイルを処理しています + Show warning dialog when PrivateSend detects that wallet has very low number of keys left. + プライベートセンドがウォレットに非常に少ないキーしか残されてないことを検知したとき警告を表示 - Invalid payment address %1 - 支払いのアドレス「%1」は無効です + Warn if PrivateSend is running out of keys + プライベートセンドがキーを使い果たしているとき警告する - Payment request file cannot be read! This can be caused by an invalid payment request file. - 支払いリクエストファイルを読み込めませんでした!無効な支払いリクエストファイルにより引き起こされた可能性があります。 + Whether to use experimental PrivateSend mode with multiple mixing sessions per block.<br/>Note: You must use this feature carefully.<br/>Make sure you always have recent wallet (auto)backup in a safe place! + プライベートセンドモードを1ブロックあたり複数のミキシングセッションで実験的に使用する場合<br/>注意: この機能を十分に注意して使用してください。<br/>常に最新のウォレットバックアップを安全な場所に保管してください。 - Payment request rejected - 支払い要求は拒否されました + Enable PrivateSend &multi-session + プライベートセンドとマルチセッションを有効にする(&M) - Payment request network doesn't match client network. - 支払いリクエストのネットワークは現在のクライアントのネットワークに一致しません。 + If you disable the spending of unconfirmed change, the change from a transaction<br/>cannot be used until that transaction has at least one confirmation.<br/>This also affects how your balance is computed. + もし未確認のチェンジを使用することを無効にする場合<br/>トランザクションから生じたチェンジはそのトランザクションが少なくとも一回コンファメーションがないと使用できません。<br/>これはまたあなたのウォレットの残高の計算にも影響を与えます。 - Payment request is not initialized. - 支払いリクエストは開始されていません。 + PrivateSend rounds to use + プライベートセンドのラウンドを使用 - Unverified payment requests to custom payment scripts are unsupported. - カスタム支払いスクリプトに対する、検証されていない支払いリクエストはサポートされていません。 + This amount acts as a threshold to turn off PrivateSend once it's reached. + この設定は閾値として機能し、この量に達するとプライベートセンドが無効になります。 - Requested payment amount of %1 is too small (considered dust). - 要求された支払額 %1 は少なすぎます (ダストとみなされてしまいます)。 + Automatically open the Dash Core client port on the router. This only works when your router supports UPnP and it is enabled. + 自動的にルーターのDash Core クライアントポートを開放します。これはルーターがUPnP機能をサポートしておりUPnP機能が有効な場合にのみ機能します。 - Refund from %1 - %1 からの返金 + Accept connections from outside + 外部からの接続を許可 - Payment request %1 is too large (%2 bytes, allowed %3 bytes). - 支払リクエスト %1 は大きすぎます(%2バイトですが、%3バイトまでが許されています)。 + Allow incoming connections + 受信中の接続を許可 - Payment request DoS protection - 支払リクエストDoS保護 + Connect to the Dash network through a SOCKS5 proxy. + SOCKS5プロキシ経由でDashネットワークに接続 - Error communicating with %1: %2 - %1: %2とコミュニケーション・エラーです + &Connect through SOCKS5 proxy (default proxy): + SOCKS5プロキシ経由で接続 (デフォルトプロキシ): (&C) - Payment request cannot be parsed! - 支払リクエストを読み込めませんでした! + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu. + ウィンドウを閉じる際にアプリケーションを終了するのではなく最小化します。このオプションが有効の場合、メニューから終了を選択した場合にのみアプリケーションは閉じられます。 - Bad response from server %1 - サーバーの返事は無効 %1 + Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items.<br/>%s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. + メニューアイテムとしてトランザクションタブに表示されるサードパーティのURL (例 ブロックエクスプローラー) :<br/>%s はトランザクションハッシュに置き換えられます。複数のURLは"|"によって区切られます。 - Network request error - ネットワーク・リクエストのエラーです + Expert + エキスパート - Payment acknowledged - 支払いは確認しました + This setting determines the amount of individual masternodes that an input will be anonymized through.<br/>More rounds of anonymization gives a higher degree of privacy, but also costs more in fees. + この設定はインプットが匿名化される個々のマスターノードの数を決めます。匿名化のラウンドが多ければ多いほどプライバシーのレベルが高くなりますがコストもかかるようになります。 - - - PeerTableModel - Address/Hostname - アドレス / ホスト名 + Whether to show coin control features or not. + コインコントロール機能の表示/非表示 - User Agent - ユーザエージェント + Automatically start %1 after logging in to the system. + システムにログインすると自動的に %1 を起動 - Ping Time - Ping時間 + &Start %1 on system login + システムログイン時に %1 を起動 (&S) - - - QObject - Amount - 総額 + Enable coin &control features + コインコントロール機能を有効化 (&C) - %1 d - %1日 + &Spend unconfirmed change + 未検証のチェンジを使用 (&S) - %1 h - %1 h + &Network + ネットワーク (&N) - %1 m - %1 m + Map port using &UPnP + UPnPを使ってポートを割り当て (&U) - %1 s - %1秒 + Proxy &IP: + プロキシの IP:(&I)  - None - なし + IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1) + プロキシのIPアドレス (例 IPv4: 127.0.0.1 / IPv6: ::1) - N/A - N/A + &Port: + ポート :(&P) - %1 ms - %1ミリ秒 + Port of the proxy (e.g. 9050) + プロキシのポート番号 (例 9050) - - - QRImageWidget - &Save Image... - 画像を保存… (&S) + Used for reaching peers via: + ピアへ到達するために使われた方法: - &Copy Image - 画像をコピー(&C) + Shows, if the supplied default SOCKS5 proxy is used to reach peers via this network type. + デフォルトのSOCKS5プロキシがこのネットワークタイプでピアに到達するのに使用された場合表示 - Save QR Code - QR コードの保存 + IPv4 + IPv4 - PNG Image (*.png) - PNG画像ファイル(*.png) + IPv6 + IPv6 - - - RPCConsole - &Information - 情報 (&I) + Tor + Tor - General - 一般 + Connect to the Dash network through a separate SOCKS5 proxy for Tor hidden services. + Tor秘匿サービスを利用するため独立なSOCKS5プロキシ経由でDashネットワークに接続 - Name - 名前 + Use separate SOCKS5 proxy to reach peers via Tor hidden services: + 独立なSOCKS5プロキシを使用してTor秘匿サービス経由でピアに到達 - Client name - クライアント名 + &Window + ウインドウ (&W) - N/A - N/A + &Hide the icon from the system tray. + システムトレイからアイコンを非表示(&W) - Number of connections - 接続数 + Hide tray icon + トレイアイコンを非表示 - &Open - 開く (&O) + Show only a tray icon after minimizing the window. + ウインドウを最小化したあとトレイアイコンだけを表示 - Startup time - 起動した日時 + &Minimize to the tray instead of the taskbar + タスクバーの代わりにトレイに最小化 (&M) - Network - ネットワーク + M&inimize on close + 閉じる時に最小化 (&I) - Last block time - 最終ブロックの日時 + &Display + 表示 (&D) - Debug log file - デバッグ用ログファイル + User Interface &language: + ユーザインターフェースの言語 :(&L) - Using OpenSSL version - 使用中の OpenSSL のバージョン + The user interface language can be set here. This setting will take effect after restarting %1. + ここでユーザーインターフェースの言語を設定できます。設定を反映するには %1 を再起動します。 - Build date - ビルドの日付 + Language missing or translation incomplete? Help contributing translations here: +https://www.transifex.com/projects/p/dash/ + 希望の言語がない場合または翻訳に問題がある場合はこちらで翻訳にご協力ください。: +https://www.transifex.com/projects/p/dash/ - Current number of blocks - 現在のブロック数 + User Interface Theme: + ユーザーインターフェイスのテーマ: - Client version - クライアントのバージョン + &Unit to show amounts in: + 表示する単位 :(&U) - Using BerkeleyDB version - 使用中のBerkleyDBバージョン + Choose the default subdivision unit to show in the interface and when sending coins. + インターフェース上の表示とコインの送金で使用するデフォルトの単位を選択 - Block chain - ブロック チェーン + Decimal digits + 10進数 - &Console - コンソール (&C) + Third party transaction URLs + サードパーティのトランザクションURL - Clear console - コンソールをクリア + Active command-line options that override above options: + 上のオプションを置き換えることのできる有効なコマンドラインオプションの一覧: - &Network Traffic - ネットワーク (&N) + Reset all client options to default. + すべてのオプションを初期値に戻します。 - &Clear - クリア(&C) + &Reset Options + オプションのリセット (&R) - Totals - 合計 + &OK + OK(&O) - Received - 受取 + &Cancel + キャンセル (&C) - Sent - 送金 + default + デフォルト設定 - &Peers - ピア (&P) + none + なし - Select a peer to view detailed information. - 詳しい情報を見たいピアを選択してください。 + Confirm options reset + オプションのリセットの確認 - Direction - 方向 + Client restart required to activate changes. + 変更を有効にするにはクライアントを再起動する必要があります。 - Version - バージョン + Client will be shut down. Do you want to proceed? + クライアントを終了します。続行してもよろしいですか? - User Agent - ユーザエージェント + This change would require a client restart. + この変更はクライアントの再起動が必要です。 - Services - サービス + The supplied proxy address is invalid. + プロキシアドレスが無効です。 + + + OverviewPage - Starting Height - 開始時のブロック高 + Form + フォーム - Sync Height - 同期済みブロック高 + The displayed information may be out of date. Your wallet automatically synchronizes with the Dash network after a connection is established, but this process has not completed yet. + 表示された情報は古いかもしれません。接続が確立されればあなたのウォレットは Dashネットワークと自動的に同期しますが、このプロセスはまだ完了していません。 - Ban Score - Banスコア + Available: + 利用可能: - Connection Time - 接続時間 + Your current spendable balance + あなたの利用可能な残高 - Last Send - 最終送信 + Pending: + 検証待ち: - Last Receive - 最終受信 + Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance + 未検証で利用可能残高に反映されていないトランザクション数 - Bytes Sent - 送信済バイト数 + Immature: + 未完成: - Bytes Received - 受信済バイト数 + Mined balance that has not yet matured + 不完全な状態の残高 - Ping Time - Ping時間 + Balances + 残高 - In: - 入力: + Unconfirmed transactions to watch-only addresses + 監視限定のアドレスへの未検証トランザクション - Out: - 出力: + Mined balance in watch-only addresses that has not yet matured + 不完全な状態の監視限定のアドレスの残高 - Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. - 上下の矢印で履歴をたどれます。 <b>Ctrl-L</b> でスクリーンを消去できます。 + Total: + 合計: - Type <b>help</b> for an overview of available commands. - 使用可能なコマンドを見るには <b>help</b> と入力します。 + Your current total balance + あなたの現在の残高の合計 - %1 B - %1 B + Current total balance in watch-only addresses + 監視限定アドレスの現在の残高の合計 - %1 KB - %1 KB + Watch-only: + ウォッチオンリー: - %1 MB - %1 MB + Your current balance in watch-only addresses + あなたのウォッチオンリーのアドレスの現在の残高の合計 - %1 GB - %1 GB + Spendable: + 使用可能: - via %1 - %1経由 + PrivateSend + プライベートセンド - never - 一度もなし + Status: + ステータス: - Inbound - 内向き + Enabled/Disabled + 有効/無効 - Outbound - 外向き + Completion: + 完了: - Unknown - 未知 + Try to manually submit a PrivateSend request. + 手動でプライベートセンドのリクエストを送信 - Fetching... - 取得中… + Reset the current status of PrivateSend (can interrupt PrivateSend if it's in the process of Mixing, which can cost you money!) + プライベートセンドの現在のステータスをリセット (これはミキシングの途中であればプライベートセンドを中止し、コストがかかります!) - - - ReceiveCoinsDialog - R&euse an existing receiving address (not recommended) - 既存の受取用アドレスを再利用する (非推奨) (&E) + Information about PrivateSend and Mixing + プライベートセンドとミキシングの情報 - &Message: - メッセージ (&M): + Info + 情報 - An optional label to associate with the new receiving address. - 受取用アドレスに紐づく任意のラベル。 + Amount and Rounds: + 金額とラウンド - Use this form to request payments. All fields are <b>optional</b>. - このフォームを使用して支払のリクエストを行いましょう。すべての項目は<b>任意入力</b>です。 + 0 DASH / 0 Rounds + 0 DASH / 0 ラウンド - &Label: - ラベル (&L): + Submitted Denom: + 通貨単位: - An optional amount to request. Leave this empty or zero to not request a specific amount. - リクエストする任意の金額。特定の金額をリクエストするのでない場合には、この欄は空白のままかゼロにしてください。 + n/a + N/A - &Amount: - 総額 (&A): + Recent transactions + 直近のトランザクション - &Request payment - 支払をリクエストする (&R) + Start/Stop Mixing + ミキシング開始/停止 - Clear all fields of the form. - 全ての入力項目をクリア + PrivateSend Balance: + プライベートセンド残高: - Clear - クリア + The denominations you submitted to the Masternode.<br>To mix, other users must submit the exact same denominations. + マスターノードに送った通貨単位<br>ミキシングをするには他のユーザーが正確に同じ通貨単位を送る必要があります。 - Requested payments history - 支払リクエスト履歴 + (Last Message) + (最新メッセージ) - Show the selected request (does the same as double clicking an entry) - 選択されたリクエストを表示する(項目をダブルクリックすることでも表示できます) + Try Mix + ミキシング開始 - Show - 表示 + Reset + リセット - Remove the selected entries from the list - リストから選択項目を削除 + out of sync + 未同期 - Remove - 削除 + Automatic backups are disabled, no mixing available! + 自動バックアップが無効でミキシングは利用不可能です! - Copy label - ラベルをコピーする + Start Mixing + ミキシング開始 - Copy message - メッセージをコピーする + Stop Mixing + ミキシング停止 - Copy amount - 総額のコピー + No inputs detected + インプットが検知できません + + + %n Rounds + %n ラウンド - - - ReceiveRequestDialog - QR Code - QR コード + Found enough compatible inputs to anonymize %1 + %1 を匿名化するのに十分なインプットを検知しました。 - Copy &URI - URI をコピーする (&U) + Not enough compatible inputs to anonymize <span style='color:red;'>%1</span>,<br>will anonymize <span style='color:red;'>%2</span> instead + <span style='color:red;'>%1</span>を匿名化するのに十分なインプットがありません、<br> <span style='color:red;'>%2</span> を代わりに匿名化します。 - Copy &Address - アドレスをコピーする (&A) + Overall progress + すべての進捗 - &Save Image... - 画像を保存… (&S) + Denominated + ミキシング準備完了 - Request payment to %1 - %1 への支払いリクエストを行う + Mixed + ミキシング完了 - Payment information - 支払い情報 + Anonymized + 匿名化完了 + + + Denominated inputs have %5 of %n rounds on average + 分割されるインプットは平均的に %5 / %n ラウンド です。 - URI - URI + keys left: %1 + 残されたキー: %1 - Address - アドレス + Disabled + 無効 - Amount - 総額 + Very low number of keys left since last automatic backup! + 前回の自動バックアップから残されたキーの数がほとんどありません! - Label - ラベル + We are about to create a new automatic backup for you, however <span style='color:red;'> you should always make sure you have backups saved in some safe place</span>! + 新しい自動バックアップを作成しますが、 <span style='color:red;'> 常にバックアップを安全な場所に作成してください</span>! - Message - メッセージ + Note: You can turn this message off in options. + 注記: このメッセージはオプションで非表示にできます。 - Resulting URI too long, try to reduce the text for label / message. - URI が長くなり過ぎます。ラベルやメッセージのテキストを短くしてください。 + WARNING! Something went wrong on automatic backup + 警告! 自動バックアップに異常が生じています。 - Error encoding URI into QR Code. - QR コード用の URI エンコードでエラー。 + ERROR! Failed to create automatic backup + エラー! 自動バックアップ作成に失敗しました。 - - - RecentRequestsTableModel - Date - 日付 + Mixing is disabled, please close your wallet and fix the issue! + ミキシングは無効です。ウォレットを閉じて問題を解決してください。 - Label - ラベル + Enabled + 有効 - Message - メッセージ + see debug.log for details. + 詳細を debug.log で確認 - Amount - 総額 + WARNING! Failed to replenish keypool, please unlock your wallet to do so. + 警告! キープールの補充に失敗しました。ウォレットをアンロックしてキープールを補充してください - (no label) - (ラベル無し) + Last PrivateSend message: + + 最後のプライベートセンドのメッセージ: + - (no message) - (メッセージなし) + N/A + N/A - (no amount) - (金額なし) + PrivateSend was successfully reset. + プライベートセンドのリセットに成功しました。 - - - SendCoinsDialog - Send Coins - コインを送る + If you don't want to see internal PrivateSend fees/transactions select "Most Common" as Type on the "Transactions" tab. + プライベートセンドのトランザクションや手数料を非表示にしたい場合はトランザクションタブ上で”通常”をタイプとして選択してください。 - Coin Control Features - コインコントロール機能 + PrivateSend requires at least %1 to use. + プライベートセンドは最低 %1 使用します。 - Inputs... - 入力… + Wallet is locked and user declined to unlock. Disabling PrivateSend. + ウォレットはロックされ、ユーザーはアンロックを拒否しました。プライベートセンドを無効にします。 + + + PaymentServer - automatically selected - 自動選択 + Payment request error + 支払いリクエストのエラー - Insufficient funds! - 残高不足です! + Cannot start dash: click-to-pay handler + Dash を起動できません: click-to-pay ハンドラー - Quantity: - 数量: + URI handling + URI のハンドリング - Bytes: - バイト: + Payment request fetch URL is invalid: %1 + 支払いリクエストの取得先URLが無効です: %1 - Amount: - 総額: + Invalid payment address %1 + 支払いのアドレス %1 は無効です - Priority: - 優先度: + URI cannot be parsed! This can be caused by an invalid Dash address or malformed URI parameters. + URI を解析できません! これは無効な Dash アドレスあるいは不正な形式の URI パラメーターによって引き起こされた可能性があります。 - medium - + Payment request file handling + 支払いリクエストファイルを処理しています - Fee: - 手数料: + Payment request file cannot be read! This can be caused by an invalid payment request file. + 支払いリクエストファイルを読み込めませんでした!無効な支払いリクエストファイルが原因である可能性があります。 - Dust: - ダスト: + Payment request rejected + 支払い要求は拒否されました - no - いいえ + Payment request network doesn't match client network. + 支払いリクエストのネットワークは現在のクライアントのネットワークに一致しません。 - After Fee: - 手数料差引後: + Payment request expired. + 支払いリクエストの期限が切れました。 - Change: - 釣り銭: + Payment request is not initialized. + 支払いリクエストは開始されていません。 - If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address. - これが有効にもかかわらずおつりアドレスが空欄であったり無効であった場合には、おつりは新しく生成されたアドレスへ送金されます。 + Unverified payment requests to custom payment scripts are unsupported. + カスタム支払いスクリプトに対する未検証の支払いリクエストはサポートされていません。 - Custom change address - カスタムおつりアドレス + Invalid payment request. + 無効な支払いリクエスト。 - Transaction Fee: - トランザクション手数料: + Requested payment amount of %1 is too small (considered dust). + リクエストされた支払い額 %1 は少なすぎます (ダストとみなされます)。 - Choose... - 選択… + Refund from %1 + %1 からのリファンド - collapse fee-settings - 手数料設定を折りたたむ + Payment request %1 is too large (%2 bytes, allowed %3 bytes). + 支払リクエスト %1 は大きすぎます(%2bytesですが、上限%3bytesまで許されます)。 - per kilobyte - 1キロバイトあたり手数料 + Error communicating with %1: %2 + %1: %2とコミュニケーションエラー - total at least - 最小手数料 + Payment request cannot be parsed! + 支払いリクエストを読み込めませんでした! - (read the tooltip) - (ツールチップをお読みください) + Bad response from server %1 + サーバー %1のレスポンスは無効 - Recommended: - 推奨: + Network request error + ネットワークリクエストのエラー - Custom: - カスタム: + Payment acknowledged + 支払いを確認しました + + + PeerTableModel - (Smart fee not initialized yet. This usually takes a few blocks...) - (スマート手数料はまだ初期化されていません。これにはおおよそ数ブロックほどかかります…) + NodeId + ノードID - Confirmation time: - 検証時間: + Node/Service + ノード/サービス - normal - 普通 + User Agent + ユーザーエージェント - fast - 高速 + Ping + Ping + + + QObject - Send as zero-fee transaction if possible - 可能な場合には手数料ゼロのトランザクションとして送金する + Dash Core + Dashコア - (confirmation may take longer) - (検証に長い時間がかかる可能性があります) + %1 didn't yet exit safely... + %1 はまだ安全に終了していません - Confirm the send action - 送る操作を確認する + Error reading masternode configuration file: %1 + マスターノード設定ファイルの読み込みエラー: %1 - S&end - 送る (&e) + Amount + 総額 - Clear all fields of the form. - 全ての入力項目をクリア + Enter a Dash address (e.g. %1) + Dashアドレスを入力してください (例 %1) - Clear &All - すべてクリア (&A) + %1 d + %1日 - Send to multiple recipients at once - 一度に複数の人に送る + %1 h + %1時 - Add &Recipient - 受取人を追加 (&R) + %1 m + %1分 - Balance: - 残高: + %1 s + %1秒 - Copy quantity - 数量をコピーする + None + なし - Copy amount - 総額のコピー + N/A + N/A - Copy fee - 手数料をコピーする + %1 ms + %1ミリ秒 + + + %n second(s) + %n 秒 + + + %n minute(s) + %n 分 + + + %n hour(s) + %n 時間 + + + %n day(s) + %n 日 + + + %n week(s) + %n 週 + + + %n year(s) + %n 年 - Copy after fee - 手数料差引後の値をコピーする + %1 and %2 + %1 と %2 - Copy bytes - バイト数をコピーする + unknown + 未確認 + + + QObject::QObject - Copy priority - 優先度をコピーする + Error: Specified data directory "%1" does not exist. + エラー: 指定のデータディレクトリ "%1" は存在しません - Copy dust - ダストをコピーする + Error: Cannot parse configuration file: %1. Only use key=value syntax. + エラー: 設定ファイル: %1をパースできません。key=value という記法のみを利用してください。 + + + Error: %1 + エラー: %1 + + + + QRDialog + + QR-Code Title + QRコードタイトル + + + QR Code + QRコード + + + &Save Image... + 画像を保存...(&S) + + + Error creating QR Code. + QRコード生成エラー + + + + QRGeneralImageWidget + + &Save Image... + 画像を保存...(&S) + + + &Copy Image + 画像をコピー(&C) + + + Save QR Code + QR コードの保存 + + + PNG Image (*.png) + PNG画像ファイル(*.png) + + + + QRImageWidget + + &Save Image... + 画像を保存… (&S) + + + &Copy Image + 画像をコピー(&C) + + + Save QR Code + QR コードの保存 + + + PNG Image (*.png) + PNG画像ファイル(*.png) + + + + RPCConsole + + Tools window + ツールウィンドウ + + + &Information + インフォメーション (&I) + + + General + 一般 + + + Name + 名前 + + + N/A + N/A + + + Number of connections + 接続数 + + + &Open + 開く (&O) + + + Startup time + 起動した日時 + + + Network + ネットワーク + + + Last block time + 最終ブロックの日時 + + + Debug log file + デバッグ用ログファイル + + + Current number of blocks + 現在のブロック数 + + + Client version + クライアントのバージョン + + + Using BerkeleyDB version + 使用中のBerkleyDBバージョン + + + Block chain + ブロックチェーン + + + Number of Masternodes + マスターノードの数 + + + Memory Pool + メモリープール + + + Current number of transactions + 現在のトランザクション数 + + + Memory usage + メモリ使用量 + + + &Console + コンソール (&C) + + + Clear console + コンソールをクリア + + + &Network Traffic + ネットワークトラフィック (&N) + + + &Clear + クリア(&C) + + + Totals + 合計 + + + Received + 受取完了 + + + Sent + 送金完了 + + + &Peers + ピア (&P) + + + Banned peers + Banされたピア + + + Select a peer to view detailed information. + 詳細を確認したいピアを選択してください。 + + + Whitelisted + ホワイトリスト + + + Direction + ディレクション + + + Version + バージョン + + + Starting Block + 開始ブロック + + + Synced Headers + 同期済みヘッダ + + + Synced Blocks + 同期済みブロック + + + Wallet Path + ウォレットパス + + + User Agent + ユーザーエージェント + + + Datadir + データディレクトリ + + + Open the %1 debug log file from the current data directory. This can take a few seconds for large log files. + 現在のデータディレクトリから %1 デバッグ用ログファイルを開きます。ログファイルが巨大な場合数秒かかることがあります。 + + + Decrease font size + 文字サイズを縮小 + + + Increase font size + 文字サイズを拡大 + + + Services + サービス + + + Ban Score + Banスコア + + + Connection Time + 接続時間 + + + Last Send + 最終送信 + + + Last Receive + 最終受信 + + + Ping Time + Ping時間 + + + The duration of a currently outstanding ping. + 現在実行中のpingにかかっている時間 + + + Ping Wait + Ping待ち + + + Min Ping + 最小Ping + + + Time Offset + 時間オフセット + + + &Wallet Repair + ウォレット修復(&W) + + + Salvage wallet + ウォレットの救出 + + + Rescan blockchain files + ブロックチェーンファイルの再スキャン + + + Recover transactions 1 + トランザクション1のリカバー + + + Recover transactions 2 + トランザクション2のリカバー + + + Upgrade wallet format + ウォレットフォーマットのアップグレード + + + The buttons below will restart the wallet with command-line options to repair the wallet, fix issues with corrupt blockhain files or missing/obsolete transactions. + このボタンは、過去の不明なトランザクションや問題のあるブロックチェーンファイルによるバグを修復するためコマンドラインオプションを使用してウォレットをリスタートします。 + + + -salvagewallet: Attempt to recover private keys from a corrupt wallet.dat. + -salvagewallet: 問題のある wallet.dat からプライベートキーをリカバーします + + + -rescan: Rescan the block chain for missing wallet transactions. + -rescan: 不明なウォレットトランザクションがあるためブロックチェーンを再スキャンします + + + -zapwallettxes=1: Recover transactions from blockchain (keep meta-data, e.g. account owner). + -zapwallettxes=1: ブロックチェーンからトランザクションをリカバーします(メタデータは保持します 例 アカウントオーナー) + + + -zapwallettxes=2: Recover transactions from blockchain (drop meta-data). + -zapwallettxes=2: ブロックチェーンからトランザクションをリカバーします (メタデータは保持しません) + + + -upgradewallet: Upgrade wallet to latest format on startup. (Note: this is NOT an update of the wallet itself!) + -upgradewallet: 起動時にウォレットを最新のフォーマットにアップグレードします(注意: これはウォレット自体のアップグレードではありません!) + + + Wallet repair options. + ウォレット修復オプション + + + Rebuild index + インデックスのリビルド + + + -reindex: Rebuild block chain index from current blk000??.dat files. + -reindex: 現在の blk000??.dat ファイルからブロックチェーンインデックスをリビルドします + + + &Disconnect + 切断 (&D) + + + Ban for + Banする: + + + 1 &hour + 1時間 (&H) + + + 1 &day + 1日 (&D) + + + 1 &week + 1週間 (&W) + + + 1 &year + 1年 (&Y) + + + &Unban + Banを解除する (&U) + + + Welcome to the %1 RPC console. + %1 RPCコンソールへようこそ。 + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + 上下の矢印で履歴をたどれます。 <b>Ctrl-L</b> でスクリーンをクリアにします。 + + + Type <b>help</b> for an overview of available commands. + 使用可能なコマンドを見るには <b>help</b> と入力してください + + + WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramification of a command. + 警告: スキャマーがユーザーにコマンドをタイプさせウォレットを盗もうとすることがあります。コマンドを充分に理解しないでコンソールを使わないでください。 + + + In: + 入力: + + + Out: + 出力: + + + Network activity disabled + ネットワーク活動は無効化されました + + + %1 B + %1 B + + + %1 KB + %1 KB + + + %1 MB + %1 MB + + + %1 GB + %1 GB + + + (node id: %1) + (ノードID: %1) + + + via %1 + %1経由 + + + never + 一度もなし + + + Inbound + インバウンド + + + Outbound + アウトバウンド + + + Yes + はい + + + No + いいえ + + + Unknown + 未知 + + + + ReceiveCoinsDialog + + Reuse one of the previously used receiving addresses.<br>Reusing addresses has security and privacy issues.<br>Do not use this unless re-generating a payment request made before. + 過去に使用した受け取り用アドレスを再使用します。<br>アドレスの再使用はセキュリティーとプライバシー保護に問題があります。<br>過去の支払いリクエストの再生成をしない限りこれを使用しないでください。 + + + R&euse an existing receiving address (not recommended) + 既存の受け取り用アドレスを再使用する (非推奨) (&E) + + + An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Dash network. + 支払いリクエストが開始された時に表示される、支払いリクエストに添付される任意のメッセージです。注意:このメッセージはDashネットワークを通じて支払いと共に送信されるわけではありません。 + + + &Message: + メッセージ: (&M) + + + An optional label to associate with the new receiving address. + 新しい受け取り用アドレスに紐づく任意のラベル。 + + + An optional message to attach to the payment request, which will be displayed when the request is opened.<br>Note: The message will not be sent with the payment over the Dash network. + 支払いリクエストが開始された時に表示される、支払いリクエストに添付される任意のメッセージです。注意:このメッセージはDashネットワークを通じて支払いと共に送信されるわけではありません。 + + + Use this form to request payments. All fields are <b>optional</b>. + このフォームを使用して支払いのリクエストを行いましょう。すべての項目は<b>任意入力</b>です。 + + + &Label: + ラベル (&L) + + + An optional amount to request. Leave this empty or zero to not request a specific amount. + リクエストする任意の金額。特定の金額をリクエストしない場合にはこの欄は空白のままかゼロにしてください。 + + + &Amount: + 総額 (&A) + + + &Request payment + 支払いをリクエストする (&R) + + + Clear all fields of the form. + このフォームの全ての入力項目をクリア + + + Clear + クリア + + + Request InstantSend + インスタントセンドのリクエスト + + + Requested payments history + 支払いリクエスト履歴 + + + Show the selected request (does the same as double clicking an entry) + 選択されたリクエストを表示(項目をダブルクリックでも表示) + + + Show + 表示 + + + Remove the selected entries from the list + リストから選択項目を削除 + + + Remove + 削除 + + + Copy URI + URI をコピー + + + Copy label + ラベルをコピーする + + + Copy message + メッセージをコピー + + + Copy amount + 総額のコピー + + + + ReceiveRequestDialog + + QR Code + QRコード + + + Copy &URI + URI をコピー (&U) + + + Copy &Address + アドレスをコピー(&A) + + + &Save Image... + 画像を保存… (&S) + + + Request payment to %1 + %1 へ支払いリクエストを行う + + + Payment information + 支払い情報 + + + URI + URI + + + Address + アドレス + + + Amount + 総額 + + + Label + ラベル + + + Message + メッセージ + + + InstantSend + インスタントセンド + + + Yes + はい + + + No + いいえ + + + Resulting URI too long, try to reduce the text for label / message. + URI が長過ぎます。ラベルやメッセージのテキストを短くしてください。 + + + Error encoding URI into QR Code. + QRコード用のURIエンコードエラー + + + + RecentRequestsTableModel + + Date + 日付 + + + Label + ラベル + + + Message + メッセージ + + + (no label) + (ラベル無し) + + + (no message) + (メッセージなし) + + + (no amount requested) + (金額指定なし) + + + Requested + リクエスト完了 + + + + SendCoinsDialog + + Send Coins + 送金 + + + Coin Control Features + コインコントロール機能 + + + Inputs... + インプット… + + + automatically selected + 自動選択 + + + Insufficient funds! + 残高不足です! + + + Quantity: + 数量: + + + Bytes: + バイト: + + + Amount: + 総額: + + + Fee: + 手数料: + + + Dust: + ダスト: + + + After Fee: + 手数料差引後: + + + Change: + チェンジ: + + + If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address. + これが有効にもかかわらずチェンジアドレスが空欄であったり無効な場合には、チェンジは新しく生成されたアドレスへ送金されます。 + + + Custom change address + カスタマイズされたチェンジアドレス + + + Transaction Fee: + トランザクション手数料: + + + Choose... + 選択… + + + Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until your have validated the complete chain. + フォールバックフィー(fallbackfee)を使用するとトランザクションを送信するのに数時間から数日以上(または永久的に) 時間がかかります。手数料を手動で設定するか完全なブロックチェーンをバリデートするまで待ってください。 + + + Warning: Fee estimation is currently not possible. + 警告: トランザクション手数料を推定する機能は現在利用できません。 + + + collapse fee-settings + 手数料設定を非表示 + + + Confirmation time target: + 検証時間ターゲット: + + + PrivateSend + プライベートセンド + + + InstantSend + インスタントセンド + + + If the custom fee is set to 1000 duffs and the transaction is only 250 bytes, then "per kilobyte" only pays 250 duffs in fee,<br />while "at least" pays 1000 duffs. For transactions bigger than a kilobyte both pay by kilobyte. + カスタマイズされた手数料が1000duffsに設定され、トランザクションが250 bytesしかなかった場合、キロバイトあたりでは250 duffsしか手数料を支払いませんが<br />最低でも1000 duffs支払います。1キロバイト以上のトランザクションに対しては、1キロバイトあたり1000duffs支払います。 + + + If the custom fee is set to 1000 duffs and the transaction is only 250 bytes, then "per kilobyte" only pays 250 duffs in fee,<br />while "total at least" pays 1000 duffs. For transactions bigger than a kilobyte both pay by kilobyte. + カスタマイズされた手数料が1000duffsに設定され、トランザクションが250 bytesしかなかった場合、キロバイトあたりでは250 duffsしか手数料を支払いませんが<br />最低でも合計1000 duffs支払います。1キロバイト以上のトランザクションに対しては、1キロバイトあたり1000duffs支払います。 + + + Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks.<br />But be aware that this can end up in a never confirming transaction once there is more demand for dash transactions than the network can process. + 最低限の手数料しか支払わなくても、トランザクション量以上のスペースが対象ブロックにあれば問題ありません。<br />しかし、ネットワークが処理できる以上のDashのトランザクション需要があれば、処理されないトランザクションになり得ることに注意してください。 + + + per kilobyte + 1キロバイトあたり手数料 + + + Hide + 非表示 + + + total at least + 合計最小手数料 + + + (read the tooltip) + (ツールチップをお読みください) + + + Recommended: + 推奨: + + + Custom: + カスタム: + + + (Smart fee not initialized yet. This usually takes a few blocks...) + (スマート手数料はまだ初期化されていません。これには約数ブロックほどかかります…) + + + normal + ノーマル + + + fast + 高速 + + + Confirm the send action + 送金確認 + + + S&end + 送金 (&E) + + + Clear all fields of the form. + 全ての入力項目をクリア + + + Clear &All + すべてクリア (&A) + + + Send to multiple recipients at once + 一度に複数のアドレスに送金 + + + Add &Recipient + 受取人を追加 (&R) + + + Balance: + 残高: + + + Copy quantity + 数量をコピー + + + Copy amount + 総額のコピー + + + Copy fee + 手数料をコピーす + + + Copy after fee + 手数料差引後の金額をコピー + + + Copy bytes + バイト数をコピー + + + Copy dust + ダストをコピー Copy change - 釣り銭をコピー + チェンジをコピー + + + using + 使用中 + + + anonymous funds + 匿名化されたファンド + + + (privatesend requires this amount to be rounded up to the nearest %1). + (プライベートセンドはこの数値を %1までラウンドアップすることを要求します) + + + any available funds (not anonymous) + 利用可能なファンド (匿名化されてない) + + + and InstantSend + /インスタントセンド + + + %1 to %2 + %1 から %2 + + + Are you sure you want to send? + 送ってよろしいですか? + + + are added as transaction fee + :トランザクション手数料として追加されます + + + Total Amount = <b>%1</b><br />= %2 + トータルの金額 = <b>%1</b><br />= %2 + + + <b>(%1 of %2 entries displayed)</b> + <b>(%1 /%2 項目を表示)</b> + + + Confirm send coins + 送金確認 + + + The recipient address is not valid. Please recheck. + 受取アドレスが正しくありません。再チェックしてください。 + + + The amount to pay must be larger than 0. + 支払い額は0より大きくないといけません。 + + + The amount exceeds your balance. + 総額が残高を超えています。 + + + The total exceeds your balance when the %1 transaction fee is included. + %1 の取引手数料を含めると総額が残高を超えています。 + + + Duplicate address found: addresses should only be used once each. + 重複したアドレスが見つかりました: アドレスは一度のみ使用することができます。 + + + Transaction creation failed! + トラザクションの作成に失敗しました! + + + The transaction was rejected with the following reason: %1 + トランザクションは以下の理由によりリジェクトされました: %1 + + + A fee higher than %1 is considered an absurdly high fee. + %1 よりも高い手数料の場合手数料が異常に高すぎると判断されます。 + + + Payment request expired. + 支払いリクエストの期限が切れました。 + + + %n block(s) + %n ブロック + + + Pay only the required fee of %1 + リクエストされた手数料 %1 のみを支払う + + + Estimated to begin confirmation within %n block(s). + %n ブロック以内に検証が開始されると予想されます + + + Warning: Invalid Dash address + 警告:Dashアドレスが無効です + + + Warning: Unknown change address + 警告:未知のチェンジアドレスです + + + Confirm custom change address + カスタムチェンジアドレスを確認 + + + The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure? + チェンジとして指定されたアドレスはこのウォレットに属していません。このウォレットの一部またはすべてのファンドがこのアドレスへ送金されます。よろしいですか? + + + (no label) + (ラベル無し) + + + + SendCoinsEntry + + This is a normal payment. + これは通常の支払いです。 + + + Pay &To: + 送り先: (&T) + + + The Dash address to send the payment to + 送金先Dashアドレス + + + Choose previously used address + 以前に使用したアドレスを選択 + + + Alt+A + Alt+A + + + Paste address from clipboard + クリップボードからアドレスをペースト + + + Alt+P + Alt+P + + + Remove this entry + この項目を削除 + + + &Label: + ラベル (&L) + + + Enter a label for this address to add it to the list of used addresses + このアドレスに対するラベルを入力することで使用済みアドレスの一覧に追加することができます + + + A&mount: + 金額: (&M) + + + The fee will be deducted from the amount being sent. The recipient will receive a lower amount of Dash than you enter in the amount field. If multiple recipients are selected, the fee is split equally. + 手数料は総額から差し引かれます。受取人は入力した金額より少ないDashを受け取ります。複数の受取人を選択した場合、手数料は等しく分割されます。 + + + S&ubtract fee from amount + 総額から手数料を差し引いて送金(&U) + + + Message: + メッセージ: + + + A message that was attached to the dash: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Dash network. + Dash: URIに添付されていたメッセージです。これは参照用としてトランザクションとともに保存されます。注意:このメッセージはDashネットワークを経由して送信されるわけではありません。 + + + This is an unauthenticated payment request. + これは未認証の支払いリクエストです。 + + + This is an authenticated payment request. + これは認証済みの支払いリクエストです。 + + + Pay To: + 支払い先: + + + Memo: + メモ: + + + Enter a label for this address to add it to your address book + アドレス帳に追加するにはこのアドレスのラベルを入力します + + + + SendConfirmationDialog + + Yes + はい + + + + ShutdownWindow + + %1 is shutting down... + %1 をシャットダウンしています... + + + Do not shut down the computer until this window disappears. + このウィンドウが消えるまでコンピュータをシャットダウンしないで下さい。 + + + + SignVerifyMessageDialog + + Signatures - Sign / Verify a Message + 署名 - メッセージの署名/検証 + + + &Sign Message + メッセージの署名 (&S) + + + You can sign messages/agreements with your addresses to prove you can receive Dash sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + あなたのアドレスを使用してあなたがDashを受け取ることができるようにメッセージやアグリーメントに署名できます。充分に詳細が判明していてあなたが確認しているものだけに署名してください。フィッシングによってあなたの署名を読み取られないように注意してください。 + + + The Dash address to sign the message with + メッセージを署名する際に使用するDashアドレス + + + Choose previously used address + 前に使用したアドレスを選ぶ + + + Alt+A + Alt+A + + + Paste address from clipboard + クリップボードからアドレスをペースト + + + Alt+P + Alt+P + + + Enter the message you want to sign here + ここにあなたが署名するメッセージを入力 + + + Signature + 署名 + + + Copy the current signature to the system clipboard + 現在の署名をシステムのクリップボードにコピー + + + Sign the message to prove you own this Dash address + この Dash アドレスを所有していることを証明するためにメッセージに署名 + + + Sign &Message + メッセージの署名 (&M) + + + Reset all sign message fields + 入力項目の内容をすべて消去 + + + Clear &All + すべてクリア (&A) + + + &Verify Message + メッセージの検証 (&V) + + + Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction! + 受取人のアドレスとメッセージ(改行やスペース、タブなども完全に一致するよう注意してください)および署名を以下に入力し、メッセージを検証してください。中間者攻撃により騙されるのを防ぐため、署名対象のメッセージに書かれていること以上の意味を署名から読み取ろうとしないよう注意してください。これは署名作成者がこのアドレスで受け取ることを証明するだけであり、トランザクションの送信者を証明するものではないことに注意してください! + + + The Dash address the message was signed with + メッセージの署名に使用されたDashアドレス + + + Verify the message to ensure it was signed with the specified Dash address + 指定されたDashアドレスで署名されたことを確認するためメッセージを検証 + + + Verify &Message + メッセージの検証 (&M) + + + Reset all verify message fields + 入力項目の内容をすべて消去 + + + Click "Sign Message" to generate signature + 署名を作成するには"メッセージの署名"をクリックしてください + + + The entered address is invalid. + 無効なアドレスが入力されました。 + + + Please check the address and try again. + アドレスを確かめてからもう一度試してください。 + + + The entered address does not refer to a key. + 入力されたアドレスに関連するキーがありません。 + + + Wallet unlock was cancelled. + ウォレットのアンロックはキャンセルされました。 + + + Private key for the entered address is not available. + 入力されたアドレスのプライベートキーは利用できません。 + + + Message signing failed. + メッセージの署名に失敗しました。 + + + Message signed. + メッセージに署名しました。 + + + The signature could not be decoded. + 署名はデコードできません。 + + + Please check the signature and try again. + 署名を確認してからもう一度試してください。 + + + The signature did not match the message digest. + 署名はメッセージダイジェストと一致しませんでした。 + + + Message verification failed. + メッセージの検証に失敗しました。 + + + Message verified. + メッセージは検証されました。 + + + + SplashScreen + + [testnet] + [testnet] + + + Version %1 + バージョン %1 + + + + TrafficGraphWidget + + KB/s + KB/s + + + + TransactionDesc + + Open for %n more block(s) + %n 以上のブロックをオープン + + + Open until %1 + %1 までオープン + + + conflicted + コンフリクト発生 + + + %1/offline + %1/オフライン + + + 0/unconfirmed, %1 + 0/未検証, %1 + + + in memory pool + メモリプール内 + + + not in memory pool + メモリプール外 + + + abandoned + 中止 + + + %1/unconfirmed + %1/未検証 + + + %1 confirmations + %1 確認 + + + verified via InstantSend + インスタントセンド経由で検証済み + + + InstantSend verification in progress - %1 of %2 signatures + インスタントセンドによる検証の進捗 - %1 /%2 署名 + + + InstantSend verification failed + インスタントセンドによる検証失敗 + + + Status + ステータス + + + , has not been successfully broadcast yet + ブロードキャストがまだ成功していません + + + , broadcast through %n node(s) + %n ノードにブロードキャスト + + + Date + 日付 + + + Source + ソース + + + Generated + 生成済 + + + From + 送信元 + + + unknown + 未確認 + + + To + 送信先 + + + own address + 自分のアドレス + + + watch-only + 監視のみ + + + label + ラベル + + + Credit + クレジット + + + matures in %n more block(s) + %n 以上のブロックがマチュア + + + not accepted + 未承認 + + + Debit + 引き落とし額 + + + Total debit + 総出金額 + + + Total credit + 総入金額 + + + Transaction fee + トランザクション手数料 + + + Net amount + 正味金額 + + + Message + メッセージ + + + Comment + コメント + + + Transaction ID + トランザクションID + + + Output index + 出力インデックス + + + Transaction total size + トランザクションの全体サイズ + + + Merchant + マーチャント + + + Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + 生成されたコインは使う前に%1のブロックを完成させる必要があります。あなたがこのブロックを生成した時、このブロックはブロックチェーンに追加されるためにネットワークにブロードキャストされました。ブロックチェーンの追加に失敗した場合、状態が"不承認"に変更されて生成されたコインは使えなくなります。これは別のノードがあなたの数秒前にブロックを生成する場合に時々起こります。 + + + Debug information + デバッグ情報 + + + Transaction + トランザクション + + + Inputs + インプット + + + Amount + 総額 + + + true + 正しい + + + false + 正しくない + + + + TransactionDescDialog + + This pane shows a detailed description of the transaction + ここではトランザクションの詳細を表示しています + + + Details for %1 + %1 の詳細 + + + + TransactionTableModel + + Date + 日付 + + + Type + タイプ + + + Address / Label + アドレス / ラベル + + + Open for %n more block(s) + %n 以上のブロックをオープン + + + Open until %1 + %1 までオープン + + + Offline + オフライン + + + Unconfirmed + 未検証 + + + Abandoned + 中止 + + + Confirming (%1 of %2 recommended confirmations) + 検証中(%2の推奨検証数のうち、%1検証が完了) + + + Confirmed (%1 confirmations) + 検証されました (%1 検証済み) + + + Conflicted + コンフリクト発生 + + + Immature (%1 confirmations, will be available after %2) + マチュアでない状態(%1検証。%2検証完了後に使用可能となります) + + + This block was not received by any other nodes and will probably not be accepted! + このブロックは他のどのノードにも受け取られてないで、今後も受け取られないでしょう! + + + Generated but not accepted + 生成されましたが承認されませんでした + + + Received with + 受信元 + + + Received from + 送信元 + + + Received via PrivateSend + プライベートセンド経由で受け取り + + + Sent to + 送金先 + + + Payment to yourself + 自分自身への支払い + + + Mined + 発掘済 + + + PrivateSend Denominate + プライベートセンド:分割開始 + + + PrivateSend Collateral Payment + プライベートセンド:コラテラル支払い + + + PrivateSend Make Collateral Inputs + プライベートセンド:コラテラル入力 + + + PrivateSend Create Denominations + プライベートセンド:デノミネーション生成 + + + PrivateSend + プライベートセンド + + + watch-only + ウォッチオンリー + + + (n/a) + (n/a) + + + (no label) + (ラベル無し) + + + Transaction status. Hover over this field to show number of confirmations. + トランザクションステータス。このフィールドの上にカーソルを置くと検証数を表示します。 + + + Date and time that the transaction was received. + トランザクションを受信した日時 + + + Type of transaction. + トランザクションの種類 + + + Whether or not a watch-only address is involved in this transaction. + ウォッチオンリーのアドレスがこのトランザクションに含まれているかどうか + + + User-defined intent/purpose of the transaction. + ユーザー定義のトランザクションの意図/目的 + + + Amount removed from or added to balance. + 残高に追加または削除された総額 + + + + TransactionView + + All + すべて + + + Today + 今日 + + + This week + 今週 + + + This month + 今月 + + + Last month + 先月 + + + This year + 今年 + + + Range... + 期間… + + + Most Common + 通常 + + + Received with + 受信元 + + + Sent to + 送金先 + + + PrivateSend + プライベートセンド + + + PrivateSend Make Collateral Inputs + プライベートセンド:コラテラル入力 + + + PrivateSend Create Denominations + プライベートセンド:デノミネーション作成 + + + PrivateSend Denominate + プライベートセンド:分割開始 + + + PrivateSend Collateral Payment + プライベートセンド:コラテラル支払い + + + To yourself + 自分自身へ + + + Mined + 採掘済 + + + Other + その他 + + + Enter address or label to search + 検索するアドレスまたはラベルを入力 + + + Min amount + 最小の額 + + + Abandon transaction + トランザクション中止 + + + Copy address + アドレスをコピー + + + Copy label + ラベルをコピー + + + Copy amount + 総額のコピー + + + Copy transaction ID + トランザクションIDをコピー + + + Copy raw transaction + 原トランザクションをコピー + + + Copy full transaction details + トランザクションの詳細すべてをコピー + + + Edit label + ラベルの編集 + + + Show transaction details + トランザクションの詳細を表示 + + + Export Transaction History + トランザクション履歴をエクスポート + + + Comma separated file (*.csv) + CSVファイル (*.csv) + + + Confirmed + 検証済 + + + Watch-only + ウォッチオンリー + + + Date + 日付 + + + Type + タイプ + + + Label + ラベル - %1 to %2 - %1 から %2 + Address + アドレス - Are you sure you want to send? - 送ってよろしいですか? + ID + ID - Confirm send coins - コインを送る確認 + Exporting Failed + エクスポート失敗 - - Estimated to begin confirmation within %n block(s). - %n ブロック以内に検証が開始されると予想されます。 + + There was an error trying to save the transaction history to %1. + トランザクション履歴を %1 へ保存する際にエラーが発生しました - The amount to pay must be larger than 0. - 支払額は0より大きくないといけません。 + Exporting Successful + エクスポート成功 - The amount exceeds your balance. - 額が残高を超えています。 + The transaction history was successfully saved to %1. + トランザクション履歴は正常に%1に保存されました - The total exceeds your balance when the %1 transaction fee is included. - %1 の取引手数料を含めると額が残高を超えています。 + Range: + 期間: - Transaction creation failed! - トラザクションの作成に失敗しました! + to + から + + + + UnitDisplayStatusBarControl + + Unit to show amounts in. Click to select another unit. + 金額を表示する際の単位。クリックすることで他の単位を選択します。 + + + WalletFrame - The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here. - トランザクションは拒否されました。wallet.dat のコピーを使い、そしてコピーしたウォレットからコインを使用したことがマークされなかったときなど、ウォレットのいくつかのコインがすでに使用されている場合に、このエラーは起こるかもしれません。 + No wallet has been loaded. + ウォレットがロードされていません + + + WalletModel - Pay only the minimum fee of %1 - 最小手数料 %1 のみを支払う + Send Coins + 送金 - Warning: Unknown change address - 警告:未知のおつりアドレスです + InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH. + インスタントセンドには現在送金できる上限があり、トランザクションは %1 DASHに限定されています。 - (no label) - (ラベル無し) + Used way too many inputs (>%1) for this InstantSend transaction, fees could be huge. + このインスタントセンドに非常に多くのインプット (>%1)を使用しているため、手数料が高額になる可能性があります。 - SendCoinsEntry + WalletView - This is a normal payment. - これは通常の支払です。 + &Export + エクスポート (&E) - Pay &To: - 送り先 (&T): + Export the data in the current tab to a file + ファイルに現在のタブのデータをエクスポート - Choose previously used address - 前に使用したアドレスを選ぶ + Selected amount: + 選択した額: + + + Backup Wallet + ウォレットのバックアップ + + + Wallet Data (*.dat) + ウォレットデータ (*.dat) + + + Backup Failed + バックアップ失敗 + + + There was an error trying to save the wallet data to %1. + ウォレットデータを%1へ保存する際にエラーが発生しました + + + Backup Successful + バックアップ成功 + + + The wallet data was successfully saved to %1. + ウォレットデータは正常に%1に保存されました + + + + dash-core + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + 指定のアドレスへバインドし、その上で常にリッスンします。IPv6 は [host]:port 番号と表記します + + + Error: Listening for incoming connections failed (listen returned error %s) + エラー: 受信中の接続をリッスンするのに失敗しました (エラー %s ) + + + Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message) + 関連のアラートを受け取るか非常に長いのフォークを確認した場合コマンドを実行 (コマンド中の%sはメッセージに置換される) + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + ウォレットのトランザクションを変更する際にコマンドを実行 (cmd の %s は TxID に置換される) + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + 最良のブロックが変更する際にコマンドを実行 (cmd の %s はブロックハッシュに置換される) + + + Name to construct url for KeePass entry that stores the wallet passphrase + ウォレットのパスフレーズを保管するKeePass入力のurlを作成 + + + Set maximum size of high-priority/low-fee transactions in bytes (default: %d) + 最優先/最低トランザクション手数料の最大サイズをバイトで指定 (初期設定: %d) + + + Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d) + スクリプト検証スレッドを設定 (%uから%dの間, 0 = 自動, <0 = たくさんのコアを自由にしておく, 初期設定: %d) + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + これはリリース前のテストビルドです - 各自の責任で利用すること - 採掘や商取引に使用しないでください + + + Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues. + 警告: ネットワークは完全に同意しないようです。マイナーは何らかの問題を経験しているようです。 + + + Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade. + 警告: ピアと完全に同意しないようです!あなたはアップグレードする必要があるかもしれません。または他のノードがアップグレードする必要があるかもしれません。 + + + You must specify a masternodeprivkey in the configuration. Please see documentation for help. + masternodeprivkeyを設定で指定する必要があります。ドキュメントを参照してください。 + + + Accept command line and JSON-RPC commands + コマンドラインと JSON-RPC コマンドを許可 + + + Add a node to connect to and attempt to keep the connection open + 接続するノードを追加し接続を持続させます + + + Allow DNS lookups for -addnode, -seednode and -connect + -addnode, -seednode と -connect で DNS ルックアップを許可 + + + Already have that input. + 常時そのインプットを保持 + + + Block creation options: + ブロック作成オプション: + + + Cannot downgrade wallet + ウォレットのダウングレードはできません + + + Cannot write default address + 初期値のアドレスを書き込むことができません + + + Collateral not valid. + コラテラルが無効です + + + Connect to a node to retrieve peer addresses, and disconnect + ピアアドレスを取得するためにノードに接続し、そして切断します + + + Connection options: + 接続オプション: + + + Corrupted block database detected + 破損したブロックデータベースが検知されました + + + Debugging/Testing options: + デバッグ/テスト用オプション: + + + Do not load the wallet and disable wallet RPC calls + ウォレットは読み込まず、ウォレットRPCコールを無効化する + + + Do you want to rebuild the block database now? + ブロックデータベースを今すぐ再構築しますか? + + + Done loading + 読み込み完了 + + + Entries are full. + エントリーがいっぱいです + + + Error initializing block database + ブロックデータベースの初期化中にエラー + + + Error initializing wallet database environment %s! + ウォレットのデータベース環境 %s 初期化エラー! + + + Error loading block database + ブロックデータベースの読み込みエラー + + + Error opening block database + ブロックデータベースの開始エラー + + + Error reading from database, shutting down. + データベースの読み込みエラー。シャットダウンします。 + + + Error + エラー + + + Error: Disk space is low! + エラー: ディスク容量不足! + + + Failed to listen on any port. Use -listen=0 if you want this. + ポートのリッスンに失敗しました。必要であれば -listen=0 を使用してください。 + + + (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data) + (1 = トランザクションのメタデータ、例えばアカウントの所有者や支払いリクエストの内容を保持する, 2 = トランザクションのメタデータを破棄する) + + + -maxtxfee is set very high! Fees this large could be paid on a single transaction. + -maxtxfee が非常に高く設定されています!ひとつのトランザクションでこの量の手数料が支払われてしまうことがあります。 + + + A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s) + 十分なデータが蓄積されていない場合に手数料推定機能が利用する手数料レート (%s/kB) (初期設定: %s) + + + Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d) + トランザクションの中継を行っていない場合でも、ホワイトリストのピアから受け取った中継トランザクションは受け取るようにする (初期設定: %d) + + + Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times + 指定したアクセス元からのJSON-RPC接続を許可する。有効な<ip>は、単一のIP (例 1.2.3.4)、ネットワーク/ネットマスク (例 1.2.3.4/255.255.255.0)、またはネットワーク/CIDR (例 1.2.3.4/24)です。このオプションは複数回指定できます。 + + + Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6 + 指定されたアドレスおよび、そこに接続を行ってきたホワイトリストのピアに対してバインドを行います。IPv6の場合には [host]:port 表記を使用してください + + + Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces) + 指定されたアドレスに対して JSON-RPC 接続をリッスンしするようバインドします。IPv6の場合には [host]:port 表記を使用してください。このオプションは複数回指定することが可能です (初期設定:すべてのインターフェースに対してバインドする) + + + Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality) + umask 077 ではなく、システムのデフォルトパーミッションで新規ファイルを作成する (ウォレット機能が無効化されていた場合にのみ有効) + + + Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup + ウォレットの全トランザクションを削除し、これらを-rescanオプションを用いることで起動時にブロックチェインのデータのみからリカバリします。 + + + Error loading %s: You can't enable HD on a already existing non-HD wallet + %s読み込みエラー: 既存の非HDウォレット上でHDを有効化できません + + + Found unconfirmed denominated outputs, will wait till they confirm to continue. + 未確認の分割されたアウトプットを検出しました。継続を確認するまで待機します。 + + + How thorough the block verification of -checkblocks is (0-4, default: %u) + -checkblocks のブロックの検証レベル (0-4, 初期設定: %u) + + + If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u) + paytxfee が設定されていなかった場合、平均して n ブロック以内にトランザクションが検証され始めるのに十分な手数料を含める (初期設定: %u) + + + 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: %s, testnet: %s) + このブロックがブロックチェーン内に含まれていた場合には、このブロックおよびそれ以前のすべてのブロックを有効であるとみなし、スクリプトの検証を省略する (0ならすべてを検証、初期設定: %s、テストネット: %s) + + + Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions) + -maxtxfee=<amount> の数量の指定が無効です: '%s' (トランザクションが詰まってしまうのを防ぐため、少なくとも %s の最小中継手数料を指定しなければいけません) + + + Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u) + getrawtransaction rpc 呼び出し時に用いる、完全なトランザクションインデックスを保持する (初期設定: %u) + + + Make sure to encrypt your wallet and delete all non-encrypted backups after you verified that wallet works! + ウォレットが機能することを検証したら、あなたのウォレットを暗号化しすべての非暗号化バックアップを削除してください。 + + + Maximum size of data in data carrier transactions we relay and mine (default: %u) + 中継およびマイニングを行う際の、データ運送トランザクションの中のデータの最大サイズ (初期設定: %u) + + + Number of seconds to keep misbehaving peers from reconnecting (default: %u) + 不正なピアを再接続するまでの秒数 (初期設定: %u) + + + Output debugging information (default: %u, supplying <category> is optional) + デバッグ情報を出力する (初期設定: %u, <category> の指定は任意です) + + + Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s) + Tor 秘匿サービスを経由して別々の SOCKS5 プロキシを用いることでピアに到達する (初期設定: %s) - Alt+A - Alt+A + User defined mnemonic for HD wallet (bip39). Only has effect during wallet creation/first start (default: randomly generated) + HDウォレット用のユーザー定義ニーモニック。ウォレット生成時にしか有効でない(初期設定:ランダムに生成) - Paste address from clipboard - クリップボードからアドレスを貼付ける + User defined seed for HD wallet (should be in hex). Only has effect during wallet creation/first start (default: randomly generated) + HDウォレット用のユーザー定義シード。ウォレット生成時にしか有効でない(初期設定:ランダムに生成) - Alt+P - Alt+P + Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway + ホワイトリストのピアはDoSによるアクセス禁止処理が無効化され、トランザクションは例えmempool内に既に存在していたとしても常にリレーされます。これは例えばゲートウェイに対して有用です - Remove this entry - この項目を削除する + You need to rebuild the database using -reindex-chainstate to change -txindex + -txindexを変更するために -reindex-chainstate を使用してデータベースをリビルドする必要があります。 - &Label: - ラベル (&L): + (default: %s) + (初期設定: %s) - Enter a label for this address to add it to the list of used addresses - このアドレスに対するラベルを入力することで、使用済みアドレスの一覧に追加することができます + Accept public REST requests (default: %u) + 公開 REST リクエストを許可する (初期設定: %u) - A&mount: - 金額 (&A): + Always query for peer addresses via DNS lookup (default: %u) + DNS ルックアップを通してピアアドレスを常に問い合わせる (初期設定: %u) - Message: - メッセージ: + Can't mix: no compatible inputs found! + ミキシング不可: 互換性のあるインプットが見つかりません - Pay To: - 支払先: + Chain selection options: + チェーン選択オプション: - Memo: - メモ: + Connect through SOCKS5 proxy + SOCKS5 プロキシ経由で接続 - Enter a label for this address to add it to your address book - アドレス帳に追加するには、このアドレスのラベルを入力します + Connect to KeePassHttp on port <port> (default: %u) + Connect to KeePassHttp に接続:ポート <port> (初期設定: %u) - - - ShutdownWindow - Do not shut down the computer until this window disappears. - このウィンドウが消えるまでコンピュータをシャットダウンしないで下さい。 + Enable the client to act as a masternode (0-1, default: %u) + クライアントに対してマスターノードとしての機能を有効化する (0-1, 初期設定: %u) - - - SignVerifyMessageDialog - Signatures - Sign / Verify a Message - 署名 - メッセージの署名/検証 + Entry exceeds maximum size. + エントリーが最大サイズを超えました。 - &Sign Message - メッセージの署名 (&S) + Error loading %s: You can't disable HD on a already existing HD wallet + %s読み込みエラー: 既存のHDウォレット上のHDを無効化できません - Choose previously used address - 前に使用したアドレスを選ぶ + Failed to load fulfilled requests cache from + リクエストキャッシュの読み込み失敗: - Alt+A - Alt+A + Failed to load governance cache from + ガバナンスキャッシュの読み込み失敗: - Paste address from clipboard - クリップボードからアドレスを貼付ける + Failed to load masternode cache from + マスターノードキャッシュの読み込み失敗: - Alt+P - Alt+P + Failed to load masternode payments cache from + マスターノードペイメントキャッシュの読み込み失敗: - Enter the message you want to sign here - ここにあなたが署名するメッセージを入力します + Found enough users, signing ( waiting %s ) + 充分なユーザーを発見しました、サインしています ( 待機中 %s ) - Signature - 署名 + Found enough users, signing ... + 充分なユーザーを発見しました、サインしています - Copy the current signature to the system clipboard - 現在の署名をシステムのクリップボードにコピーする + How many blocks to check at startup (default: %u, 0 = all) + 起動時に点検するブロック数 (初期設定: %u, 0=すべて) - Sign &Message - メッセージの署名 (&M) + Importing... + インポートしています… - Reset all sign message fields - 入力項目の内容をすべて消去します + Include IP addresses in debug output (default: %u) + デバッグ出力にIPアドレスを含める (初期設定: %u) - Clear &All - すべてクリア (&A) + Incompatible mode. + 非互換性モード - &Verify Message - メッセージの検証 (&V) + Incompatible version. + 非互換性バージョン - Verify &Message - メッセージの検証 (&M) + Incorrect or no genesis block found. Wrong datadir for network? + 不正なブロックが発見されたか、またはジェネシスブロックが発見されませんでした。ネットワークの datadir が間違っていませんか? - Reset all verify message fields - 入力項目の内容をすべて消去します + Information + インフォメーション - Click "Sign Message" to generate signature - 署名を作成するには"メッセージの署名"をクリック + Input is not valid. + インプットが無効です - The entered address is invalid. - 不正なアドレスが入力されました。 + Insufficient funds. + 残高が不足しています - Please check the address and try again. - アドレスを確かめてからもう一度試してください。 + Invalid -onion address: '%s' + 無効な -onion アドレス:'%s' - The entered address does not refer to a key. - 入力されたアドレスに関連するキーがありません。 + Invalid -proxy address: '%s' + 無効な -proxy アドレス: '%s' - Wallet unlock was cancelled. - ウォレットのアンロックはキャンセルされました。 + Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s) + -paytxfee=<amount> に対する無効な数量です: '%s' (少なくとも %s でなければいけません) - Private key for the entered address is not available. - 入力されたアドレスのプライベート キーが無効です。 + Keypool ran out, please call keypoolrefill first + キープールが枯渇しました。まずはじめに keypoolrefill を呼び出してください。 - Message signing failed. - メッセージの署名に失敗しました。 + Listen for JSON-RPC connections on <port> (default: %u or testnet: %u) + <port> で JSON-RPC 接続をリッスン (初期設定: %u、テストネット: %u) - Message signed. - メッセージに署名しました。 + Listen for connections on <port> (default: %u or testnet: %u) + 接続のリッスンを <port> で行う (初期設定: %u、テストネット: %u) - The signature could not be decoded. - 署名がデコードできません。 + Loading banlist... + banリストを読み込んでいます... - Please check the signature and try again. - 署名を確認してからもう一度試してください。 + Loading fulfilled requests cache... + 承認されたリクエストキャッシュを読み込んでいます... - The signature did not match the message digest. - 署名はメッセージ ダイジェストと一致しませんでした。 + Loading masternode cache... + マスターノードキャッシュを読み込んでいます... - Message verification failed. - メッセージの検証に失敗しました。 + Loading masternode payment cache... + マスターノードペイメントキャッシュを読み込んでいます... - Message verified. - メッセージは検証されました。 + Lock is already in place. + すでにロックされています - - - SplashScreen - The Bitcoin Core developers - ビットコインコアの開発者 + Lock masternodes from masternode configuration file (default: %u) + マスターノード設定ファイルからマスターノードをロック(初期設定: %u) - [testnet] - [testnet] + Maximum per-connection receive buffer, <n>*1000 bytes (default: %u) + 接続毎の最大受信バッファ <n>*1000 バイト (初期設定: %u) - - - TrafficGraphWidget - KB/s - KB/s + Maximum per-connection send buffer, <n>*1000 bytes (default: %u) + 接続毎の最大送信バッファ <n>*1000 バイト (初期設定: %u) - - - TransactionDesc - - Open for %n more block(s) - %n 以上のブロックを開く + + Mixing in progress... + ミキシング中... - Open until %1 - ユニット %1 を開く + Mnemonic passphrase is too long, must be at most 256 characters + ニーモニックパスフレーズが長すぎます。最長で256文字です。 - conflicted - 衝突 + Need to specify a port with -whitebind: '%s' + -whitebind を用いてポートを指定する必要があります: '%s' - %1/offline - %1/オフライン + No Masternodes detected. + マスターノードが検出されません - %1/unconfirmed - %1/未検証 + No compatible Masternode found. + 互換性のあるマスターノードが検出されません - %1 confirmations - %1 確認 + Not in the Masternode list. + マスターノードリストにありません - Status - ステータス + Only connect to nodes in network <net> (ipv4, ipv6 or onion) + <net> (ipv4, ipv6 または onion) ネットワーク内のノードだけに接続する - , has not been successfully broadcast yet - まだブロードキャストが成功していません + Port: %d + ポート: %d - - , broadcast through %n node(s) - %n ノードにブロードキャスト + + Prepend debug output with timestamp (default: %u) + デバッグ出力にタイムスタンプを付ける (初期設定: %u) - Date - 日付 + Rebuild chain state and block index from the blk*.dat files on disk + チェーン状態およびブロックインデックスをディスク上の blk*.dat ファイルから再構築する - Source - ソース + Rebuild chain state from the currently indexed blocks + 既にインデックスされたブロックからチェイン状態を再構築する - Generated - 生成された + Send trace/debug info to debug.log file (default: %u) + トレース/デバッグ情報を debug.logファイルに送信 (初期設定: %u) - From - 送信 + Send transactions as zero-fee transactions if possible (default: %u) + 可能な場合には手数料ゼロのトランザクションとしてトランザクションを送信する (初期設定: %u) - unknown - 未確認 + Set key pool size to <n> (default: %u) + key pool のサイズを <n> (初期設定: %u) にセット - To - 宛先 + Set the number of threads to service RPC calls (default: %d) + RPC 呼び出しのスレッド数を設定 (初期設定: %d) - own address - 自分のアドレス + Specify configuration file (default: %s) + 設定ファイルの指定 (初期設定: %s) - watch-only - 監視限定 + Specify connection timeout in milliseconds (minimum: 1, default: %d) + 接続のタイムアウトをミリ秒単位で指定 (最小値: 1, 初期設定:: %d) - label - ラベル + Specify masternode configuration file (default: %s) + マスターノード設定ファイルを指定 (初期設定: %s) - Credit - クレジット + Specify pid file (default: %s) + pid ファイルの指定 (初期設定: %s) - - matures in %n more block(s) - %n 以上のブロックが満期 + + Spend unconfirmed change when sending transactions (default: %u) + トランザクション送信時に未検証のチェンジを使用する (初期設定: %u) - not accepted - 承認されなかった + Submitted following entries to masternode: %u / %d + 以下のエントリーをマスターノードにサブミット: %u / %d - Debit - 引き落とし額 + Submitted to masternode, waiting for more entries ( %u / %d ) %s + マスターノードにサブミット完了、追加のエントリーのために待機中 ( %u / %d ) %s - Total debit - 総出金額 + Submitted to masternode, waiting in queue %s + マスターノードにサブミット、待機中 %s - Total credit - 総入金額 + Synchroning blockchain... + ブロックに同期中... - Transaction fee - 取引手数料 + Synchronization failed + 同期失敗 - Net amount - 正味金額 + Synchronization finished + 同期完了 - Message - メッセージ + Synchronizing masternodes... + マスターノード同期中... - Comment - コメント + This is not a Masternode. + これはマスターノードではありません - Transaction ID - 取引 ID + Threshold for disconnecting misbehaving peers (default: %u) + 不正なピアを切断するためのしきい値 (初期設定: %u) - Merchant - 商人 + Too many %f denominations, removing. + 異常に多くの %f デノミネーション、削除中 - Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. - 生成されたコインは使う前に%1のブロックを完成させる必要があります。あなたが生成した時、このブロックはブロック チェーンに追加されるネットワークにブロードキャストされました。チェーンに追加されるのが失敗した場合、状態が"不承認"に変更されて使えなくなるでしょう。これは、別のノードがあなたの数秒前にブロックを生成する場合に時々起こるかもしれません。 + Tor control port password (default: empty) + Tor管理ポートのパスワード (初期値: 空文字) - Debug information - デバッグ情報 + Tor control port to use if onion listening enabled (default: %s) + Onion のリッスンが有効になっている場合に使用するTor管理ポート (初期値: %s) - Transaction - 取引 + Unable to start HTTP server. See debug log for details. + HTTPサーバを開始できませんでした。詳細はデバッグログをご確認ください。 - Inputs - 入力 + Unknown response. + 不明なレスポンス - Amount - 総額 + Unsupported argument -benchmark ignored, use -debug=bench. + サポートされていない引数 -benchmark は無視されました。-debug=bench を使用してください。 - true - 正しい + Unsupported argument -debugnet ignored, use -debug=net. + サポートされていない引数 -debugnet は無視されました。-debug=net を使用してください。 - false - 正しくない + Unsupported argument -tor found, use -onion. + サポートされていない引数 -tor が見つかりました。-onion を使用してください。 - - - TransactionDescDialog - Transaction details - 取引の詳細 + Upgrade wallet to latest format on startup + 起動時にウォレットを最新のフォーマットにアップグレード - This pane shows a detailed description of the transaction - ここでは取引の詳細を表示しています + Use KeePass 2 integration using KeePassHttp plugin (default: %u) + KeePassHttp プラグインを使用してKeePass 2 インテグレーションを使用 (初期設定: %u) - - - TransactionTableModel - Date - 日付 + Use UPnP to map the listening port (default: %u) + リッスンポートの割当に UPnP を使用 (初期設定: %u) - Type - タイプ + Use the test chain + テストチェーンを使用 - Address - アドレス + User Agent comment (%s) contains unsafe characters. + ユーザーエージェントのコメント (%s) には安全でない文字が含まれています。 - - Open for %n more block(s) - %n 以上のブロックを開く + + Will retry... + 再試行... - Open until %1 - ユニット %1 を開く + Invalid masternodeprivkey. Please see documenation. + 無効な masternodeprivkey、ドキュメントを参照してください。 - Offline - オフライン + Can't find random Masternode. + ランダムなマスターノードを発見できません - Unconfirmed - 未検証 + Can't mix while sync in progress. + 同期中はミキシングできません - Confirming (%1 of %2 recommended confirmations) - 検証中(%2の推奨検証数のうち、%1検証が完了) + Could not parse masternode.conf + masternode.confをパースできません - Confirmed (%1 confirmations) - 検証されました (%1 検証済み) + Invalid netmask specified in -whitelist: '%s' + -whitelist で指定された無効なネットマスク: '%s' - Conflicted - 衝突 + Invalid port detected in masternode.conf + 無効なポートがmasternode.confで検出されました - Immature (%1 confirmations, will be available after %2) - 未成熟(%1検証。%2検証完了後に使用可能となります) + Invalid script detected. + 無効なスクリプトが検出されました - This block was not received by any other nodes and will probably not be accepted! - このブロックは他のどのノードによっても受け取られないで、多分受け入れられないでしょう! + KeePassHttp id for the established association + 確立された接続用のKeePassHttp ID - Generated but not accepted - 生成されましたが承認されませんでした + KeePassHttp key for AES encrypted communication with KeePass + KeePassとの AES 暗号化接続用のKeePassHttp キー - Received with - 受信元 + Keep at most <n> unconnectable transactions in memory (default: %u) + 最大で <n> 個の孤立したトランザクションをメモリの中に保持する (初期設定: %u) - Received from - 送り主 + Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) + Dashに固有の機能(マスターノード, プライベートセンド, インスタントセンド, ガバナンス) を無効化(0-1, 初期設定: %u) - Sent to - 送り先 + %s file contains all private keys from this wallet. Do not share it with anyone! + %s ファイルはこのウォレットのプライベートキーをすべて保有しています。誰ともシェアしないでください。 - Payment to yourself - 自分自身への支払い + Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) + 自分のIPアドレスを解決する (初期値: リッスンをしており、-externalipまたは-proxyオプションが指定されていない場合は1) - Mined - 発掘した + Do not keep transactions in the mempool longer than <n> hours (default: %u) + mempool内でトランザクションを <n> 時間以上保持しない (初期値: %u) - watch-only - 監視限定 + Enable InstantSend, show confirmations for locked transactions (0-1, default: %u) + インスタントセンドを有効化しロックされたトランザクションのコンファメーションを表示 (0-1, 初期設定: %u) - (n/a) - (n/a) + Enable multiple PrivateSend mixing sessions per block, experimental (0-1, default: %u) + ブロック毎に複数のミキシングセッションを有効化する、実験的 (0-1, 初期値: %u) - Transaction status. Hover over this field to show number of confirmations. - 取引の状況。このフィールドの上にカーソルを置くと検証の数を表示します。 + Enable use of automated PrivateSend for funds stored in this wallet (0-1, default: %u) + このウォレットにあるファンドに対して自動的なプライベートセンドの使用を有効化 (0-1, 初期設定: %u) - Date and time that the transaction was received. - 取引を受信した日時。 + Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID) + インスタントセンドのトランザクションのロックに成功したとき、コマンドを実行 (コマンドの%sは TxIDに置換) - Type of transaction. - 取引の種類。 + Extra transactions to keep in memory for compact block reconstructions (default: %u) + コンパクトなブロック再構築のために追加のトランザクションをメモリーに保管 (初期設定: %u) - Whether or not a watch-only address is involved in this transaction. - 監視限定アドレスがこのトランザクションに含まれているかどうか + Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. + バックアップ作成失敗、ファイルは既に存在します!これはウォレットを60秒以内にリスタートしたときに発生することがあります。これで問題なければ先に進むことができます。 - Amount removed from or added to balance. - 残高に追加または削除された総額。 + Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s) + トランザクションの中継、採掘および作成の際には、この値未満の手数料 (%s/kB単位) はゼロであるとみなす (初期設定: %s) - - - TransactionView - All - すべて + Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s) + トランザクション作成の際、この値未満の手数料 (%s/kB単位) はゼロであるとみなす (デフォルト: %s) - Today - 今日 + If <category> is not supplied or if <category> = 1, output all debugging information. + <category> が与えられなかった場合や <category> = 1 の場合には、すべてのデバッグ情報が出力されます。 - This week - 今週 + InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH. + インスタントセンドはこのような高額の送金をサポートしていません。トランザクションは現在%1 DASHに限定されています。 - This month - 今月 + InstantSend requires inputs with at least %d confirmations, you might need to wait a few minutes and try again. + インスタントセンドは最低%d コンファメーションのインプットを必要とします。数分間待ってから再試行する必要があるかもしれません - Last month - 先月 + Maintain a full address index, used to query for the balance, txids and unspent outputs for addresses (default: %u) + 残高、txids、未使用のアウトプットへの問い合わせに使用される、フルアドレスインデックスを保持 (初期設定: %u) - This year - 今年 + Maintain a full spent index, used to query the spending txid and input index for an outpoint (default: %u) + txidとインプットインデックスの問い合わせに使用される、フルスペントインデックス (初期設定: %u) - Range... - 期間… + Maintain a timestamp index for block hashes, used to query blocks hashes by a range of timestamps (default: %u) + ブロックハッシュの問い合わせに使用される、ブロックハッシュ用のタイムスタンプインデックスを保持(初期設定: %u) - Received with - 受信元 + Maintain at most <n> connections to peers (temporary service connections excluded) (default: %u) + 最大 <n> 接続を保持 (一時的な接続を除外) (初期設定: %u) - Sent to - 送り先 + PrivateSend uses exact denominated amounts to send funds, you might simply need to anonymize some more coins. + プライベートセンドは正確に分割された額を使用して送金します。あなたはさらに匿名化する必要があるかもしれません。 - To yourself - 自分自身 + Prune configured below the minimum of %d MiB. Please use a higher number. + 剪定が最小値の %d MiB以下に設定されています。もっと大きな値を使用してください。 - Mined - 発掘した + Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node) + 剪定: 最後のウォレット同期ポイントは、剪定されたデータよりも過去のものとなっています。-reindexをする必要があります (剪定されたノードの場合、ブロックチェイン全体をダウンロードしてください) - Other - その他 + Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u) + 認証情報をプロキシー接続ごとにランダム化する。これによりTorストリーム分離をすることができます (初期設定: %u) - Enter address or label to search - 検索するアドレスまたはラベルを入力 + Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again. + 剪定モードでは再スキャンを行うことはできません。-reindexを使用し、ブロックチェーン全体を再ダウンロードする必要があります。 + + + Specify full path to directory for automatic wallet backups (must exist) + 自動ウォレットバックアップ用のディレクトリへのフルパスを指定してください - Min amount - 最小の額 + Support filtering of blocks and transaction with bloom filters (default: %u) + Bloomフィルタによる、ブロックおよびトランザクションのフィルタリングをサポート (初期値: %u) - Copy address - アドレスをコピーする + The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct + ブロックのデータベースに未来の時刻のブロックが含まれています。これはおそらくお使いのコンピュータに設定されている日時が間違っていることを示しています。お使いのコンピュータの日時が本当に正しい場合にのみ、ブロックのデータベースの再構築を行ってください。 - Copy label - ラベルをコピーする + The transaction amount is too small to send after the fee has been deducted + 手数料差引後のトランザクションの金額が小さすぎるため、送金できません。 - Copy amount - 総額のコピー + Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. + ネットワークバージョン文字 (%i) の長さが最大の長さ (%i) を超えています。UAコメントの数や長さを削減してください。 - Copy transaction ID - 取引 ID をコピー + Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index. + トランザクションインデックスはフルモードでは無効化できません。 -litemodeコマンドラインスイッチでスタートするかトランザクションインデックスを有効にしてください。 - Edit label - ラベルの編集 + Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) + 送信転送量を与えられた目標値以下に維持するようにする (24時間あたり何MiBかで指定)。0 の場合は無制限 (初期値: %d) - Show transaction details - 取引の詳細を表示 + Unable to locate enough PrivateSend denominated funds for this transaction. + このトランザクションのためのプライベートセンド用の分割された充分なファンドが見つかりません。 - Export Transaction History - トランザクション履歴をエクスポートする + Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported. + サポートされていない引数 -socks が見つかりました。SOCKSバージョンの設定はできないようになりました。SOCKS5プロキシのみがサポートされています。 - Comma separated file (*.csv) - CSVファイル (*.csv) + Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay. + サポートされていない引数 -whitelistalwaysrelay は無視されました。-whitelistrelay または -whitelistforcerelay を利用してください - Confirmed - 検証済み + Use UPnP to map the listening port (default: 1 when listening and no -proxy) + リッスン ポートの割当に UPnP を使用 (初期値: リッスン中および-proxy が指定されていない場合は1) - Watch-only - 監視限定 + WARNING! Failed to replenish keypool, please unlock your wallet to do so. + 警告! キープールの補充に失敗しました、ウォレットをアンロックしてキープールを補充してください - Date - 日付 + Wallet is locked, can't replenish keypool! Automatic backups and mixing are disabled, please unlock your wallet to replenish keypool. + ウォレットがロックされているのでキープールを補充できません!自動バックアップとミキシングが無効なので、ウォレットをアンロックしてキープールを補充してください。 - Type - タイプ + Warning: Unknown block versions being mined! It's possible unknown rules are in effect + 警告: 未知のバージョンのブロックが採掘されました。未知のルールが導入された可能性があります。 - Label - ラベル + You are starting in lite mode, all Dash-specific functionality is disabled. + ライトモードでスタートしたため、Dash固有の機能はすべて無効です。 - Address - アドレス + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain + 非剪定モードに戻るためには-reindexオプションを使用してデータベースを再構築する必要があります。これによりブロックチェーン全体の再ダウンロードが行われます。 - ID - ID + (%d could be used only on mainnet) + (%d はメインネットのみで使用が可能です) - Exporting Failed - エクスポート失敗 + (default: %u) + (初期値: %u) - There was an error trying to save the transaction history to %1. - トランザクション履歴を %1 へ保存する際にエラーが発生しました。 + (must be %d for mainnet) + (メインネット用に常時 %d ) - Exporting Successful - エクスポートに成功しました + -maxmempool must be at least %d MB + -maxmempoolは最低でも %d MB必要です - The transaction history was successfully saved to %1. - トランザクション履歴は正常に%1に保存されました。 + <category> can be: + <category>は以下の値を指定できます: - Range: - 期間: + Append comment to the user agent string + ユーザエージェント文字列にコメントを追加 - to - から + Automatic backups disabled + 自動バックアップ無効 - - - UnitDisplayStatusBarControl - Unit to show amounts in. Click to select another unit. - 金額を表示する際の単位。クリックすることで他の単位を選択します。 + Automatically create Tor hidden service (default: %d) + Tor秘匿サービスを自動的に作成する (初期値: %d) - - - WalletFrame - No wallet has been loaded. - ウォレットがロードされていません + ERROR! Failed to create automatic backup + エラー! 自動バックアップ作成失敗 - - - WalletModel - Send Coins - コインを送る + Enable publish hash block in <address> + <address>に対し、ハッシュブロックの公開を有効にする - - - WalletView - &Export - エクスポート (&E) + Enable publish hash of governance objects (like proposals) in <address> + <address>に対し、ガバナンスオブジェクトのハッシュの公開を有効にする (プロポーザル等) - Export the data in the current tab to a file - ファイルに現在のタブのデータをエクスポート + Enable publish hash of governance votes in <address> + <address>に対し、ガバナンスの投票のハッシュの公開を有効にする - Backup Wallet - ウォレットのバックアップ + Enable publish hash transaction (locked via InstantSend) in <address> + <address>に対し、ハッシュトランザクションの公開を有効にする (インスタントセンド経由でロック済) - Wallet Data (*.dat) - ウォレット データ (*.dat) + Enable publish hash transaction in <address> + <address> に対し、ハッシュトランザクションの公開を有効にする - Backup Failed - バックアップに失敗しました + Enable publish raw block in <address> + <address> に対し、原ブロックの公開を有効にする - There was an error trying to save the wallet data to %1. - ウォレットデータを%1へ保存する際にエラーが発生しました。 + Enable publish raw transaction (locked via InstantSend) in <address> + <address>に対し、原トランザクションの公開を有効にする (インスタントセンド経由でロック済) - Backup Successful - バックアップ成功 + Enable publish raw transaction in <address> + <address> に対し、原トランザクションの公開を有効にする - The wallet data was successfully saved to %1. - ウォレット データは正常に%1に保存されました。 + Enable transaction replacement in the memory pool (default: %u) + メモリプール内のトランザクションの置換を有効化する (初期設定: %u) - - - dash-core - Bind to given address and always listen on it. Use [host]:port notation for IPv6 - 指定のアドレスへバインドし、その上で常にリスンします。IPv6 は [host]:port 番号 と表記します + Error: A fatal internal error occurred, see debug.log for details + エラー:致命的な内部エラーが発生しました。詳細はdebug.logを参照してください - Error: Listening for incoming connections failed (listen returned error %s) - エラー: 内向きの接続をリッスンするのに失敗しました (エラー %s が返却されました) + Failed to create backup %s! + バックアップ作成 %s失敗! - Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message) - 関連のアラートをもらってもすごく長いのフォークを見てもコマンドを実行 (コマンドの中にあるの%sはメッセージから置き換えさせる) + Failed to create backup, error: %s + バックアップ作成失敗、エラー: %s - Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) - ウォレットの取引を変更する際にコマンドを実行 (cmd の %s は TxID に置換される) + Failed to delete backup, error: %s + バックアップ削除失敗、エラー: %s - Execute command when the best block changes (%s in cmd is replaced by block hash) - 最良のブロックに変更する際にコマンドを実行 (cmd の %s はブロック ハッシュに置換される) + Failed to load InstantSend data cache from + インスタントセンドデータキャッシュの読み込み失敗: - Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect) - 保有するピアアドレスが少ない場合、DNS ルックアップによりピアアドレスを問い合わせる (-connect を使っていない場合のデフォルト設定: 1) + Failed to load sporks cache from + スポークキャッシュの読み込み失敗: - Set maximum size of high-priority/low-fee transactions in bytes (default: %d) - 最優先/最低手数料の最大サイズをバイトで指定 (デフォルト設定: %d) + Failed to parse host:port string + host:port 文字列のパース失敗 - Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d) - スクリプト検証スレッドを設定 (%uから%dの間, 0 = 自動, <0 = たくさんのコアを自由にしておく, デフォルト設定: %d) + Fee (in %s/kB) to add to transactions you send (default: %s) + 送信するトランザクションに付加する手数料 (%s/kB単位) (初期値: %s) - This is a pre-release test build - use at your own risk - do not use for mining or merchant applications - これはリリース前のテストビルドです - 各自の責任で利用すること - 採掘や商取引に使用しないでください + Imports blocks from external blk000??.dat file on startup + 起動時に外部の blk000??.dat ファイルからブロックをインポート - Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. - 警告: -paytxfee が非常に高く設定されています! これは取引を送信する場合に支払う取引手数料です。 + InstantSend options: + インスタントセンドオプション: - Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues. - 警告: ネットワークは完全に同意しないみたいです。マイナーは何かの問題を経験してるみたいなんです。 + Invalid amount for -fallbackfee=<amount>: '%s' + 無効な額 -fallbackfee=<amount>: '%s' - Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade. - 警告: ピアーと完全に同意しないみたいです!アップグレードは必要かもしれません、それとも他のノードはアップグレードは必要かもしれません。 + Invalid input count. + 無効なインプットカウント - Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. - 警告: wallet.dat の読み込みエラー! すべてのキーは正しく読み取れますが、取引データやアドレス帳のエントリが失われたか、正しくない可能性があります。 + Keep the transaction memory pool below <n> megabytes (default: %u) + トランザクションのメモリープールの総量を <n> メガバイト以下に維持する (初期値: %u) - Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. - 警告: wallet.dat が壊れたのでデータを復旧しました! オリジナルの wallet.dat は wallet.{timestamp}.bak として %s に保存されました; もしもあなたの残高や取引が正しくないならバックアップから復元してください。 + Last PrivateSend was too recent. + 最後のプライベートセンドを実行してから充分な時間が経っていません。 - (default: 1) - (デフォルト設定: 1) + Last successful PrivateSend action was too recent. + 最後の成功したプライベートセンドを実行してから充分な時間が経っていません。 - Accept command line and JSON-RPC commands - コマンドラインと JSON-RPC コマンドを許可 + Line: %d + ライン: %d - Accept connections from outside (default: 1 if no -proxy or -connect) - 外部からの接続を許可 (デフォルト設定: -proxy または -connect を使用していない場合は1) + Loading InstantSend data cache... + インスタントセンドデータキャッシュを読み込み中... - Add a node to connect to and attempt to keep the connection open - 接続するノードを追加し接続を持続するように試します + Loading addresses... + アドレスを読み込んでいます… - Allow DNS lookups for -addnode, -seednode and -connect - -addnode, -seednode と -connect で DNS ルックアップを許可する + Loading block index... + ブロックインデックスを読み込んでいます… - Attempt to recover private keys from a corrupt wallet.dat - 壊れた wallet.dat から秘密鍵を復旧することを試す + Loading governance cache... + ガバナンスキャッシュを読み込み中... - Block creation options: - ブロック作成オプション: + Loading sporks cache... + スポークキャッシュを読み込み中... - Cannot downgrade wallet - ウォレットのダウングレードはできません + Loading wallet... (%3.2f %%) + ウォレットを読み込んでいます… (%3.2f %%) - Cannot resolve -bind address: '%s' - -bind のアドレス '%s' を解決できません + Loading wallet... + ウォレットを読み込んでいます… - Cannot resolve -externalip address: '%s' - -externalip のアドレス '%s' を解決できません + Location of the auth cookie (default: data dir) + 認証クッキーの場所 (初期設定: データディレクトリ) - Cannot write default address - 初期値のアドレスを書き込むことができません + Make the wallet broadcast transactions + ウォレットにトランザクションをブロードキャストさせる - Connect only to the specified node(s) - 指定したノードだけに接続 + Masternode cache is empty, skipping payments and governance cache... + マスターノードキャッシュが空です、ペイメントとガバナンスキャッシュをスキップしています... - Connect to a node to retrieve peer addresses, and disconnect - ピア アドレスを取得するためにノードに接続し、そして切断します + Masternode options: + マスターノードオプション: - Connection options: - 接続オプション: + Masternode queue is full. + マスターノードキューがいっぱいです - Corrupted block database detected - 破損したブロック データベースが見つかりました - + Masternode: + マスターノード: - Debugging/Testing options: - デバッグ/テスト用オプション: + Minimum bytes per sigop in transactions we relay and mine (default: %u) + 中継またはマイニングするトランザクションのsigop毎の最小バイト (初期設定: %u) - Do not load the wallet and disable wallet RPC calls - ウォレットは読み込まず、ウォレットRPCコールを無効化する + Missing input transaction information. + 不明なインプットトランザクション情報 - Do you want to rebuild the block database now? - ブロック データベースを今すぐ再構築しますか? + No errors detected. + エラーは検出されていません。 - Done loading - 読み込み完了 + No matching denominations found for mixing. + ミキシング用の分割がマッチしていません。 - Error initializing block database - ブロック データベースの初期化中にエラー + Node relay options: + ノード中継オプション: - Error initializing wallet database environment %s! - ウォレットのデータベース環境 %s 初期化エラー! + Non-standard public key detected. + スタンダードでないパブリックキーが検出されました - Error loading block database - ブロック データベースの読み込みエラー + Not compatible with existing transactions. + 既存のトランザクションと互換性がありません。 - Error loading wallet.dat - wallet.dat 読み込みエラー + Not enough file descriptors available. + 使用可能なファイルディスクリプタが不足しています。 - Error loading wallet.dat: Wallet corrupted - wallet.dat 読み込みエラー: ウォレットが壊れました + Not enough funds to anonymize. + 匿名化のための資金が不足しています。 - Error opening block database - ブロック データベースの開始エラー + Number of automatic wallet backups (default: %u) + 自動ウォレットバックアップの数 (初期設定: %u) - Error reading from database, shutting down. - データベースの読み込みエラー。シャットダウンします。 + Options: + オプション: - Error - エラー + Password for JSON-RPC connections + JSON-RPC 接続のパスワード - Error: Disk space is low! - エラー: ディスク容量不足! + Print version and exit + バージョンを表示し終了 - Failed to listen on any port. Use -listen=0 if you want this. - ポートのリスンに失敗しました。必要であれば -listen=0 を使用してください。 + PrivateSend is idle. + プライベートセンドはアイドル状態です。 - If <category> is not supplied, output all debugging information. - <category> が与えられなかった場合には、すべてのデバッグ情報が出力されます。 + PrivateSend options: + プライベートセンドオプション: - (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data) - (1 = トランザクションのメタデータ、例えばアカウントの所有者や支払リクエストの内容を保持する, 2 = トランザクションのメタデータを破棄する) + PrivateSend request complete: + プライベートセンドリクエスト完了: - Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times - 指定したアクセス元からのJSON-RPC接続を許可する。有効な<ip>は、単一のIP (例 1.2.3.4)、ネットワーク/ネットマスク (1.2.3.4/255.255.255.0)、またはネットワーク/CIDR (1.2.3.4/24)です。このオプションは複数回指定できます。 + PrivateSend request incomplete: + プライベートセンドリクエスト未完了: - An error occurred while setting up the RPC address %s port %u for listening: %s - リッスンする RPC アドレス %s、ポート %u の設定中にエラーが発生しました: %s + Prune cannot be configured with a negative value. + 剪定値は負の値に設定できません。 - Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6 - 指定されたアドレスおよび、そこに接続を行ってきたホワイトリストのピアに対してバインドを行います。IPv6の場合には [host]:port 表記を使用してください + Prune mode is incompatible with -txindex. + 剪定モードは-txindexと互換性がありません。 - Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces) - 指定されたアドレスに対して JSON-RPC 接続をリッスンしするようバインドします。IPv6の場合には [host]:port 表記を使用してください。このオプションは複数回指定することが可能です (デフォルト設定:すべてのインターフェースに対してバインドする) + Pruning blockstore... + ブロックストアを剪定しています… - Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality) - umask 077 ではなく、システムのデフォルトパーミッションで新規ファイルを作成する (ウォレット機能が無効化されていた場合にのみ有効) + Rescan the block chain for missing wallet transactions on startup + 起動時に失ったウォレットのトランザクションのブロックチェーンを再スキャン - Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup - ウォレットの全トランザクションを削除し、これらを-rescanオプションを用いることで起動時にブロックチェインのデータのみからリカバリします。 + The wallet will avoid paying less than the minimum relay fee. + ウォレットは最小中継手数料を下回る額の支払を拒否します。 - Distributed under the MIT software license, see the accompanying file COPYING or <http://www.opensource.org/licenses/mit-license.php>. - MITソフトウェアライセンスのもとで配布されています。付属のCOPYINGファイルまたは<http://www.opensource.org/licenses/mit-license.php>を参照してください。 + This is the minimum transaction fee you pay on every transaction. + これはすべてのトランザクションに対して最低限支払うべき手数料です。 - Error: Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported. - エラー:サポートされていない引数 -socks が見つかりました。SOCKSバージョンの設定はできないようになりました。SOCKS5プロキシのみがサポートされています。 + This is the transaction fee you will pay if you send a transaction. + これは取引を送信する場合に支払う取引手数料です。 - How thorough the block verification of -checkblocks is (0-4, default: %u) - -checkblocks のブロックの検証レベル (0-4, デフォルト設定: %u) + Transaction amounts must not be negative + トランザクションの総額はマイナスではあってはいけません。。 - If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u) - paytxfee が設定されていなかった場合、平均して n ブロック以内にトランザクションが検証され始めるのに十分な手数料を含める (デフォルト設定: %u) + Transaction has too long of a mempool chain + トランザクションのmempoolチェーンが長過ぎます。 - Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions) - -maxtxfee=<amount> の数量の指定が不正です: '%s' (トランザクションが詰まってしまうのを防ぐため、少なくとも %s の最小中継手数料を指定しなければいけません) + Transaction must have at least one recipient + トランザクションは最低ひとつの受取先が必要です。 - Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u) - getrawtransaction rpc 呼び出し時に用いる、完全なトランザクションインデックスを保持する (デフォルト設定: %u) + Transaction too large + トランザクション量が大きすぎます。 - Maximum size of data in data carrier transactions we relay and mine (default: %u) - 中継および採掘を行う際の、データ運送トランザクションの中のデータの最大サイズ (デフォルト設定: %u) + Trying to connect... + 接続中です... - Number of seconds to keep misbehaving peers from reconnecting (default: %u) - 不正なピアを再接続するまでの秒数 (デフォルト設定: %u) + Unable to bind to %s on this computer. %s is probably already running. + このコンピュータの %s にバインドすることができません。おそらく %s は既に実行されています。 - Output debugging information (default: %u, supplying <category> is optional) - デバッグ情報を出力する (デフォルト設定: %u, <category> の指定は任意です) + Upgrading UTXO database + UTXOデータベースを更新しています - Set the number of threads for coin generation if enabled (-1 = all cores, default: %d) - コイン生成が有効になっていた場合の利用スレッド数を設定する (-1 = すべてのコア, デフォルト設定: %d) + Use devnet chain with provided name + devnet チェーンを与えられた名前とともに使用します。 - This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit <https://www.openssl.org/> and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard. - この製品はOpenSSLプロジェクトにより開発されたソフトウェアをOpenSSLツールキットとして利用しています <https://www.openssl.org/>。また、Eric Young氏により開発された暗号ソフトウェア、Thomas Bernard氏により書かれたUPnPソフトウェアを用いています。 + Wallet debugging/testing options: + ウォレットのデバッグ/テスト用オプション: - Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s) - Tor 秘匿サービスを通し、別々の SOCKS5 プロキシを用いることでピアに到達する (デフォルト設定: %s) + Wallet is not initialized + ウォレットが初期化されていません - Warning: -maxtxfee is set very high! Fees this large could be paid on a single transaction. - 警告: -maxtxfee が非常に高く設定されています!ひとつのトランザクションでこの量の手数料が支払われてしまうことがあります。 + Wallet needed to be rewritten: restart %s to complete + ウォレットが書き直される必要がありました: 完了するために %s を再起動します - Whitelist peers connecting from the given netmask or IP address. Can be specified multiple times. - 与えられたネットマスクやIPアドレスから接続を行う、ホワイトリストのピア。複数回指定できます。 + Warning: unknown new rules activated (versionbit %i) + 警告: 未知の新しいルールがアクティベートされました (バージョンビット %i) - Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway - ホワイトリストのピアはDoSによるアクセス禁止処理が無効化され、トランザクションは例えmempool内に既に存在していたとしても常にリレーされます。これは例えばゲートウェイに対して有用です + Wasn't able to create wallet backup folder %s! + ウォレットバックアップフォルダー作成不可 %s! - (default: %s) - (デフォルト設定: %s) + Whether to operate in a blocks only mode (default: %u) + ブロック限定モードにおいて動作を行うかどうか (初期値: %u) - Accept public REST requests (default: %u) - 公開 REST リクエストを許可する (デフォルト設定: %u) + You can not start a masternode in lite mode. + ライトモードではマスターノードをスタートできません。 - Acceptable ciphers (default: %s) - 受付可能な暗号化方式 (デフォルト設定: %s) + ZeroMQ notification options: + ZeroMQ通知オプション: - Always query for peer addresses via DNS lookup (default: %u) - DNS ルックアップを通してピアアドレスを常に問い合わせる (デフォルト設定: %u) + no mixing available. + ミキシングは利用できません。 - Cannot resolve -whitebind address: '%s' - -whitebind アドレス '%s' を解決できません + see debug.log for details. + 詳細はdebug.logを参照してください。 - Connect through SOCKS5 proxy - SOCKS5 プロキシ経由で接続する + RPC server options: + RPCサーバのオプション: - Copyright (C) 2009-%i The Bitcoin Core Developers - Copyright (C) 2009-%i Bitcoin Core 開発者 + Dash Core + Dashコア - Could not parse -rpcbind value %s as network address - -rpcbind の値 %s をネットワークアドレスとして解釈できませんでした + The %s developers + %s の開発者 - Error: A fatal internal error occured, see debug.log for details - エラー:致命的な内部エラーが発生しました。詳細はdebug.logを参照してください + Accept connections from outside (default: 1 if no -proxy or -connect/-noconnect) + 外部からの接続を許可(初期設定: 1 -proxy または -connect/-noconnectの場合) - Error: Unsupported argument -tor found, use -onion. - エラー: サポートされていない引数 -tor が見つかりました。-onion を使用してください。 + Cannot obtain a lock on data directory %s. %s is probably already running. + データディレクトリ %s のロックを取得することができません。おそらく %s は実行中です。 - Generate coins (default: %u) - コインを生成 (デフォルト設定: %u) + Connect only to the specified node(s); -noconnect or -connect=0 alone to disable automatic connections + 特定のノードのみに接続; -noconnect または -connect=0 の場合においてのみ自動接続を無効化 - How many blocks to check at startup (default: %u, 0 = all) - 起動時に点検するブロック数 (デフォルト設定: %u, 0=すべて) + Distributed under the MIT software license, see the accompanying file %s or %s + MITソフトウェアライセンスのもとで配布されています。付属のファイル %s または %s を参照してください - Importing... - インポートしています… + Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + %s の読み込みエラー! すべてのキーは正しく読み取れますが、トランザクションデータやアドレス帳のエントリが失われたか、正しくない可能性があります。 - Imports blocks from external blk000??.dat file - 外部の blk000??.dat ファイルからブロックをインポート + Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d) + ホワイトリストのピアから受け取ったトランザクションに関しては、たとえローカルの中継ポリシーに違反しているとしても中継を行うようにする (初期設定: %d) - Include IP addresses in debug output (default: %u) - デバッグ出力にIPアドレスを含める (デフォルト設定: %u) + Incorrect or no devnet genesis block found. Wrong datadir for devnet specified? + 無効なdevnetジェネシスブロックが発見されたか、またはdevnetジェネシスブロックが発見されません。 特定のdevnet用のデータディレクトリが間違ってませんか? - Incorrect or no genesis block found. Wrong datadir for network? - 不正なブロックあるいは、生成されていないブロックが見つかりました。ネットワークの datadir が間違っていませんか? + Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds) + 時間オフセット調整値のピア中央値に対する最大の許容値。ローカル時間の見込み値は、接続するピアにより前方ないし後方へ影響されます。(初期値: %u 秒) - Information - 情報 + Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s) + ひとつのウォレットトランザクションまたは原トランザクションで使用する合計手数料の最大値 (%s 単位)。低すぎる値を指定すると巨大なトランザクションの作成ができなくなります (初期設定: %s) - Invalid -onion address: '%s' - 無効な -onion アドレス:'%s' + Override spork address. Only useful for regtest and devnet. Using this on mainnet or testnet will ban you. + スポークアドレスを上書きします。regtest または devnetでのみ有効です。mainnet または testnet で使用した場合Banされます。 - Invalid -proxy address: '%s' - 無効な -proxy アドレス: '%s' + Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly. + あなたのPCの日付と時刻が正しいことを確認して下さい! もしあなたの時計が正しくなければ %s が正確に動作しません。 - Invalid amount for -maxtxfee=<amount>: '%s' - -maxtxfee=<amount> に対する無効な数量です: '%s' + Please contribute if you find %s useful. Visit %s for further information about the software. + %s が有用だと感じられた方はぜひプロジェクトへの貢献をお願いします。ソフトウェアのより詳細な情報については %s をご覧ください。 - Invalid amount for -minrelaytxfee=<amount>: '%s' - 不正な額 -minrelaytxfee=<amount>: '%s' + 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) + 継続的にミキシングしてプライベートセンドに流動性を提供します (%u-%u, 初期設定: %u, 1=非常に活発, 高額手数料, %u=ほとんどなし, 低額手数料) - Invalid amount for -mintxfee=<amount>: '%s' - 不正な額 -minrelaytxfee=<amount>: '%s' + Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect/-noconnect) + 必要な場合DNS ルックアップを経由してピアアドレスに問い合わせ (初期設定: 1 -connect/-noconnectでない場合) - Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s) - -paytxfee=<amount> に対する無効な数量です: '%s' (少なくとも %s でなければいけません) + Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB) + 古いブロックの剪定 (削除) を有効にすることでストレージの必要量を削減します。これにより pruneblockchain RPC を呼び出すことで指定されたブロックを削除することができます。またターゲットサイズが MiB 単位で指定された場合には古いブロックの自動剪定が有効となります。このモードは -txindex および -rescan オプションと互換性がありません。警告: この設定を再有効化するにはすべてのブロックチェーンの再ダウンロードが必要となります。(初期設定: 0 = ブロックの剪定を無効化する, 1 = RPC 経由での手動剪定を許可する, >%u = MiB 単位で指定されたターゲットサイズを常に下回るようにブロックファイルを自動的に剪定する) - Invalid amount for -paytxfee=<amount>: '%s' - -paytxfee=<amount> の額 '%s' が無効です + Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) + ブロック生成時に取り込まれるトランザクションの最低手数料率 (%s/kB 単位)。(初期設定: %s) - Listen for JSON-RPC connections on <port> (default: %u or testnet: %u) - <port> で JSON-RPC 接続をリスン (デフォルト設定: %u、testnet は %u) + This is the transaction fee you may pay when fee estimates are not available. + これは手数料の推定機能が利用できない場合に支払うトランザクション手数料です。 - Listen for connections on <port> (default: %u or testnet: %u) - 接続のリッスンを <port> で行う (デフォルト設定: %u、testnet: %u) + This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit %s and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard. + この製品はOpenSSLプロジェクトにより開発されたソフトウェアをOpenSSLツールキット %s として利用しています <https://www.openssl.org/>。また、Eric Young氏により開発された暗号ソフトウェア、Thomas Bernard氏により書かれたUPnPソフトウェアを用いています。 - Maintain at most <n> connections to peers (default: %u) - ピアの接続数を最大でも <n> 個に維持する (デフォルト設定: %u) + Unable to locate enough PrivateSend non-denominated funds for this transaction. + このトランザクションに対してプライベートセンド用の分割されてない充分なファンドが検出できません。 - Maximum per-connection receive buffer, <n>*1000 bytes (default: %u) - 接続毎の最大受信バッファ <n>*1000 バイト (デフォルト設定: %u) + Use N separate masternodes for each denominated input to mix funds (%u-%u, default: %u) + それぞれの分割されたインプットに対してN 個の別々のマスターノードを使用してファンドをミキシングします。(%u-%u, 初期設定: %u) - Maximum per-connection send buffer, <n>*1000 bytes (default: %u) - 接続毎の最大送信バッファ <n>*1000 バイト (デフォルト設定: %u) + Use hierarchical deterministic key generation (HD) after BIP39/BIP44. Only has effect during wallet creation/first start + BIP39/BIP44のあとにHDキー生成を使用します。ウォレット作成時にのみ有効です。 - Need to specify a port with -whitebind: '%s' - -whitebind を用いてポートを指定する必要があります: '%s' + User defined mnemonic passphrase for HD wallet (BIP39). Only has effect during wallet creation/first start (default: empty string) + ユーザー定義ニーモニックパスフレーズをHDウォレットに使用します (BIP39)。ウォレット作成時にのみ有効です。(初期設定: 空文字) - Only connect to nodes in network <net> (ipv4, ipv6 or onion) - <net> (ipv4, ipv6 または onion) ネットワーク内のノードだけに接続する + Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times + JSON-RPC 接続時のユーザ名とハッシュ化されたパスワード。<userpw> フィールドのフォーマットは <USERNAME>:<SALT>$<HASH>。標準的な Python スクリプトが share/rpcuser 内に含まれています。クライアントは通常の場合には rpcuser=<USERNAME>/rpcpassword=<PASSWORD> を利用して接続を行います。このオプションは複数回指定できます。 - Prepend debug output with timestamp (default: %u) - デバッグ出力にタイムスタンプを付ける (デフォルト設定: %u) + Wallet will not create transactions that violate mempool chain limits (default: %u) + ウォレットがmempoolチェーン制限数を超えてトランザクションを作らないようにする (初期値: %u) - Send transactions as zero-fee transactions if possible (default: %u) - 可能な場合には手数料ゼロのトランザクションとしてトランザクションを送信する (デフォルト設定: %u) + Warning: At least %d of %d masternodes are running on a newer software version. Please check latest releases, you might need to update too. + 警告:少なくとも%d / %d マスターノードは最新のソフトウェアを運用しています。最新のリリースをチェックしてください。アップデートが必要になるかもしれません。 - Server certificate file (default: %s) - サーバ証明書ファイル (デフォルト設定: %s) + Warning: Every masternode (out of %d known ones) is running on a newer software version. Please check latest releases, it's very likely that you missed a major/critical update. + 警告: すべてのマスターノード (%dのうち ) 最新のバージョンのソフトウェアを運用しています。最新のリリースをチェックしてください。重要なアップデートを見落とした可能性があります。 - Server private key (default: %s) - サーバの秘密鍵 (デフォルト設定: %s) + Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup. + 警告: ウォレットファイルが破損していましたのでデータを復旧しました!元の %s は %s として %s に保存されました; 残高やトランザクションが正しくない場合にはバックアップから復元してください。 - Set key pool size to <n> (default: %u) - key pool のサイズを <n> (デフォルト設定: %u) にセット + Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times. + ホワイトリストとして登録するピア、接続元の IP アドレス (例: 1.2.3.4) または CIDR 表現のネットワーク (例: 1.2.3.0/24)。複数回指定することも可能。 - Set minimum block size in bytes (default: %u) - 最小ブロックサイズをバイトで設定 (デフォルト設定: %u) + %s corrupt, salvage failed + %s が壊れています。復旧にも失敗しました - Set the number of threads to service RPC calls (default: %d) - RPC サービスのスレッド数を設定 (デフォルト設定: %d) + %s is not a valid backup folder! + %s は無効なバックアップフォルダーです! - Specify configuration file (default: %s) - 設定ファイルの指定 (デフォルト設定: %s) + %s is set very high! + %s の設定値は高すぎます! - Specify connection timeout in milliseconds (minimum: 1, default: %d) - 接続のタイムアウトをミリセコンドで指定 (最小値: 1, デフォルト設定:: %d) + (press q to shutdown and continue later) + ( qを押してシャットダウンし、あとで再開してください) - Specify pid file (default: %s) - pid ファイルの指定 (デフォルト設定: %s) + -devnet can only be specified once + -devnet は一度だけ指定できます - Spend unconfirmed change when sending transactions (default: %u) - トランザクション送信時に未検証のおつりを使用する (デフォルト設定: %u) + -port must be specified when -devnet and -listen are specified + -port は、 -devnet と -listen が指定されたときに指定しなければなりません。 - Threshold for disconnecting misbehaving peers (default: %u) - 不正なピアを切断するためのしきい値 (デフォルト設定: %u) + -rpcport must be specified when -devnet and -server are specified + -rpcport は、 -devnet と -server が指定されたとき指定しなければなりません。 - Use UPnP to map the listening port (default: %u) - リッスンポートの割当に UPnP を使用 (デフォルト設定: %u) + Allow RFC1918 addresses to be relayed and connected to (default: %u) + RFC1918 アドレスが中継され接続されるのを許可 (初期設定: %u) - Warning: Unsupported argument -benchmark ignored, use -debug=bench. - 警告: サポートされていない引数 -benchmark は無視されました。-debug=bench を使用してください。 + Attempt to recover private keys from a corrupt wallet on startup + 起動時に壊れたウォレットから秘密鍵を復旧することを試す - Warning: Unsupported argument -debugnet ignored, use -debug=net. - 警告: サポートされていない引数 -debugnet は無視されました。-debug=net を使用してください。 + Cannot resolve -%s address: '%s' + -%s アドレス '%s' を解決できません - Invalid netmask specified in -whitelist: '%s' - -whitelist に対する無効なネットマスクです: '%s' + Change index out of range + チェンジインデックスが範囲外です - Keep at most <n> unconnectable transactions in memory (default: %u) - 最大で <n> 個の孤立したトランザクションをメモリの中に保持する (デフォルト設定: %u) + Copyright (C) + Copyright (C) - Loading addresses... - アドレスを読み込んでいます… + Error loading %s + %s 読み込みエラー - Loading block index... - ブロック インデックスを読み込んでいます… + Error loading %s: Wallet corrupted + %s 読み込みエラー: ウォレットが壊れました - Loading wallet... (%3.2f %%) - ウォレットを読み込んでいます… (%3.2f %%) + Error loading %s: Wallet requires newer version of %s + %s の読み込みに失敗しました: ウォレットにはより新しいバージョンの %s が必要です。 - Loading wallet... - ウォレットを読み込んでいます… + Error upgrading chainstate database + チェーンステートデータベースのアップグレードエラー - Node relay options: - ノード中継オプション: + Failed to find mixing queue to join + ミキシングキューの検出に失敗 - Not enough file descriptors available. - 使用可能なファイルディスクリプタが不足しています。 + Failed to start a new mixing queue + 新しいミキシングキューの開始に失敗 - Options: - オプション: + Initialization sanity check failed. %s is shutting down. + 初期化時の健全性チェックに失敗しました。%s を終了します。 - Password for JSON-RPC connections - JSON-RPC 接続のパスワード + Invalid amount for -%s=<amount>: '%s' + 無効な額 -%s=<amount>: '%s' - RPC SSL options: (see the Bitcoin Wiki for SSL setup instructions) - RPC SSL オプション: (SSLのセットアップ手順はビットコインWikiを参照してください) + Invalid spork address specified with -sporkaddr + -sporkaddrに指定された無効なスポークアドレスI - RPC server options: - RPCサーバのオプション: + Keep N DASH anonymized (%u-%u, default: %u) + 匿名化された N DASH を保持 (%u-%u, 初期設定: %u) - RPC support for HTTP persistent connections (default: %d) - RPCにおけるHTTPの持続的接続のサポート (デフォルト設定:%d) + Print this help message and exit + このヘルプメッセージを表示し終了する Receive and display P2P network alerts (default: %u) - P2Pネットワークのアラートの受け取りと表示を行う (デフォルト設定: %u) + P2Pネットワークのアラートの受け取りと表示を行う (初期設定: %u) - Relay and mine data carrier transactions (default: %u) - データ運送トランザクションのリレーおよび採掘を行う (デフォルト設定: %u) + Reducing -maxconnections from %d to %d, because of system limitations. + システム上の制約から、-maxconnections を %d から %d に削減します。 - Relay non-P2SH multisig (default: %u) - P2SHでないマルチシグトランザクションをリレーする (デフォルト設定: %u) + Relay and mine data carrier transactions (default: %u) + データ運送トランザクションのリレーおよび採掘を行う (初期設定: %u) - Rescan the block chain for missing wallet transactions - 失ったウォレットの取引のブロック チェーンを再スキャン + Relay non-P2SH multisig (default: %u) + P2SHでないマルチシグトランザクションをリレーする (初期設定: %u) Rescanning... @@ -3011,13 +5166,29 @@ Run in the background as a daemon and accept commands デーモンとしてバックグランドで実行しコマンドを許可 + + Send trace/debug info to console instead of debug.log file + トレース/デバッグ情報を debug.log ファイルの代わりにコンソールへ送る + + + Session not complete! + セッション未完了! + + + Session timed out. + セッションタイムアウト + Set database cache size in megabytes (%d to %d, default: %d) - データベースのキャッシュサイズをメガバイトで設定 (%d から %d。デフォルト設定: %d) + データベースのキャッシュサイズをメガバイトで設定 (%d から %d。初期設定: %d) Set maximum block size in bytes (default: %d) - 最大ブロックサイズをバイトで設定 (デフォルト設定: %d) + 最大ブロックサイズをバイトで設定 (初期設定: %d) + + + Set the masternode private key + マスターノードプライベートセンドキーを設定 Show all debugging options (usage: --help -help-debug) @@ -3025,11 +5196,11 @@ Shrink debug.log file on client startup (default: 1 when no -debug) - クライアント起動時に debug.log ファイルを縮小 (デフォルト設定: -debug オプションを指定しない場合は1) + クライアント起動時に debug.log ファイルを縮小 (初期設定:1 -debug オプションを指定しない場合) Signing transaction failed - 取引の署名に失敗しました + トランザクションの署名に失敗 Specify data directory @@ -3037,15 +5208,35 @@ Specify wallet file (within data directory) - ウォレットのファイルを指定 (データ・ディレクトリの中に) + ウォレットのファイルを指定 (データディレクトリの中で) Specify your own public address あなた自身のパブリックなアドレスを指定 - This help message - このヘルプ メッセージ + Starting network threads... + ネットワークのスレッドを起動しています... + + + Synchronization pending... + 同期保留中... + + + Synchronizing governance objects... + ガバナンスオブジェクトを同期中... + + + Synchronizing masternode payments... + マスターノードペイメント同期中... + + + The source code is available from %s. + ソースコードは %s より入手可能です。 + + + The transaction amount is too small to pay the fee + トランザクションの金額が小さすぎて手数料を支払えません This is experimental software. @@ -3053,43 +5244,39 @@ Transaction amount too small - 取引の額が小さ過ぎます - - - Transaction amounts must be positive - 取引の額は0より大きくしてください + トランザクションの額が少な過ぎます - Transaction too large for fee policy - 手数料ポリシーに対してトランザクションが大きすぎます + Transaction created successfully. + トランザクション作成に成功 - Transaction too large - 取引が大き過ぎます + Transaction fees are too high. + トランザクション手数料が高すぎます - Unable to bind to %s on this computer (bind returned error %s) - このコンピュータの %s にバインドすることができません (バインドが返したエラーは %s) + Transaction not valid. + トランザクションが無効です - Unknown network specified in -onlynet: '%s' - -onlynet で指定された '%s' は未知のネットワークです + Transaction too large for fee policy + 手数料ポリシーに対してトランザクションが大きすぎます - Upgrade wallet to latest format - ウォレットを最新のフォーマットにアップグレード + Unable to bind to %s on this computer (bind returned error %s) + このコンピュータの %s にバインドすることができません (バインドが返したエラーは %s) - Use OpenSSL (https) for JSON-RPC connections - JSON-RPC 接続に OpenSSL (https) を使用 + Unable to sign spork message, wrong key? + スポークメッセージに署名できません、キーが間違ってませんか? - Use UPnP to map the listening port (default: 1 when listening) - リスン ポートの割当に UPnP を使用 (デフォルト設定: リスン中は1) + Unknown network specified in -onlynet: '%s' + -onlynet で指定された '%s' は未知のネットワークです - Use the test network - テストのためのネットワークを使用 + Unknown state: id = %u + 未知の状態: id = %u Username for JSON-RPC connections @@ -3103,33 +5290,41 @@ Verifying wallet... ウォレットの検証中… + + Very low number of keys left: %d + 非常に少ない数のキー: %d + Wallet %s resides outside data directory %s - 財布 %s はデータ・ディレクトリ%sの外にあります + ウォレット %s はデータディレクトリ%sの外にあります。 + + + Wallet is locked. + ウォレットはロックされています。 Wallet options: ウォレットオプション: - Warning - 警告 + Wallet window title + ウォレットウィンドウタイトル - You need to rebuild the database using -reindex to change -txindex - -txindex を変更するには -reindex を使用してデータベースを再構築する必要があります + Warning + 警告 - Zapping all transactions from wallet... - ウォレットからすべてのトランザクションを消去しています… + Your entries added successfully. + あなたの追加エントリーは成功しました。 - on startup - 起動時 + Your transaction was accepted into the pool! + あなたのトランザクションはプールにアクセプトされました。 - wallet.dat corrupt, salvage failed - wallet.dat が壊れ、復旧に失敗しました + Zapping all transactions from wallet... + ウォレットからすべてのトランザクションを消去しています… \ No newline at end of file diff --git a/src/qt/locale/dash_ko.ts b/src/qt/locale/dash_ko.ts new file mode 100644 index 000000000000..33e376abc021 --- /dev/null +++ b/src/qt/locale/dash_ko.ts @@ -0,0 +1,5329 @@ + + + AddressBookPage + + Right-click to edit address or label + 오른쪽 마우스를 클릭하여 주소와 라벨 편집 + + + Create a new address + 새로운 주소를 생성합니다. + + + &New + 생성(&N) + + + Copy the currently selected address to the system clipboard + 현재 선택된 주소를 시스템 클립보드로 복사합니다. + + + &Copy + 복사(&C) + + + Delete the currently selected address from the list + 현재 선택된 주소를 목록에서 제거합니다. + + + &Delete + 삭제(&D) + + + Export the data in the current tab to a file + 현재 탭의 데이터를 파일로 추출합니다. + + + &Export + 추출(&E) + + + C&lose + 닫기(&L) + + + Choose the address to send coins to + 코인을 보내실 주소를 선택하세요. + + + Choose the address to receive coins with + 코인을 받으실 주소를 선택하세요. + + + C&hoose + 선택 (&H) + + + Sending addresses + 보내기 주소 + + + Receiving addresses + 받기 주소 + + + These are your Dash addresses for sending payments. Always check the amount and the receiving address before sending coins. + 송금을 위한 대시 주소입니다. 코인을 보내기 전에 항상 금액과 받는 주소를 확인하세요. + + + These are your Dash addresses for receiving payments. It is recommended to use a new receiving address for each transaction. + 대시를 송금 받기 위한 주소입니다. 매 거래시 새로운 주소를 사용할 것을 권장합니다. + + + &Copy Address + 주소 복사(&C) + + + Copy &Label + 라벨 복사(&L) + + + &Edit + 편집(&E) + + + Export Address List + 주소 목록 추출 + + + Comma separated file (*.csv) + 쉼표로 구분된 파일 (*.csv) + + + Exporting Failed + 추출에 실패하였습니다. + + + There was an error trying to save the address list to %1. Please try again. + %1에 주소 리스트를 저장하는 동안 오류가 발생했습니다. 다시 시도해주세요. + + + + AddressTableModel + + Label + 라벨 + + + Address + 주소 + + + (no label) + (라벨 없음) + + + + AskPassphraseDialog + + Passphrase Dialog + 암호문 대화상자 + + + Enter passphrase + 암호문 입력 + + + New passphrase + 새로운 암호 + + + Repeat new passphrase + 새로운 암호문 확인 + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>. + 지갑에 새로운 암호문을 입력하세요.<br/>다음의 요건을 충족하는 암호문을 지정하세요.<b>열 개 이상의 무작위 글자</b>, 혹은 <b>여덟 개 이상의 무작위 단어 </b>. + + + Encrypt wallet + 지갑 암호화 + + + This operation needs your wallet passphrase to unlock the wallet. + 이 작업을 실행하려면 사용자 지갑의 암호문이 필요합니다. + + + Unlock wallet for mixing only + 믹싱만을 위해 지갑 잠금 해제 + + + Unlock wallet + 지갑 잠금 해제 + + + This operation needs your wallet passphrase to decrypt the wallet. + 지갑을 해독하기 위한 작업에는 사용자 지갑의 암호문이 필요합니다. + + + Decrypt wallet + 지갑 해독 + + + Change passphrase + 암호문 변경 + + + Enter the old passphrase and new passphrase to the wallet. + 지갑의 기존 암호문과 새로운 암호문을 입력하세요. + + + Confirm wallet encryption + 지갑 암호화 승인 + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR DASH</b>! + 경고: 지갑을 암호화하고 암호문을 잃어버리는 경우 당신은 <b>모든 대시를 잃게 됩니다</b>! + + + Are you sure you wish to encrypt your wallet? + 지갑 암호화를 진행하시겠습니까? + + + Wallet encrypted + 지갑 암호화 완료 + + + %1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your funds from being stolen by malware infecting your computer. + 암호화 진행을 완료하기 위하여 %1 이/가 종료됩니다. 사용자 지갑 암호화를 진행한다고 하더라도 컴퓨터에 영향을 끼치는 맬웨어로부터 자금을 완전히 보호하지 못한다는 점을 기억하세요. + + + IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. Previous backups of the unencrypted wallet file contain the same HD seed and still have full access to all your funds just like the new, encrypted wallet. + 중요: 당신의 지갑 파일에서 생성된 이전 백업은 새로 생성된 암호화 지갑 파일로 교체되어야 합니다.암호화되지 않은 이전 지갑 파일의 백업은 동일한 HD 시드를 가지며, 새로운 암호화 지갑과 마찬가지로 당신의 자금에 완전한 접근성을 제공합니다. + + + IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet. + 중요: 귀하의 지갑 파일에서 만든 예전 백업이 새로 생성한 암호화된 지갑 파일로 교체되어야 합니다. 새롭게 암호화된 지갑을 사용하는 경우, 보안상 이유로 인하여 암호화되지 않은 이전 지갑의 백업 파일은 사용할 수 없게 됩니다. + + + Wallet encryption failed + 지갑 암호화 실패 + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + 내부 에러로 인해 지갑 암호화에 실패했습니다. 당신의 지갑은 암호화 되지 않았습니다. + + + The supplied passphrases do not match. + 입력한 암호문이 일치하지 않습니다. + + + Wallet unlock failed + 지갑 잠금 해제 실패 + + + The passphrase entered for the wallet decryption was incorrect. + 지갑 해독을 위해 입력한 암호문이 틀립니다. + + + Wallet decryption failed + 지갑 해독 실패 + + + Wallet passphrase was successfully changed. + 지갑 암호문이 성공적으로 변경되었습니다. + + + Warning: The Caps Lock key is on! + 경고: Caps Lock키가 켜져있습니다! + + + + BanTableModel + + IP/Netmask + IP주소/넷마스크 + + + Banned Until + 다음의 일시까지 정지되었습니다: + + + + BitcoinGUI + + A fatal error occurred. Dash Core can no longer continue safely and will quit. + 치명적인 오류가 발생하였습니다. 더 이상 대시 코어가 안전하지 않을 가능성이 있으며 대시 코어는 곧 종료될 예정입니다. + + + Dash Core + 대시 코어 + + + Wallet + 지갑 + + + Node + 노드 + + + &Overview + 개요(&O) + + + Show general overview of wallet + 지갑의 일반적 개요를 보여줍니다. + + + &Send + 보내기(&S) + + + Send coins to a Dash address + 대시 주소로 코인 전송 + + + &Receive + 받기(&R) + + + Request payments (generates QR codes and dash: URIs) + 지불 요청하기 (QR코드와 대시 URI가 생성됩니다.) + + + &Transactions + 거래(&T) + + + Browse transaction history + 거래내역을 검색합니다. + + + &Masternodes + 마스터노드(&M) + + + Browse masternodes + 마스터노드 보기 + + + E&xit + 나가기(&X) + + + Quit application + 어플리케이션 종료 + + + Show information about Dash Core + 대시 코어에 관한 정보를 표시합니다. + + + About &Qt + &Qt 에 관하여 + + + Show information about Qt + Qt 정보를 표시합니다 + + + &Options... + 옵션(&O) + + + &About %1 + %1 정보(&A) + + + Modify configuration options for %1 + %1 설정 옵션 수정 + + + &Show / Hide + 보이기 / 숨기기(&S) + + + Show or hide the main Window + 메인창 보이기 또는 숨기기 + + + &Encrypt Wallet... + 지갑 암호화(&E)... + + + Encrypt the private keys that belong to your wallet + 지갑에 해당하는 개인 키 암호화하기 + + + &Backup Wallet... + 지갑 백업(&B)... + + + Backup wallet to another location + 지갑을 다른 장소에 백업 + + + &Change Passphrase... + 암호문 변경(&C)... + + + Change the passphrase used for wallet encryption + 지갑 암호화에 사용되는 암호문을 변경합니다 + + + &Unlock Wallet... + 지갑 잠금해제 (&U) + + + Unlock wallet + 지갑 잠금해제 + + + &Lock Wallet + 지갑 잠금(&L) + + + Sign &message... + 메시지 서명...(&M) + + + Sign messages with your Dash addresses to prove you own them + 본인의 대시 주소임을 증명하기 위하여 메시지에 서명합니다. + + + &Verify message... + 메시지 검증...(&V) + + + Verify messages to ensure they were signed with specified Dash addresses + 특정 대시 주소에 서명된 것인지 확인하기 위하여 메시지를 검증합니다. + + + &Information + 정보(&I) + + + Show diagnostic information + 진단 정보를 표시합니다. + + + &Debug console + 디버그 콘솔(&D) + + + Open debugging console + 디버깅 콘솔을 엽니다. + + + &Network Monitor + 네트워크 모니터(&N) + + + Show network monitor + 네트워크 모니터를 표시합니다. + + + &Peers list + 피어 리스트(&P) + + + Show peers info + 피어 정보를 표시합니다. + + + Wallet &Repair + 지갑 복구(&R) + + + Show wallet repair options + 지갑 복구 옵션을 표시합니다. + + + Open Wallet &Configuration File + 지갑 설정 파일 열기(&C) + + + Open configuration file + 설정 파일을 엽니다. + + + Open &Masternode Configuration File + 마스터노드 설정 파일 열기(&M) + + + Open Masternode configuration file + 마스터노드 설정 파일을 엽니다. + + + Show Automatic &Backups + 자동 백업 표시(&B) + + + Show automatically created wallet backups + 자동으로 생성된 지갑 백업을 보여줍니다. + + + &Sending addresses... + 보내기 주소...(&S) + + + Show the list of used sending addresses and labels + 사용한 보내기 주소와 라벨을 보여줍니다. + + + &Receiving addresses... + 받기 주소...(&R) + + + Show the list of used receiving addresses and labels + 사용한 받기 주소와 라벨을 표시합니다. + + + Open &URI... + URI 열기(&U)... + + + Open a dash: URI or payment request + Dash: URI 또는 지불 요청을 엽니다. + + + &Command-line options + 명령줄 옵션(&C) + + + Show the %1 help message to get a list with possible Dash command-line options + 사용 가능한 대시 명령줄 옵션 목록을 가져오기 위해 %1 도움말 메시지를 표시합니다. + + + %1 client + %1 클라이언트 + + + &PrivateSend information + 프라이빗샌드 정보(&P) + + + Show the PrivateSend basic information + 프라이빗샌드에 대한 기본적인 정보를 보여줍니다. + + + &File + 파일(&F) + + + &Settings + 설정(&S) + + + &Tools + 도구(&T) + + + &Help + 도움말(&H) + + + Tabs toolbar + 도구 모음 탭 + + + %n active connection(s) to Dash network + 대시 네트워크의 활성 연결 수는 %n 입니다. + + + Network activity disabled + 네트워크 활동 정지 + + + Syncing Headers (%1%)... + 헤더 동기화중 (%1%)... + + + Synchronizing with network... + 네트워크와 동기화중... + + + Indexing blocks on disk... + 디스크에서 블록 색인중... + + + Processing blocks on disk... + 디스크에서 블록 처리중... + + + Reindexing blocks on disk... + 디스크에서 블록 다시 색인중... + + + Connecting to peers... + 피어에 연결중... + + + Processed %n block(s) of transaction history. + 거래 히스토리의 %n 블록을 처리하였습니다. + + + %1 behind + %1 전의 정보 처리 중 + + + Catching up... + 블록 따라잡기... + + + Last received block was generated %1 ago. + 최근에 받은 블록은 %1 전에 생성되었습니다. + + + Transactions after this will not yet be visible. + 이 이후의 거래는 아직 보이지 않습니다. + + + Up to date + 최신 + + + Synchronizing additional data: %p% + 추가 정보를 동기화 중: %p% + + + Error + 에러 + + + Warning + 경고 + + + Information + 정보 + + + Date: %1 + + 날짜: %1 + + + + Amount: %1 + + 수량: %1 + + + + Type: %1 + + 종류: %1 + + + + Label: %1 + + 라벨: %1 + + + + Address: %1 + + 주소: %1 + + + + Sent transaction + 전송된 거래 + + + Incoming transaction + 진행 중인 거래 + + + HD key generation is <b>enabled</b> + HD 키 생성이 <b>활성화되었습니다</b> + + + HD key generation is <b>disabled</b> + HD 키 생성이 <b>비활성화되었습니다</b> + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + 지갑이 <b>암호화</b>되어 있으며, 현재 <b>잠금 해제</b> 상태입니다. + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> for mixing only + 지갑이 <b>암호화</b>되어 있으며, 현재 믹싱만을 위하여<b>잠금 해제</b> 되어 있습니다. + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + 지갑이 <b>암호화</b> 되었고 현재 <b>잠겨져</b> 있습니다 + + + + ClientModel + + Total: %1 (PS compatible: %2 / Enabled: %3) + 전체: %1 (PS 호환: %2 / 가능: %3) + + + Network Alert + 네트워크 경고 + + + + CoinControlDialog + + Quantity: + 수량: + + + Bytes: + 바이트: + + + Amount: + 거래액: + + + Fee: + 수수료: + + + Coin Selection + 코인 선택 + + + Dust: + 더스트: + + + After Fee: + 수수료 이후: + + + Change: + 잔돈: + + + (un)select all + 모두 선택(취소) + + + toggle lock state + 토글 잠금 상태 + + + Tree mode + 트리 모드 + + + List mode + 리스트 모드 + + + (1 locked) + (1 잠금) + + + Amount + 거래액 + + + Received with label + 함께 수신된 라벨 + + + Received with address + 함께 수신된 주소 + + + PS Rounds + PS 라운드 + + + Date + 날짜 + + + Confirmations + 확인 + + + Confirmed + 확인됨 + + + Copy address + 주소 복사 + + + Copy label + 라벨 복사 + + + Copy amount + 거래액 복사 + + + Copy transaction ID + 거래 아이디 복사 + + + Lock unspent + 사용되지 않은 주소 잠금 + + + Unlock unspent + 사용되지 않은 주소 잠금 해제 + + + Copy quantity + 수량 복사 + + + Copy fee + 수수료 복사 + + + Copy after fee + 수수료 이후 복사 + + + Copy bytes + 바이트 복사 + + + Copy dust + 더스트 복사 + + + Copy change + 잔돈 복사 + + + Please switch to "List mode" to use this function. + 이 기능을 사용하기 위해서는 '리스트 모드'를 켜주세요. + + + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. + 익명화 되지 않은 입력이 선택되었습니다. <b>프라이빗샌드 기능이 비활성화됩니다. </b><br><br>계속해서 프라이빗샌드 기능을 사용하고자 하신다면 모든 익명화되지 않은 입력 선택을 취소하시고 프라이빗샌드 체크박스를 다시 선택해주세요. + + + (%1 locked) + (%1 잠금) + + + yes + + + + no + 아니오 + + + This label turns red if any recipient receives an amount smaller than the current dust threshold. + 수령인이 현재 더스트 임계값보다 작은 양을 수신하면 이 라벨이 빨간색으로 변합니다. + + + Can vary +/- %1 duff(s) per input. + 입력당 +/- %1 더프가 달라질 수 있습니다. + + + (no label) + (라벨 없음) + + + change from %1 (%2) + %1 로부터 잔돈 (%2) + + + (change) + (잔돈) + + + n/a + 없음 + + + + EditAddressDialog + + Edit Address + 주소 편집 + + + &Label + 라벨(&L) + + + The label associated with this address list entry + 이 주소 목록에 입력된 주소와 연결된 라벨 + + + &Address + 주소(&A) + + + The address associated with this address list entry. This can only be modified for sending addresses. + 이 주소 목록에 입력된 주소와 연결된 주소. 이 경우 보내기 주소만 변경이 가능합니다. + + + New receiving address + 새로운 받기 주소 + + + New sending address + 새로운 보내기 주소 + + + Edit receiving address + 받기 주소 편집 + + + Edit sending address + 보내기 주소 편집 + + + The entered address "%1" is not a valid Dash address. + 입력한 주소 "%1" 는 올바른 대시 주소가 아닙니다. + + + The entered address "%1" is already in the address book. + 입력된 주소 "%1" 는 이미 주소록에 있습니다. + + + Could not unlock wallet. + 지갑을 잠금 해제 할 수 없습니다. + + + New key generation failed. + 새로운 키 생성에 실패하였습니다. + + + + FreespaceChecker + + A new data directory will be created. + 새로운 데이터 디렉토리가 생성됩니다. + + + name + 이름 + + + Directory already exists. Add %1 if you intend to create a new directory here. + 디렉토리가 이미 존재합니다. 새로운 디렉토리 생성을 원하시면 %1 을/를 추가하세요. + + + Path already exists, and is not a directory. + 경로가 이미 존재합니다. 또한 디렉토리가 아닙니다. + + + Cannot create data directory here. + 이곳에 데이터 디렉토리를 생성할 수 없습니다. + + + + HelpMessageDialog + + version + 버전 + + + (%1-bit) + (%1-비트) + + + About %1 + %1에 관하여 + + + Command-line options + 명령줄 옵션 + + + Usage: + 사용법: + + + command-line options + 명령줄 옵션 + + + UI Options: + UI 옵션: + + + Choose data directory on startup (default: %u) + 실행시 데이터 디렉토리 선택하기 (기본값: %u) + + + Set language, for example "de_DE" (default: system locale) + 언어를 설정하십시오. 예: "kr_KR" (기본값: 시스템 로케일) + + + Start minimized + 최소화된 상태에서 시작 + + + Set SSL root certificates for payment request (default: -system-) + 지불 요청을 위한 SSL 루트 인증서 설정 (기본값: -system-) + + + Show splash screen on startup (default: %u) + 실행시 시작 화면 보기 (기본값: %u) + + + Reset all settings changed in the GUI + GUI를 통해 수정된 모든 설정을 초기화 + + + PrivateSend information + 프라이빗샌드 정보 + + + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. + <h3>프라이빗샌드에 관한 기본 정보</h3> 프라이빗 샌드는 당신의 자금 출처를 가려 진정한 재정적 프라이버시를 보장합니다. 당신의 지갑에 보관된 모든 대시는 각기 다른 ‘입력값’으로 구성되어 있습니다. 쉽게 생각하면 별도의 분리된 동전으로 구성되어 있다고 볼 수 있습니다.<br> 프라이빗샌드는 당신의 코인이 지갑을 떠나지 않고도 그 입력값을 다른 두 사람의 입력값과 섞는 혁신적인 방식을 사용합니다. 이로써 당신의 자금은 언제나 당신의 통제 하에 있습니다.<hr><b> 프라이빗샌드는 다음과 같은 방식으로 작동합니다.</b><ol type="1"><li> 프라이빗샌드는 당신의 거래 입력값을 표준 단위로 쪼개는 것으로 시작합니다. 이렇게 분할하는 단위는 0.01대시, 0.1대시, 1대시 혹은 10대시 입니다 - 귀하가 일상적으로 사용하는 지폐와 비슷하다고 생각하셔도 좋습니다. </li><li>이후 당신의 지갑은 ‘마스터노드’라고 불리는 네트워크의 특수 구성 소프트웨어 노드에 요청을 전송합니다. 이로써 이들 마스터노드는 당신이 특정 금액을 믹싱하고자 한다는 정보를 수신합니다. 마스터노드에는 당신의 개인 정보를 식별할 수 있는 어떤 내용도 전송되지 않습니다. 즉 당신이 ‘누구’인지 알 수 있는 방법은 없습니다.</li><li> 두 명의 다른 사람이 비슷한 메시지를 보내어 그들 역시 같은 단위의 금액을 믹싱하고 싶다는 정보를 표시하면 믹싱 세션이 시작됩니다. 마스터노드는 이들 입력값을 믹싱하여 세 사용자의 지갑에 변환된 입력을 지불하도록 지시합니다. 당신의 지갑은 해당 입력값을 지불하지만, 지불되는 주소는 '변경 주소'라고 불리는 다른 주소로 보내집니다.</li><li> 당신의 자금을 완전히 가리기 위해서, 당신의 지갑은 이 과정을 각 대시 분할 단위만큼 여러번 반복합니다. 이러한 반복을 ‘라운드’라고 부릅니다. 프라이빗샌드의 각 라운드가 진행됨에 따라 당신이 송금하고자 하는 자금의 출처는 기하급수적으로 높은 수준으로 가려집니다.</li><li> 이 믹싱 과정은 당신이 개입할 필요 없이 백그라운드에서 진행됩니다. 당신이 거래를 원하시는 경우, 당신의 자금은 이미 익명화가 진행되는 중입니다. 추가적으로 대기할 필요 역시 없습니다. </li></ol><hr><b>중요:</b> 당신의 지갑은 1,000개의 ‘변경 주소’만을 가지고 있습니다. 믹싱을 실행할 때마다 최대 9개의 주소가 사용됩니다. 이는 곧 이들 1000개의 주소가 약 100개의 믹싱 작업을 위해 사용될 수 있다는 것을 의미합니다. 전체 1,000개의 주소 중 900개가 사용된 경우 당신의 지갑은 더 많은 주소를 만들어야 합니다. 그러나 이 작업은 당신이 자동 백업을 설정한 경우에만 수행됩니다. <br>따라서 백업이 비활성화된 사용자는 프라이빗샌드 역시 비활성화 됩니다. <hr>더 많은 정보를 위해서는 다음을 참조하세요 <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">프라이빗샌드 설명서</a>. + + + + Intro + + Welcome + 환영합니다 + + + Welcome to %1. + %1에 오신것을 환영합니다. + + + As this is the first time the program is launched, you can choose where %1 will store its data. + 프로그램을 처음으로 실행합니다. 어디에 %1 데이터를 저장 할 지 선택할 수 있습니다. + + + %1 will download and store a copy of the Dash block chain. At least %2GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory. + %1이/가 다운로드 되며 사본이 대시 블록체인 상에 저장됩니다. 이 디렉토리에 최소 %2GB의 데이터가 저장되며, 저장되는 데이터의 양은 시간이 지남에 따라 증가합니다. 지갑 역시 이 디렉토리에 저장될 예정입니다. + + + Use the default data directory + 기본 데이터 디렉토리 사용하기 + + + Use a custom data directory: + 사용자 설정 데이터 디렉토리 사용하기 + + + Error: Specified data directory "%1" cannot be created. + 오류: 지정한 데이터 디렉토리 "%1" 를 생성할 수 없습니다. + + + Error + 오류 + + + %1 GB of free space available + 저장 공간이 %1 GB 남았습니다. + + + (of %1 GB needed) + (필요한 %1 GB 중) + + + + MasternodeList + + Form + 유형 + + + My Masternodes + 나의 마스터노드 + + + Note: Status of your masternodes in local wallet can potentially be slightly incorrect.<br />Always wait for wallet to sync additional data and then double check from another node<br />if your masternode should be running but you still do not see "ENABLED" in "Status" field. + 주의: 로컬 지갑의 마스터노드 상태가 약간 다를 수 있습니다. 만일 당신의 마스터노드가 작동하고 있으나 여전히 '상태' 란에서 '활성화'가 표시되지 않은 경우 <br />지갑이 추가적인 데이터와 동기화를 마치도록 기다리고 다른 노드로부터 이중 검증하세요.<br /> + + + Alias + 에일리어스 + + + Address + 주소 + + + Protocol + 프로토콜 + + + Status + 상태 + + + Active + 활동 중 + + + Last Seen + 마지막 확인 + + + Payee + 수취인 + + + S&tart alias + 에일리어스 시작(&S) + + + Start &all + 모두 시작(&A) + + + Start &MISSING + 미싱 시작(&M) + + + &Update status + 업데이트 상태(&U) + + + Show additional Masternode information + 마스터노드 추가 정보를 보여줍니다. + + + Show &Info... + 정보 보기...(&I) + + + Status will be updated automatically in (sec): + 상태가 (초)안에 자동으로 업데이트 됩니다: + + + 0 + 0 + + + All Masternodes + 모든 마스터노드 + + + Filter List: + 필터 목록: + + + Filter masternode list + 마스터노드 목록 필터 + + + Node Count: + 노드 개수: + + + Start alias + 에일리어스 시작 + + + Confirm masternode start + 마스터노드 실행 확인 + + + Are you sure you want to start masternode %1? + 정말로 마스터노드 %1를 실행하시겠습니까? + + + Confirm all masternodes start + 모든 마스터노드 실행 확인 + + + Are you sure you want to start ALL masternodes? + 정말로 모든 마스터노드를 실행하시겠습니까? + + + Command is not available right now + 현재 이 명령을 실행할 수 없습니다. + + + You can't use this command until masternode list is synced + 마스터노드 리스트가 동기화 될 때까지 이 명령을 실행할 수 없습니다. + + + Confirm missing masternodes start + 미싱 마스터노드 시작 확인 + + + Are you sure you want to start MISSING masternodes? + 정말로 미싱 마스터노드를 시작하시겠습니까? + + + Additional information for Masternode %1 + 마스터노드 %1에 대한 추가 정보 + + + Masternode Private Key + 마스터노드 개인 키 + + + Private Key + 개인 키 + + + Collateral + 콜래트럴 + + + IP + IP + + + Version + 버전 + + + Unknown + 알 수 없음 + + + Sentinel + 센티넬 + + + + ModalOverlay + + Form + 유형 + + + Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the Dash network, as detailed below. + 최근 거래는 아직 표시되지 않을 수 있으며, 따라서 당신 지갑의 잔고 역시 실제와 다를 수 있습니다. 이는 당신의 지갑이 대시 네트워크와 동기화를 마치는대로 아래의 세부 내용과 같이 정확히 표시될 예정입니다. + + + Attempting to spend Dash that are affected by not-yet-displayed transactions will not be accepted by the network. + '아직 표시 되지 않은 거래'의 영향을 받는 대시를 사용하시려는 경우, 네트워크에 의해 승인되지 않습니다. + + + Number of blocks left + 남은 블록의 수 + + + Unknown... + 알 수 없음... + + + Last block time + 최종 블록 시각 + + + Progress + 진행 + + + Progress increase per hour + 시간당 진행 증가율 + + + calculating... + 계산 중... + + + Estimated time left until synced + 동기화 완료까지 예상 시간 + + + Hide + 숨기기 + + + Unknown. Syncing Headers (%1)... + 알 수 없음. 헤더 동기화중 (%1)... + + + + OpenURIDialog + + Open URI + URI 열기 + + + Open payment request from URI or file + 지급 요청 URI 또는 파일 열기 + + + URI: + URI: + + + Select payment request file + 지불 요청 파일을 선택하세요. + + + Select payment request file to open + 열려는 지불 요청 파일을 선택하세요. + + + + OptionsDialog + + Options + 옵션 + + + &Main + 메인(&M) + + + Size of &database cache + 데이터베이스 캐시 크기(&d) + + + MB + MB + + + Number of script &verification threads + 스크립트 인증 스레드의 개수(&v) + + + (0 = auto, <0 = leave that many cores free) + (0 = 자동, <0 = 지정된 코어 개수만큼 사용 안함) + + + Amount of Dash to keep anonymized + 익명을 유지할 대시의 개수 + + + W&allet + 지갑(&a) + + + Show additional tab listing all your masternodes in first sub-tab<br/>and all masternodes on the network in second sub-tab. + 추가적인 탭 목록을 보여줍니다. 당신의 모든 마스터노드는 첫 번째 서브 탭에 표시되고,<br/> 네트워크상의 전체 마스터노드는 두 번째 서브 탭에 표시됩니다. + + + Show Masternodes Tab + 마스터노드 탭을 보여줍니다. + + + Show additional information and buttons for PrivateSend on overview screen. + 추가적인 정보와 프라이빗샌드를 위한 버튼을 개요에 나타냅니다. + + + Enable advanced PrivateSend interface + 향상된 프라이빗샌드 인터페이스 사용하기 + + + Show warning dialog when PrivateSend detects that wallet has very low number of keys left. + 프라이빗샌드가 지갑에 남은 키의 숫자가 적은 것을 감지하는 경우 대화 상자에 오류를 나타냅니다. + + + Warn if PrivateSend is running out of keys + 프라이빗샌드에 사용할 키가 없는 경우 경고문을 표시합니다. + + + Whether to use experimental PrivateSend mode with multiple mixing sessions per block.<br/>Note: You must use this feature carefully.<br/>Make sure you always have recent wallet (auto)backup in a safe place! + 블록당 다수의 믹싱 세션을 이용하는 실험적 프라이빗샌드 모드를 사용할 지 여부를 결정합니다. <br/>주의: 이 기능을 사용하기 위해서는 특별히 주의를 기울여야 합니다.<br/>언제나 안전한 장소에 최신의 지갑 (자동)백업이 되고 있는지 확인하세요! + + + Enable PrivateSend &multi-session + 프라이빗샌드 멀티 세션 활성화(&m) + + + If you disable the spending of unconfirmed change, the change from a transaction<br/>cannot be used until that transaction has at least one confirmation.<br/>This also affects how your balance is computed. + 확인되지 않은 잔액 사용을 중지하는 경우, 거래로부터 발생하는 잔액은 <br/>최소 한 번의 거래 확인이 발생할 때 까지 사용할 수 없습니다.<br/>이는 또한 당신의 잔고가 계산되는 방식에 영향을 미칠 수 있습니다. + + + PrivateSend rounds to use + 프라이빗샌드 라운드 사용 + + + This amount acts as a threshold to turn off PrivateSend once it's reached. + 이 금액에 도달하는 경우 프라이빗샌드를 끄기 위한 한계점으로 작동합니다. + + + Automatically open the Dash Core client port on the router. This only works when your router supports UPnP and it is enabled. + 라우터에서 대시 코어 클라이언트를 자동으로 엽니다. 이 기능은 당신의 라우터가 UPnP를 지원하고 해당 기능이 작동하는 경우에만 가능합니다. + + + Accept connections from outside + 외부로부터의 연결 허용 + + + Allow incoming connections + 수신 연결 허용 + + + Connect to the Dash network through a SOCKS5 proxy. + SOCKS5 프록시를 통해 대시 네트워크 연결 + + + &Connect through SOCKS5 proxy (default proxy): + SOCKS5 프록시를 거쳐 연결합니다(&C) (기본 프록시): + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu. + 창이 닫히는 경우 종료하지 않고 축소합니다. 이 옵션을 활성화하면 메뉴에서 종료를 선택하는 경우에만 어플리케이션이 종료됩니다. + + + Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items.<br/>%s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. + 거래 탭에 강조 표시된 메뉴 항목으로 나타나는 제3자 URL(예: 블록 탐색기). <br/>해당 URL의 %s 는 거래 해시에 의해 대체됩니다. 다중 URL은 수직 바 | 에 의해 분리됩니다. + + + Expert + 전문가 + + + This setting determines the amount of individual masternodes that an input will be anonymized through.<br/>More rounds of anonymization gives a higher degree of privacy, but also costs more in fees. + 이 설정은 얼마나 많은 개별 마스터노드를 거쳐 익명화를 진행할 지를 결정합니다.<br/>익명화를 위해 보다 많은 라운드를 거치는 것은 향상된 개인 정보 보호 수준을 제공하지만 더 많은 수수료가 발생합니다. + + + Whether to show coin control features or not. + 코인 제어 기능을 표시할 지 여부를 선택합니다. + + + Automatically start %1 after logging in to the system. + 시스템 로그인 후 %1을 자동으로 시작합니다. + + + &Start %1 on system login + 시스템 로그인 시 %1 시작(&S) + + + Enable coin &control features + 코인 제어 기능 활성화(&C) + + + &Spend unconfirmed change + 검증되지 않은 잔돈 쓰기 (&S) + + + &Network + 네트워크(&N) + + + Map port using &UPnP + UPnP를 사용하는 맵 포트(&U) + + + Proxy &IP: + 프록시 IP:(&i) + + + IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1) + 프록시 아이피 주소 (예. IPv4:127.0.0.1 / IPv6: ::1) + + + &Port: + 포트:(&P) + + + Port of the proxy (e.g. 9050) + 프록시 포트 번호 (예: 9050) + + + Used for reaching peers via: + 피어에 연결하기 위해 사용된 방법: + + + Shows, if the supplied default SOCKS5 proxy is used to reach peers via this network type. + 선택된 기본 SOCKS5 프록시가 이 네트워크 유형을 통해 피어에 연결하는 경우에 보여주기 + + + IPv4 + IPv4 + + + IPv6 + IPv6 + + + Tor + Tor + + + Connect to the Dash network through a separate SOCKS5 proxy for Tor hidden services. + Tor 숨김 서비스를 위한 별도의 SOCKS5 프록시를 사용하여 대시 네트워크에 연결 + + + Use separate SOCKS5 proxy to reach peers via Tor hidden services: + Tor 숨김 서비스로 피어에 접속하기 위해 별도의 SOCKS5 프록시 사용 + + + &Window + 창(&W) + + + &Hide the icon from the system tray. + 시스템 트레이에서 아이콘 숨기기(&H) + + + Hide tray icon + 트레이 아이콘을 숨깁니다. + + + Show only a tray icon after minimizing the window. + 창을 최소화한 후 트레이 아이콘만 표시합니다. + + + &Minimize to the tray instead of the taskbar + 작업 표시줄 대신 트레이로 최소화(&M) + + + M&inimize on close + 종료 시 최소화(&i) + + + &Display + 디스플레이(&D) + + + User Interface &language: + 사용자 인터페이스 언어:(&l) + + + The user interface language can be set here. This setting will take effect after restarting %1. + 이곳에서 사용자 인터페이스 언어를 설정 할 수 있습니다. 이 설정은 %1을/를 다시 시작할 때 적용됩니다. + + + Language missing or translation incomplete? Help contributing translations here: +https://www.transifex.com/projects/p/dash/ + 지정하려는 언어가 목록에 없거나 번역이 완성되지 않았다면? 다음의 주소에서 번역을 도와주세요: +https://www.transifex.com/projects/p/dash/ + + + User Interface Theme: + 사용자 인터페이스 테마: + + + &Unit to show amounts in: + 거래액을 표시할 단위(&U): + + + Choose the default subdivision unit to show in the interface and when sending coins. + 인터페이스에 표시하고 코인을 보낼 때 사용하는 기본 최소화 단위를 선택하십시오. + + + Decimal digits + 십진수 + + + Third party transaction URLs + 제3자 거래 URL + + + Active command-line options that override above options: + 위 옵션에 우선하여 명령줄 옵션을 활성화합니다. + + + Reset all client options to default. + 모든 클라이언트 옵션을 기본값으로 재설정 + + + &Reset Options + 옵션 초기화(&R) + + + &OK + 확인(&O) + + + &Cancel + 취소(&C) + + + default + 기본값 + + + none + 없음 + + + Confirm options reset + 옵션 초기화 확인 + + + Client restart required to activate changes. + 변경 사항을 적용하기 위해서는 프로그램 종료 후 재시작 하여야 합니다. + + + Client will be shut down. Do you want to proceed? + 클라이언트가 종료됩니다, 계속 진행하시겠습니까? + + + This change would require a client restart. + 이 변경 사항을 적용하기 위해서는 프로그램을 재시작하여야 합니다. + + + The supplied proxy address is invalid. + 입력한 프록시 주소를 인식할 수 없습니다. + + + + OverviewPage + + Form + 유형 + + + The displayed information may be out of date. Your wallet automatically synchronizes with the Dash network after a connection is established, but this process has not completed yet. + 표시된 정보가 오래된 것으로 보입니다. 당신의 지갑은 대시 네트워크에 연결되는 대로 동기화 될 예정입니다. 이 과정은 아직 종료되지 않았습니다. + + + Available: + 사용 가능: + + + Your current spendable balance + 현재 사용할 수 있는 잔고 + + + Pending: + 진행 중: + + + Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance + 전체 거래가 아직 확인 되지 않은 관계로, 아직 사용 가능한 잔고에 반영되지 않았습니다. + + + Immature: + 미성숙 잔고: + + + Mined balance that has not yet matured + 아직 사용할 수 없는 채굴된 잔고 + + + Balances + 잔고 + + + Unconfirmed transactions to watch-only addresses + 조회 전용 주소의 검증되지 않은 거래 + + + Mined balance in watch-only addresses that has not yet matured + 조회 전용 주소의 채굴된 잔액 중 사용할 수 있는 잔고로 발전되지 않은 미발전 잔고 + + + Total: + 총액: + + + Your current total balance + 당신의 현재 총 잔고 + + + Current total balance in watch-only addresses + 조회 전용 주소의 현재 잔고 + + + Watch-only: + 조회 전용: + + + Your current balance in watch-only addresses + 조회 전용 주소의 현재 잔고 + + + Spendable: + 사용 가능한 잔고: + + + PrivateSend + 프라이빗샌드 + + + Status: + 상태: + + + Enabled/Disabled + 활성화/비활성화 + + + Completion: + 완료: + + + Try to manually submit a PrivateSend request. + 프라이빗샌드 요청을 수동으로 제출합니다. + + + Reset the current status of PrivateSend (can interrupt PrivateSend if it's in the process of Mixing, which can cost you money!) + 프라이빗샌드의 현재 상태를 재설정합니다. (프라이빗샌드가 믹싱 단계에 있다면 이를 방해할 수도 있습니다. 이 경우에 수수료가 발생할 수 있습니다!) + + + Information about PrivateSend and Mixing + 프라이빗샌드와 믹싱에 관한 정보 + + + Info + 정보 + + + Amount and Rounds: + 금액과 라운드: + + + 0 DASH / 0 Rounds + 0 대시 / 0 라운드 + + + Submitted Denom: + 제출한 분할 단위: + + + n/a + 해당 사항 없음 + + + Recent transactions + 최근 거래 + + + Start/Stop Mixing + 믹싱 시작/정지 + + + PrivateSend Balance: + 프라이빗샌드 잔고 + + + The denominations you submitted to the Masternode.<br>To mix, other users must submit the exact same denominations. + 당신이 마스터노드에 제출한 분할 단위.<br>믹싱을 위해서는 다른 사용자들이 같은 단위를 제출하여야만 합니다. + + + (Last Message) + (지난 메시지) + + + Try Mix + 믹스하기 + + + Reset + 재설정 + + + out of sync + 동기화 되지 않음 + + + Automatic backups are disabled, no mixing available! + 자동 백업이 비활성화 되어 있어 믹싱이 불가능합니다! + + + Start Mixing + 믹싱을 시작합니다. + + + Stop Mixing + 믹싱을 정지합니다. + + + No inputs detected + 입력이 감지되지 않습니다. + + + %n Rounds + &n 라운드 + + + Found enough compatible inputs to anonymize %1 + %1 을/를 익명으로 처리하기 위해 충분한 호환 가능 입력을 찾았습니다. + + + Not enough compatible inputs to anonymize <span style='color:red;'>%1</span>,<br>will anonymize <span style='color:red;'>%2</span> instead + <span style='color:red;'>%1</span>을/를 익명으로 처리하기 위한 호환 가능 입력이 충분하지 않아, 이를 대신 하여 <span style='color:red;'>%2</span>을/를 <br>익명화 할 예정입니다. + + + Overall progress + 전체 과정 + + + Denominated + 단위 분할 완료 + + + Mixed + 믹싱 완료 + + + Anonymized + 익명화 처리 완료 + + + Denominated inputs have %5 of %n rounds on average + 분할된 단위 입력값은 평균적으로 %n 라운드 중 %5 라운드를 갖습니다. + + + keys left: %1 + 남은 키의 수: %1 + + + Disabled + 비활성화 + + + Very low number of keys left since last automatic backup! + 지난 자동 백업 이후 매우 적은 수의 키가 남아 있습니다! + + + We are about to create a new automatic backup for you, however <span style='color:red;'> you should always make sure you have backups saved in some safe place</span>! + 당신을 위한 새로운 자동 백업을 생성하고자 합니다. 그러나 <span style='color:red;'>언제나 안전한 장소에 백업 파일을 저장해두어야 한다는 점을 명심하세요</span>! + + + Note: You can turn this message off in options. + 참고: 옵션에서 이 메시지를 끌 수 있습니다. + + + WARNING! Something went wrong on automatic backup + 경고! 자동 백업에 문제가 발생했습니다. + + + ERROR! Failed to create automatic backup + 에러! 자동 백업 생성에 실패하였습니다. + + + Mixing is disabled, please close your wallet and fix the issue! + 믹싱이 비활성화 되었습니다. 지갑을 종료하고 해당 문제를 해결해야 합니다! + + + Enabled + 비활성화 됨 + + + see debug.log for details. + 세부 사항을 위해서는 debug.log 를 확인하세요. + + + WARNING! Failed to replenish keypool, please unlock your wallet to do so. + 경고! 키풀 보충에 실패하였습니다. 키풀 보충을 위해서는 지갑을 잠금 해제 하세요. + + + Last PrivateSend message: + + 지난 프라이빗샌드 메시지: + + + + N/A + 없음 + + + PrivateSend was successfully reset. + 프라이빗샌드가 성공적으로 재설정 되었습니다. + + + If you don't want to see internal PrivateSend fees/transactions select "Most Common" as Type on the "Transactions" tab. + 프라이빗샌드 수수료/거래의 내부 사항을 보고 싶지 않으면 '거래' 탭에서 유형을 클릭하고 '자주 사용된 항목'을 선택하세요. + + + PrivateSend requires at least %1 to use. + 프라이빗샌드를 사용하기 위해서는 최소한 %1이/가 필요합니다. + + + Wallet is locked and user declined to unlock. Disabling PrivateSend. + 지갑이 잠겨있으며 사용자가 잠금 해제를 거부하였습니다. 프라이빗샌드를 비활성화 합니다. + + + + PaymentServer + + Payment request error + 지불 요청 오류 + + + Cannot start dash: click-to-pay handler + 대시를 시작할 수 없습니다: 원클릭 지불 핸들러 + + + URI handling + URI 처리 + + + Payment request fetch URL is invalid: %1 + 지불 요청을 위해 불러온 URL이 유효하지 않습니다: %1 + + + Invalid payment address %1 + 유효하지 않은 지불 주소 %1 + + + URI cannot be parsed! This can be caused by an invalid Dash address or malformed URI parameters. + URI를 분석할 수 없습니다! 대시 주소가 유효하지 않거나 URI 파라미터 구성에 오류가 존재할 수 있습니다. + + + Payment request file handling + 지불 요청 파일 처리 + + + Payment request file cannot be read! This can be caused by an invalid payment request file. + 지불 요청 파일을 읽을 수 없습니다! 잘못된 지불 요청 파일에 의해 발생하는 오류일 수 있습니다. + + + Payment request rejected + 지불 요청이 거부됨 + + + Payment request network doesn't match client network. + 지급 요청 네트워크가 클라이언트 네트워크와 일치하지 않습니다. + + + Payment request expired. + 지불 요청이 만료됨 + + + Payment request is not initialized. + 지불 요청이 초기화 되지 않았습니다. + + + Unverified payment requests to custom payment scripts are unsupported. + 임의로 변경한 결제 스크립트 기반의 지불 요청 양식은 검증되기 전까지는 지원되지 않습니다. + + + Invalid payment request. + 잘못된 지불 요청입니다. + + + Requested payment amount of %1 is too small (considered dust). + (더스트를 고려할 때) 요청한 금액 %1의 양이 너무 적습니다. + + + Refund from %1 + %1 으로부터 환불 + + + Payment request %1 is too large (%2 bytes, allowed %3 bytes). + 지불 요청 금액인 %1은/는 너무 큽니다 (%2 바이트, %3 바이트까지 허용). + + + Error communicating with %1: %2 + %1과의 소통 에러: %2 + + + Payment request cannot be parsed! + 지불 요청을 분석 할 수 없습니다! + + + Bad response from server %1 + %1 서버로부터 잘못된 응답을 받았습니다. + + + Network request error + 네트워크 요청 에러 + + + Payment acknowledged + 지불이 진행 중입니다. + + + + PeerTableModel + + NodeId + 노드 ID + + + Node/Service + 노드/서비스 + + + User Agent + 사용자 에이전트 + + + Ping + Ping + + + + QObject + + Dash Core + 대시 코어 + + + %1 didn't yet exit safely... + %1가 아직 안전하게 종료되지 않았습니다... + + + Error reading masternode configuration file: %1 + 마스터노드 설정을 읽는 과정에서 에러가 발생하였습니다: %1 + + + Amount + 거래액 + + + Enter a Dash address (e.g. %1) + 대시 주소 입력 (예: %1) + + + %1 d + %1 일 + + + %1 h + %1 시간 + + + %1 m + %1 분 + + + %1 s + %1 초 + + + None + 없음 + + + N/A + 해당 사항 없음 + + + %1 ms + %1 밀리세컨드 + + + %n second(s) + %n 초 + + + %n minute(s) + %n 분 + + + %n hour(s) + %n 시간 + + + %n day(s) + %n 일 + + + %n week(s) + %n 주 + + + %n year(s) + %n 년 + + + %1 and %2 + %1 및 %2 + + + unknown + 알 수 없음 + + + + QObject::QObject + + Error: Specified data directory "%1" does not exist. + 에러: 지정한 데이터 디렉토리 "%1"은/는 존재하지 않습니다. + + + Error: Cannot parse configuration file: %1. Only use key=value syntax. + 에러: 설정 파일 :%1 을/를 분석 할 수 없습니다. key=value syntax 만 사용 가능합니다. + + + Error: %1 + 에러: %1 + + + + QRDialog + + QR-Code Title + QR 코드 표제 + + + QR Code + QR 코드 + + + &Save Image... + 이미지 저장(&S)... + + + Error creating QR Code. + QR 코드를 생성하는 데 에러가 발생하였습니다. + + + + QRGeneralImageWidget + + &Save Image... + 이미지 저장(&S)... + + + &Copy Image + 이미지 복사(&C) + + + Save QR Code + QR 코드 저장 + + + PNG Image (*.png) + PNG 이미지 (*.png) + + + + QRImageWidget + + &Save Image... + 이미지 저장(&S)... + + + &Copy Image + 이미지 복사(&C) + + + Save QR Code + QR 코드 저장 + + + PNG Image (*.png) + PNG 이미지 (*.png) + + + + RPCConsole + + Tools window + 도구 창 + + + &Information + 정보(&I) + + + General + 일반 + + + Name + 이름 + + + N/A + 없음 + + + Number of connections + 연결 수 + + + &Open + 열기(&O) + + + Startup time + 시작 시간 + + + Network + 네트워크 + + + Last block time + 최종 블록 시각 + + + Debug log file + 로그 파일 디버그 + + + Current number of blocks + 현재 블록 수 + + + Client version + 클라이언트 버전 + + + Using BerkeleyDB version + 사용 중인 BerkeleyDB 버전 + + + Block chain + 블록 체인 + + + Number of Masternodes + 마스터노드 수 + + + Memory Pool + 메모리 풀 + + + Current number of transactions + 현재 거래 수 + + + Memory usage + 메모리 사용량 + + + &Console + 콘솔(&C) + + + Clear console + 콘솔 지우기 + + + &Network Traffic + 네트워크 트래픽(&N) + + + &Clear + 지우기(&C) + + + Totals + 총액 + + + Received + 받음 + + + Sent + 보냄 + + + &Peers + 피어(&P) + + + Banned peers + 차단된 피어 + + + Select a peer to view detailed information. + 자세한 정보를 보려면 피어를 선택하세요. + + + Whitelisted + 화이트리스트에 포함 + + + Direction + 방향 + + + Version + 버전 + + + Starting Block + 블록 시작 + + + Synced Headers + 동기화 된 헤더 + + + Synced Blocks + 동기화 된 블록 + + + Wallet Path + 지갑 경로 + + + User Agent + 유저 에이전트 + + + Datadir + 데이터 폴더 + + + Open the %1 debug log file from the current data directory. This can take a few seconds for large log files. + 현재 데이터 디렉토리에서 %1 디버그 로그 파일을 엽니다. 용량이 큰 로그 파일들은 여는 데 수 초가 걸릴 수 있습니다. + + + Decrease font size + 글자 크기 축소 + + + Increase font size + 글자 크기 확대 + + + Services + 서비스 + + + Ban Score + 밴 스코어 + + + Connection Time + 접속 시간 + + + Last Send + 마지막으로 보낸 시간 + + + Last Receive + 마지막으로 받은 시간 + + + Ping Time + Ping 시간 + + + The duration of a currently outstanding ping. + 현재 Ping 지속 시간 + + + Ping Wait + Ping 대기 + + + Min Ping + 최소 Ping + + + Time Offset + 시간 오프셋 + + + &Wallet Repair + 지갑 복구(&W) + + + Salvage wallet + 지갑 복원 + + + Rescan blockchain files + 블록체인 파일을 다시 스캔 + + + Recover transactions 1 + 거래 1 복구 + + + Recover transactions 2 + 거래 2 복구 + + + Upgrade wallet format + 지갑 포맷 업그레이드 + + + The buttons below will restart the wallet with command-line options to repair the wallet, fix issues with corrupt blockhain files or missing/obsolete transactions. + 하단의 버튼은 지갑을 복구하고, 손상된 블록체인 파일 및 누락/오래된 거래로 인한 문제를 해결하기 위하여 명령줄 옵션을 통해 지갑을 재시작합니다. + + + -salvagewallet: Attempt to recover private keys from a corrupt wallet.dat. + -salvagewallet: 손상된 wallet.dat에서 개인 키 복원을 시도합니다. + + + -rescan: Rescan the block chain for missing wallet transactions. + -rescan: 누락된 지갑 거래를 위해 블록체인을 다시 검색합니다. + + + -zapwallettxes=1: Recover transactions from blockchain (keep meta-data, e.g. account owner). + -zapwallettxes=1: 블록체인에서 거래를 복원합니다 (메타 데이터는 유지, 예: 계정 소유자). + + + -zapwallettxes=2: Recover transactions from blockchain (drop meta-data). + -zapwallettxes=2: 블록체인에서 거래를 복원합니다 (메타 데이터 유지하지 않음). + + + -upgradewallet: Upgrade wallet to latest format on startup. (Note: this is NOT an update of the wallet itself!) + -upgradewallet: 시작 시 지갑을 최신 포맷으로 업그레이드합니다 (주의: 이 옵션은 지갑 자체의 업그레이드가 아닙니다!) + + + Wallet repair options. + 지갑 복구 옵션 + + + Rebuild index + 인덱스를 재구성합니다. + + + -reindex: Rebuild block chain index from current blk000??.dat files. + -reindex: 현재의 blk000??.dat 파일로부터 블록체인 인덱스를 재구성합니다. + + + &Disconnect + 접속 끊기(&D) + + + Ban for + 추방 + + + 1 &hour + 1시간(&h) + + + 1 &day + 1일(&d) + + + 1 &week + 1주(&w) + + + 1 &year + 1년(&y) + + + &Unban + 추방 취소(&U) + + + Welcome to the %1 RPC console. + %1 RPC 콘솔에 오신걸 환영합니다 + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + 기록을 찾아보려면 위 아래 화살표 키를, 화면을 지우려면 <b>Ctrl-L</b>키를 사용하십시오. + + + Type <b>help</b> for an overview of available commands. + 사용 가능한 명령을 둘러보려면 <b>help</b>를 입력하십시오. + + + WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramification of a command. + 주의: 사기범들이 사용자들에게 이곳에 명령을 입력하도록 유도하면서 지갑의 내용물을 탈취할 수 있습니다. 명령의 결과에 관해 충분히 숙지하기 전에는 이 콘솔을 사용하지 마십시오. + + + In: + In: + + + Out: + Out: + + + Network activity disabled + 네트워크 활동 비활성화 + + + %1 B + %1 B + + + %1 KB + %1 KB + + + %1 MB + %1 MB + + + %1 GB + %1 GB + + + (node id: %1) + (노드 ID: %1) + + + via %1 + %1 경유 + + + never + 없음 + + + Inbound + 인바운드 + + + Outbound + 아웃바운드 + + + Yes + + + + No + 아니오 + + + Unknown + 알 수 없음 + + + + ReceiveCoinsDialog + + Reuse one of the previously used receiving addresses.<br>Reusing addresses has security and privacy issues.<br>Do not use this unless re-generating a payment request made before. + 이전에 사용한 적 있는 받기 주소를 다시 사용합니다. <br>주소를 다시 사용하는 것은 보안 및 프라이버시 문제에 연결됩니다. <br>이전에 이루어진 지불 요청을 다시 생성하는 것이 아니면 해당 주소를 사용하지 마십시오. + + + R&euse an existing receiving address (not recommended) + 이미 존재하는 받기 주소를 다시 사용하기(권장하지 않음) (&R) + + + An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Dash network. + 지불 요청에 메시지를 첨부할 지 선택할 수 있습니다. 해당 메세지는 지불 요청이 열릴 때 표시될 것 입니다. 메모: 이 메시지는 대시 네트워크로 전송되지 않습니다. + + + &Message: + 메시지:(&M) + + + An optional label to associate with the new receiving address. + 새로운 받기 주소와 결합하는 임의의 라벨 + + + An optional message to attach to the payment request, which will be displayed when the request is opened.<br>Note: The message will not be sent with the payment over the Dash network. + 지불 요청에 메시지를 첨부할 지 선택할 수 있습니다. 해당 메세지는 지불 요청이 열릴 때 표시될 것 입니다. 메모: 이 메시지는 대시 네트워크로 전송되지 않습니다. + + + Use this form to request payments. All fields are <b>optional</b>. + 지급을 요청하기 위해 아래 형식을 사용하세요. 모든입력 값은 <b>선택 사항</b> 입니다. + + + &Label: + 라벨:(&L) + + + An optional amount to request. Leave this empty or zero to not request a specific amount. + 요청할 금액 입력칸은 선택 사항 입니다. 특정한 금액이 필요하지 않은 경우 빈 칸으로 두거나 0을 입력하세요. + + + &Amount: + 거래액:(&A) + + + &Request payment + 지불 요청(&R) + + + Clear all fields of the form. + 양식의 모든 필드를 지웁니다 + + + Clear + 지우기 + + + Request InstantSend + 인스턴트샌드 요청 + + + Requested payments history + 지불 요청 히스토리 + + + Show the selected request (does the same as double clicking an entry) + 선택된 요청을 표시합니다(더블 클릭으로 항목을 표시할 수 있습니다). + + + Show + 보기 + + + Remove the selected entries from the list + 목록에서 선택한 항목을 삭제합니다. + + + Remove + 삭제 + + + Copy URI + URI 복사 + + + Copy label + 라벨 복사 + + + Copy message + 메시지 복사 + + + Copy amount + 거래액 복사 + + + + ReceiveRequestDialog + + QR Code + QR 코드 + + + Copy &URI + URI 복사(&U) + + + Copy &Address + 주소 복사(&A) + + + &Save Image... + 이미지 저장(&S)... + + + Request payment to %1 + %1에 지불을 요청했습니다 + + + Payment information + 지불 정보 + + + URI + URI + + + Address + 주소 + + + Amount + 거래액 + + + Label + 라벨 + + + Message + 메시지 + + + InstantSend + 인스턴트샌드 + + + Yes + + + + No + 아니요 + + + Resulting URI too long, try to reduce the text for label / message. + URI 결과가 너무 깁니다. 라벨/메세지의 글을 줄이세요. + + + Error encoding URI into QR Code. + URI를 QR 코드로 인코딩하는 중 오류가 발생했습니다. + + + + RecentRequestsTableModel + + Date + 날짜 + + + Label + 라벨 + + + Message + 메시지 + + + (no label) + (라벨 없음) + + + (no message) + (메세지가 없습니다) + + + (no amount requested) + (요청한 거래액 없음) + + + Requested + 요청됨 + + + + SendCoinsDialog + + Send Coins + 코인들 보내기 + + + Coin Control Features + 코인 컨트롤 기능 + + + Inputs... + 입력... + + + automatically selected + 자동 선택 + + + Insufficient funds! + 자금이 부족합니다! + + + Quantity: + 수량: + + + Bytes: + 바이트: + + + Amount: + 금액: + + + Fee: + 수수료: + + + Dust: + 더스트: + + + After Fee: + 수수료 이후: + + + Change: + 잔돈: + + + If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address. + 이 기능이 활성화되면, 잔돈 주소가 공란이거나 무효인 경우 해당 잔돈은 새롭게 생성된 주소로 송금됩니다. + + + Custom change address + 사용자 지정 변경 주소 + + + Transaction Fee: + 거래 수수료: + + + Choose... + 선택... + + + Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until your have validated the complete chain. + 폴백수수료(fallbackfee)를 사용하는 경우 거래를 전송하는 데 수 시간, 수 일이 소요되거나 거래가 전송되지 않을 수 있습니다. 수수료를 수동으로 선택하시거나 완전한 블록체인에 승인할 때 까지 기다리실 것을 권장합니다. + + + Warning: Fee estimation is currently not possible. + 경고: 지금은 수수료 예측이 불가능합니다. + + + collapse fee-settings + 수수료 설정 접기 + + + Confirmation time target: + 승인 시간 목표: + + + PrivateSend + 프라이빗샌드 + + + InstantSend + 인스턴트샌드 + + + If the custom fee is set to 1000 duffs and the transaction is only 250 bytes, then "per kilobyte" only pays 250 duffs in fee,<br />while "at least" pays 1000 duffs. For transactions bigger than a kilobyte both pay by kilobyte. + 만일 사용자 지정 수수료가 1000 더프로 설정되어 있으나 거래는 250바이트에 불과한 경우, 'KB 당'은 오직 250 더프를 수수료로 지불하는 한편 <br />'최소'는 1000더프를 지불합니다. 1 KB보다 큰 거래를 진행하는 경우 두 옵션 모두 KB 당으로 지불합니다. + + + If the custom fee is set to 1000 duffs and the transaction is only 250 bytes, then "per kilobyte" only pays 250 duffs in fee,<br />while "total at least" pays 1000 duffs. For transactions bigger than a kilobyte both pay by kilobyte. + 만일 사용자 지정 수수료가 1000 더프로 설정되어 있으나 거래는 250바이트에 불과한 경우, 'KB 당'은 250 더프를 수수료로 지불하는 한편, <br />'전체 최소'는 1000더프를 지불합니다. 1 KB보다 큰 거래를 진행하는 경우 두 옵션 모두 KB 당으로 지불합니다. + + + Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks.<br />But be aware that this can end up in a never confirming transaction once there is more demand for dash transactions than the network can process. + 최소한의 수수료만 지불하기는 블록체인 내의 공간보다 적은 거래가 진행 중인 경우에만 제대로 작동합니다.<br />이 옵션을 선택하면 대시 네트워크가 처리할 수 있는 것 보다 많은 거래가 진행되는 경우 거래 확인이 이루어지지 않을 수도 있다는 점을 주의하세요. + + + per kilobyte + 킬로바이트 당 + + + Hide + 숨기기 + + + total at least + 최소 총액 + + + (read the tooltip) + (도구 팁을 읽어보세요) + + + Recommended: + 권장: + + + Custom: + 사용자 정의: + + + (Smart fee not initialized yet. This usually takes a few blocks...) + (Smart fee가 아직 초기화 되지 않았습니다. 이 과정에는 수 블록이 소요됩니다...) + + + normal + 일반 + + + fast + 빠름 + + + Confirm the send action + 보낸 내용 확인 + + + S&end + 보내기(&e) + + + Clear all fields of the form. + 양식의 모든 필드를 지웁니다 + + + Clear &All + 모두 지우기(&A) + + + Send to multiple recipients at once + 다수의 수령인에게 한번에 보내기 + + + Add &Recipient + 수령인 추가하기(&R) + + + Balance: + 잔고: + + + Copy quantity + 수량 복사 + + + Copy amount + 금액 복사 + + + Copy fee + 수수료 복사 + + + Copy after fee + 수수료 이후 복사 + + + Copy bytes + bytes 복사 + + + Copy dust + 더스트 복사 + + + Copy change + 잔돈 복사 + + + using + 사용 중 + + + anonymous funds + 익명 처리된 자금 + + + (privatesend requires this amount to be rounded up to the nearest %1). + (프라이빗샌드는 %1 가까이 라운드를 진행하기 위해서 이 금액을 필요로 합니다). + + + any available funds (not anonymous) + 이용 가능한 모든 자금(비익명 포함) + + + and InstantSend + 및 인스턴트샌드 + + + %1 to %2 + %1을(를) %2(으)로 + + + Are you sure you want to send? + 정말로 보내시겠습니까? + + + are added as transaction fee + 이/가 수수료로 추가되었습니다. + + + Total Amount = <b>%1</b><br />= %2 + 총 금액 = <b>%1</b><br /> = %2 + + + <b>(%1 of %2 entries displayed)</b> + <b>(%2 중 %1 입력값 표시됨)</b> + + + Confirm send coins + 코인 전송 확인 + + + The recipient address is not valid. Please recheck. + 수령인의 주소가 정확하지 않습니다. 다시 확인하세요. + + + The amount to pay must be larger than 0. + 지불하는 금액은 0보다 커야 합니다. + + + The amount exceeds your balance. + 잔고를 초과하였습니다. + + + The total exceeds your balance when the %1 transaction fee is included. + %1 의 거래 수수료가 추가되는 경우 총 금액이 잔고를 초과합니다. + + + Duplicate address found: addresses should only be used once each. + 중복된 주소 발견: 주소는 한 번만 사용할 수 있습니다. + + + Transaction creation failed! + 거래 생성에 실패하였습니다! + + + The transaction was rejected with the following reason: %1 + 거래가 다음과 같은 이유로 거절 되었습니다: %1 + + + A fee higher than %1 is considered an absurdly high fee. + %1 보다 높은 수수료는 너무 높은 수수료 입니다. + + + Payment request expired. + 지불 요청이 만료 되었습니다. + + + %n block(s) + %n 블록 + + + Pay only the required fee of %1 + 오직 %1 만의 수수료를 지불하기 + + + Estimated to begin confirmation within %n block(s). + %n 블록 내 거래 확인을 시작하기 위해 추정 완료 + + + Warning: Invalid Dash address + 경고: 잘못된 대시 주소입니다 + + + Warning: Unknown change address + 경고: 알 수 없는 변경 주소 입니다. + + + Confirm custom change address + 사용자 지정 변경 주소 확인 + + + The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure? + 변경을 위해 선택한 주소는 이 지갑에 포함되지 않았습니다. 지갑에 있는 일부 또는 모든 금액을 이 주소로 보낼 수 있습니다. 진행하시겠습니까? + + + (no label) + (라벨 없음) + + + + SendCoinsEntry + + This is a normal payment. + 이것은 정상적인 지불입니다. + + + Pay &To: + 송금할 대상(&T): + + + The Dash address to send the payment to + 이 대시 주소로 송금합니다. + + + Choose previously used address + 이전에 사용한 주소를 선택하십시오 + + + Alt+A + Alt+A + + + Paste address from clipboard + 클립보드로부터 주소 붙여넣기 + + + Alt+P + Alt+P + + + Remove this entry + 항목을 지웁니다 + + + &Label: + 라벨:(&L) + + + Enter a label for this address to add it to the list of used addresses + 사용된 주소 목록에 새 주소를 추가하기 위해 라벨 이름을 입력합니다. + + + A&mount: + 금액:(&m) + + + The fee will be deducted from the amount being sent. The recipient will receive a lower amount of Dash than you enter in the amount field. If multiple recipients are selected, the fee is split equally. + 수수료는 보내는 금액에서 차감됩니다. 수령인은 당신이 수량란에 입력한 것 보다 적은 금액을 받게 됩니다. 만일 다중의 수령인이 선택되었다면 수수료는 수령인간에 동일하게 분할됩니다. + + + S&ubtract fee from amount + 송금액에서 수수료 차감(&u) + + + Message: + 메시지: + + + A message that was attached to the dash: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Dash network. + 대시: URI에 첨부된 메시지는 거래와 함께 참고용으로 저장됩니다. 주의: 이 메시지는 대시 네트워크로 전송되지 않습니다. + + + This is an unauthenticated payment request. + 인증 되지 않은 지급 요청입니다. + + + This is an authenticated payment request. + 인증된 지급 요청 입니다. + + + Pay To: + 송금 대상: + + + Memo: + 메모: + + + Enter a label for this address to add it to your address book + 주소록에 추가하기 위해서는 라벨을 입력하세요. + + + + SendConfirmationDialog + + Yes + + + + + ShutdownWindow + + %1 is shutting down... + %1 을/를 종료 중입니다... + + + Do not shut down the computer until this window disappears. + 이 창이 사라지기 전까지 컴퓨터를 끄지 마세요. + + + + SignVerifyMessageDialog + + Signatures - Sign / Verify a Message + 서명 - 사인 / 메시지 확인 + + + &Sign Message + 메시지 서명(&S) + + + You can sign messages/agreements with your addresses to prove you can receive Dash sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + 당신의 주소로 대시를 받을 수 있다는 점을 증명하기 위하여 메시지/계약서에 서명할 수 있습니다. 내용이 분명하지 않거나 무작위인 경우 서명하지 않도록 주의하세요. 피싱 공격이 당신의 서명을 얻기 위해 유도하는 것일 수 있습니다. 오직 완전한 세부 내용을 갖추고 당신이 동의하는 내용에만 서명하세요. + + + The Dash address to sign the message with + 메시지에 서명하기 위한 대시 주소 + + + Choose previously used address + 이전에 사용한 주소를 선택합니다. + + + Alt+A + Alt+A + + + Paste address from clipboard + 클립보드로부터 주소 붙여넣기 + + + Alt+P + Alt+P + + + Enter the message you want to sign here + 이곳에 서명하려는 메시지를 입력하십시오. + + + Signature + 서명 + + + Copy the current signature to the system clipboard + 현재 서명을 시스템 클립보드에 복사 + + + Sign the message to prove you own this Dash address + 이 대시 주소가 당신의 소유임을 증명하기 위해 메시지에 서명하세요. + + + Sign &Message + 메시지에 서명(&M) + + + Reset all sign message fields + 메시지 필드의 모든 서명 재설정 + + + Clear &All + 모두 지우기(&A) + + + &Verify Message + 메시지 검증(&V) + + + Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction! + 메시지를 검증하기 위하여 아래에 수신인의 주소, 메시지(띄어쓰기, 들여쓰기, 행 나눔 등을 정확히 입력하기 위해 복사하여 입력) 및 서명을 입력하세요. 중간자 공격에 의한 피해를 피하기 위해 서명된 메시지 자체에 필요한 것 보다 많은 정보를 읽어 들이지 않도록 주의하세요. 이 기능은 서명 당사자가 해당 주소로 수신하였다는 점을 증명할 뿐이며, 거래의 발송 정보를 담지 않습니다. + + + The Dash address the message was signed with + 메시지에 서명된 대시 주소 + + + Verify the message to ensure it was signed with the specified Dash address + 특정 대시 주소에 서명된 것인지 확인하기 위하여 메시지를 검증합니다. + + + Verify &Message + 메시지 검증(&M) + + + Reset all verify message fields + 모든 검증 메시지 필드 재설정 + + + Click "Sign Message" to generate signature + 서명을 생성하려면 "메시지 서명"을 클릭하세요. + + + The entered address is invalid. + 입력한 주소를 인식할 수 없습니다. + + + Please check the address and try again. + 주소를 확인하고 다시 시도하십시오. + + + The entered address does not refer to a key. + 입력한 주소는 키에 적용되지 않습니다. + + + Wallet unlock was cancelled. + 지갑 잠금 해제를 취소했습니다. + + + Private key for the entered address is not available. + 입력한 주소에 대한 개인 키를 사용할 수 없습니다. + + + Message signing failed. + 메시지 서명에 실패했습니다. + + + Message signed. + 메시지에 서명했습니다. + + + The signature could not be decoded. + 서명을 해독할 수 없습니다. + + + Please check the signature and try again. + 서명을 확인하고 다시 시도하십시오. + + + The signature did not match the message digest. + 메시지 다이제스트와 서명이 일치하지 않습니다. + + + Message verification failed. + 메시지 검증에 실패했습니다. + + + Message verified. + 메시지를 검증했습니다. + + + + SplashScreen + + [testnet] + [테스트넷] + + + Version %1 + 버전 %1 + + + + TrafficGraphWidget + + KB/s + KB/s + + + + TransactionDesc + + Open for %n more block(s) + %n 개의 블록을 더 엽니다. + + + Open until %1 + %1 까지 열림 + + + conflicted + 충돌 + + + %1/offline + %1/오프라인 + + + 0/unconfirmed, %1 + 0/미승인, %1 + + + in memory pool + 메모리 풀 안에 있음 + + + not in memory pool + 메모리 풀 안에 없음 + + + abandoned + 버려진 + + + %1/unconfirmed + %1/미확인 + + + %1 confirmations + %1 확인 + + + verified via InstantSend + 인스턴트샌드를 통해 검증됨 + + + InstantSend verification in progress - %1 of %2 signatures + 인스턴트샌드 검증이 진행 중입니다 - %2 서명 중 %1 + + + InstantSend verification failed + 인스턴트샌드 검증에 실패하였습니다. + + + Status + 상태 + + + , has not been successfully broadcast yet + . 아직 성공적으로 통보되지 않았습니다. + + + , broadcast through %n node(s) + ,%n 노드에 통보하였습니다. + + + Date + 날짜 + + + Source + 소스 + + + Generated + 생성됨 + + + From + 으로부터 + + + unknown + 알 수 없음 + + + To + 에게 + + + own address + 자신의 주소 + + + watch-only + 조회 전용 + + + label + 라벨 + + + Credit + 입금 + + + matures in %n more block(s) + %n 블록 내에 이용 가능합니다. + + + not accepted + 허용되지 않음 + + + Debit + 출금 + + + Total debit + 총 출금 + + + Total credit + 총 입금 + + + Transaction fee + 거래 수수료 + + + Net amount + 순 잔고 + + + Message + 메시지 + + + Comment + 설명 + + + Transaction ID + 거래 ID + + + Output index + 출력 인덱스 + + + Transaction total size + 총 거래 크기 + + + Merchant + 사업체 + + + Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + 새로이 생성된 코인은 %1 블록을 경과해 성숙하는 경우에만 사용할 수 있습니다. 블록을 생성하면 블록체인에 추가되도록 네트워크에 통보하는 과정을 거치게 됩니다. 만일 블록체인 내에 추가되는 데 실패하면 해당 상태는 '허용되지 않음'으로 변하고 사용할 수 없게 됩니다. 이러한 현상은 다른 노드가 수 초 간격으로 당신과 동시에 블록을 생성하는 경우 드물게 발생할 수 있습니다. + + + Debug information + 디버그 정보 + + + Transaction + 거래 + + + Inputs + 입력 + + + Amount + 거래액 + + + true + + + + false + 거짓 + + + + TransactionDescDialog + + This pane shows a detailed description of the transaction + 이 창은 거래의 세부 내역을 보여줍니다. + + + Details for %1 + %1에 대한 세부 정보 + + + + TransactionTableModel + + Date + 날짜 + + + Type + 형식 + + + Address / Label + 주소 / 라벨 + + + Open for %n more block(s) + %n 개의 블록을 더 엽니다. + + + Open until %1 + %1 까지 열림 + + + Offline + 오프라인 + + + Unconfirmed + 미확인 + + + Abandoned + 버려진 + + + Confirming (%1 of %2 recommended confirmations) + 승인 중 (권장되는 승인 회수 %2 중 %1 승인 진행 중) + + + Confirmed (%1 confirmations) + 승인 됨 (%1 승인) + + + Conflicted + 충돌 + + + Immature (%1 confirmations, will be available after %2) + 이용 불가능한 상태 (%1 승인, %2 후에 사용 가능합니다) + + + This block was not received by any other nodes and will probably not be accepted! + 이 블록은 다른 노드로부터 전달받지 않은 것으로, 받아들여지지 않을 것입니다! + + + Generated but not accepted + 생성되었으나 받아들여지지 않음 + + + Received with + 받은 주소 + + + Received from + 보낸 주소 + + + Received via PrivateSend + 프라이빗샌드를 통해 받음 + + + Sent to + 보낸 주소 + + + Payment to yourself + 자신에게 지불 + + + Mined + 채굴 + + + PrivateSend Denominate + 프라이빗샌드 단위 분할 + + + PrivateSend Collateral Payment + 프라이빗샌드 콜래트럴 지불 + + + PrivateSend Make Collateral Inputs + 프라이빗샌드 콜래트럴 입력값 생성 + + + PrivateSend Create Denominations + 프라이빗샌드 분할 단위 생성 + + + PrivateSend + 프라이빗샌드 + + + watch-only + 조회 전용 + + + (n/a) + (없음) + + + (no label) + (라벨 없음) + + + Transaction status. Hover over this field to show number of confirmations. + 거래 상황. 마우스를 올리면 검증 횟수가 표시됩니다. + + + Date and time that the transaction was received. + 거래가 이루어진 날짜와 시각 + + + Type of transaction. + 거래의 종류 + + + Whether or not a watch-only address is involved in this transaction. + 이 거래에 조회 전용 주소가 포함 되는지의 여부 + + + User-defined intent/purpose of the transaction. + 거래의 의도/목적에 대한 사용자 정의 + + + Amount removed from or added to balance. + 잔고에서 출금되거나 잔고로 입금된 금액 + + + + TransactionView + + All + 전체 + + + Today + 오늘 + + + This week + 이번주 + + + This month + 이번 달 + + + Last month + 지난 달 + + + This year + 올 해 + + + Range... + 범위... + + + Most Common + 자주 사용된 항목 + + + Received with + 받은 주소 + + + Sent to + 보낸 주소 + + + PrivateSend + 프라이빗샌드 + + + PrivateSend Make Collateral Inputs + 프라이빗샌드 콜래트럴 입력값 생성 + + + PrivateSend Create Denominations + 프라이빗샌드 분할 단위 생성 + + + PrivateSend Denominate + 프라이빗샌드 단위 분할 + + + PrivateSend Collateral Payment + 프라이빗샌드 콜래트럴 지불 + + + To yourself + 나에게 + + + Mined + 채굴 + + + Other + 기타 + + + Enter address or label to search + 검색을 위해 주소 또는 라벨을 입력하세요. + + + Min amount + 최소 거래액 + + + Abandon transaction + 중단된 거래 + + + Copy address + 주소 복사 + + + Copy label + 라벨 복사 + + + Copy amount + 거래액 복사 + + + Copy transaction ID + 거래 아이디 복사 + + + Copy raw transaction + 원 거래 복사 + + + Copy full transaction details + 거래 세부 내역 복사 + + + Edit label + 라벨 수정 + + + Show transaction details + 거래 세부 내역 보기 + + + Export Transaction History + 거래 기록 내보내기 + + + Comma separated file (*.csv) + 쉼표로 구분된 파일 (*.csv) + + + Confirmed + 확인 완료 + + + Watch-only + 조회 전용 + + + Date + 날짜 + + + Type + 형식 + + + Label + 라벨 + + + Address + 주소 + + + ID + 아이디 + + + Exporting Failed + 내보내기 실패 + + + There was an error trying to save the transaction history to %1. + %1에 거래 기록을 저장하는데 에러가 발생하였습니다. + + + Exporting Successful + 내보내기 성공 + + + The transaction history was successfully saved to %1. + 거래 기록이 성공적으로 %1에 저장되었습니다. + + + Range: + 범위: + + + to + 에게 + + + + UnitDisplayStatusBarControl + + Unit to show amounts in. Click to select another unit. + 거래액을 표시하는 단위. 클릭하면 다른 단위를 선택할 수 있습니다. + + + + WalletFrame + + No wallet has been loaded. + 불러올 지갑이 없습니다. + + + + WalletModel + + Send Coins + 코인 보내기 + + + InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH. + 인스턴트샌드는 이와 같은 고액 송금 기능을 아직 지원하지 않습니다. 현재 인스턴트샌드로 보낼 수 있는 최대한의 금액은 %1 DASH로 제한되어 있습니다. + + + Used way too many inputs (>%1) for this InstantSend transaction, fees could be huge. + 인스턴트샌드 거래를 위해서 지나치게 큰 입력값입니다(>%1). 높은 수수료가 발생할 수 있습니다. + + + + WalletView + + &Export + 내보내기(&E) + + + Export the data in the current tab to a file + 현재 탭에 있는 데이터를 파일로 내보내기 + + + Selected amount: + 선택된 금액: + + + Backup Wallet + 지갑 백업 + + + Wallet Data (*.dat) + 지갑 데이터 (*.dat) + + + Backup Failed + 백업 실패 + + + There was an error trying to save the wallet data to %1. + 지갑 데이터를 %1 폴더에 저장하는 동안 오류가 발생했습니다. + + + Backup Successful + 백업 성공 + + + The wallet data was successfully saved to %1. + 지갑 정보가 %1에 성공적으로 저장되었습니다. + + + + dash-core + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + 선택한 주소에 바인드하고 언제나 수신합니다. IPv6인 경우 [host]:port 명령어 표기법을 사용합니다. + + + Error: Listening for incoming connections failed (listen returned error %s) + 오류: 들어오는 연결을 수신하는 데 실패했습니다 (수신 반환 오류 %s) + + + Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message) + 관련 경고를 받거나 매우 긴 포크를 감지하는 경우 명령을 실행합니다 (cmd의 %s이/가 메시지로 변경됩니다). + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + 지갑 거래가 변경되면 명령을 실행합니다(cmd의 %s이/가 TxID로 변경됩니다). + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + 베스트 블록이 변경되면 명령을 실행합니다(cmd에 있는 %s은/는 블록 해시로 변경됩니다). + + + Name to construct url for KeePass entry that stores the wallet passphrase + 지갑 암호문을 저장하는 KeePass에서 사용할 URL을 구성하기 위한 이름 + + + Set maximum size of high-priority/low-fee transactions in bytes (default: %d) + 우선 순위/적은 수수료 거래 크기의 최대치를 바이트로 설정하세요(디폴트: %d). + + + Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d) + 스크립트 인증 스레드의 갯수 설정 (%u-%d, 0 = 자동, <0 = 지정된 코어 개수만큼 사용 안함, 기본값: %d) + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + 출시 전의 테스트 빌드 입니다 - 스스로의 책임하에 사용하십시오 - 채굴이나 상업적 프로그램으로 사용하지 마십시오 + + + Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues. + 경고 : 모든 네트워크가 완전히 일치하지 않은 것으로 보입니다! 일부 채굴자들이 문제를 겪고 있을 수 있습니다. + + + Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade. + 경고: 피어와 완전히 일치하지 않는 것으로 보입니다! 당신 혹은 다른 노드들의 업그레이드가 필요합니다. + + + You must specify a masternodeprivkey in the configuration. Please see documentation for help. + 마스터노드 설정에서 마스터노드 개인 키를 지정하십시오. 도움이 필요하신 경우 문서를 확인하십시오. + + + Accept command line and JSON-RPC commands + 명령줄과 JSON-RPC 명령 수락 + + + Add a node to connect to and attempt to keep the connection open + 연결을 위해 노드를 추가하고 연결을 유지합니다. + + + Allow DNS lookups for -addnode, -seednode and -connect + -addnode, -seednode, -connect 에 대해 DNS 탐색 허용 + + + Already have that input. + 이미 입력된 값입니다. + + + Block creation options: + 블록 생성 옵션: + + + Cannot downgrade wallet + 지갑을 다운그레이드 할 수 없습니다. + + + Cannot write default address + 기본 주소에 기록할 수 없습니다. + + + Collateral not valid. + 유효하지 않은 콜래트럴입니다. + + + Connect to a node to retrieve peer addresses, and disconnect + 피어 주소를 검색하기 위해 노드에 연결하고 검색이 끝나면 연결을 해제합니다. + + + Connection options: + 연결 설정 : + + + Corrupted block database detected + 손상된 블록 데이터베이스가 감지되었습니다 + + + Debugging/Testing options: + 디버깅 및 테스팅 옵션: + + + Do not load the wallet and disable wallet RPC calls + 지갑을 로딩하지 마십시오. 또한 지갑 RPC 연결을 비활성화 하십시오. + + + Do you want to rebuild the block database now? + 블록 데이터베이스를 지금 다시 구축하시겠습니까? + + + Done loading + 로딩 완료 + + + Entries are full. + 항목이 가득 찼습니다. + + + Error initializing block database + 블록 데이터베이스를 초기화하는데 오류가 발생하였습니다. + + + Error initializing wallet database environment %s! + 지갑 데이터베이스 환경을 초기화하는데 오류가 발생하였습니다 %s! + + + Error loading block database + 블록 데이터베이스를 로딩하는 데 오류가 발생하였습니다. + + + Error opening block database + 블록 데이터베이스를 여는 데 오류가 발생하였습니다. + + + Error reading from database, shutting down. + 데이터베이스를 불러오는 데 오류가 발생하였습니다. 곧 종료됩니다. + + + Error + 에러 + + + Error: Disk space is low! + 에러: 디스크 공간이 부족합니다! + + + Failed to listen on any port. Use -listen=0 if you want this. + 포트를 감지하는 데 실패하였습니다. 이를 진행하시려면 -listen=0 을 사용하세요. + + + (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data) + (1 = 거래 메타 데이터를 유지함 예. 계정 소유주와 지불 요청 정보, 2 = 거래 메타 데이터 파기) + + + -maxtxfee is set very high! Fees this large could be paid on a single transaction. + -maxtxfee 값이 너무 크게 설정 되었습니다! 이 거래에 너무 큰 수수료가 지불 됩니다. + + + A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s) + 수수료 추정 기능이 충분한 데이터를 축적하지 않은 상태에서 사용 될 수수료 비율(%s/kB) (디폴트: %s) + + + Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d) + 거래를 중계하지 않은 상태라도 화이트리스트에 포함된 피어에서 수신된 거래 중계를 받습니다. (디폴트:%d) + + + Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times + 특정 소스에서의 JSON-RPC 연결을 허용합니다. 이 설정은 다음에 유효합니다. <ip> 하나의 IP주소 (예 1.2.3.4), 네트워크/넷마스크 (예 1.2.3.4/255.255.255.0) 혹은 네트워크/CIDR (예 1.2.3.4/24). 이 옵션은 복수로 설정 할 수 있습니다. + + + Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6 + 선택한 주소와 이에 연결된 화이트리스트 피어를 바인드 합니다. IPv6인 경우 [host]:port 명령어 표기법을 사용합니다. + + + Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces) + JSON-RPC 연결을 수신하기 위해 선택한 주소를 바인드 합니다. IPv6인 경우 [host]:port 명령어 표기법을 사용합니다. 이 옵션은 복수로 설정할 수 있습니다. (디폴트: 모든 인터페이스를 바인드) + + + Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality) + umask 077 를 대신하여 시스템 기본 권한으로 새 파일을 만듭니다. (지갑 기능이 비활성화 상태에서만 유효합니다) + + + Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup + 시작 시 모든 지갑 거래를 삭제하고 -rescan을 통해 블록체인의 특정 부분만을 복구합니다. + + + Error loading %s: You can't enable HD on a already existing non-HD wallet + %s 로딩 에러: HD 지갑이 아닌 경우 HD를 활성화 할 수 없습니다. + + + Found unconfirmed denominated outputs, will wait till they confirm to continue. + 확인 되지 않은 분할 단위 출력을 찾았습니다. 계속하기 위해 확인될 때까지 기다립니다. + + + How thorough the block verification of -checkblocks is (0-4, default: %u) + -checkblocks을 통한 철저한 블록 점검 (0-4, 디폴트: %u) + + + If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u) + paytxfee가 설정되어 있지 않다면 평균 n 블록 내에 승인이 이루어지도록 충분한 수수료를 포함해 주세요. (디폴트: %u) + + + 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: %s, testnet: %s) + 이 블록이 체인에 있으면 해당 블록과 그 조상이 유효하며, 스크립트 확인을 건너 뛸 수 있습니다. (0은 모두 확인, 기본값: %s, 테스트넷: %s) + + + Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions) + 다음을 위한 금액이 유효하지 않습니다 -maxtxfee=<amount>: '%s' (거래가 막히는 상황을 방지하게 위해 적어도 %s 의 최소 중계 수수료를 지정해야 합니다) + + + Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u) + getrawtransaction를 RPC CALL를 통해 완전한 거래 인덱스 유지 (기본값: %u) + + + Make sure to encrypt your wallet and delete all non-encrypted backups after you verified that wallet works! + 해당 지갑이 작동하는 지 여부를 확인한 이후에는 당신의 지갑을 암호화하고, 암호화하지 않은 지갑 백업은 지워야 한다는 점을 주의하세요! + + + Maximum size of data in data carrier transactions we relay and mine (default: %u) + 중계 및 채굴 시 데이터 운송 거래에서 데이터의 최대 크기 (디폴트: %u) + + + Number of seconds to keep misbehaving peers from reconnecting (default: %u) + 이상 행동을 하는 피어를 다시 연결시키는데 걸리는 시간 (디폴트: %u) + + + Output debugging information (default: %u, supplying <category> is optional) + 디버그 정보 출력 (디폴트: %u, <category> 제공 선택 가능) + + + Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s) + Tor 숨김 서비스로 피어에 접속하기 위해 별도의 SOCKS5 프록시를 사용하기 (디폴트: %s) + + + User defined mnemonic for HD wallet (bip39). Only has effect during wallet creation/first start (default: randomly generated) + HD 지갑을 위해 사용자가 지정한 연상 기호 암호문(bip39)은 지갑 생성/첫 시작에만 효력이 있습니다. (디폴트: 무작위 생성) + + + User defined seed for HD wallet (should be in hex). Only has effect during wallet creation/first start (default: randomly generated) + HD 지갑을 위해 사용자가 지정한 시드(hex여야 함)은 지갑 생성/첫 시작시에만 효력이 있습니다. (디폴트: 무작위 생성) + + + Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway + 화이트리스트에 포함된 피어는 이미 멤풀에 포함되어 있다고 하더라도 DoS 추방이 되지 않으며, 이들의 거래는 항상 중계됩니다. 이는 예컨대 게이트웨이에서 유용합니다. + + + You need to rebuild the database using -reindex-chainstate to change -txindex + -txindex를 변경하기 위해서는 -reindex- 체인 상태를 사용하여 데이터를 재구성해야 합니다. + + + (default: %s) + (디폴트: %s) + + + Accept public REST requests (default: %u) + 공개 REST 요청을 허가 (디폴트: %u) + + + Always query for peer addresses via DNS lookup (default: %u) + DNS lookup을 통해 항상 피어주소에 대한 쿼리 보내기 (디폴트: %u) + + + Can't mix: no compatible inputs found! + 믹스할 수 없습니다: 호환 가능한 입력값을 찾을 수 없습니다! + + + Chain selection options: + 체인 선택 옵션: + + + Connect through SOCKS5 proxy + SOCK5 프록시를 통해 연결 + + + Connect to KeePassHttp on port <port> (default: %u) + 포트 <port>로 KeePassHttp에 접속합니다. (디폴트: %u) + + + Enable the client to act as a masternode (0-1, default: %u) + 클라이언트가 마스터노드로 활동하도록 활성화 합니다. (0-1, 디폴트: %u) + + + Entry exceeds maximum size. + 입력값이 최대치를 초과하였습니다. + + + Error loading %s: You can't disable HD on a already existing HD wallet + %s 로딩 에러: 이미 존재하는 HD 지갑에서 HD를 비활성화 할 수 없습니다. + + + Failed to load fulfilled requests cache from + 다음으로부터 수행된 요청 캐시를 불러오는 데 실패하였습니다. + + + Failed to load governance cache from + 다음으로부터 거버넌스 캐시를 불러오는 데 실패하였습니다. + + + Failed to load masternode cache from + 다음으로부터 마스터노드 캐시를 불러오는 데 실패하였습니다. + + + Failed to load masternode payments cache from + 다음으로부터 마스터노드 지불 캐시를 불러오는 데 실패하였습니다. + + + Found enough users, signing ( waiting %s ) + 충분한 사용자를 감지하였습니다. 신호를 보내는 중입니다. ( %s 기다리기 ) + + + Found enough users, signing ... + 충분한 사용자를 감지하였습니다. 신호를 보내는 중입니다... + + + How many blocks to check at startup (default: %u, 0 = all) + 시작 시 점검할 블록 갯수 (기본값: %u, 0 = 모두) + + + Importing... + 가져오는 중... + + + Include IP addresses in debug output (default: %u) + 디버그 출력에 IP주소 포함하기 (기본값: %u) + + + Incompatible mode. + 호환 불가 모드 + + + Incompatible version. + 호환 불가 버전 + + + Incorrect or no genesis block found. Wrong datadir for network? + 올바르지 않거나 생성된 블록을 찾을 수 없습니다. 네트워크 데이터 디렉토리가 잘못된 것일 수 있습니다. + + + Information + 정보 + + + Input is not valid. + 유효하지 않은 입력입니다. + + + Insufficient funds. + 잔고가 충분하지 않습니다. + + + Invalid -onion address: '%s' + 잘못된 -onion 주소입니다: '%s' + + + Invalid -proxy address: '%s' + 잘못된 -proxy 주소입니다: '%s' + + + Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s) + 유효하지 않은 금액입니다. -paytxfee=<amount>: '%s' (최소 %s 이상이어야 합니다) + + + Keypool ran out, please call keypoolrefill first + Keypool을 모두 사용하였습니다. 우선 keypoolrefill을 호출하십시오. + + + Listen for JSON-RPC connections on <port> (default: %u or testnet: %u) + JSON-RPC 연결을 수신합니다. <port> (기본값: %u 혹은 테스트넷: %u) + + + Listen for connections on <port> (default: %u or testnet: %u) + 연결을 수신합니다. <port>(디폴트: %u 혹은 테스트넷: %u) + + + Loading banlist... + 추방 리스트를 불러오는 중... + + + Loading fulfilled requests cache... + 수행된 요청 캐시를 불러오는 중... + + + Loading masternode cache... + 마스터노드 캐시를 불러오는 중... + + + Loading masternode payment cache... + 마스터노드 지불 캐시를 불러오는 중... + + + Lock is already in place. + 이미 잠금 상태입니다. + + + Lock masternodes from masternode configuration file (default: %u) + 마스터노드 설정 파일로부터 마스터노드를 잠급니다. (디폴트: %u) + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: %u) + 접속 당 최대 수신 버퍼, <n>*1000 bytes (디폴트: %u) + + + Maximum per-connection send buffer, <n>*1000 bytes (default: %u) + 접속 당 최대 전송 버퍼, <n>*1000 bytes (디폴트: %u) + + + Mixing in progress... + 믹싱이 진행 중입니다... + + + Mnemonic passphrase is too long, must be at most 256 characters + 연상 기호 암호문이 너무 깁니다. 최대 256 글자까지 허용됩니다. + + + Need to specify a port with -whitebind: '%s' + -whitebind를 통해 포트를 지정해야 합니다: '%s' + + + No Masternodes detected. + 마스터노드가 탐지되지 않습니다. + + + No compatible Masternode found. + 호환되는 마스터노드를 찾을 수 없습니다. + + + Not in the Masternode list. + 마스터노드 리스트에 없습니다. + + + Only connect to nodes in network <net> (ipv4, ipv6 or onion) + 오직 네트워크에서만 노드에 접속합니다. <net>(ipv4, ipv6 혹은 onion) + + + Port: %d + 포트: %d + + + Prepend debug output with timestamp (default: %u) + 디버그 출력 앞에 타임 스탬프를 추가합니다. (기본값: %u) + + + Rebuild chain state and block index from the blk*.dat files on disk + 디스크상의 blk*.dat 파일로부터 블록체인 상태와 색인을 재구성합니다. + + + Rebuild chain state from the currently indexed blocks + 현재 색인된 블록으로부터 테인 상태를 재구성합니다. + + + Send trace/debug info to debug.log file (default: %u) + 추적/디버그 정보를 debug.log 파일로 보냅니다. (디폴트: %u) + + + Send transactions as zero-fee transactions if possible (default: %u) + 가능한 경우 수수료 제로 방식으로 거래합니다. (디폴트: %u) + + + Set key pool size to <n> (default: %u) + 키풀 크기를 <n> 로 설정합니다. (기본값: %u) + + + Set the number of threads to service RPC calls (default: %d) + RPC 호출 서비스를 위한 스레드의 개수 설정 (기본값: %d) + + + Specify configuration file (default: %s) + 설정 파일을 지정합니다. (기본값: %s) + + + Specify connection timeout in milliseconds (minimum: 1, default: %d) + 밀리세컨드 단위로 연결 제한 시간을 설정합니다. (최소값: 1, 기본값: %d) + + + Specify masternode configuration file (default: %s) + 마스터노드 설정 파일을 지정합니다. (디폴트: %s) + + + Specify pid file (default: %s) + pid 파일을 지정합니다. (기본값: %s) + + + Spend unconfirmed change when sending transactions (default: %u) + 거래를 보낼 때 검증되지 않은 잔돈 쓰기 (기본값: %u) + + + Submitted following entries to masternode: %u / %d + 다음의 입력값을 마스터노드에 제출하기: %u / %d + + + Submitted to masternode, waiting for more entries ( %u / %d ) %s + 마스터노드에 제출, 더 많은 입력값을 기다리는 중 (%u / %d ) %s + + + Submitted to masternode, waiting in queue %s + 마스터노드에 제출, 대기열에서 기다리는 중 %s + + + Synchroning blockchain... + 블록체인 동기화 중... + + + Synchronization failed + 동기화에 실패하였습니다. + + + Synchronization finished + 동기화가 끝났습니다. + + + Synchronizing masternodes... + 마스터노드 동기화 중... + + + This is not a Masternode. + 이는 마스터노드가 아닙니다. + + + Threshold for disconnecting misbehaving peers (default: %u) + 비정상적인 피어의 연결을 차단시키기 위한 임계값 (기본값: %u) + + + Too many %f denominations, removing. + %f 분할 단위가 너무 많습니다. 삭제합니다. + + + Tor control port password (default: empty) + Tor 관리 포트 암호 (기본값: 공란) + + + Tor control port to use if onion listening enabled (default: %s) + onion 열림이 활성화시 Tor 관리 포트 사용 (기본값: %s) + + + Unable to start HTTP server. See debug log for details. + HTTP 서버를 시작할 수 없습니다. 자세한 사항은 디버그 로그를 확인 하세요. + + + Unknown response. + 알 수 없는 반응. + + + Unsupported argument -benchmark ignored, use -debug=bench. + 지원하지 않는 인수 -benchmark 은 무시됩니다, -debug=bench 형태로 사용하세요. + + + Unsupported argument -debugnet ignored, use -debug=net. + 지원하지 않는 인수 -debugnet 은 무시됩니다, -debug=net 형태로 사용하세요. + + + Unsupported argument -tor found, use -onion. + 지원하지 않는 인수 -tor를 찾았습니다. -onion를 사용해주세요. + + + Upgrade wallet to latest format on startup + 시작 시 지갑 포맷을 최신으로 업그레이드 합니다 + + + Use KeePass 2 integration using KeePassHttp plugin (default: %u) + KeePassHttp 플러그인을 사용하여 KeePass 2 통합을 사용합니다. (디폴트: %u) + + + Use UPnP to map the listening port (default: %u) + 수신 포트를 맵핑하기 위해 UPnP 사용 (디폴트: %u) + + + Use the test chain + 테스트 체인 사용 + + + User Agent comment (%s) contains unsafe characters. + 사용자 정의 코멘트 (%s)에 안전하지 못한 글자가 포함되어 있습니다. + + + Will retry... + 다시 시도할 예정입니다... + + + Invalid masternodeprivkey. Please see documenation. + 유효하지 않은 masternodeprivkey 입니다. 문서를 확인하세요. + + + Can't find random Masternode. + 임의의 마스터노드를 찾을 수 없습니다. + + + Can't mix while sync in progress. + 동기화가 진행되는 동안 믹싱할 수 없습니다. + + + Could not parse masternode.conf + masternode.conf 를 분석할 수 없습니다. + + + Invalid netmask specified in -whitelist: '%s' + -whitelist: '%s'로 지정된 넷마스크가 유효하지 않습니다. + + + Invalid port detected in masternode.conf + masternode.conf 에서 유효하지 않은 포트가 감지되었습니다. + + + Invalid script detected. + 유효하지 않은 스크립트가 감지되었습니다. + + + KeePassHttp id for the established association + 설립된 단체를 위한 KeePassHttp 아이디 + + + KeePassHttp key for AES encrypted communication with KeePass + KeePass를 통해 AES 암호화된 의사 소통을 위한 KeePassHttp 키 + + + Keep at most <n> unconnectable transactions in memory (default: %u) + 최대 <n>개의 연결할 수 없는 거래를 메모리에 저장 (기본값: %u) + + + Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) + 모든 대시만의 기능(마스터노드, 프라이빗샌드, 인스턴트샌드, 거버넌스)을 비활성화 (0-1, 디폴트: %u) + + + %s file contains all private keys from this wallet. Do not share it with anyone! + %s 파일에는이 지갑의 모든 프라이빗키가 들어 있습니다. 절대 공유하지 마십시오! + + + Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) + 자신의 IP 주소를 탐색 (디폴트: 수신하는 경우 및 -externalip 또는 -proxy 가 없는 경우 1) + + + Do not keep transactions in the mempool longer than <n> hours (default: %u) + 멤풀에 있는 거래 기록을 <n>시간 후 부터는 유지하지 않기 (디폴트: %u) + + + Enable InstantSend, show confirmations for locked transactions (0-1, default: %u) + 인스턴트샌드를 활성화하고 잠긴 거래를 위한 거래 확인을 보여줍니다. (0-1, 디폴트: %u) + + + Enable multiple PrivateSend mixing sessions per block, experimental (0-1, default: %u) + 실험적으로 블록당 다수의 프라이빗샌드 믹싱 세션을 활성화합니다. (0-1, 디폴트: %u) + + + Enable use of automated PrivateSend for funds stored in this wallet (0-1, default: %u) + 이 지갑에 보관된 자금을 위해 자동화된 프라이빗샌드를 활성화 합니다. (0-1. 디폴트: %u) + + + Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID) + 지갑의 인스턴트샌드 거래가 성공적으로 잠기면 명령을 실행합니다. (cmd의 %s가 TxID로 대체됩니다) + + + Extra transactions to keep in memory for compact block reconstructions (default: %u) + 컴팩트 블록 재구성을 위해 메모리에 보관해야 하는 추가 트랜잭션 (default: %u) + + + Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. + 백업 생성에 실패하였습니다. 파일이 이미 존재합니다! 이는 당신이 60초 내에 지갑을 재시작 하는 경우 발생할 수 있습니다. 이러한 내용을 확인하시고 진행해 주세요. + + + Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s) + 해당 금액(%s/kB) 보다 적은 수수료는 중계, 채굴, 거래 생성에서 수수료 면제로 간주됩니다 (기본값: %s) + + + Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s) + 해당 금액(%s/kB) 보다 적은 수수료는 수수료 면제로 간주됩니다.(기본값: %s) + + + If <category> is not supplied or if <category> = 1, output all debugging information. + <category>가 제공되지 않거나 <category> = 1 인 경우, 모든 디버깅 정보를 출력 + + + InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH. + 인스턴트샌드는 이와 같은 고액 송금 기능을 아직 지원하지 않습니다. 현재 인스턴트샌드로 보낼 수 있는 최대한의 금액은 %1 DASH로 제한되어 있습니다. + + + InstantSend requires inputs with at least %d confirmations, you might need to wait a few minutes and try again. + 인스턴트샌드는 최소 %d의 확인을 거친 입력값을 필요로 합니다. 몇 분 더 기다리신 후 다시 시도하세요. + + + Maintain a full address index, used to query for the balance, txids and unspent outputs for addresses (default: %u) + 전체 주소 인덱스를 유지, 잔고를 위해 쿼리를 사용, 주소를 위한 txid와 사용되지 않은 출력 (디폴트: %u) + + + Maintain a full spent index, used to query the spending txid and input index for an outpoint (default: %u) + 전체 이용한 인덱스를 유지합니다. 이는 아웃포인트를 위한 이용 txid와 입력 인덱스를 쿼리하는 데 사용됩니다. (디폴트: %u) + + + Maintain a timestamp index for block hashes, used to query blocks hashes by a range of timestamps (default: %u) + 블록 해시를 위한 타임스탬프 인덱스를 유지합니다. 이는 범위 내의 타임스탬프로 블록 해시를 쿼리하는 데 사용됩니다. (디폴트: %u) + + + Maintain at most <n> connections to peers (temporary service connections excluded) (default: %u) + 최대한 <n>개의 피어 연결을 유지합니다. (일시적인 서비스 연결은 제외) (디폴트: %u) + + + PrivateSend uses exact denominated amounts to send funds, you might simply need to anonymize some more coins. + 프라이빗샌드는 송금을 위하여 정확한 분할 단위 금액을 사용합니다. 단순히 더 많은 코인을 익명화함으로써 문제를 해결할 수 있을 지도 모릅니다. + + + Prune configured below the minimum of %d MiB. Please use a higher number. + 축소 환경이 최소치인 %d MiB 이하로 설정되어 있습니다. 더 높은 값을 사용하십시오. + + + Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node) + 블록 축소: 마지막 지갑 동기화가 축소된 데이터보다 오래 되었습니다. -reindex가 필요합니다. (축소된 노드의 경우 모든 블록체인을 재다운로드 하십시오) + + + Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u) + 매 프록시 연결시 인증 정보를 무작위화 합니다. 이는 Tor 스트림 아이솔레이션 기능을 활성화시킵니다. (디폴트: %u) + + + Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again. + 블록 축소 모드에서는 재검색이 불가능 합니다. -reindex 명령을 사용해서 모든 블록체인을 다시 다운로드 해야 합니다. + + + Specify full path to directory for automatic wallet backups (must exist) + 자동 지갑 백업을 위한 디렉토리의 전체 경로를 지정하십시오. (꼭 존재하야 합니다) + + + Support filtering of blocks and transaction with bloom filters (default: %u) + 블룸 필터를 통해 블록과 거래의 필터링을 지원합니다. (기본값: %u) + + + The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct + 블록 데이터베이스에 미래로부터 출연한 블록이 포함되어 있습니다. 이는 사용자의 컴퓨터의 날짜와 시간이 올바르게 설정되어 있지 않기 때문일 수 있습니다. 사용자 컴퓨터의 날짜와 시간이 올바르게 설정되어있는 경우에만 블록 데이터베이스를 재구성하십시오. + + + The transaction amount is too small to send after the fee has been deducted + 수수료를 지불하기에는 거래액이 너무 적습니다. + + + Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. + 네트워크 버전의 총 문자 길이(%i)가 최대 길이(%i)를 초과합니다. UA코멘트의 갯수나 길이를 줄이십시오. + + + Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index. + 전체 모드에서는 트랜잭션 색인을 비활성화 할 수 없습니다. -litemode 명령 행 스위치로 시작하거나 트랜잭션 색인을 사용 가능하게 하십시오. + + + Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) + 아웃바운드 트래픽을 설정된 목표치 이하로 유지하기 (24시간당 MiB기준), 0 = 무제한 (기본값: %d) + + + Unable to locate enough PrivateSend denominated funds for this transaction. + 이 거래를 위해 필요한 충분한 프라이빗샌드 분할 단위 자금을 위치할 수 없습니다. + + + Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported. + 지원하지 않는 인수 -socks를 찾았습니다. 설정된 SOCKS의 버전은 더이상 사용할 수 없으며, SOCK5 프록시만을 지원합니다. + + + Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay. + 지원하지 않는 인수 -whitelistalwaysrelay 는 무시됩니다, -whitelistrelay 나 -whitelistforcerelay 를 사용해 주세요. + + + Use UPnP to map the listening port (default: 1 when listening and no -proxy) + 수신 포트를 맵핑하기 위해 UPnP 사용 (기본값: 수신하는 경우 및 -proxy가 없는 경우 1) + + + WARNING! Failed to replenish keypool, please unlock your wallet to do so. + 경고! 키풀을 보충하는 데 실패하였습니다. 키풀 보충을 위해서는 지갑을 잠금 해제 하세요. + + + Wallet is locked, can't replenish keypool! Automatic backups and mixing are disabled, please unlock your wallet to replenish keypool. + 지갑이 잠금 상태 입니다. 키풀을 보충할 수 없습니다! 자동 백업과 믹싱이 비활성화 됩니다. 키풀 보충을 위해서는 지갑을 잠금 해제 하세요. + + + Warning: Unknown block versions being mined! It's possible unknown rules are in effect + 경고: 알려지지 않은 버전의 블록이 채굴되었습니다! 알려지지 않은 규칙이 적용되었을 가능성이 있습니다. + + + You are starting in lite mode, all Dash-specific functionality is disabled. + 라이트 모드로 시작하면 모든 대시 관련 기능이 비활성화됩니다. + + + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain + 축소 모드를 해제하고 데이터베이스를 재구성 하기 위해 -reindex를 사용해야 합니다. 이 명령은 모든 블록체인을 다시 다운로드 할 것 입니다. + + + (%d could be used only on mainnet) + (%d 은/는 오직 메인넷에서만 이용할 수 있습니다) + + + (default: %u) + (기본값: %u) + + + (must be %d for mainnet) + (메인넷을 위해서는 %d여야 합니다) + + + -maxmempool must be at least %d MB + -maxmempool은 최소한 %d MB가 필요합니다 + + + <category> can be: + <category> 지정 가능: + + + Append comment to the user agent string + 사용자 에이전트 문자열에 코멘트 첨부 + + + Automatic backups disabled + 자동 백업 해제 + + + Automatically create Tor hidden service (default: %d) + Tor 숨김 서비스를 자동적으로 생성 (기본값: %d) + + + ERROR! Failed to create automatic backup + 오류! 자동 백업 생성에 실패하였습니다. + + + Enable publish hash block in <address> + <address>에 대한 해시 블록 공개 활성화 + + + Enable publish hash of governance objects (like proposals) in <address> + <address> 에서 거버넌스 객체(예: 제안서)의 해시 게시 사용 + + + Enable publish hash of governance votes in <address> + <address> 에서 거버넌스 투표의 공개 해시 게시 사용 + + + Enable publish hash transaction (locked via InstantSend) in <address> + <address>의 공개 해시 거래를 활성화합니다 (인스턴트샌드를 통해 잠김) + + + Enable publish hash transaction in <address> + <address>의 공개 해시 거래를 활성화합니다. + + + Enable publish raw block in <address> + <address>의 공개 원시 블록을 활성화합니다. + + + Enable publish raw transaction (locked via InstantSend) in <address> + <address>의 공개 원시 거래를 활성화합니다. (인스턴트샌드를 통해 잠김) + + + Enable publish raw transaction in <address> + <address>에 대한 원시 거래 공개 활성화 + + + Enable transaction replacement in the memory pool (default: %u) + 메모리 풀 내의 거래 대체를 활성화합니다. (기본값: %u) + + + Error: A fatal internal error occurred, see debug.log for details + 에러: 치명적인 내부 오류가 발생했습니다, 자세한 내용은 debug.log 를 확인해주세요. + + + Failed to create backup %s! + %s 백업 생성에 실패하였습니다! + + + Failed to create backup, error: %s + 백업 생성에 실패하였습니다. 에러: %s + + + Failed to delete backup, error: %s + 백업 삭제에 실패하였습니다. 에러: %s + + + Failed to load InstantSend data cache from + 인스턴트샌드 데이터 캐시 로딩 실패 + + + Failed to load sporks cache from + 스포크 캐시 로딩 실패 + + + Failed to parse host:port string + host:post 문자열을 분석하는 데 실패하였습니다. + + + Fee (in %s/kB) to add to transactions you send (default: %s) + 송금 시 추가되는 수수료 (%s/kB) (기본값: %s) + + + Imports blocks from external blk000??.dat file on startup + 시작 시 외부 blk000??.dat 파일에서 블록을 가져옵니다. + + + InstantSend options: + 인스턴트샌드 옵션: + + + Invalid amount for -fallbackfee=<amount>: '%s' + 유효하지 않은 금액 -fallbackfee=<amount>: '%s' + + + Invalid input count. + 잘못된 입력 횟수입니다. + + + Keep the transaction memory pool below <n> megabytes (default: %u) + 거래 메모리 풀의 용량을 <n>메가바이트 아래로 유지하기 (기본값: %u) + + + Last PrivateSend was too recent. + 지난 프라이빗샌드가 너무 최신입니다. + + + Last successful PrivateSend action was too recent. + 지난 성공적 프라이빗샌드 활동이 너무 최신입니다. + + + Line: %d + 라인: %d + + + Loading InstantSend data cache... + 인스턴트샌드 데이터 캐시로드 중... + + + Loading addresses... + 주소를 불러오는 중... + + + Loading block index... + 블록 인덱스를 불러오는 중... + + + Loading governance cache... + 거버넌스 캐시를 불러오는 중... + + + Loading sporks cache... + 스포크 캐시 로딩중... + + + Loading wallet... (%3.2f %%) + 지갑을 불러오는 중... (%3.2f %%) + + + Loading wallet... + 지갑을 불러오는 중... + + + Location of the auth cookie (default: data dir) + 인증 쿠키의 위치 (기본값: data dir) + + + Make the wallet broadcast transactions + 지갑 브로드캐스트 거래 생성 + + + Masternode cache is empty, skipping payments and governance cache... + 마스터노드 캐시가 비어 있습니다. 지불 및 거버넌스 캐시를 건너뜁니다... + + + Masternode options: + 마스터노드 옵션: + + + Masternode queue is full. + 마스터노드 대기열이 가득 찼습니다. + + + Masternode: + 마스터노드: + + + Minimum bytes per sigop in transactions we relay and mine (default: %u) + 중계 및 채굴 시 거래의 Sigop 당 최소 바이트 (디폴트: %u) + + + Missing input transaction information. + 누락된 입력값 거래 정보 + + + No errors detected. + 에러가 감지되지 않았습니다. + + + No matching denominations found for mixing. + 믹싱을 위해 필요한 분할 단위를 찾을 수 없습니다. + + + Node relay options: + 노드 중계 옵션: + + + Non-standard public key detected. + 규격 외의 공개 키가 감지되었습니다. + + + Not compatible with existing transactions. + 기존의 거래와 호환 할 수 없습니다. + + + Not enough file descriptors available. + 사용 가능한 파일 설명자가 부족합니다. + + + Not enough funds to anonymize. + 익명화를 진행하기에 잔고가 충분하지 않습니다. + + + Number of automatic wallet backups (default: %u) + 자동 지갑 백업의 수 (디폴트: %u) + + + Options: + 옵션: + + + Password for JSON-RPC connections + JSON-RPC 연결에 사용할 암호 + + + Print version and exit + 버전 출력 후 종료 + + + PrivateSend is idle. + 프라이빗샌드가 가동되지 않았습니다. + + + PrivateSend options: + 프라이빗샌드 옵션: + + + PrivateSend request complete: + 프라이빗샌드 요청이 완료되었습니다: + + + PrivateSend request incomplete: + 프라이빗샌드 요청이 완료되지 않았습니다: + + + Prune cannot be configured with a negative value. + 블록 축소는 음수로 설정할 수 없습니다. + + + Prune mode is incompatible with -txindex. + 블록 축소 모드는 -txindex와 호환되지 않습니다. + + + Pruning blockstore... + 블록 데이터를 축소 중입니다.. + + + Rescan the block chain for missing wallet transactions on startup + 시작할 때 블록체인에서 누락된 지갑 거래를 다시 검색합니다. + + + The wallet will avoid paying less than the minimum relay fee. + 이 지갑은 최소 중계 수수료보다 적은 수수료를 지불하지 않을 것입니다. + + + This is the minimum transaction fee you pay on every transaction. + 모든 거래를 수행하는 데 필요한 최소 거래 수수료입니다. + + + This is the transaction fee you will pay if you send a transaction. + 당신의 거래를 처리하는 데 필요한 거래 수수료입니다. + + + Transaction amounts must not be negative + 거래 금액은 반드시 정수여야 합니다. + + + Transaction has too long of a mempool chain + 거래의 멤풀 체인이 너무 깁니다. + + + Transaction must have at least one recipient + 거래에는 최소 한 명의 수령인이 필요합니다. + + + Transaction too large + 거래액이 너무 큽니다 + + + Trying to connect... + 연결 시도 중... + + + Unable to bind to %s on this computer. %s is probably already running. + 이 컴퓨터의 %s에 바인딩 할 수 없습니다. %s이 이미 실행 중인 것으로 보입니다. + + + Upgrading UTXO database + UTXO 데이터베이스 업그레이드 + + + Use devnet chain with provided name + 제공된 이름으로 개발 네트워크 체인을 사용 + + + Wallet debugging/testing options: + 지갑 디버깅/테스트 옵션: + + + Wallet is not initialized + 지갑이 초기화되지 않았습니다. + + + Wallet needed to be rewritten: restart %s to complete + 지갑을 새로 써야 합니다: 완성을 위해 %s을/를 다시 시작하십시오. + + + Warning: unknown new rules activated (versionbit %i) + 경고: 알려지지 않은 새로운 규칙이 활성화되었습니다. (versionbit %i) + + + Wasn't able to create wallet backup folder %s! + 지갑 백업 폴더 %s 를 생성할 수 없었습니다! + + + Whether to operate in a blocks only mode (default: %u) + 블록 전용 모드로 동작할 지 여부를 결정합니다. (기본값: %u) + + + You can not start a masternode in lite mode. + 라이트 모드에서는 마스터 노드를 시작할 수 없습니다. + + + ZeroMQ notification options: + ZeroMQ 알림 옵션: + + + no mixing available. + 믹싱을 할 수 없습니다. + + + see debug.log for details. + 세부 사항을 확인하시려면 debug.log를 참조하세요. + + + RPC server options: + RPC 서버 설정 + + + Dash Core + 대시 코어 + + + The %s developers + %s 개발자 + + + Accept connections from outside (default: 1 if no -proxy or -connect/-noconnect) + 외부로부터의 연결을 허용합니다. (-proxy 가 없는 경우 혹은 -connect/-noconnect 인 경우 디폴트: 1) + + + Cannot obtain a lock on data directory %s. %s is probably already running. + %s 데이터 디렉토리에 잠금을 설정 할 수 없습니다. %s가 이미 실행 중인 것으로 보입니다. + + + Connect only to the specified node(s); -noconnect or -connect=0 alone to disable automatic connections + 지정된 노드(들)에만 연결합니다: 자동 연결을 해제하기 위하여 -noconnect 혹은 -connect=0 만을 사용합니다. + + + Distributed under the MIT software license, see the accompanying file %s or %s + MIT 소프트웨어 라이센스에 따라 배포 되었습니다. 첨부 파일 %s 또는 %s을 참조하십시오. + + + Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + %s 불러오기 오류 입니다. 주소 키는 모두 정확하게 읽혔으나 거래 데이터 혹은 주소록에서 누락이나 오류가 존재할 수 있습니다. + + + Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d) + 피어들이 로컬 중계 정책을 위반하더라도 화이트 리스트에 포함된 피어인경우 강제로 중계하기 (기본값: %d) + + + Incorrect or no devnet genesis block found. Wrong datadir for devnet specified? + 올바르지 않거나 개발 네트워크 생성 블록을 찾을 수 없습니다. 개발 네트워크에 지정된 데이터 디렉토리가 잘못된 것일 수 있습니다. + + + Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds) + 허용 된 최대 중간 피어 시간 오프셋 조정. 시간에 대한 지역적 전망치는 전방 또는 후방의 피어에 의해 영향을 받을 수 있습니다. (디폴트: %u 초) + + + Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s) + 단일 지갑 거래 또는 원시 거래에서 사용할 최대 총 수수료(단위 %s). 이 값을 너무 낮게 설정하면 크기가 큰 거래를 중단시킬 수 있습니다. (기본값: %s) + + + Override spork address. Only useful for regtest and devnet. Using this on mainnet or testnet will ban you. + 스포크 주소를 무시합니다. 이 기능은 오직 회귀 테스트와 개발 네트워크에서 유용합니다. 메인넷이나 테스트넷에서 사용하는 경우에는 금지당할 수 있습니다. + + + Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly. + 컴퓨터의 날짜와 시간이 올바른지 확인하십시오! 시간이 잘못되면 %s은 제대로 동작하지 않습니다. + + + Please contribute if you find %s useful. Visit %s for further information about the software. + %s이/가 유용하다고 생각한다면 프로젝트에 공헌해주세요. 이 소프트웨어에 대한 보다 자세한 정보는 %s를 방문해주십시오. + + + 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) + 코인 믹싱을 자주 반복하지 않음으로써 프라이빗샌드에 유동성 제공 (%u-%u, 디폴트: %u, 1=매우 빈번, 높은 수수료, %u=매우 드뭄, 적은 수수료) + + + Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect/-noconnect) + 주소가 부족한 경우 DNS 검색을 통해 피어 주소를 요청합니다. (디폴트: -connect/-noconnect가 아닌 한 1) + + + Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB) + 오래된 블록을 정리(삭제) 함으로써 스토리지 용량 요구 사항을 줄입니다. 이로써 pruneblockchain RPC를 호출하여 특정 블록을 삭제하고, MiB로 목표 사이즈를 설정하는 경우 오래된 블록을 자동으로 정리합니다. 이 모드는 -txindex와 -rescan과 호환되지 않습니다. 경고: 이 설정을 되돌리기 위해서는 전체 블록체인을 다시 다운로드 해야 합니다. (디폴트: 0 = 블록 정리 기능 해제, 1 = RPC를 통한 수동 블록 정리 허용, >%u = 자동으로 블록 파일을 정리하여 MiB로 지정된 목표 사이즈로 유지) + + + Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) + 블록 생성에 포함되기 위한 거래의 최저 수수료 비율(%s/kB)을 설정하십시오. (기본값: %s) + + + This is the transaction fee you may pay when fee estimates are not available. + 이것은 수수료 견적을 이용할 수 없는 경우 당신이 지불 하게 될 것으로 예상되는 거래 수수료입니다. + + + This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit %s and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard. + 이 제품에는 OpenSSL Project에 의해 penSSL Toolkit %s용으로 사용하기 위해 개발된 소프트웨어, Eric Young이 작성한 암호화 소프트웨어 및 Thomas Bernard가 작성한 UPnP 소프트웨어가 포함되어 있습니다. + + + Unable to locate enough PrivateSend non-denominated funds for this transaction. + 이 거래를 수행하는 데 필요한 충분한 프라이빗샌드 단위 분할 금액을 찾을 수 없습니다. + + + Use N separate masternodes for each denominated input to mix funds (%u-%u, default: %u) + N개의 마스터노드를 사용하여 분할한 각 입력값을 섞습니다. (%u-%u, 디폴트: %u) + + + Use hierarchical deterministic key generation (HD) after BIP39/BIP44. Only has effect during wallet creation/first start + BIP39/BIP44 이후 HD 키 생성을 사용하십시오. 지갑 생성/첫 시작에만 시행됩니다. + + + User defined mnemonic passphrase for HD wallet (BIP39). Only has effect during wallet creation/first start (default: empty string) + HD 지갑을 위해 사용자가 지정한 연상 기호 암호문(BIP39)은 지갑 생성/첫 시작에만 효력이 있습니다. (디폴트: 빈 문자열) + + + Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times + 클라이언트JSON-RPC 연결에 필요한 사용자 이름과 해시화된 암호문. 해당 필드는 <userpw> 다음과 같은 필드로 구성되어 있습니다: <USERNAME>:<SALT>$<HASH>. share/rpcuser에는 표준 파이톤 스크립트가 포함됩니다. 이후 클라이언트는 rpcuser=<USERNAME>/ rpcpassword=<PASSWORD> 쌍의 매개 변수를 이용하여 정상적으로 연결합니다. 이 옵션은 복수로 지정할 수 있습니다. + + + Wallet will not create transactions that violate mempool chain limits (default: %u) + 지갑은 멤풀 체인 한계값(기본값: %u)을 넘는 거래를 생성하지 않습니다. + + + Warning: At least %d of %d masternodes are running on a newer software version. Please check latest releases, you might need to update too. + 경고: %d개의 마스터노드 중 최소 %d 개가 새로운 버전의 소프트웨어에서 구동 중입니다. 최신 버전을 확인하세요. 업데이트가 필요할 수 있습니다. + + + Warning: Every masternode (out of %d known ones) is running on a newer software version. Please check latest releases, it's very likely that you missed a major/critical update. + 경고: (알려진 %d 중) 모든 마스터노드가 새로운 버전의 소프트웨어에서 구동 중입니다. 최신의 버전을 확인하세요. 중요한 업데이트가 실행되지 않은 것으로 보입니다. + + + Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup. + 경고: 지갑 파일이 손상, 데이터가 복구되었습니다! 기존의 %s 파일은 %s에 %s라는 이름으로 저장됩니다. 잔액과 거래 내역이 정확하지 않다면 백업 파일로 복원해야 합니다. + + + Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times. + 지정된 IP주소 (예 1.2.3.4) 혹은 CIDR로 작성된 네트워크 (예 1.2.3.0/24)로 화이트리스트 피어에 접속합니다. 이는 복수로 지정 할 수 있습니다. + + + %s corrupt, salvage failed + %s 손상, 복구에 실패하였습니다. + + + %s is not a valid backup folder! + %s는 올바른 백업 폴더가 아닙니다! + + + %s is set very high! + %s가 매우 높게 설정되었습니다! + + + (press q to shutdown and continue later) + (q를 눌러 종료하고 나중에 계속하기) + + + -devnet can only be specified once + -devnet은 한 번만 지정할 수 있습니다. + + + -port must be specified when -devnet and -listen are specified + -devnet과 -listen이 지정될 때에는 -port도 지정되어야 합니다. + + + -rpcport must be specified when -devnet and -server are specified + -devnet과 -server가 지정될 때에는 -rpcport도 지정되어야 합니다. + + + Allow RFC1918 addresses to be relayed and connected to (default: %u) + RFC1918 주소가 릴레이되고 (default: %u)에 연결되는 것을 허용합니다. + + + Attempt to recover private keys from a corrupt wallet on startup + 시작 시 오류가 있는 지갑에서 개인 키 복원을 시도합니다. + + + Cannot resolve -%s address: '%s' + %s주소: '%s'를 확인할 수 없습니다. + + + Change index out of range + 범위 밖의 인덱스 변경 + + + Copyright (C) + Copyright (C) + + + Error loading %s + %s 불러오기 오류 + + + Error loading %s: Wallet corrupted + %s 불러오기 오류: 지갑에 오류가 있습니다. + + + Error loading %s: Wallet requires newer version of %s + %s 불러오기 에러: 지갑에 새 버전의 %s이/가 필요합니다 + + + Error upgrading chainstate database + 체인 상태 데이터베이스 업그레이드 중 오류가 발생했습니다. + + + Failed to find mixing queue to join + 참여할 수 있는 믹싱 대기열을 찾는 데 실패하였습니다. + + + Failed to start a new mixing queue + 새로운 믹싱 대기열을 시작하는 데 실패하였습니다. + + + Initialization sanity check failed. %s is shutting down. + 초기치 설정 정상 여부 검사에 실패하였습니다. %s가 종료됩니다. + + + Invalid amount for -%s=<amount>: '%s' + 유효하지 않은 금액 -%s=<amount>: '%s' + + + Invalid spork address specified with -sporkaddr + -sporkaddr로 지정된 스포크 주소가 유효하지 않습니다. + + + Keep N DASH anonymized (%u-%u, default: %u) + N 대시를 익명으로 유지합니다(%u-%u, 디폴트: %u). + + + Print this help message and exit + 이 도움말 메시지를 출력 후 종료 + + + Receive and display P2P network alerts (default: %u) + P2P 네트워크 경고를 받고 표시합니다(디폴트: %u). + + + Reducing -maxconnections from %d to %d, because of system limitations. + 시스템 상 한계로 인하여 -maxconnections를 %d 에서 %d로 줄였습니다. + + + Relay and mine data carrier transactions (default: %u) + 데이터 캐리어 거래를 릴레이하고 채굴 합니다. (기본값: %u) + + + Relay non-P2SH multisig (default: %u) + 비-P2SH 다중 서명을 릴레이 (기본값: %u) + + + Rescanning... + 다시 스캔 중... + + + Run in the background as a daemon and accept commands + 데몬으로 백그라운드에서 실행하고 명령을 허용 + + + Send trace/debug info to console instead of debug.log file + 추적/디버그 정보를 degug.log 파일 대신 콘솔로 보내기 + + + Session not complete! + 세션이 완료되지 않았습니다! + + + Session timed out. + 세션이 만료되었습니다. + + + Set database cache size in megabytes (%d to %d, default: %d) + 데이터베이스 케시 크기를 메가바이트로 설정(%d 부터 %d, 기본값: %d) + + + Set maximum block size in bytes (default: %d) + 최대 블록 사이즈를 바이트로 설정합니다(디폴트: %d). + + + Set the masternode private key + 마스터노드 개인 키를 설정합니다. + + + Show all debugging options (usage: --help -help-debug) + 모든 디버그 설정 보기(설정: --help -help-debug) + + + Shrink debug.log file on client startup (default: 1 when no -debug) + 클라이언트 시작 시 debug.log 파일 축소 (기본값: -debug 아닌 경우 1) + + + Signing transaction failed + 거래에 서명하는 데 실패하였습니다. + + + Specify data directory + 데이터 디렉토리를 지정하십시오. + + + Specify wallet file (within data directory) + (데이터 디렉토리 내의) 지갑 파일을 지정하십시오. + + + Specify your own public address + 공개 주소를 지정하십시오. + + + Starting network threads... + 네트워크 스레드 시작중... + + + Synchronization pending... + 처리 중인 데이터 동기화중... + + + Synchronizing governance objects... + 거버넌스 객체 동기화중... + + + Synchronizing masternode payments... + 마스터노드 지불 동기화중... + + + The source code is available from %s. + 소스 코드는 %s 에서 확인하실 수 있습니다. + + + The transaction amount is too small to pay the fee + 수수료를 지불하기에 거래 금액이 너무 작습니다. + + + This is experimental software. + 이 소프트웨어는 실험적으로 운영되고 있습니다. + + + Transaction amount too small + 거래 금액이 너무 적습니다 + + + Transaction created successfully. + 거래가 성공적으로 생성되었습니다. + + + Transaction fees are too high. + 거래 수수료가 너무 높습니다. + + + Transaction not valid. + 거래가 유효하지 않습니다. + + + Transaction too large for fee policy + 수수료 정책에 비해 거래 액수가 너무 큽니다 + + + Unable to bind to %s on this computer (bind returned error %s) + 이 컴퓨터의 %s에 바인딩할 수 없습니다 (바인딩 과정에 %s 오류 발생) + + + Unable to sign spork message, wrong key? + 스포크 메시지에 사인할 수 없습니다. 키가 잘못된 것일 수 있습니다. + + + Unknown network specified in -onlynet: '%s' + -onlynet에 지정한 네트워크를 알 수 없습니다: '%s' + + + Unknown state: id = %u + 알 수 없는 상태: id = %u + + + Username for JSON-RPC connections + JSON-RPC 연결에 사용할 사용자 이름 + + + Verifying blocks... + 블록 검증 중... + + + Verifying wallet... + 지갑 검증 중... + + + Very low number of keys left: %d + 매우 적은 숫자의 키가 남았습니다: %d + + + Wallet %s resides outside data directory %s + 지갑 %s 이 데이터 디렉토리 외부에 위치합니다 %s. + + + Wallet is locked. + 지갑이 잠금 상태입니다. + + + Wallet options: + 지갑 옵션: + + + Wallet window title + 지갑 창 제목 + + + Warning + 경고 + + + Your entries added successfully. + 당신의 입력이 성공적으로 추가되었습니다. + + + Your transaction was accepted into the pool! + 당신의 거래가 성공적으로 풀에 반영되었습니다! + + + Zapping all transactions from wallet... + 지갑의 모든 거래 내역 건너뛰는 중... + + + \ No newline at end of file diff --git a/src/qt/locale/dash_nl.ts b/src/qt/locale/dash_nl.ts index 18e9a1cecdd1..268cfa39bce2 100644 --- a/src/qt/locale/dash_nl.ts +++ b/src/qt/locale/dash_nl.ts @@ -59,7 +59,7 @@ Receiving addresses - Ontvanstgadressen + Ontvangstadressen These are your Dash addresses for sending payments. Always check the amount and the receiving address before sending coins. @@ -71,15 +71,15 @@ &Copy Address - Kopieer adres + &Kopiëer Adres Copy &Label - Kopieer label + Kopiëer &Label &Edit - Bewerken + &Bewerken Export Address List @@ -95,7 +95,7 @@ There was an error trying to save the address list to %1. Please try again. - Er was een probleem tijdens het opslaan van de adressenlijst naar %1. Probeer het alstublieft opnieuw. + Er is een fout is opgetreden tijdens het opslaan van deze adreslijst naar %1. Probeer het opnieuw. @@ -125,11 +125,11 @@ New passphrase - Nieuw wachtwoordzin + Nieuwe wachtwoordzin Repeat new passphrase - Herhaal nieuw wachtwoordzin + Herhaal nieuwe wachtwoordzin Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>. @@ -283,7 +283,7 @@ &Transactions - &Transactis + &Transacties Browse transaction history @@ -535,15 +535,15 @@ Indexing blocks on disk... - Bezig met indexeren van blokken op harde schijf... + Bezig met indexeren van blocks op harde schijf... Processing blocks on disk... - Bezig met verwerken van blokken op harde schijf... + Bezig met verwerken van blocks op harde schijf... Reindexing blocks on disk... - Bezig met herindexeren van blokken op harde schijf... + Bezig met herindexeren van blocks op harde schijf... Connecting to peers... @@ -551,7 +551,7 @@ Processed %n block(s) of transaction history. - %n blok van transactiehistorie verwerkt.%n blokken van transactiehistorie verwerkt. + %n blok van transactiehistorie verwerkt.%n blocks van transactiehistorie verwerkt. %1 behind @@ -794,8 +794,8 @@ Schakel over naar "Lijst mode" om deze functie te gebruiken. - Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again. - Niet-anoniem invoer geselecteerd. <b>PrivateSend wordt afgesloten. </b><br><br> Indien u toch PrivateSend wenst te gebruiken, deselecteer dan eerst alle niet-anonieme invoer. Vink daarna PrivateSend opnieuw aan. + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. + Niet-anonieme invoer geselecteerd. <b>PrivateSend wordt uitgeschakeld. </b><br><br> Indien je toch PrivateSend wenst te gebruiken, deselecteer dan eerst alle niet-anonieme invoer. Vink daarna PrivateSend opnieuw aan. (%1 locked) @@ -834,73 +834,6 @@ N.v.t. - - DarksendConfig - - Configure PrivateSend - Configureer PrivateSend - - - Basic Privacy - Eenvoudige Privacy - - - High Privacy - Hoge Privacy - - - Maximum Privacy - Maximum Privacy - - - Please select a privacy level. - Kies een privacyniveau - - - Use 2 separate masternodes to mix funds up to 1000 DASH - Gebruik 2 verschillende masternodes om tot en met 100 Dash te mixen - - - Use 8 separate masternodes to mix funds up to 1000 DASH - Gebruik 8 verschillende masternodes om tot en met 1000 Dash te mixen - - - Use 16 separate masternodes - Gebruik 16 verschillende masternodes - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - Deze optie is het snelst en kost ongeveer ~0.025 D om 1000 D te anonimiseren - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - Deze optie is relatief snel en zal ongeveer 0.05 DASH kosten om 1000 DASH te anonimiseren - - - 0.1 DASH per 1000 DASH you anonymize. - 0.1 DASH per 1000 DASH die je anonimiseert. - - - This is the slowest and most secure option. Using maximum anonymity will cost - Deze optie is het langzaamst en meest veilig. Maximimum anonimiteit kost - - - PrivateSend Configuration - PrivateSend configuratie - - - PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend is succesvol ingesteld op basis (%1 en 2 rondes). U kunt dit op elk gewenst moment wijzigen door het Dash configuratiescherm te openen. - - - PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend is succesvol ingesteld op hoog (%1 en 8 rondes). U kunt dit op elk gewenst moment wijzigen door het Dash configuratiescherm te openen. - - - PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend is succesvol ingesteld op maximaal (%1 en 16 rondes). U kunt dit op elk gewenst moment wijzigen door het Dash configuratiescherm te openen. - - EditAddressDialog @@ -1038,8 +971,8 @@ PrivateSend informatie - <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more info see <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> - <h3>Basics van PrivateSend</h3> PrivateSend geeft u echte financiële privacy door de oorsprong van uw geld te verbergen. Alle Dash in uw wallet bestaat uit verschillende "inputs" die u kunt beschouwen als afzonderlijke, discrete munten.<br> PrivateSend maakt gebruik van een innovatief proces om uw input te mengen met de input van twee andere mensen, zonder dat uw munten ooit uw wallet verlaten . U behoudt te alle tijden de controle over uw geld. <hr> <b>Het PrivateSend-proces werkt als volgt: </b><ol type="1"> <li>PrivateSend begint door uw transactie-inputs in standaard denominaties op te breken. Deze denominaties zijn 0,01 DASH, 0,1 DASH, 1 DASH en 10 DASH - eigenlijk net zoals de muntstukken van contant geld dat u elke dag gebruikt.</li> Uw wallet stuurt daarna verzoeken naar speciaal geconfigureerde softwarenodes op het netwerk, zogenaamde "masternodes." Deze masternodes worden daarmee op de hoogte gesteld dat u geïnteresseerd bent in het mixen van een bepaalde denominatie. Er wordt geen identificeerbare informatie naar de masternodes gestuurd, ze kunnen dus niet weten wie u bent.</li> <li>Wanneer twee andere mensen vergelijkbare berichten sturen, waarin ze aangeven dat ze dezelfde denominaties willen mixen, begint een mixsessie. De masternode mixt alle invoeren en instrueert de wallets van alle drie de gebruikers om de nu getransformeerde input aan zichzelf terug te betalen. Uw wallet betaalt die denominaties rechtstreeks naar zichzelf, maar met een ander adres (een wisselgeldadres genoemd).</li> <li>Om uw geld volledig te verbergen, moet uw portemonnee dit proces een aantal keer herhalen voor elke denominatie. Iedere keer dat het proces wordt voltooid, wordt een 'ronde' genoemd. Elke ronde van PrivateSend maakt het exponentieel moeilijker om de herkomst van uw geld te bepalen.</li> <li>Dit mixproces gebeurt op de achtergrond zonder enige tussenkomst van uw kant. Wanneer u een transactie wenst uit te voeren, zijn uw fondsen al geanonimiseerd. Er is geen extra wachttijd nodig.</li> </ol> <hr><b>BELANGRIJK:</b> Uw wallet bevat slechts 1000 van deze "wisselgeldadressen". Elke keer dat er een mixsessie plaatsvindt, worden maximaal 9 van uw adressen opgebruikt. Dit betekent dat deze 1000 adressen volstaan voor ongeveer 100 mixsessies. Wanneer er 900 adressen zijn opgebruikt, moet uw wallet nieuwe adressen aanmaken. Dit kan alleen maar als u automatische back-ups hebt ingeschakeld.<br> Daarom is voor gebruikers die back-ups hebben uitgeschakeld, ook PrivateSend uitgeschakeld. <hr>Voor meer informatie kijk op<a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https: //dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. + <h3>PrivateSend Basics</h3> PrivateSend geeft u een echte financiële privacy door de oorsprong van uw geld te verschuilen. Alle Dash in uw portefeuille bestaat uit verschillende "inputs" die u kunt beschouwen als afzonderlijke, discrete munten.<br> PrivateSend maakt gebruik van een innovatief proces om uw invoer te mengen met de invoer van twee andere mensen, zonder dat uw munten ooit uw portemonnee verlaten . U behoudt de controle over uw geld te allen tijde. <hr> <b>Het PrivateSend-proces werkt als volgt: </b><ol type="1"> <li>PrivateSend begint door uw transactie-inputs in de standaard denominaties te breken. Deze denominaties zijn 0,01 DASH, 0,1 DASH, 1 DASH en 10 DASH - zoals de papiergeld dat u elke dag gebruikt.</li> <li>Jouw portemonnee stuurt dan verzoeken naar speciaal geconfigureerde software nodes op het netwerk, genaamd "masternodes." Deze masternodes worden dan op de hoogte gesteld dat u geïnteresseerd bent in het mengen van een bepaalde denominaties. Er wordt geen identificeerbare informatie naar de masternodes gestuurd, zodat ze nooit weten wie je bent.</li> <li>Wanneer twee andere mensen dezelfde berichten sturen, die aanduiden dat ze dezelfde denominaties willen vermengen, begint een mengsessie. De masternode mengt alle invoeren en instrueert de portefeuilles van alle drie de gebruikers om de nu getransformeerde invoer aan zichzelf terug te betalen. Jouw portemonnee betaalt die denominaties rechtstreeks naar zichzelf, maar met een ander adres (een wijziging adres genoemd).</li> <li>Om uw geld volledig te verschuilen, moet uw portemonnee dit proces een aantal keren herhalen voor elke denominaties. Elke keer dat het proces wordt voltooid, dit heet een 'ronde'. Elke ronde van PrivateSend maakt het exponentieel moeilijker om te bepalen waar uw geld van afkomstig is.</li> <li>Dit mengproces gebeurt op de achtergrond zonder enige interventie van uw kant. Wanneer u een transactie wenst uit te voeren, zijn uw fondsen al geanonimiseerd. Er is geen extra wachttijd nodig.</li> </ol> <hr><b>BELANGRIJK:</b> Je portemonnee bevat slechts 1000 van deze "wijzigingsadressen". Elke keer dat er een mengsessie plaatsvindt, worden maximaal 9 van uw adressen opgebruikt. Dit betekent dat deze 1000 adressen voor ongeveer 100 mengsessies volstaat. Wanneer er 900 werden gebruikt, moet je portemonnee meer adressen aanmaken. Dit kan alleen maar als u automatische back-ups hebt ingeschakeld.<br> Bijgevolg hebben gebruikers die back-ups hebben uitgeschakeld, ook PrivateSend uitgeschakeld. <hr>Voor meer informatie zie de <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentatie</a>. @@ -1260,7 +1193,7 @@ Number of blocks left - Aantal blokken resterend. + Aantal blocks resterend. Unknown... @@ -1834,7 +1767,7 @@ Om te mixen moeten andere gebruikers exact dezelfde denominaties inbrengen. Note: You can turn this message off in options. - Opmerking: Dit bericht kan kan worden uitgeschakeld in de instellingen + Opmerking: Dit bericht kan kan worden uitgeschakeld in de instellingen. WARNING! Something went wrong on automatic backup @@ -2200,7 +2133,7 @@ Om te mixen moeten andere gebruikers exact dezelfde denominaties inbrengen. Current number of blocks - Huidig aantal blokken + Huidig aantal blocks Client version @@ -2292,7 +2225,7 @@ Om te mixen moeten andere gebruikers exact dezelfde denominaties inbrengen. Synced Blocks - Gesynchroniseerde blokken + Gesynchroniseerde blocks Wallet Path @@ -2364,7 +2297,7 @@ Om te mixen moeten andere gebruikers exact dezelfde denominaties inbrengen. Salvage wallet - Gerede portemonnee + Herstel portemonnee Rescan blockchain files @@ -2372,15 +2305,15 @@ Om te mixen moeten andere gebruikers exact dezelfde denominaties inbrengen. Recover transactions 1 - Herstelde transacties 1 + Herstel transacties 1 Recover transactions 2 - Herstelde transacties 2 + Herstel transacties 2 Upgrade wallet format - Upgrade portemonnee formaat + Upgrade portemonnee The buttons below will restart the wallet with command-line options to repair the wallet, fix issues with corrupt blockhain files or missing/obsolete transactions. @@ -2388,7 +2321,7 @@ Om te mixen moeten andere gebruikers exact dezelfde denominaties inbrengen. -salvagewallet: Attempt to recover private keys from a corrupt wallet.dat. - -red portemonnee: Poging om geheime sleutels terug te halen uit het beschadigede portemonnee bestand (wallet.dat.) + -herstel portemonnee: Poging om geheime sleutels terug te halen uit het beschadigde portemonnee bestand (wallet.dat.) -rescan: Rescan the block chain for missing wallet transactions. @@ -2396,15 +2329,15 @@ Om te mixen moeten andere gebruikers exact dezelfde denominaties inbrengen. -zapwallettxes=1: Recover transactions from blockchain (keep meta-data, e.g. account owner). - -zapwallettxes=1: Herstelde transacties van de blokketen (behoud metadata; bv. accounteigenaar) + -zapwallettxes=1: Herstel transacties van de blocks(behoud metadata; bv. accounteigenaar) -zapwallettxes=2: Recover transactions from blockchain (drop meta-data). - -zapwallettxed=2: Herstelde transacties vanuit de blokketen (laat metadata vervallen) + -zapwallettxes=2: Herstel transacties vanuit de blocks (laat metadata vervallen) -upgradewallet: Upgrade wallet to latest format on startup. (Note: this is NOT an update of the wallet itself!) - -upgrade portemonnee= Upgrade portemonnee naar het laatste formaat bij het starten: (Noot: dit is GEEN update van de portemonnee zelf!) + -upgrade portemonnee= Upgrade portemonnee naar het laatste formaat bij het starten: (Let op: dit is GEEN update van de portemonnee zelf!) Wallet repair options. @@ -2412,7 +2345,7 @@ Om te mixen moeten andere gebruikers exact dezelfde denominaties inbrengen. Rebuild index - Herbouw de index + Herbouw index -reindex: Rebuild block chain index from current blk000??.dat files. @@ -2819,7 +2752,7 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks.<br />But be aware that this can end up in a never confirming transaction once there is more demand for dash transactions than the network can process. - Het betalen van de minimale vergoeding is prima zo lang er minder transactie volume is dan ruimte in de blokken. <br />Maar houd er rekening mee dat dit kan eindigen in een nooit bevestigde transactie zodra er meer vraag is voor Dash transacties fdan het netwerk kan verwerken. + Het betalen van de minimale vergoeding is prima zo lang er minder transactie volume is dan ruimte in de blocks. <br />Maar houd er rekening mee dat dit kan eindigen in een nooit bevestigde transactie zodra er meer vraag is voor Dash transacties fdan het netwerk kan verwerken. per kilobyte @@ -2847,7 +2780,7 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer (Smart fee not initialized yet. This usually takes a few blocks...) - (Slimme kosten zijn nog niet geïnitialiseerd Dit duurt meestal een paar blokken...) + (Slimme kosten zijn nog niet geïnitialiseerd Dit duurt meestal een paar blocks ...) normal @@ -2995,7 +2928,7 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer %n block(s) - %n blok%n blokken + %n blok%n blocks Pay only the required fee of %1 @@ -3003,7 +2936,7 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Estimated to begin confirmation within %n block(s). - Schatting is dat bevestiging begint binnen %n blok.Schatting is dat bevestiging begint binnen %n blokken. + Schatting is dat bevestiging begint binnen %n blok.Schatting is dat bevestiging begint binnen %n blocks. Warning: Invalid Dash address @@ -3288,7 +3221,7 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer TransactionDesc Open for %n more block(s) - Open voor nog %n blokOpen voor nog %n blokken + Open voor nog %n blokOpen voor nog %n blocks Open until %1 @@ -3392,7 +3325,7 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer matures in %n more block(s) - komt beschikbaar na %n nieuw blokkomt beschikbaar na %n nieuwe blokken + komt beschikbaar na %n nieuw blokkomt beschikbaar na %n nieuwe blocks not accepted @@ -3444,7 +3377,7 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. - Gegenereerde munten moeten %1 blokken rijpen voordat ze kunnen worden besteed. Toen dit blok gegenereerd werd, werd het uitgezonden naar het netwerk om aan de blokketen toegevoegd te worden. Als het niet lukt om in de keten toegevoegd te worden, zal de status te veranderen naar "niet geaccepteerd" en zal het niet besteedbaar zijn. Dit kan soms gebeuren als een ander knooppunt een blok genereert binnen een paar seconden na die van u. + Gegenereerde munten moeten %1 blocks rijpen voordat ze kunnen worden besteed. Toen dit blok gegenereerd werd, werd het uitgezonden naar het netwerk om aan de blocks toegevoegd te worden. Als het niet lukt om in de keten toegevoegd te worden, zal de status te veranderen naar "niet geaccepteerd" en zal het niet besteedbaar zijn. Dit kan soms gebeuren als een ander knooppunt een block genereert binnen een paar seconden na die van u. Debug information @@ -3498,7 +3431,7 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Open for %n more block(s) - Open voor nog %n blokOpen voor nog %n blokken + Open voor nog %n blokOpen voor nog %n blocks Open until %1 @@ -4060,11 +3993,11 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer How thorough the block verification of -checkblocks is (0-4, default: %u) - Hoe grondig de blokverificatie van -checkblocks is (0-4, standaard: %u) + Hoe grondig de blockverificatie van -checkblocks is (0-4, standaard: %u) If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u) - Als paytxfee niet is ingesteld, voeg voldoende vergoeding toe zodat transacties starten met bevestigingen binnen n blokken (standaard: %u) + Als paytxfee niet is ingesteld, voeg voldoende vergoeding toe zodat transacties starten met bevestigingen binnen n blocks (standaard: %u) 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: %s, testnet: %s) @@ -4180,7 +4113,7 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer How many blocks to check at startup (default: %u, 0 = all) - Aantal te checken blokken bij het opstarten (standaard: %u, 0 = allemaal) + Aantal te checken blocks bij het opstarten (standaard: %u, 0 = allemaal) Importing... @@ -4312,7 +4245,7 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Rebuild chain state from the currently indexed blocks - Herbouw ketenstaat vanuit de huidige geindexeerde blokken + Herbouw ketenstaat vanuit de huidige geindexeerde blocks Send trace/debug info to debug.log file (default: %u) @@ -4486,6 +4419,10 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) Uitschakelen van alle Dash specifieke functies (Masternodes, PrivateSend, InstantSend, Governance) (0-1, standaard: %u) + + %s file contains all private keys from this wallet. Do not share it with anyone! + %s bestand bevat alle persoonlijke sleutel van deze portemonnee. Deel deze met niemand! + Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) Ontdek eigen IP-adressen (standaard: 1 voor luisteren en geen -externalip of -proxy) @@ -4510,6 +4447,10 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID) Voer opdracht uit zodra een wallettransactie succesvol vastgezet is (%s in cmd wordt vervangen door TxID) + + Extra transactions to keep in memory for compact block reconstructions (default: %u) + Extra transactie om in het geheugen te houden voor de reconstructie van compacte blokken (standaard: %u) + Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. Backup maken is mislukt, het bestand bestaat al! Dit kan gebeuren als u de wallet herstart binnen 60 seconden. U kunt verder gaan als u hier ok mee bent. @@ -4544,7 +4485,7 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Maintain a timestamp index for block hashes, used to query blocks hashes by a range of timestamps (default: %u) - Hou een tijdstempelindex bij voor blok hashes, wordt gebruikt om blokhashes op te vragen voor een bereik aan tijdstempels (standaard: %u) + Hou een tijdstempelindex bij voor block hashes, wordt gebruikt om blockhashes op te vragen voor een bereik aan tijdstempels (standaard: %u) Maintain at most <n> connections to peers (temporary service connections excluded) (default: %u) @@ -4576,7 +4517,7 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Support filtering of blocks and transaction with bloom filters (default: %u) - Ondersteun filtering van blokken en transacties met bloomfilters (standaard: %u) + Ondersteun filtering van blocks en transacties met bloomfilters (standaard: %u) The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct @@ -4590,6 +4531,10 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. Totale lengte van netwerkversiestring (%i) overschrijdt maximale lengte (%i). Verminder het aantal of grootte van uacomments. + + Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index. + Transactie index kan niet uitgeschakeld worden in volledige modus. Start met -litemode commando-optie, of activeer transactie index. + Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) Pogingen om uitgaand verkeer onder een bepaald doel te houden (in MiB per 24u), 0 = geen limiet (standaard: %d) @@ -4622,6 +4567,10 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Warning: Unknown block versions being mined! It's possible unknown rules are in effect Waarschuwing: Onbekende blok versies worden gemined! Er zijn mogelijk onbekende regels in werking getreden + + You are starting in lite mode, all Dash-specific functionality is disabled. + Je start in lite modus. Alle Dash specifieke functionaliteit is uitgeschakeld. + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain U moet de database herbouwen met -reindex om terug te gaan naar de niet-prune modus. Dit zal de gehele blockchain opnieuw downloaden. @@ -4666,6 +4615,14 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Enable publish hash block in <address> Sta toe om hashblok te publiceren in <adres> + + Enable publish hash of governance objects (like proposals) in <address> + Inschakelen van hash van governance objecten (zoals voorstellen) in <address> + + + Enable publish hash of governance votes in <address> + Inschakelen van publiceren van governance stemmen in <address> + Enable publish hash transaction (locked via InstantSend) in <address> Inschakelen publiceren van de hashtransactie (vastgezet met InstantSend) in <address> @@ -4706,6 +4663,14 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Failed to delete backup, error: %s Verwijderen van de back-up is mislukt, foutmelding: %s + + Failed to load InstantSend data cache from + Fout bij het laden van InstandSend data cache uit + + + Failed to load sporks cache from + Fout bij het inladen van de sporks cache uit + Failed to parse host:port string Inlezen van de host:poort string is mislukt @@ -4716,7 +4681,7 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Imports blocks from external blk000??.dat file on startup - Importeer blokken van externe blk000??.dat-bestand bij opstarten + Importeer blocks van externe blk000??.dat-bestand bij opstarten InstantSend options: @@ -4726,6 +4691,10 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Invalid amount for -fallbackfee=<amount>: '%s' Ongeldig bedrag voor -fallbackfee=<bedrag>: '%s' + + Invalid input count. + Ongeldig invoer aantal. + Keep the transaction memory pool below <n> megabytes (default: %u) De transactiegeheugenpool moet onder de <n> megabytes blijven (standaard: %u) @@ -4742,6 +4711,10 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Line: %d Lijn: %d + + Loading InstantSend data cache... + Laden van InstantSend data cache... + Loading addresses... Laden adresen... @@ -4754,6 +4727,10 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Loading governance cache... Laden governance cache... + + Loading sporks cache... + Laden sporks cache... + Loading wallet... (%3.2f %%) Laden portemonnee... (%3.2f %%) @@ -4894,6 +4871,10 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Transaction must have at least one recipient Transactie moet ten minste één ontvanger hebben + + Transaction too large + Transactie te groot + Trying to connect... Proberen te verbinden... @@ -4914,6 +4895,10 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Wallet debugging/testing options: Portemonee debugging/testing opties: + + Wallet is not initialized + Portemonnee is niet geïnitialiseerd + Wallet needed to be rewritten: restart %s to complete Portemonnee moest herschreven worden: Herstart %s om te voltooien @@ -4928,7 +4913,11 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Whether to operate in a blocks only mode (default: %u) - Om in alleen een blokmodus te opereren (standaard: %u) + Om in alleen een blockmodus te opereren (standaard: %u) + + + You can not start a masternode in lite mode. + Je kan geen masternode starten in lite modus. ZeroMQ notification options: @@ -5012,16 +5001,12 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB) - Beperk benodigde opslag door het prunen (verwijderen) van oude blokken in te schakelen. Dit maakt het mogelijk om het pruneblockchain RPC commando aan te roepen om specifieke bloks te verwijderen, en maakt het automatische prunen van oude bloks mogelijk wanneer een doelgrootte in MiB is ingesteld. Deze modus is niet compatibel met -txindex en -rescan. Waarschuwing: ongedaan maken van deze instellingen vereist het opnieuw downloaden van gehele de blokketen. (standaard:0 = uitschakelen prunen, 1 = handmatig prunen via RPC toestaan, >%u = automatisch blokketen bestanden prunen om beneden de gespecificeerde doelgrootte in MiB te blijven) + Beperk benodigde opslag door het prunen (verwijderen) van oude blocks in te schakelen. Dit maakt het mogelijk om het pruneblockchain RPC commando aan te roepen om specifieke blocks te verwijderen, en maakt het automatische prunen van oude blocks mogelijk wanneer een doelgrootte in MiB is ingesteld. Deze modus is niet compatibel met -txindex en -rescan. Waarschuwing: ongedaan maken van deze instellingen vereist het opnieuw downloaden van gehele de blocks. (standaard:0 = uitschakelen prunen, 1 = handmatig prunen via RPC toestaan, >%u = automatisch blokketen bestanden prunen om beneden de gespecificeerde doelgrootte in MiB te blijven) Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) Specificeer het laagste tarief (in %s/kB) voor transacties die bij het maken van een blok moeten worden in rekening worden gebracht (standaard: %s) - - Show N confirmations for a successfully locked transaction (%u-%u, default: %u) - Toon N bevestigingen voor een succesvol vergrendelde transactie (%u-%u, standaard: %u) - This is the transaction fee you may pay when fee estimates are not available. Dit is de transactievergoeding die je mogelijk betaalt indien geschatte tarief niet beschikbaar is @@ -5304,7 +5289,7 @@ Nota: Het bericht zal niet verzonden worden met de betaling over het Dash netwer Verifying blocks... - Blokken aan het controleren... + blocks aan het controleren... Verifying wallet... diff --git a/src/qt/locale/dash_pl.ts b/src/qt/locale/dash_pl.ts index 3b87c0031c93..90564883c895 100644 --- a/src/qt/locale/dash_pl.ts +++ b/src/qt/locale/dash_pl.ts @@ -794,7 +794,7 @@ W celu użycia tej funkcji, przełącz na "Tryb Listy" - Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again. + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. Wybrano niezanonimizowane środki. <b> PrivateSend zostanie wyłączony.</b><br><br>Jeśli nadal chcesz użyć PrivateSend, cofnij wybór niezanonimizowanych środków, a następnie zaznacz pole wyboru PrivateSend. @@ -834,73 +834,6 @@ nie dotyczy - - DarksendConfig - - Configure PrivateSend - Skonfiguruj PrivateSend - - - Basic Privacy - Podstawowa prywatność - - - High Privacy - Wysoka prywatność - - - Maximum Privacy - Maksymalna prywatność - - - Please select a privacy level. - Proszę wybrać poziom prywatności. - - - Use 2 separate masternodes to mix funds up to 1000 DASH - Użyj 2 oddzielnych masternodów, aby wymieszać środki max. do 1000 DASH - - - Use 8 separate masternodes to mix funds up to 1000 DASH - Użyj 8 oddzielnych masternodów, aby wymieszać środki max. do 1000 DASH - - - Use 16 separate masternodes - Użyj 16 oddzielnych masternodów - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - Ta opcja jest najszybsza i kosztuje około 0.025 DASH za zanonimizowanie 1000 DASH - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - Ta opcja jest w miarę szybka i kosztuje około 0.05 DASH za zanonimizowanie 1000 DASH - - - 0.1 DASH per 1000 DASH you anonymize. - 0.1 DASH za każde 1000 DASH, które zanonimizujesz. - - - This is the slowest and most secure option. Using maximum anonymity will cost - Jest to najwolniejsza, lecz najbardziej bezpieczna opcja. Maksymalny poziom zanonimizowania będzie kosztować - - - PrivateSend Configuration - Konfiguracja PrivateSend - - - PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend pomyślnie ustawiono na poziom podstawowy (%1 oraz 2 rundy). Możesz to zmienić w każdej chwili, otwierając ekran konfiguracji Dash. - - - PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend pomyślnie ustawiono na poziom wysoki (%1 oraz 8 rund). Możesz to zmienić w każdej chwili, otwierając ekran konfiguracji Dash. - - - PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend pomyślnie ustawiono na poziom najwyższy (%1 oraz 16 rund). Możesz to zmienić w każdej chwili, otwierając ekran konfiguracji Dash. - - EditAddressDialog @@ -1038,8 +971,8 @@ Informacje o PrivateSend - <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more info see <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> - <h3>Podstawy PrivateSend</h3> PrivateSend zapewnia prawdziwą prywatność poprzez ukrycie pochodzenia środków. Wszystkie dashe w twoim portfelu składają się z różnych "kwot wejściowych", które można traktować jako oddzielne, indywidualne monety.<br> PrivateSend wykorzystuje rewolucyjny proces mieszania twoich "kwot wejściowych" z "kwotami wejściowymi" dwóch innych użytkowników, przy czym twoje środki nigdy nie opuszczają twojego portfela. W każdej chwili zachowujesz nad nimi pełną kontrolę.<hr> <b>Proces PrivateSend przebiega w następujący sposób:</b><ol type="1"> <li>Najpierw PrivateSend dzieli twoje kwoty wejściowe twoich transakcji na standardowe nominały. Są kwoty 0.01 DASH, 0.1 DASH, 1 DASH oraz 10 DASH – mniej więcej jak banknoty, których codziennie używasz.</li> <li>Następnie twój portfel wysyła żądania do specjalnie skonfigurowanych węzłów sieci, zwanych "masternodami." Masternody otrzymują komunikat, że jesteś zainteresowany wymieszaniem określonego nominału. Masternody nie otrzymują żadnych informacji pozwalających na identyfikację, w związku z tym nigdy nie wiedzą "kim" jesteś.</li> <li>Kiedy dwaj inni użytkownicy wyślą podobne komunikaty, z żądanie wymieszanie takiego samego nominału, rozpocznie się sesja mieszania. Masternod wymiesza kwoty wejściowe i nakaże portfelom takich trzech użytkowników wypłacić sobie samemu przetworzone kwoty wejściowe. Twój portfel wpłaci dany nominał do siebie samego, jednak na inny adres (tzw. adres reszty).</li> <li>W celu pełnego zaciemnienia pochodzenia środków, twój portfel musi powtórzyć te czynności dla każdego nominału określoną ilość razy. Każde wykonanie tego procesu określamy mianem "rundy." Każda runda procesu PrivateSend wykładniczo zwiększa trudność ustalenia pochodzenia twoich środków.</li> <li>Proces mieszania odbywa się w tle, bez dodatkowego zaangażowania z twojej strony. Kiedy zechcesz wykonać transakcję, twoje środki będą już zanonimizowane. Nie będzie konieczne dodatkowe oczekiwanie.</li> </ol> <hr><b>UWAGA:</b> Portfel zawiera tylko 1000 "adresów reszty." Każde mieszanie zużywa do 9 adresów. Oznacza to, że po 100 mieszaniach zużyjesz ok. 1000 adresów. Po wykorzystaniu 900 adresów, portfel musi wygenerować nowe adresy. Może not nastąpić jedynie, jeżeli włączone jest automatyczne tworzenie kopii zapasowych.<br> Dlatego też, PrivateSend jest wyłączone, jeżeli użytkownik wyłączył automatyczne tworzenie kopii zapasowych. <hr>Więcej informacji znajdziesz na stronie <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. + <h3>Podstawy PrivateSend</h3> PrivateSend zapewnia prawdziwą prywatność poprzez ukrycie pochodzenia środków. Wszystkie dashe w twoim portfelu składają się z różnych "kwot wejściowych", które można traktować jako oddzielne, indywidualne monety.<br> PrivateSend wykorzystuje rewolucyjny proces mieszania twoich "kwot wejściowych" z "kwotami wejściowymi" dwóch innych użytkowników, przy czym twoje środki nigdy nie opuszczają twojego portfela. W każdej chwili zachowujesz nad nimi pełną kontrolę.<hr> <b>Proces PrivateSend przebiega w następujący sposób:</b><ol type="1"> <li>Najpierw PrivateSend dzieli twoje kwoty wejściowe twoich transakcji na standardowe nominały. Są kwoty 0.01 DASH, 0.1 DASH, 1 DASH oraz 10 DASH – mniej więcej jak banknoty, których codziennie używasz.</li> <li>Następnie twój portfel wysyła żądania do specjalnie skonfigurowanych węzłów sieci, zwanych "masternodami." Masternody otrzymują komunikat, że jesteś zainteresowany wymieszaniem określonego nominału. Masternody nie otrzymują żadnych informacji pozwalających na identyfikację, w związku z tym nigdy nie wiedzą "kim" jesteś.</li> <li>Kiedy dwaj inni użytkownicy wyślą podobne komunikaty, z żądanie wymieszanie takiego samego nominału, rozpocznie się sesja mieszania. Masternod wymiesza kwoty wejściowe i nakaże portfelom takich trzech użytkowników wypłacić sobie samemu przetworzone kwoty wejściowe. Twój portfel wpłaci dany nominał do siebie samego, jednak na inny adres (tzw. adres reszty).</li> <li>W celu pełnego zaciemnienia pochodzenia środków, twój portfel musi powtórzyć te czynności dla każdego nominału określoną ilość razy. Każde wykonanie tego procesu określamy mianem "rundy." Każda runda procesu PrivateSend wykładniczo zwiększa trudność ustalenia pochodzenia twoich środków.</li> <li>Proces mieszania odbywa się w tle, bez dodatkowego zaangażowania z twojej strony. Kiedy zechcesz wykonać transakcję, twoje środki będą już zanonimizowane. Nie będzie konieczne dodatkowe oczekiwanie.</li> </ol> <hr><b>UWAGA:</b> Portfel zawiera tylko 1000 "adresów reszty." Każde mieszanie zużywa do 9 adresów. Oznacza to, że po 100 mieszaniach zużyjesz ok. 1000 adresów. Po wykorzystaniu 900 adresów, portfel musi wygenerować nowe adresy. Może not nastąpić jedynie, jeżeli włączone jest automatyczne tworzenie kopii zapasowych.<br> Dlatego też, PrivateSend jest wyłączone, jeżeli użytkownik wyłączył automatyczne tworzenie kopii zapasowych. <hr>Więcej informacji na temat PrivateSend<a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html"> można znaleźć w dokumentacji technicznej</a>. @@ -4094,7 +4027,7 @@ https://www.transifex.com/projects/p/dash/ User defined mnemonic for HD wallet (bip39). Only has effect during wallet creation/first start (default: randomly generated) - Lista słów dla porfela HD stworzona przez użytkownika (bip39). Tyczy się jedynie kiedy tworzysz nowy porfel (domyślnie: tworozne losowo) + Lista słów dla portfela HD stworzona przez użytkownika (bip39). Tyczy się jedynie kiedy tworzysz nowy portfel (domyślnie: tworzone losowo) User defined seed for HD wallet (should be in hex). Only has effect during wallet creation/first start (default: randomly generated) @@ -4480,6 +4413,10 @@ https://www.transifex.com/projects/p/dash/ Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) Wyłącz wszystkie funkcjonalności charakterystyczne dla Dash (Masternody, PrivateSend, InstantSend, Governance) (0-1, domyślnie: %u) + + %s file contains all private keys from this wallet. Do not share it with anyone! + %s plik zawiera wszystkie klucze prywatne przechowywane w tym portfelu. Nie dawaj nikomu dostępu do tego pliku. + Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) Wykryj własny adres IP (domyślnie: 1 kiedy nasłuchuje, bez stosowania -externalip lub -proxy) @@ -4504,6 +4441,10 @@ https://www.transifex.com/projects/p/dash/ Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID) Wykonaj komendę, kiedy się transakcja InstantSend portfela zostanie pomyślnie zamknięta (%s w konsoli jest zastąpione przez TxID) + + Extra transactions to keep in memory for compact block reconstructions (default: %u) + Dodatkowe transakcje trzymane w pamięci aby móc zrekonstruować kompaktowe bloki (domyślnie: %u) + Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. Tworzenie kopii zapasowej nieudane, plik już istnieje! Taka sytuacja może wystąpić, jeżeli minęło mniej niż 60 od ostatniego zamknięcia portfela. Jeżeli Ci to nie przeszkadza, możesz kontynuować. @@ -4584,6 +4525,10 @@ https://www.transifex.com/projects/p/dash/ Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. Całkowita długość sieciowej wersji struny (%i) przekracza maksymalną długość (%i). Zredukuj liczbę lub rozmiar uacomments. + + Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index. + Indeks tranaskcji nie może zostać wyłączony w pełnym trybe. Albo uruchom z komendy -lighmode lub włącz indeksowanie transakcji. + Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) Próbuje utrzymać wychodzący ruch sieciowy poniżej podanej wartości (MiB/24 h), 0 = bez ograniczeń (domyślnie: %d) @@ -4616,6 +4561,10 @@ https://www.transifex.com/projects/p/dash/ Warning: Unknown block versions being mined! It's possible unknown rules are in effect Uwaga: Wykopywane są bloki o nieznanej wersji! Możliwe, że zostały aktywowane inne zasady na których opiera się sieć. + + You are starting in lite mode, all Dash-specific functionality is disabled. + Uruchamiasz porfel w trybie lite, wszystkie dodatkowe funkcje Dasha zostaną wyłączone. + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain Aby wrócić do trybu bez bez obcinki, musisz odtworzyć bazę danych za pomocą komendy -reindex. Cały blockchain zostanie ponownie ściągnięty. @@ -4660,6 +4609,14 @@ https://www.transifex.com/projects/p/dash/ Enable publish hash block in <address> Pozwól na publikację hash bloku w <address> + + Enable publish hash of governance objects (like proposals) in <address> + Pozwól na publikację hash obiektu zarządzania (np. propozycji) w <address> + + + Enable publish hash of governance votes in <address> + Pozwól na publikację hash głosów w <address> + Enable publish hash transaction (locked via InstantSend) in <address> Pozwól na publikację hash transakcji (zabezpiecznona przez InstantSend) w <address> @@ -4700,6 +4657,14 @@ https://www.transifex.com/projects/p/dash/ Failed to delete backup, error: %s Skasowanie kopii zapasowej nie powiodło się, błąd: %s + + Failed to load InstantSend data cache from + Nie powiodło się ładowanie bufora InstantSend z + + + Failed to load sporks cache from + Nie powiodło się ładowanie bufora sporków z + Failed to parse host:port string Nieudane analizowanie hosta:port string @@ -4720,6 +4685,10 @@ https://www.transifex.com/projects/p/dash/ Invalid amount for -fallbackfee=<amount>: '%s' Błędna ilość -fallbackfee=<amount>: '%s' + + Invalid input count. + Niewłaściwa ilość transakcji wejściowych. + Keep the transaction memory pool below <n> megabytes (default: %u) Utrzymuj rozmiar memory pool poniżej <n> megabajtów (domyślnie: %u) @@ -4736,6 +4705,10 @@ https://www.transifex.com/projects/p/dash/ Line: %d Linia: %d + + Loading InstantSend data cache... + Ładowanie bufora danych InstandSend... + Loading addresses... Wczytuję adresy... @@ -4748,6 +4721,10 @@ https://www.transifex.com/projects/p/dash/ Loading governance cache... Wczytuję bufor governance + + Loading sporks cache... + Ładowanie bufora sporków.... + Loading wallet... (%3.2f %%) Ładuję portfel... (%3.2f %%) @@ -4888,6 +4865,10 @@ https://www.transifex.com/projects/p/dash/ Transaction must have at least one recipient Transakcja wymaga co najmniej jednego odbiorcy + + Transaction too large + Za duża transakcja + Trying to connect... Staram się połączyć @@ -4908,6 +4889,10 @@ https://www.transifex.com/projects/p/dash/ Wallet debugging/testing options: Opcje debugowania/testowania portfela: + + Wallet is not initialized + Portfel nie został uruchomiony + Wallet needed to be rewritten: restart %s to complete Portfel wymaga przepisania: zrestartuj %s aby ukończyć @@ -4924,6 +4909,10 @@ https://www.transifex.com/projects/p/dash/ Whether to operate in a blocks only mode (default: %u) Czy operować jedynie w trybie bloków (domyślnie: %u) + + You can not start a masternode in lite mode. + Nie możesz uruchomić masternoda w trybie lite. + ZeroMQ notification options: ZeroMQ opcje powiadomień: @@ -5012,10 +5001,6 @@ https://www.transifex.com/projects/p/dash/ Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) Ustaw najnższą opłatę (in %s/kB) za transakcje aby została ona włączona do bloku. (domyślnie: %s) - - Show N confirmations for a successfully locked transaction (%u-%u, default: %u) - Pokaż N potwierdzeń dla skutecznie zamkniętej transakcji (%u-%u, domyślnie: %u) - This is the transaction fee you may pay when fee estimates are not available. To jest prawdopodobny koszt transakcj jeśli szacunki opłat są niedostępne. diff --git a/src/qt/locale/dash_pt.ts b/src/qt/locale/dash_pt.ts index 0815cbce3b68..66e2d643a027 100644 --- a/src/qt/locale/dash_pt.ts +++ b/src/qt/locale/dash_pt.ts @@ -1,4 +1,4 @@ - + AddressBookPage @@ -7,7 +7,7 @@ Create a new address - Criar novo endereço + Criar um novo endereço &New @@ -15,7 +15,7 @@ Copy the currently selected address to the system clipboard - Copiar o endereço seleccionado para a área de transferência + Copiar o endereço selecionado para a área de transferência do sistema &Copy @@ -23,15 +23,15 @@ Delete the currently selected address from the list - Apagar o endereço seleccionado + Excluir os endereços selecionados da lista &Delete - &Apagar + E&xcluir Export the data in the current tab to a file - Exportar os dados do separador actual para um ficheiro + Exportar os dados na aba atual para um arquivo &Export @@ -43,11 +43,11 @@ Choose the address to send coins to - Escolha o endereço para onde enviar as moedas + Escolha o endereço no qual receberá as moedas Choose the address to receive coins with - Escolha o endereço onde deseja receber as moedas + Escolha o endereço para receber moedas com ele C&hoose @@ -55,19 +55,19 @@ Sending addresses - Endereços para envio + Endereços de envio Receiving addresses - Endereços para recepção + Endereços de recebimento These are your Dash addresses for sending payments. Always check the amount and the receiving address before sending coins. - Estes são os endereços Dash para envio de pagamentos. Confirme sempre a quantia e o endereço antes de enviar as moedas. + Esses são os seus endereços para enviar pagamentos. Sempre cheque a quantia e o endereço de recebimento antes de enviar moedas, These are your Dash addresses for receiving payments. It is recommended to use a new receiving address for each transaction. - Estes são os seus endereços Dash para receber pagamentos. É recomendado que seja usado um novo endereço para cada transacção. + Estes são os seus endereços para receber pagamentos. É recomendado usar um novo para cada transação. &Copy Address @@ -83,19 +83,19 @@ Export Address List - Exportar lista de endereços + Exportar lista de endereço Comma separated file (*.csv) - Ficheiro separado por vírgulas (*.csv) + Arquivo separado por vírgulas (*. csv) Exporting Failed - A exportação falhou + Falha ao exportar There was an error trying to save the address list to %1. Please try again. - Ocorreu um erro ao tentar gravar a lista de endereços para %1. Por favor, tente de novo. + Houve um erro ao tentar salvar a lista de endereços em %1. Por favor tente novamente. @@ -117,39 +117,35 @@ AskPassphraseDialog Passphrase Dialog - Diálogo de Palavra Passe + Janela da Frase de Segurança Enter passphrase - Palavra Passe Actual + Digite a frase de segurança New passphrase - Nova Palavra Passe + Nova frase de segurança Repeat new passphrase - Repita a Nova Palavra Passe - - - Serves to disable the trivial sendmoney when OS account compromised. Provides no real security. - Serve para desactivar o envio de dinheiro quando a conta do SO for comprometida. Não oferece segurança real. - - - For anonymization only - Apenas para anonimização + Repita a nova frase de segurança Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>. - Insira a nova palavra-passe para a carteira.<br/>Por favor use uma palavra-chave com <b>10 ou mais caracteres aleatórios</b>, ou <b>8 ou mais palavras</b>. + Insira a nova senha para a carteira.<br/>Favor usar uma senha com <b>dez ou mais caracteres aleatórios</b>, ou <b>oito ou mais palavras</b>. Encrypt wallet - Cifrar carteira + Criptografar carteira This operation needs your wallet passphrase to unlock the wallet. - Esta operação necessita da sua palavra-passe para desbloquear a carteira. + Esta operação precisa da sua senha para desbloquear a carteira. + + + Unlock wallet for mixing only + Desbloquear carteira appenas para misturar Unlock wallet @@ -157,55 +153,59 @@ This operation needs your wallet passphrase to decrypt the wallet. - Esta operação necessita da sua palavra-passe para decifrar a carteira. + Esta operação precisa da sua senha para descriptografar a carteira Decrypt wallet - Decifrar carteira + Descriptografar carteira Change passphrase - Mudar palavra-passe + Alterar frase de segurança - Enter the old and new passphrase to the wallet. - Insira a antiga e a nova palavra-passe para a carteira. + Enter the old passphrase and new passphrase to the wallet. + Insira a senha antiga e a nova para a carteira. Confirm wallet encryption - Confirme cifra da carteira + Confirmar criptografia da carteira Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR DASH</b>! - Atenção: Se cifrar a carteira e perder a palavra-passe, irá <b>PERDER TODAS AS SUAS MOEDAS DASH</b>! + Aviso: Se você encriptar sua wallet e perder sua senha, você vai <b>PERDER TODAS AS SUAS DASH</b>! Are you sure you wish to encrypt your wallet? - Tem a certeza que quer cifrar a carteira? + Tem certeza de que deseja criptografar sua carteira? Wallet encrypted - Carteira cifrada + Carteira criptografada + + + %1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your funds from being stolen by malware infecting your computer. + A %1 irá fechar agora para terminar o processo de encriptação. Lembre-se que encriptar sua carteira não fará com que seus Dash estejam totalmente a salvo de serem roubados por malwares que infectem seu computador. - Dash will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your dashs from being stolen by malware infecting your computer. - O cliente Dash irá fechar para finalizar o processo de cifra. Lembre-se que cifrar a sua carteira não consegue proteger totalmente as suas dashs contra roubos feitos por malware presente no seu computador. + IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. Previous backups of the unencrypted wallet file contain the same HD seed and still have full access to all your funds just like the new, encrypted wallet. + IMPORTANTE: Todos os backups anteriores feitos do seu arquivo de carteira devem ser substituídos pelo arquivo de carteira criptografado recém-gerado. Os backups anteriores do arquivo de carteira não criptografada contêm a mesma semente HD e ainda têm acesso total a todos os seus fundos, assim como a nova carteira criptografada. IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet. - IMPORTANTE: Quaisquer copias de segurança feitas anteriormente à sua carteira, devem ser substituídas pelo novo ficheiro cifrado. Por razões de segurança, as copias de segurança anteriores não cifradas ficarão obsoletas assim que comece a usar a nova carteira cifrada, + IMPORTANTE: Qualquer backup prévio que você tenha feito da sua carteira deve ser substituído pelo novo e encriptado arquivo gerado. Por razões de segurança, qualquer backup do arquivo não criptografado se tornará inútil assim que você começar a usar uma nova carteira criptografada. Wallet encryption failed - Cifra da carteira falhou + A criptografia da carteira falhou Wallet encryption failed due to an internal error. Your wallet was not encrypted. - A cifra da carteira falhou devido a um erro interno. A sua carteira não foi cifrada. + Falha na criptografia devido a um erro inerno. Sua carteira não foi criptografada. The supplied passphrases do not match. - As palavras-passe fornecidas não coincidem. + A frase de segurança fornecida não confere. Wallet unlock failed @@ -213,23 +213,38 @@ The passphrase entered for the wallet decryption was incorrect. - A palavra-passe fornecida para decifrar a carteira está incorrecta. + A senha inserida para descriptografar a carteira está incorreta. Wallet decryption failed - Decifra da carteira falhou + A descriptografia da carteira falhou Wallet passphrase was successfully changed. - A palavra-passe foi alterada com sucesso. + A frase de segurança da carteira foi alterada com êxito. Warning: The Caps Lock key is on! - Atenção: O Caps Lock está activo! + Atenção: A tecla Caps Lock está ligada! + + + + BanTableModel + + IP/Netmask + IP/Máscara de rede + + + Banned Until + Banido até BitcoinGUI + + A fatal error occurred. Dash Core can no longer continue safely and will quit. + Ocorreu um erro fatal. A Dash não pode mais continuar de forma segura, e irá fechar + Dash Core Dash Core @@ -244,11 +259,11 @@ &Overview - &Global + &Visão geral Show general overview of wallet - Mostrar visão global da carteira + Mostrar visão geral da carteira &Send @@ -256,7 +271,7 @@ Send coins to a Dash address - Enviar moedas para um endereço Dash + Envie moedas para um endereço Dash &Receive @@ -264,83 +279,91 @@ Request payments (generates QR codes and dash: URIs) - Solicitar pagamentos (gera códigos QR e dash: URIs) + Solicitações de pagamentos (gera códigos QR e Dash: URIs) &Transactions - &Transacções + &Transações Browse transaction history - Visualiza histórico de transacções + Navegar pelo histórico de transações + + + &Masternodes + &Masternodes + + + Browse masternodes + Exibir masternodes E&xit - &Sair + S&air Quit application Sair da aplicação - - &About Dash Core - &Acerca do Dash Core - Show information about Dash Core - Mostra informação acerca do Dash Core + Exibe informações sobre Dash Core About &Qt - Acerca do &Qt + Sobre &Qt Show information about Qt - Mostra informação acerca do Qt + Exibe informações sobre Qt &Options... &Opções... - Modify configuration options for Dash - Modificar opções de configuração do Dash + &About %1 + &Sobre %1 + + + Modify configuration options for %1 + Modificar opções de configuração para o %1 &Show / Hide - &Mostar / Esconder + &Exibir/Ocultar Show or hide the main Window - Mostrar ou esconder a Janela principal + Mostrar ou esconder a Janela Principal. &Encrypt Wallet... - &Cifrar Carteira + &Criptografar Carteira... Encrypt the private keys that belong to your wallet - Cifra as chaves privadas que pertencem à sua carteira + Criptografar as chaves privadas que pertencem à sua carteira &Backup Wallet... - Copia de &Segurança + Fazer &Backup da Carteira... Backup wallet to another location - Criar copia de segurança da carteira noutra localização + Faça o Backup da carteira para outro local &Change Passphrase... - &Mudar Palavra-passe + Alterar frase de segurança Change the passphrase used for wallet encryption - Mudar a palavra-passe usada na cifra da carteira + Mudar a frase de segurança utilizada na criptografia da carteira &Unlock Wallet... - &Desbloquear Carteira + &Desbloquear carteira... Unlock wallet @@ -348,23 +371,23 @@ &Lock Wallet - &Bloquear Carteira + Bloquear carteira Sign &message... - &Assinar Mensagem + Assinar &Mensagem... Sign messages with your Dash addresses to prove you own them - Assine mensagens com os seus endereços Dash para provar que são seus + Assine mensagens com seus endereços Dash para provar que você é dono delas &Verify message... - &Verificar Mensagem + &Verificar mensagem... Verify messages to ensure they were signed with specified Dash addresses - Verifica mensagens para garantir que foram assinadas com um endereço Dash específico + Verifique as mensagens para ter certeza de que elas foram assinadas com o endereço da Dash especificado &Information @@ -372,71 +395,79 @@ Show diagnostic information - Mostra informação de diagnóstico + Mostrar informação de diagnóstico &Debug console - Consola de &Depuração + &Console de depuração Open debugging console - Abrir consola de depuração + Abrir o console de depuração &Network Monitor - Monitor de &Rede + &Monitor de Rede Show network monitor - Mostrar monitor de rede + Exibe o monitor de rede &Peers list - Lista de &Nós + &Peers Show peers info - Mostrar informação dos nós + mostrar informações dos peers Wallet &Repair - &Reparar Carteira + &Reparar carteira Show wallet repair options - Mostra as opções de reparação da carteira + Mostrar opções de reparação da carteira - Open &Configuration File - Abrir Ficheiro de &Configuração + Open Wallet &Configuration File + Abrir arquivo de &Configuração da carteira Open configuration file - Abrir ficheiro de configuração + Abrir arquivo de configuração + + + Open &Masternode Configuration File + Abrir arquivo de configuração do &Masternode + + + Open Masternode configuration file + Abrir arquivo de configuração de Masternode Show Automatic &Backups - Mostrar &Cópias Automáticas + Mostrar &backups automáticos Show automatically created wallet backups - Mostra as cópias automáticas da carteira + Mostrar backups da carteira criados automaticamente &Sending addresses... - &Endereços de envio... + Endereço&s de envio... Show the list of used sending addresses and labels - Mostra a lista de endereços de envio e respectivos rótulos + Mostrar a lista de endereços de envio e rótulos usados &Receiving addresses... - Endereços de &Recepção... + Endereços de &recebimento... Show the list of used receiving addresses and labels - Mostra a lista de endereços de recepção e respectivos rótulos + Mostrar a lista de endereços de recebimento usados ​​e rótulos Open &URI... @@ -444,31 +475,35 @@ Open a dash: URI or payment request - Abre um dash: URI ou solicitação de pagamento + Abrir um Dash: URI ou cobrança &Command-line options - Opções de linha de &comandos + Opções de linha de &comando - Dash Core client - Cliente Dash Core + Show the %1 help message to get a list with possible Dash command-line options + Mostrar a mensagem de ajuda do %1 para obter uma lista com possíveis opções de linha de comando Dash - - Processed %n blocks of transaction history. - %n bloco do histórico de transacções processado.%n blocos do histórico de transacções processados. + + %1 client + %1 cliente - Show the Dash Core help message to get a list with possible Dash command-line options - Mostra a mensagem de ajuda do Dash Core para obter a lista com as possíveis opções de linha de comandos + &PrivateSend information + Informação de Envio&Privado + + + Show the PrivateSend basic information + Mostra informações básicas de EnvioPrivado &File - &Ficheiro + &Arquivo &Settings - &Definições + &Configurações &Tools @@ -484,63 +519,63 @@ %n active connection(s) to Dash network - %n ligação activa à rede Dash%n ligações activas à rede Dash + %n conexões ativas com a rede Dash%n conexões ativas com a rede Dash - Synchronizing with network... - A sincronizar com a rede... + Network activity disabled + Atividade da rede disativada - Importing blocks from disk... - A carregar blocos do disco... + Syncing Headers (%1%)... + Sincronizando cabeçahos (%1%)... - Reindexing blocks on disk... - A indexar blocos no disco... + Synchronizing with network... + Sincronizando com a rede... - No block source available... - Nenhuma fonte de blocos disponível... + Indexing blocks on disk... + Indexando blocos no disco... - Up to date - Actualizado - - - %n hour(s) - %n hora%n horas - - - %n day(s) - %n dia%n dias + Processing blocks on disk... + Processando blocos no disco... - - %n week(s) - %n semana%n semanas + + Reindexing blocks on disk... + Reindexando blocos no disco... - %1 and %2 - %1 e %2 + Connecting to peers... + Conectando... - %n year(s) - %n ano%n anos + Processed %n block(s) of transaction history. + Processados %n blocos do histórico de transações.Processados %n blocos do histórico de transações. %1 behind - %1 de atraso + %1 atrás Catching up... - A alcançar + Recuperando o atraso ... Last received block was generated %1 ago. - O último bloco recebido foi gerado à %1. + Último bloco recebido foi gerado %1 atrás. Transactions after this will not yet be visible. - Transacções posteriores ainda não serão visíveis. + Transações após isso ainda não estão visíveis. + + + Up to date + Atualizado + + + Synchronizing additional data: %p% + Sincronizando dados adicionais: %p% Error @@ -548,54 +583,80 @@ Warning - Aviso + Atenção Information Informação - Sent transaction - Transacção enviada + Date: %1 + + Data: %1 + - Incoming transaction - Transacção recebida + Amount: %1 + + Quantia: %1 + - Date: %1 -Amount: %2 -Type: %3 -Address: %4 + Type: %1 - Data: %1 -Quantia: %2 -Tipo: %3 -Endereço: %4 + Tipo: %1 + + + + Label: %1 + + Rótulo: %1 + + + + Address: %1 + + Endereço: %1 + + Sent transaction + Transação enviada + + + Incoming transaction + Transação recebida + + + HD key generation is <b>enabled</b> + Geração de chave HD está <b>ativada</b> + + + HD key generation is <b>disabled</b> + Geração de chave HD está <b>desativada</b> + Wallet is <b>encrypted</b> and currently <b>unlocked</b> - A carteira encontra-se <b>cifrada</b> e actualmente <b>desbloqueada</b> + Carteira está <b>criptografada</b> e atualmente <b>desbloqueada</b> - Wallet is <b>encrypted</b> and currently <b>unlocked</b> for anonimization only - A carteira encontra-se <b>cifrada</b> e actualmente <b>desbloqueada</b> somente para anonimização + Wallet is <b>encrypted</b> and currently <b>unlocked</b> for mixing only + A carteira está <b>encriptada</b> e atualmente <b>desbloqueada</b> apenas para anonimização Wallet is <b>encrypted</b> and currently <b>locked</b> - A carteira encontra-se <b>cifrada</b> e actualmente <b>bloqueada</b> + Carteira está <b>criptografada</b> e atualmente <b>bloqueada</b> ClientModel - Total: %1 (DS compatible: %2 / Enabled: %3) - Total: %1 (Compatível DS: %2 / Ligados: %3) + Total: %1 (PS compatible: %2 / Enabled: %3) + Total: %1 (Compatível c/ EP: %2 / Habilitado: %3) Network Alert - Alerta de Rede + Alerta da Rede @@ -612,17 +673,13 @@ Endereço: %4 Amount: Quantia: - - Priority: - Prioridade: - Fee: Taxa: Coin Selection - Selecção de Moedas + Seleção da Moeda Dust: @@ -630,31 +687,35 @@ Endereço: %4 After Fee: - Com taxa: + Depois da taxa: Change: - Troco: + trocar (un)select all - (des)seleccionar todos + (de)selecionar tudo + + + toggle lock state + alternar estado de bloqueio Tree mode - Vista em árvore + Modo árvore List mode - Vista em lista + Modo lista (1 locked) - (1 bloqueada) + (1 travado) Amount - Quantia + Quantidade Received with label @@ -665,8 +726,8 @@ Endereço: %4 Recebido com endereço - DS Rounds - Rondas DS + PS Rounds + Rounds de EP Date @@ -678,15 +739,11 @@ Endereço: %4 Confirmed - Confirmada - - - Priority - Prioridade + Confirmado Copy address - Copiar endereço + Copie endereço Copy label @@ -694,11 +751,11 @@ Endereço: %4 Copy amount - Copiar quantia + Copie quantia Copy transaction ID - Copiar ID de transacção + Copie ID da transação Lock unspent @@ -714,20 +771,16 @@ Endereço: %4 Copy fee - Copiar taxa + Copie taxa Copy after fee - Copiar depois da taxa + Copiar pós taxa Copy bytes Copiar bytes - - Copy priority - Copiar prioridade - Copy dust Copiar poeira @@ -737,61 +790,17 @@ Endereço: %4 Copiar troco - Non-anonymized input selected. <b>Darksend will be disabled.</b><br><br>If you still want to use Darksend, please deselect all non-nonymized inputs first and then check Darksend checkbox again. - Entrada não anonimizada seleccionada. <b>O Darksend será desactivado.</b><br><br>Se deseja usar o Darksend, por favor desseleccione primeiro todas as entradas não anonimizadas e em seguida volte a marcar a opção Darksend. + Please switch to "List mode" to use this function. + Por favor alterne para "Modo lista" para usar essa função. - highest - a-mais-alta - - - higher - mais-alta - - - high - alta - - - medium-high - média-alta - - - Can vary +/- %1 duff(s) per input. - Pode variar +/- %1 duff(s) por input - - - n/a - n/d - - - medium - média - - - low-medium - média-baixa - - - low - baixa - - - lower - mais-baixa - - - lowest - a-mais-baixa + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. + Entrada não anônima selecionada. <b>O PrivateSend será desativado.</b> <br> <br> Se você ainda quiser usar o PrivateSend, desmarque primeiro todas as entradas não modificadas e marque a caixa de seleção PrivateSend novamente. (%1 locked) (%1 bloqueado) - - none - nenhuma - yes sim @@ -801,28 +810,12 @@ Endereço: %4 não - This label turns red, if the transaction size is greater than 1000 bytes. - Este rótulo fica vermelho se o tamanho da transacção exceder 1000 bytes. - - - This means a fee of at least %1 per kB is required. - Isto significa que é necessária uma taxa de pelo menos %1 por kB. - - - Can vary +/- 1 byte per input. - Pode variar +/- 1 byte por entrada. - - - Transactions with higher priority are more likely to get included into a block. - Transacções com prioridade mais alta tem uma maior probabilidade de ser incluídas num bloco. + This label turns red if any recipient receives an amount smaller than the current dust threshold. + Este texto fica vermelho se qualquer destinatário receber uma quantidade menor que que o dust. - This label turns red, if the priority is smaller than "medium". - Este rótulo fica vermelho se a prioridade for inferior a "média". - - - This label turns red, if any recipient receives an amount smaller than %1. - Este rótulo fica vermelho se algum destinatário receber uma quantia inferior a %1. + Can vary +/- %1 duff(s) per input. + Pode variar +/- %1 duff(s) por entrada. (no label) @@ -836,72 +829,9 @@ Endereço: %4 (change) (troco) - - - DarksendConfig - - Configure Darksend - Configurar Darksend - - - Basic Privacy - Privacidade Básica - - - High Privacy - Privacidade Alta - - - Maximum Privacy - Privacidade Máxima - - - Please select a privacy level. - Por favor seleccione um nível de privacidade. - - - Use 2 separate masternodes to mix funds up to 1000 DASH - Usar 2 masternodes distintos para misturar os fundos até 1000DRK - - - Use 8 separate masternodes to mix funds up to 1000 DASH - Usar 8 masternodes distintos para misturar os fundos até 1000DRK - - - Use 16 separate masternodes - Usar 16 masternodes - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - Esta opção é a mais rápida e irá custar cerca de ~0.025 DASH para anonimizar 1000 DASH - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - Esta opção é relativamente rápida e irá custar cerca de ~0.05 DASH para anonimizar 1000 DASH - - - 0.1 DASH per 1000 DASH you anonymize. - 0.1 DASH por cada 1000 DASH a anonimizar. - - - This is the slowest and most secure option. Using maximum anonymity will cost - Esta é a opção mais lenta e mais segura. Usando anonimização máxima irá custar - - - Darksend Configuration - Configuração Darksend - - - Darksend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - Darksend foi colocado com sucesso no modo básico (%1 e 2 voltas). Pode mudar esta definição em qualquer altura abrindo o ecrã de configuração Dash. - - - Darksend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - Darksend foi colocado com sucesso no modo alto (%1 e 8 voltas). Pode mudar esta definição em qualquer altura abrindo o ecrã de configuração Dash. - - Darksend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - Darksend foi colocado com sucesso no modo máximo (%1 e 16 voltas). Pode mudar esta definição em qualquer altura abrindo o ecrã de configuração Dash. + n/a + n/d @@ -916,7 +846,7 @@ Endereço: %4 The label associated with this address list entry - Rótulo associado com este item da lista de endereços + O rótulo associado a esta entrada na lista de endereços &Address @@ -924,11 +854,11 @@ Endereço: %4 The address associated with this address list entry. This can only be modified for sending addresses. - O endereço associado com este item da lista. Isto só pode ser modificado para endereços de envio. + O endereço associado a esta lista de endereços de entrada. Isso só pode ser modificado para o envio de endereços. New receiving address - Novo endereço de recepção + Novo endereço de recebimento New sending address @@ -936,7 +866,7 @@ Endereço: %4 Edit receiving address - Editar endereço de recepção + Editar endereço de recebimento Edit sending address @@ -944,26 +874,26 @@ Endereço: %4 The entered address "%1" is not a valid Dash address. - O endereço introduzido "%1" não é um endereço Dash válido. + O endereço digitado "%1" não é um endereço válido. The entered address "%1" is already in the address book. - O endereço introduzido "%1" já se encontra no livro de endereços. + O endereço digitado "%1" já se encontra no catálogo de endereços. Could not unlock wallet. - Não foi possível desbloquear carteira. + Não foi possível desbloquear a carteira. New key generation failed. - Falhou a geração de nova chave. + Falha na geração de nova chave. FreespaceChecker A new data directory will be created. - Será criada uma nova pasta de dados. + Um novo diretório de dados será criado. name @@ -971,23 +901,19 @@ Endereço: %4 Directory already exists. Add %1 if you intend to create a new directory here. - A pasta já existe. Adicione %1 se desejar criar uma nova pasta aqui. + O diretório já existe. Adicione %1 se você pretende criar um novo diretório aqui. Path already exists, and is not a directory. - O caminho já existe e não é uma pasta. + Esta pasta já existe, e não é um diretório. Cannot create data directory here. - Não é possível criar a pasta de dados aqui. + Não é possível criar um diretório de dados aqui HelpMessageDialog - - Dash Core - Dash Core - version versão @@ -997,44 +923,56 @@ Endereço: %4 (%1-bit) - About Dash Core - Acerca do Dash Core + About %1 + Sobre %1 Command-line options - Opções de linha de comandos + opções da linha de comando Usage: - Utilização: + Uso: command-line options - opções de linha de comandos + opções da linha de comando - UI options - Opções do interface + UI Options: + opções da UI - Choose data directory on startup (default: 0) - Escolher pasta de dados no arranque (omissão: 0) + Choose data directory on startup (default: %u) + Escolher diretório de dados na inicialização (padrão: %u) Set language, for example "de_DE" (default: system locale) - Defina a linguagem, por exemplo "pt-PT" (omissão: linguagem do sistema) + Definir idioma, por exemplo "de_DE" (padrão: idioma do sistema) Start minimized - Arrancar minimizado + Iniciar minimizado Set SSL root certificates for payment request (default: -system-) - Definir certificados de raiz SSL para solicitações de pagamento (omissão: -sistema-) + Definir certificados de root SSL para requisições de pagamento (padrão: -sistema-) + + + Show splash screen on startup (default: %u) + Exibir tela de abertura na inicialização (padrão: %u) + + + Reset all settings changed in the GUI + Resetar todas as configuraçãoes do GUI + + + PrivateSend information + Informação de Envio&Privado - Show splash screen on startup (default: 1) - Mostrar ecrã de boas vindas no arranque (omissão: 1) + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. + <h3>O básico sobre EnvioPrivado</h3>O EnvioPrivado te dá verdadeira privacidade financeira ao obscurecer a origem dos seus fundos. Todos os dashs na sua carteira é composto de "inputs" diferentes que você pode pensar como sendo moedas separadas, discretas.<br>O EnvioPrivado usa um processo inovador para misturas os inputs de duas pessoas diferentes, sem que suas moedas saiam de suas carteiras. Você mantém o controle de seu dinheiro o tempo todo.. <hr><b>O processo do EnvioPrivado funciona assim: </b><li>O EnvioPrivado começa quebrando os inputs de suuas transações em denominações padrão. Essas denominações são 0.01 DASH, 0.1 DASH, 1 DASH e 10 DASH -- algo como o dinheiro de papel que você usa todo dia.</li>Sua carteira então envia uma requisição para um software especialmente configurado na rede, chamados de "masternodes." Esses masternodes são informados que você está interessado em misturar uma denominação específica. Nenhuma informação identificável é enviada aos masternodes, então eles nunca sabem "quem" você é.<li>Quando duas pessoas enviam mensagens similares, uma sessão de mistura tem início. O masternode mistura os inputs e instrui os três usuários da carteira para pagar o input agora transformado de volta a si mesmos. Sua carteira paga a denominação diretamente a si mesmo, mas em um endereço diferente (chamado de endereço mudado).<li>A fim de obscurecer plenamente seus fundos, sua carteira repete esse processo várias vezes com cada denominação. Cada vez que o processo é completado, se chama um "round". Cada round de EnvioPrivado torna exponencialmente mais difícil determinar onde os fundos se originaram.<li>Esse processo de mistura acontece no background sem nenhuma intervenção da sua parte. Quando você deseja fazer uma transação, seus fundos jã estarão anonimizados. Nenhuma espera adicional é requerida.<li><ol type="1"><hr>IMPORTANTE: <b>Sua carteira só contém 1000 desses "endereços mudados." Cada vez que um evento de mistura acontece, 9 de seus endereços são usados. Isso indica que esses 1000 endereços duram por cerca de 100 eventos de mistura. Quando 900 desses endereços são usados, sua carteira deve criar mais endereços. Contudo, ela só é capaz de fazer isso se você tem o backup automático habilitado.<br>Consequentemente, usuários que têm seus backups desabilitados também terão o EnvioPrivado desabilitado. <hr>Para mais informações, consulte a <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">documentação do PrivateSend</a>. @@ -1044,32 +982,28 @@ Endereço: %4 Bem-vindo - Welcome to Dash Core. - Bem-vindo ao Dash Core. + Welcome to %1. + Bem vindo ao %1 - As this is the first time the program is launched, you can choose where Dash Core will store its data. - Como esta é a primeira vez que o programa é executado, pode escolher onde o cliente Dash Core irá guardar os seus dados. + As this is the first time the program is launched, you can choose where %1 will store its data. + Como essa é a primeira vez que o programa é executado, você pode escolher onde %1 armazenará seus dados. - Dash Core will download and store a copy of the Dash block chain. At least %1GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory. - O cliente Dash Core irá descarregar e guardar uma copia da cadeia de blocos Dash. Pelo menos %1GB de dados serão guardados nesta pasta e irá aumentar com o tempo. A carteira também ficará guardada nesta pasta. + %1 will download and store a copy of the Dash block chain. At least %2GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory. + %1 fará o download e armazenará uma cópia da cadeia de blocos do Dash. Pelo menos %2GB de dados serão armazenados neste diretório e crescerão com o tempo. A carteira também será armazenada neste diretório. Use the default data directory - Usar a pasta de dados por omissão + Use o diretório de dados padrão Use a custom data directory: - Usar uma pasta de dados personalizada - - - Dash Core - Dash Core + Use um diretório de dados personalizado: Error: Specified data directory "%1" cannot be created. - Erro: A pasta especificada "%1" não pode ser criada. + Erro: Diretório de dados "%1" não pode ser criado. Error @@ -1077,227 +1011,505 @@ Endereço: %4 %1 GB of free space available - %1 GB de espaço livre disponível + %1 GB de espaço livre disponível (of %1 GB needed) - (de %1 GB necessários) + (Mais de %1GB necessário) - OpenURIDialog + MasternodeList - Open URI - Abrir URI + Form + Formulário - Open payment request from URI or file - Abrir solicitação de pagamento a partir de um URI ou ficheiro + My Masternodes + Meus Masternodes - URI: - URI: + Note: Status of your masternodes in local wallet can potentially be slightly incorrect.<br />Always wait for wallet to sync additional data and then double check from another node<br />if your masternode should be running but you still do not see "ENABLED" in "Status" field. + Nota: O status de seus masternodes na carteira local pode potencialmente ser ligeiramente incorreto. <br />Sempre espere que a carteira sincronize dados adicionais e depois cheque novamente de outro nó <br />, se o seu masternode estiver em execução, mas você ainda não vê "ATIVADO" no campo "Status". - Select payment request file - Seleccionar ficheiro de solicitação de pagamento + Alias + Apelido - Select payment request file to open - Seleccionar ficheiro de solicitação de pagamento para abrir + Address + Endereço - - - OptionsDialog - Options - Opções + Protocol + Protocolo - &Main - &Geral + Status + Status - Automatically start Dash after logging in to the system. - Executar automaticamente o cliente Dash quando entrar no sistema. + Active + Ativo - &Start Dash on system login - &Iniciar o cliente Dash ao entrar no sistema + Last Seen + visto pela última vez - Size of &database cache - Tamanho da &cache de dados + Payee + Payee - MB - MB + S&tart alias + Iniciar Apelido - Number of script &verification threads - Número de processos de &verificação de scripts + Start &all + Iniciar tudo - (0 = auto, <0 = leave that many cores free) - (0 = auto, <0 = deixar este número de cores livres) + Start &MISSING + Iniciar &FALTANTES - Darksend rounds to use - Número de voltas Darksend a usar + &Update status + &Update status - This amount acts as a threshold to turn off Darksend once it's reached. - Esta quantia actua como um limite, a partir do qual o Darksend é desligado. + Show additional Masternode information + Mostrar informações adicionais do Masternode - Amount of Dash to keep anonymized - Quantia de Dash a manter anonimizada + Show &Info... + Mostrar &Info... - W&allet - C&arteira + Status will be updated automatically in (sec): + O estado será atualizado automaticamente em (seg): - If you disable the spending of unconfirmed change, the change from a transaction<br/>cannot be used until that transaction has at least one confirmation.<br/>This also affects how your balance is computed. - Se desabilitar a funcionalidade de gastar o troco não confirmado, este troco <br/> não poderá ser usado até que a transacção tenha pelo menos uma confirmação. <br/> Isto também afecta a maneira como o seu saldo é calculado. + 0 + 0 - Accept connections from outside - Aceitar ligações do exterior + All Masternodes + Todos os Masternodes - Allow incoming connections - Aceitar ligações de entrada + Filter List: + Filtrar Lista: - Connect to the Dash network through a SOCKS5 proxy. - Efectuar a ligação à rede Dash através de um proxy SOCKS5. + Filter masternode list + Filtrar lista de masternodes - &Connect through SOCKS5 proxy (default proxy): - &Ligar através de um proxy SOCKS5 (proxy por omissão) + Node Count: + Contagem de Masternodes - Expert - Avançado + Start alias + Iniciar apelido - This setting determines the amount of individual masternodes that an input will be anonymized through.<br/>More rounds of anonymization gives a higher degree of privacy, but also costs more in fees. - Esta opção determina o número de masternodes pelos quais uma entrada será anonimizada. <br/> Mais rondas de anonimização fornecem um maior grau de privacidade, mas também tem um custo mais elevado em taxas. + Confirm masternode start + Confirmar início de masternode - Whether to show coin control features or not. - Se deve mostrar as opções de coin control. + Are you sure you want to start masternode %1? + Tem certeza de que deseja iniciar o masternode %1? - Enable coin &control features - Activar opções coin &control + Confirm all masternodes start + Confirmar inicialização de todos masternodes - &Spend unconfirmed change - &Gastar troco não confirmado + Are you sure you want to start ALL masternodes? + Tem certeza que quer inicializar TODOS os masternodes? - &Network - &Rede + Command is not available right now + Esse comando não está disponível nesse momento - Automatically open the Dash client port on the router. This only works when your router supports UPnP and it is enabled. - Abrir automaticamente a porta do cliente Dash no router. Isto só funciona quando o seu router suporta UPnP e este está activo. + You can't use this command until masternode list is synced + Você não pode usar este comando até que a lista do masternode seja sincronizada - Map port using &UPnP - Mapear porta usando &UPnP + Confirm missing masternodes start + Confirmar inicialização dos masternodes faltantes - Proxy &IP: - IP do proxy + Are you sure you want to start MISSING masternodes? + Tem certeza que quer inicializar os masternodes FALTANTES? - IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1) - Endereço IP do proxy (ex. IPv4: 127.0.0.1 / IPv6: ::1) + Additional information for Masternode %1 + Informações adicionais para o Masternode %1 - &Port: - &Porta: + Masternode Private Key + Chave Privada Masternode - Port of the proxy (e.g. 9050) - Porta do servidor proxy (ex. 9050) + Private Key + Chave Privada - &Window - &Janela + Collateral + Garantia - Show only a tray icon after minimizing the window. - Somente mostrar o ícone no tabuleiro após minimizar aplicação + IP + IP - &Minimize to the tray instead of the taskbar - &Minimizar para o tabuleiro, em vez da barra de tarefas + Version + Versão - Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Quit in the menu. - Minimizar em vez de fechar ao sair da aplicação. Quando esta opção está activada, a aplicação só será fechada quando seleccionar Sair no menu. + Unknown + Desconhecido - M&inimize on close - M&inimizar ao fechar + Sentinel + Sentinel + + + ModalOverlay - &Display - &Interface + Form + Forma - User Interface &language: - &Linguagem do interface: + Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the Dash network, as detailed below. + Transações recentes podem não estar visíveis ainda, portanto o seu saldo pode estar incorreto. Esta informação será corrigida assim que sua carteira for sincronizada com a rede, como detalhado abaixo. - The user interface language can be set here. This setting will take effect after restarting Dash. - A linguagem do interface pode ser definida aqui. Esta definição terá efeito depois de reiniciar a aplicação. + Attempting to spend Dash that are affected by not-yet-displayed transactions will not be accepted by the network. + A tentativa de gastar o Dash que é afetado por transações ainda não exibidas não será aceita pela rede. - Language missing or translation incomplete? Help contributing translations here: -https://www.transifex.com/projects/p/dash/ - Linguagem não encontrada ou tradução incompleta? Ajude a traduzir aqui: -https://www.transifex.com/projects/p/dash/ + Number of blocks left + Número de blocos que faltam - User Interface Theme: - Tema do interface: + Unknown... + Desconhecido... - &Unit to show amounts in: - &Unidade por omissão usada para mostrar as quantias: + Last block time + Hora do último bloco - Choose the default subdivision unit to show in the interface and when sending coins. - Escolha a unidade subdivisão por omissão para mostrar na interface e no envio de moedas. + Progress + Progresso - Decimal digits - Dígitos decimais + Progress increase per hour + Aumento do progresso por hora - Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. - URLs de terceiros (ex. explorador de blocos) que aparecem no separador de transacções como itens do menu de contexto. %s no URL é substituído pelo hash da transacção. URLs múltiplos são separados pela barra vertical |. + calculating... + calculando - Third party transaction URLs - URLs de transacções de terceiros + Estimated time left until synced + Tempo estimado para sincronizar - Active command-line options that override above options: - Opções activas de linha de comandos que sobrescrevem as opções acima: + Hide + Esconder - Reset all client options to default. - Reiniciar todas as opções do cliente para os valores por omissão. + Unknown. Syncing Headers (%1)... + Desconhecido. Sincronizando cabeçalhos (%1)... + + + OpenURIDialog - &Reset Options - &Reiniciar Opções + Open URI + Abrir URI + + + Open payment request from URI or file + Cobrança aberta de URI ou arquivo + + + URI: + URI: + + + Select payment request file + Selecione o arquivo de cobrança + + + Select payment request file to open + Selecione o arquivo de cobrança para ser aberto + + + + OptionsDialog + + Options + Opções + + + &Main + Principal + + + Size of &database cache + Tamanho do banco de &dados do cache + + + MB + MB + + + Number of script &verification threads + Número de threads do script de &verificação + + + (0 = auto, <0 = leave that many cores free) + (0 = automático, <0 = número de cores deixados livres) + + + Amount of Dash to keep anonymized + Quantidade de dashs para manter anonimizado + + + W&allet + C&arteira + + + Show additional tab listing all your masternodes in first sub-tab<br/>and all masternodes on the network in second sub-tab. + Mostre uma guia adicional listando todos os seus masternodes na primeira subposição <br/> e todos os masternodes na rede na segunda sub-guia. + + + Show Masternodes Tab + Mostrar a aba Masternodes + + + Show additional information and buttons for PrivateSend on overview screen. + Mostre informações adicionais e botões para PrivateSend na tela. + + + Enable advanced PrivateSend interface + Ativar interface avançada PrivateSend + + + Show warning dialog when PrivateSend detects that wallet has very low number of keys left. + Mostrar mensagem de aviso quando o PrivateSend detectar que a carteira tem um número muito baixo de chaves restantes. + + + Warn if PrivateSend is running out of keys + Avisar se o PrivateSend estiver sem as chaves + + + Whether to use experimental PrivateSend mode with multiple mixing sessions per block.<br/>Note: You must use this feature carefully.<br/>Make sure you always have recent wallet (auto)backup in a safe place! + Se você deseja usar o modo PrivateSend com várias sessões de mixagem por bloco. <br/> Nota: Você deve usar este recurso com cuidado. <br/> Certifique-se de ter sempre um backup recente de sua carteira em um local seguro. + + + Enable PrivateSend &multi-session + Ativar PrivateSend &multi-sessão + + + If you disable the spending of unconfirmed change, the change from a transaction<br/>cannot be used until that transaction has at least one confirmation.<br/>This also affects how your balance is computed. + Se você desabilitar o gasto de um troco não confirmado, o troco da transação<br/>não poderá ser utilizado até a transação ter pelo menos uma confirmação.<br/>Isso também afeta seu saldo computado. + + + PrivateSend rounds to use + PrivateSend rodadas para usar + + + This amount acts as a threshold to turn off PrivateSend once it's reached. + Esse valor atua como um limite para desativar o PrivateSend assim que ele for atingido. + + + Automatically open the Dash Core client port on the router. This only works when your router supports UPnP and it is enabled. + Abre automaticamente a porta para o cliente Dash Core no roteador. Essa função apenas funciona se o seu roteador oferece suporte a UPnP e a opção estiver habilitada. + + + Accept connections from outside + Aceitar conexões externas + + + Allow incoming connections + Permitir conexões de entrada + + + Connect to the Dash network through a SOCKS5 proxy. + Conecta à rede Dash através de um proxy SOCKS5. + + + &Connect through SOCKS5 proxy (default proxy): + &Conectar via proxy SOCKS5 (proxy padrão): + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu. + Minimizar em vez de sair do aplicativo quando a janela for fechada. Quando esta opção está ativada, o aplicativo só será fechado selecionando Sair no menu. + + + Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items.<br/>%s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. + As URLs de terceiros (por exemplo, um explorador de blocos) que aparecem na guia de transações como itens de menu de contexto. <br/>%s na URL são substituídos por hash de transação. Vários URLs são separados pela barra vertical |. + + + Expert + Avançado + + + This setting determines the amount of individual masternodes that an input will be anonymized through.<br/>More rounds of anonymization gives a higher degree of privacy, but also costs more in fees. + Esta configuração determina a quantidade de Masternodes individuais através dos quais um input será anonimizado. <br/>Mais rodadas de anonimização garantem um maior grau de privacidade, mas também custarão mais em fees. + + + Whether to show coin control features or not. + Mostrar ou não opções de controle da moeda. + + + Automatically start %1 after logging in to the system. + Executar o %1 automaticamente ao iniciar o sistema. + + + &Start %1 on system login + &Iniciar %1 ao fazer login no sistema + + + Enable coin &control features + Habilitar opções de &controle de moedas + + + &Spend unconfirmed change + Ga&star mudança não confirmada + + + &Network + Rede + + + Map port using &UPnP + Mapear porta usando &UPnP + + + Proxy &IP: + &IP do proxy: + + + IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1) + Endereço de IP do proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1) + + + &Port: + &Porta: + + + Port of the proxy (e.g. 9050) + Porta do serviço de proxy (ex. 9050) + + + Used for reaching peers via: + Usado para alcançar peers através de: + + + Shows, if the supplied default SOCKS5 proxy is used to reach peers via this network type. + Mostra, se o proxy SOCKS5 padrão fornecido for usado para alcançar pares através deste tipo de rede. + + + IPv4 + IPv4 + + + IPv6 + IPv6 + + + Tor + Tor + + + Connect to the Dash network through a separate SOCKS5 proxy for Tor hidden services. + Conecte-se à rede Dash através de um proxy SOCKS5 separado para serviços ocultos Tor + + + Use separate SOCKS5 proxy to reach peers via Tor hidden services: + Usar proxy SOCKS5 separado para alcançar pares via serviços ocultos Tor: + + + &Window + &Janela + + + &Hide the icon from the system tray. + &Ocultar o ícone da bandeja do sistema. + + + Hide tray icon + Ocultar ícone da bandeja + + + Show only a tray icon after minimizing the window. + Mostrar apenas um ícone na bandeja ao minimizar a janela. + + + &Minimize to the tray instead of the taskbar + &Minimizar para a bandeja em vez da barra de tarefas. + + + M&inimize on close + M&inimizar ao sair + + + &Display + &Mostrar + + + User Interface &language: + &Língua da interface com usuário: + + + The user interface language can be set here. This setting will take effect after restarting %1. + O idioma da interface pode ser definido aqui. Essa configuração terá efeito após reiniciar o %1. + + + Language missing or translation incomplete? Help contributing translations here: +https://www.transifex.com/projects/p/dash/ + Idioma inexistente ou tradução incompleta? Contribua com a tradução aqui: +https://www.transifex.com/projects/p/dash/ + + + User Interface Theme: + Tema da Interface do usuário: + + + &Unit to show amounts in: + &Unidade usada para mostrar quantidades: + + + Choose the default subdivision unit to show in the interface and when sending coins. + Escolha a unidade padrão de subdivisão para interface mostrar quando enviar bitcoins. + + + Decimal digits + Dígitos decimais + + + Third party transaction URLs + URLs da transação de terceiros + + + Active command-line options that override above options: + Ativa as opções de linha de comando que sobrescreve as opções acima: + + + Reset all client options to default. + Redefinir todas as opções do cliente para opções padrão. + + + &Reset Options + &Redefinir opções &OK @@ -1309,27 +1521,27 @@ https://www.transifex.com/projects/p/dash/ default - omissão + padrão none - nenhum + Nenhum Confirm options reset - Confirme reinicialização das opções + Confirmar redefinição de opções Client restart required to activate changes. - Para activar as alterações é necessário reiniciar o cliente. + Reinicialização do aplicativo necessária para efetivar alterações. - Client will be shutdown, do you want to proceed? - O cliente será fechado, deseja continuar? + Client will be shut down. Do you want to proceed? + O programa será encerrado. Deseja continuar? This change would require a client restart. - Esta alteração necessita que o cliente seja reiniciado. + Essa mudança requer uma reinicialização do aplicativo. The supplied proxy address is invalid. @@ -1340,11 +1552,11 @@ https://www.transifex.com/projects/p/dash/ OverviewPage Form - De + Formulário The displayed information may be out of date. Your wallet automatically synchronizes with the Dash network after a connection is established, but this process has not completed yet. - A informação mostrada pode estar desactualizada. A sua carteira sincroniza automaticamente com a rede Dash assim que for estabelecida uma ligação, mas este processo ainda não terminou. + A informação exibida pode estar desatualizada. Sua wallet é sincronizada automaticamente com a rede Dash tão logo uma conexão se estabeleça, entrtanto esse processo ainda não está completo. Available: @@ -1352,7 +1564,7 @@ https://www.transifex.com/projects/p/dash/ Your current spendable balance - O seu saldo disponível + Seu saldo atual spendable Pending: @@ -1360,7 +1572,7 @@ https://www.transifex.com/projects/p/dash/ Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance - Total de transacções que ainda não foram confirmadas e não contam para o saldo disponível + Total de transações que ainda têm de ser confirmados, e ainda não contam para o equilíbrio spendable Immature: @@ -1368,7 +1580,7 @@ https://www.transifex.com/projects/p/dash/ Mined balance that has not yet matured - Saldo minado que ainda não atingiu a maturidade + Saldo minerado que ainda não maturou Balances @@ -1376,11 +1588,11 @@ https://www.transifex.com/projects/p/dash/ Unconfirmed transactions to watch-only addresses - Transações não confirmadas para endereços somente de visualização + Transações não confirmadas para endereços apenas para exibição Mined balance in watch-only addresses that has not yet matured - Saldo minado que ainda não atingiu a maturidade em endereços somente de visualização + Saldo minerado em endereços somente para observação que ainda não maturou Total: @@ -1388,107 +1600,115 @@ https://www.transifex.com/projects/p/dash/ Your current total balance - O seu saldo total + Seu saldo total atual Current total balance in watch-only addresses - Saldo total em endereços somente de visualização + Saldo total atual em endereços de monitoração Watch-only: - Somente de visualização: + Observar apenas: Your current balance in watch-only addresses - O seu saldo actual em endereços somente de visualização + Seu saldo atual em endereços somente de monitoração Spendable: - Disponível: + Saldo disponível: + + + PrivateSend + PrivateSend Status: - Estado: + Status: Enabled/Disabled - Activado/Desactivado + Ativado/Desativado Completion: - Progresso: + Conclusão: + + + Try to manually submit a PrivateSend request. + Tente enviar manualmente uma solicitação PrivateSend. + + + Reset the current status of PrivateSend (can interrupt PrivateSend if it's in the process of Mixing, which can cost you money!) + Reseta o estado atual do PrivateSend (pode interromper o PrivateSend se este estiver em processo de Mixing, isso pode te custar dinheiro!) + + + Information about PrivateSend and Mixing + Informações sobre PrivateSend e Mixing - Darksend Balance: - Saldo Darksend: + Info + Info Amount and Rounds: - Quantia e Voltas: + Quantia e Rodadas: 0 DASH / 0 Rounds - 0 DASH / 0 Voltas + 0 DASH / 0 Rodadas Submitted Denom: - Denominação submetida: + Denom Submetido: n/a - n/d - - - Darksend - Darksend + n/a Recent transactions - Transacções recentes + Transações recentes Start/Stop Mixing - Iniciar/Parar Mistura + Iniciar/Interronper o Mixing + + + PrivateSend Balance: + Saldo PrivateSend The denominations you submitted to the Masternode.<br>To mix, other users must submit the exact same denominations. - As denominações que foram submetidas para o Masternode.<br>Para misturar, outros utilizadores terão que submeter denominações iguais. + As denominações submetidas por você ao Masternode.<br/>Para a realização do mixing, outros usuários precisam submeter exatamente as mesmas denominações. (Last Message) (Última Mensagem) - - Try to manually submit a Darksend request. - Tentar submeter um pedido Darksend manualmente. - Try Mix - Tentar Mistura - - - Reset the current status of Darksend (can interrupt Darksend if it's in the process of Mixing, which can cost you money!) - Reinicia o estado actual Darksend (pode interromper o Darksend mesmo quando em processo de Mistura, o que pode ter custos!) + Tentar o Mixing Reset - Reiniciar + Resetar out of sync - fora de sincronia + Sem sincronia - Disabled - Desactivado + Automatic backups are disabled, no mixing available! + Backups automáticos estão desabilitados, Mixing não disponível! - Start Darksend Mixing - Iniciar Mistura Darksend + Start Mixing + Iniciar o Mixing - Stop Darksend Mixing - Parar Mistura Darksend + Stop Mixing + Interromper o Mixing No inputs detected @@ -1496,76 +1716,116 @@ https://www.transifex.com/projects/p/dash/ %n Rounds - %n volta%n voltas + %n Rodadas%n Rodadas + + + Found enough compatible inputs to anonymize %1 + Encontrou entradas compatíveis para anonimizar %1 Not enough compatible inputs to anonymize <span style='color:red;'>%1</span>,<br>will anonymize <span style='color:red;'>%2</span> instead - Número insuficiente de entradas para anonimizar <span style='color:red;'>%1</span>,<br/>a anonimizar <span style='color:red;'>%2</span> em alternativa + Não há entradas compatíveis suficientes para anonimizar <span style='color:red;'>%1</span>,<br> vamos anonimizar <span style='color:red;'>%2</span> em vez disso Overall progress - Progresso Geral + Progresso geral Denominated Denominado + + Mixed + Misturado + Anonymized Anonimizado Denominated inputs have %5 of %n rounds on average - Entradas denominadas tem %5 de %n volta em médiaEntradas denominadas tem %5 de %n voltas em média + Os insumos denominados têm %5 de %n rodadas em médiaOs insumos denominados têm %5 de %n rodadas em média - Found enough compatible inputs to anonymize %1 - Encontradas entradas suficientes para anonimizar %1 + keys left: %1 + Chaves restantes: %1 - Mixed - Misturado + Disabled + Desativado + + + Very low number of keys left since last automatic backup! + Número muito baixo de chaves deixadas desde o último backup automático! + + + We are about to create a new automatic backup for you, however <span style='color:red;'> you should always make sure you have backups saved in some safe place</span>! + Estamos prestes a criar um novo backup automático para você, no entanto<span style='color:red;'>, você deve sempre garantir que você tenha backups salvos em algum lugar seguro</span>! + + + Note: You can turn this message off in options. + Nota: Você pode desativar essa mensagem nas opções. + + + WARNING! Something went wrong on automatic backup + ALERTA! Algo de errado ocorreu com o backup automático + + + ERROR! Failed to create automatic backup + ERRO! Falha ao criar o backup automático + + + Mixing is disabled, please close your wallet and fix the issue! + Mixing está desabilitado, por favor feche sua carteira e corrija o problema! Enabled - Activado + Ativado + + + see debug.log for details. + Consulte debug.log para obter detalhes. - Last Darksend message: + WARNING! Failed to replenish keypool, please unlock your wallet to do so. + ATENÇÃO! Falha ao repor o keypool, desbloqueie sua carteira para fazer isso. + + + Last PrivateSend message: - Última mensagem Darksend: + Última mensagem do PrivateSend: N/A - N/D + N/A - Darksend was successfully reset. - Darksend foi reposto com sucesso. + PrivateSend was successfully reset. + PrivateSend foi reiniciado com sucesso. - If you don't want to see internal Darksend fees/transactions select "Most Common" as Type on the "Transactions" tab. - Se não deseja visualizar as taxas/transacções internas Darksend seleccione "Mais Comuns" como Tipo no separador "Transacções" + If you don't want to see internal PrivateSend fees/transactions select "Most Common" as Type on the "Transactions" tab. + Se você não quiser ver as taxas / transações internas do PrivateSend selecione "Mais comum" como Tipo na guia "Transações". - Darksend requires at least %1 to use. - Darksend necessita de pelo menos %1 para ser usado. + PrivateSend requires at least %1 to use. + PrivateSend requer pelo menos %1 para usar. - Wallet is locked and user declined to unlock. Disabling Darksend. - A carteira está bloqueada e o utilizador recusou o desbloqueamento. A desactivar Darksend. + Wallet is locked and user declined to unlock. Disabling PrivateSend. + A Carteira está bloqueada e o usuário recusou a desbloquear. Desativando o PrivateSend. PaymentServer Payment request error - Erro na solicitação de pagamento + Erro no pedido de pagamento Cannot start dash: click-to-pay handler - Não é possível iniciar o dash: click-to-pay handler + Não foi possível iniciar Dash: manipulador click-to-pay URI handling @@ -1573,11 +1833,7 @@ https://www.transifex.com/projects/p/dash/ Payment request fetch URL is invalid: %1 - URL de solicitação de pagamento é inválido: %1 - - - Payment request file handling - Manipulação de ficheiros de solicitação de pagamento + URL de cobrança é inválida: %1 Invalid payment address %1 @@ -1585,93 +1841,113 @@ https://www.transifex.com/projects/p/dash/ URI cannot be parsed! This can be caused by an invalid Dash address or malformed URI parameters. - O URI não pode ser carregado! Isto pode ser causado por um endereço Dash inválido ou parâmetros do URI incorrectos. + A URI não pode ser analisada! Isto pode ser causado por um endereço inválido ou um parâmetro URI malformado. + + + Payment request file handling + Manipulação de arquivo de cobrança Payment request file cannot be read! This can be caused by an invalid payment request file. - O ficheiro de solicitação de pagamento não pode ser aberto ou processado! Isto pode ser causado por um ficheiro de solicitação de pagamento inválido. + Arquivo de pedido de pagamento não pode ser lido! Isto pode ser causado por uma requisição de pagamento inválida. Payment request rejected - Solicitação de pagamento rejeitada + Pedido de pagamento rejeitado Payment request network doesn't match client network. - A rede da solicitação de pagamento não corresponde à rede do cliente. + Rede do pedido de pagamento não corresponde rede do cliente. - Payment request has expired. - A solicitação de pagamento expirou. + Payment request expired. + Pedido de pagamento expirado. Payment request is not initialized. - A solicitação de pagamento não está inicializada. + A requisição do pagamento não foi inicializada. Unverified payment requests to custom payment scripts are unsupported. - Solicitações de pagamento não verificadas para scripts de pagamento personalizados não são suportadas. + Pedidos de pagamento não verificados para scripts de pagamento personalizados não são suportados. + + + Invalid payment request. + Pedido de pagamento inválido Requested payment amount of %1 is too small (considered dust). - A quantia %1 para solicitação de pagamento é demasiado pequena (considerada poeira) + Valor do pagamento solicitado de %1 é muito pequeno (Considerado poeira). Refund from %1 - Devolução de %1 + Reembolso de %1 Payment request %1 is too large (%2 bytes, allowed %3 bytes). - A solicitação de pagamento %1 é demasiado grande (%2 bytes, permitido %3 bytes). - - - Payment request DoS protection - Protecção DoS à solicitação de pagamento + Pedido de pagamento %1 é muito grande (%2 bytes, permitido %3 bytes). Error communicating with %1: %2 - Erro de comunicação com %1: %2 + Erro na comunicação com %1: %2 Payment request cannot be parsed! - A solicitação de pagamento não pode ser carregada! + Pedido de pagamento não pode ser analisado! Bad response from server %1 - Resposta inválida do servidor %1 + Má resposta do servidor %1 Network request error - Erro no pedido à rede + Erro de solicitação de rede Payment acknowledged - Pagamento confirmado + Pagamento reconhecido PeerTableModel - Address/Hostname - Endereço/Servidor + NodeId + ID do nó + + + Node/Service + Nó/Serviço User Agent - Agente de Usuário + User Agent - Ping Time - Tempo de Ping + Ping + Ping QObject + + Dash Core + Dash Core + + + %1 didn't yet exit safely... + %1 ainda não terminou com segurança... + + + Error reading masternode configuration file: %1 + Erro ao ler o arquivo de configuração do masternode: %1 + Amount Quantia Enter a Dash address (e.g. %1) - Insira um endereço Dash (ex. %1) + Informe um endereço Dash (ex: %1) %1 d @@ -1689,26 +1965,103 @@ https://www.transifex.com/projects/p/dash/ %1 s %1 s - - NETWORK - REDE - - - UNKNOWN - DESCONHECIDO - None Nenhum N/A - N/D + N/A %1 ms %1 ms + + %n second(s) + %n segundos%n segundos + + + %n minute(s) + %n minutos%n minutos + + + %n hour(s) + %n horas%n horas + + + %n day(s) + %n dias%n dias + + + %n week(s) + %n semanas%n semanas + + + %n year(s) + %n anos%n anos + + + %1 and %2 + %1 e %2 + + + unknown + desconhecido + + + + QObject::QObject + + Error: Specified data directory "%1" does not exist. + Erro: Diretório de dados especificado "%1" não existe. + + + Error: Cannot parse configuration file: %1. Only use key=value syntax. + Erro: Não foi possível interpretar arquivo de configuração: %1. Utilize apenas a sintaxe chave=valor. + + + Error: %1 + Erro: %1 + + + + QRDialog + + QR-Code Title + QR-Code Titulo + + + QR Code + Código QR + + + &Save Image... + &Salvar Imagem... + + + Error creating QR Code. + Erro ao criar o QR Code. + + + + QRGeneralImageWidget + + &Save Image... + &Salvar Imagem... + + + &Copy Image + &Copiar imagem + + + Save QR Code + Salvar código QR + + + PNG Image (*.png) + Imagem PNG (*.png) + QRImageWidget @@ -1718,11 +2071,11 @@ https://www.transifex.com/projects/p/dash/ &Copy Image - &Copiar Imagem + &Copiar imagem Save QR Code - Guardar código QR + Salvar código QR PNG Image (*.png) @@ -1737,7 +2090,7 @@ https://www.transifex.com/projects/p/dash/ &Information - &Information + &Informação General @@ -1747,21 +2100,13 @@ https://www.transifex.com/projects/p/dash/ Name Nome - - Client name - Nome do Cliente - N/A - N/D + N/A Number of connections - Número de ligações - - - Open the Dash debug log file from the current data directory. This can take a few seconds for large log files. - Abrir o ficheiro de registo de depuração Dash a partir da pasta de dados actual. Para grandes ficheiros de registo pode demorar alguns segundos. + Número de conexões &Open @@ -1769,7 +2114,7 @@ https://www.transifex.com/projects/p/dash/ Startup time - Hora de inicialização + Horário de inicialização Network @@ -1777,51 +2122,55 @@ https://www.transifex.com/projects/p/dash/ Last block time - Data do último bloco + Horário do último bloco Debug log file - Ficheiro de registo de depuração - - - Using OpenSSL version - A usar versão do OpenSSL - - - Build date - Data de compilação + Arquivo de log de Depuração Current number of blocks - Número actual de blocos + Quantidade atual de blocos Client version - Versão do Cliente + Versão do cliente Using BerkeleyDB version - A usar a versão do BerkeleyDB + Usando BerkeleyDB versão Block chain - Cadeia de blocos + Corrente de blocos Number of Masternodes Número de Masternodes + + Memory Pool + Pool de memória + + + Current number of transactions + Número atual de transações + + + Memory usage + Uso de memória + &Console - &Consola + &Console Clear console - Limpar consola + Limpar console &Network Traffic - &Tráfego de Rede + Tráfico de Rede &Clear @@ -1841,111 +2190,151 @@ https://www.transifex.com/projects/p/dash/ &Peers - &Nós + &Peers + + + Banned peers + Nós banidos Select a peer to view detailed information. - Seleccione um nó para visualizar informação detalhada. + Selecione um ponto para ver informações detalhadas. + + + Whitelisted + Lista branca Direction - Direcção + Direção Version Versão + + Starting Block + Bloco inicial + + + Synced Headers + Cabeçalhos Sincronizados + + + Synced Blocks + Blocos Sincronizados + + + Wallet Path + Caminho da carteira + User Agent - Agente de Usuário + Agente de Usuário - Services - Serviços + Datadir + Datadir + + + Open the %1 debug log file from the current data directory. This can take a few seconds for large log files. + Abrir o arquivo de log de depuração do %1 localizado no diretório atual de dados. Isso pode levar alguns segundos para arquivos de log grandes. - Starting Height - Altura Inicial + Decrease font size + Diminuir o tamanho da fonte - Sync Height - Altura de Sincronização + Increase font size + Aumentar o tamanho da fonte + + + Services + Serviços Ban Score - Pontuação para Banir + Banir pontuação Connection Time - Tempo de Ligação + Tempo de conexão Last Send - Última Enviado + Último envio Last Receive - Último Recebido + Último recebido - Bytes Sent - Bytes Enviados + Ping Time + Tempo de Ping - Bytes Received - Bytes Recebidos + The duration of a currently outstanding ping. + A duração de um ping excepcional no momento. - Ping Time - Tempo de Ping + Ping Wait + Espera de ping + + + Min Ping + Ping min + + + Time Offset + Offset de tempo &Wallet Repair - &Reparar Carteira + &Reparar carteira Salvage wallet - Recuperar Carteira + Salvar wallet Rescan blockchain files - Reprocessar ficheiros da cadeia de blocos + reescanear o blockchain Recover transactions 1 - Recuperar transacções 1 + Recuperar transações 1 Recover transactions 2 - Recuperar transacções 2 + Recuperar transações 2 Upgrade wallet format - Actualizar o formato da carteira + Atualizar formato da carteira The buttons below will restart the wallet with command-line options to repair the wallet, fix issues with corrupt blockhain files or missing/obsolete transactions. - Os botões abaixo irão reiniciar a carteira passando parâmetros da linha de comandos para reparar a carteira, corrigir problemas com a cadeia de blocos ou com transacções em falta ou obsoletas. + Os botões abaixo reiniciarão a carteira com opções de linha de comando para reparar a carteira, corrigir problemas com arquivos blockhain corrompidos ou transações ausentes/obsoletas. -salvagewallet: Attempt to recover private keys from a corrupt wallet.dat. - -salvagewallet: Tenta recuperar as chaves privadas a partir de um ficheiro wallet.dat corrompido. + -salvagewallet: tenta recuperar chaves privadas de um wallet.dat corrompido. -rescan: Rescan the block chain for missing wallet transactions. - -rescan: Examina novamente a cadeia de blocos para encontrar transacções inexistentes na carteira + -rescan: Reinicia a cadeia de bloco para transações de carteira ausentes. -zapwallettxes=1: Recover transactions from blockchain (keep meta-data, e.g. account owner). - -zapwallettxes=1: Recupera transacções da cadeia de blocos (mantendo os meta-dados, ex. dono da conta) + -zapwallettxes = 1: Recupere as transações do blockchain (mantenha metadados, por exemplo, proprietário da conta). -zapwallettxes=2: Recover transactions from blockchain (drop meta-data). - -zapwallettxes=2: Recupera transacções da cadeia de blocos (apagando os meta-dados) + -zapwallettxes = 2: Recupere as transações do blockchain (elimine metadados). -upgradewallet: Upgrade wallet to latest format on startup. (Note: this is NOT an update of the wallet itself!) - -upgradewallet: Actualiza a carteira para o formato mais recente quando arranca. (Nota: Isto NÃO é uma actualização da carteira em si!) + -upgradewallet: Atualize a carteira para o formato mais recente na inicialização. (Nota: esta não é uma atualização da carteira em si!) Wallet repair options. @@ -1953,31 +2342,67 @@ https://www.transifex.com/projects/p/dash/ Rebuild index - Reconstruir indexação + Reconstrução de Índice -reindex: Rebuild block chain index from current blk000??.dat files. - -reindex: Reconstroi o índice da cadeia de blocos a partir dos ficheiros actuais blk000??.dat. + -reindex: Reconstrói o índice de cadeia de blocos a partir dos arquivos atuais de dados blk000??.dat . - In: - Entrada: + &Disconnect + &Desconectar - Out: - Saída: + Ban for + Banir por + + + 1 &hour + 1 &hora + + + 1 &day + 1 &dia + + + 1 &week + 1 &semana + + + 1 &year + 1 &ano - Welcome to the Dash RPC console. - Bem-vindo à consola RPC Dash + &Unban + &Desbanir + + + Welcome to the %1 RPC console. + Bem-vindo ao console RPC do %1. Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. - Use as setas para cima / baixo para navegar no histórico, e <b>Ctrl-L</b> para limpar o ecran. + Use as setas para cima e para baixo para navegar pelo histórico, e <b>Ctrl-L</b> para limpar a tela. Type <b>help</b> for an overview of available commands. - Digite <b>help</b> para ter uma visão global dos comandos disponíveis. + Digite <b>help</b> para uma visão geral dos comandos disponíveis. + + + WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramification of a command. + AVISO: Os golpistas estão ativos, dizendo aos usuários para digitar comandos aqui, roubando o conteúdo da carteira. Não use este console sem compreender totalmente o contexto de um comando. + + + In: + Entrada: + + + Out: + Saída: + + + Network activity disabled + Atividade da rede disativada %1 B @@ -1995,9 +2420,13 @@ https://www.transifex.com/projects/p/dash/ %1 GB %1 GB + + (node id: %1) + (id do nó: %1) + via %1 - via %1 + por %1 never @@ -2012,43 +2441,47 @@ https://www.transifex.com/projects/p/dash/ Saída - Unknown - Desconhecido + Yes + sim + + + No + não - Fetching... - Carregando... + Unknown + Desconhecido ReceiveCoinsDialog Reuse one of the previously used receiving addresses.<br>Reusing addresses has security and privacy issues.<br>Do not use this unless re-generating a payment request made before. - Reutilizar um dos endereços de recebimento usados anteriormente. <br> Reutilizar endereços tem problemas de segurança e privacidade. <br>Não use isto a menos que se trate de uma reutilização de um pedido de pagamento feito anteriormente. + Reutilize um dos endereços de recebimento usados anteriormente.<br> Os endereços que estão sendo usados têm problemas de segurança e privacidade. <br>Não use isso, a menos que você gere novamente uma solicitação de pagamento feita anteriormente. R&euse an existing receiving address (not recommended) - R&eutilizar um endereço de recebimento existente (não recomendado) + R&eutilize um endereço de recebimento (não recomendado) An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Dash network. - Mensagem opcional para anexar à solicitação de pagamento, que será mostrada quando a solicitação for aberta. Nota: A mensagem não será enviada com o pagamento através da rede Dash. + Uma mensagem opcional que será anexada na cobrança e será mostrada quando ela for aberta. Nota: A mensagem não será enviada com o pagamento pela rede Dash. &Message: - &Mensagem: + &Mensagem An optional label to associate with the new receiving address. - Rótulo opcional para associar com o novo endereço de recebimento. + Um marcador opcional para associar ao novo endereço de recebimento. An optional message to attach to the payment request, which will be displayed when the request is opened.<br>Note: The message will not be sent with the payment over the Dash network. - Mensagem opcional para anexar à solicitação de pagamento, que será mostrada quando a solicitação for aberta. <br>Nota: A mensagem não será enviada com o pagamento através da rede Dash. + Uma mensagem opcional para anexar à solicitação de pagamento, que será exibida quando a solicitação for aberta. <br>Observação: a mensagem não será enviada com o pagamento pela rede do Dash. Use this form to request payments. All fields are <b>optional</b>. - Utilize este formulário para solicitar pagamentos. Todos os campos são <b>opcionais</b>. + Use esse formulário para fazer cobranças. Todos os campos são <b>opcionais</b>. &Label: @@ -2056,31 +2489,35 @@ https://www.transifex.com/projects/p/dash/ An optional amount to request. Leave this empty or zero to not request a specific amount. - Quantia opcional a solicitar. Deixar vazio ou zero para não solicitar uma quantia específica. + Uma quantia opcional para cobrar. Deixe vazio ou em branco se o pagador puder especificar a quantia. &Amount: - &Quantia + Qu&antia: &Request payment - &Pedir pagamento + &Requisitar Pagamento Clear all fields of the form. - Limpar todos os campos do formulário. + Limpa todos os campos do formulário. Clear Limpar + + Request InstantSend + Solicitar o InstantSend + Requested payments history - Histórico de solicitações de pagamento + Histórico de cobranças Show the selected request (does the same as double clicking an entry) - Mostrar a solicitação seleccionada (igual a clicar duas vezes) + Mostra a cobrança selecionada (o mesmo que clicar duas vezes em um registro) Show @@ -2088,12 +2525,16 @@ https://www.transifex.com/projects/p/dash/ Remove the selected entries from the list - Remover as entradas seleccionadas da lista + Remove o registro selecionado da lista Remove Remover + + Copy URI + Copiar URI + Copy label Copiar rótulo @@ -2111,7 +2552,7 @@ https://www.transifex.com/projects/p/dash/ ReceiveRequestDialog QR Code - QR Code + Código QR Copy &URI @@ -2119,7 +2560,7 @@ https://www.transifex.com/projects/p/dash/ Copy &Address - Copiar &Endereço + &Copiar Endereço &Save Image... @@ -2127,11 +2568,11 @@ https://www.transifex.com/projects/p/dash/ Request payment to %1 - Solicitar pagamento a %1 + Pedido de pagamento para %1 Payment information - Informação de pagamento + Informação do pagamento URI @@ -2151,15 +2592,27 @@ https://www.transifex.com/projects/p/dash/ Message - Mensagem + mensagem + + + InstantSend + EnvioInstantâneo + + + Yes + Sim + + + No + Não Resulting URI too long, try to reduce the text for label / message. - O URI resultante é demasiado longo, tente reduzir o texto do rótulo ou da mensagem. + URI resultante muito longa. Tente reduzir o texto do rótulo ou da mensagem. Error encoding URI into QR Code. - Erro ao codificar o URI para código QR. + Erro ao codigicar o URI em código QR @@ -2176,10 +2629,6 @@ https://www.transifex.com/projects/p/dash/ Message Mensagem - - Amount - Quantia - (no label) (sem rótulo) @@ -2189,19 +2638,23 @@ https://www.transifex.com/projects/p/dash/ (sem mensagem) - (no amount) - (sem quantia) + (no amount requested) + (nenhuma quantia solicitada) + + + Requested + Solicitado SendCoinsDialog Send Coins - Enviar Moedas + Enviar dinheiro Coin Control Features - Funcionalidades de Coin Control + Opções de Controle da Moeda Inputs... @@ -2209,11 +2662,11 @@ https://www.transifex.com/projects/p/dash/ automatically selected - seleccionadas automáticamente + automaticamente selecionado Insufficient funds! - Fundos insuficientes! + Saldo insuficiente! Quantity: @@ -2227,14 +2680,6 @@ https://www.transifex.com/projects/p/dash/ Amount: Quantia: - - Priority: - Prioridade: - - - medium - média - Fee: Taxa: @@ -2243,57 +2688,85 @@ https://www.transifex.com/projects/p/dash/ Dust: Poeira: - - no - não - After Fee: - Com taxa: + Depois da taxa: Change: - Troco: + trocar If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address. - Se isto for activado e o endereço de troco se encontrar vazio, o troco será enviado para um novo endereço gerado. + Se isso estiver ativo e o endereço de troco estiver vazio ou inválido, o troco será enviado a um novo endereço gerado na hora. Custom change address - Endereço de troco personalizado + Endereço específico de troco Transaction Fee: - Taxa de Transacção: + Taxa de transação Choose... Escolha... + + Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until your have validated the complete chain. + Usar o fallbackfee pode resultar no envio de uma transação que levará várias horas ou dias (ou nunca) para confirmação. Considere escolher sua taxa manualmente ou espere até que você tenha validado a cadeia completa. + + + Warning: Fee estimation is currently not possible. + Atenção: Estimativa de taxa não disponível no momento + collapse fee-settings - fechar opções de taxa + Ocultar painel + + + Confirmation time target: + Tempo alvo de confirmação: + + + PrivateSend + EnvioPrivado + + + InstantSend + EnvioInstantâneo + + + If the custom fee is set to 1000 duffs and the transaction is only 250 bytes, then "per kilobyte" only pays 250 duffs in fee,<br />while "at least" pays 1000 duffs. For transactions bigger than a kilobyte both pay by kilobyte. + Se a taxa por defeito está definida em 1000 duffs e a transação é apenas 250 bytes, então "per kilobyte" paga apenas 250 duffs de taxa,<br />enquanto "no mínimo" paga 1000 duffs. Para transações maiores que um kilobyte, ambas pagam por kilobyte. + + + If the custom fee is set to 1000 duffs and the transaction is only 250 bytes, then "per kilobyte" only pays 250 duffs in fee,<br />while "total at least" pays 1000 duffs. For transactions bigger than a kilobyte both pay by kilobyte. + Se a taxa por defeito está definida em 1000 duffs e a transação é apenas 250 bytes, então "per kilobyte" paga apenas 250 duffs de taxa,<br />enquanto "no total pelo menos" paga 1000 duffs. Para transações maiores que um kilobyte, ambas pagam por kilobyte. - Minimize - Minimizar + Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks.<br />But be aware that this can end up in a never confirming transaction once there is more demand for dash transactions than the network can process. + Não há problema em pagar apenas a taxa mínima desde que exista um volume transacional inferior ao espaço nos blocos.<br />Mas tenha em conta que pode ocorrer uma transação que nunca será confirmada devido a existir uma maior procura para transações em dash, do que a rede tem capacidade para processar. per kilobyte por kilobyte + + Hide + Esconder + total at least - pelo menos total + total pelo menos (read the tooltip) - (leia a tooltip) + (Leia o popup) Recommended: - Recomendado: + Recomendado Custom: @@ -2301,11 +2774,7 @@ https://www.transifex.com/projects/p/dash/ (Smart fee not initialized yet. This usually takes a few blocks...) - (A taxa inteligente ainda não foi inicializada. Normalmente demora alguns blocos...) - - - Confirmation time: - Tempo de confirmação: + (Smart fee não iniciado. Isso requer alguns blocos...) normal @@ -2315,21 +2784,13 @@ https://www.transifex.com/projects/p/dash/ fast rápido - - Send as zero-fee transaction if possible - Enviar com taxa-zero se possível - - - (confirmation may take longer) - (a confirmação pode demorar mais) - Confirm the send action - Confirmar envio + Confirmar o envio S&end - &Enviar + Enviar Clear all fields of the form. @@ -2337,23 +2798,15 @@ https://www.transifex.com/projects/p/dash/ Clear &All - Limpar &Tudo + Limpar Tudo Send to multiple recipients at once - Enviar para múltiplos destinatários de uma vez + Enviar para vários destinatários de uma só vez Add &Recipient - Adicionar &Destinatário - - - Darksend - Darksend - - - InstantX - InstantX + Adicionar destinatário Balance: @@ -2373,19 +2826,15 @@ https://www.transifex.com/projects/p/dash/ Copy after fee - Copiar depois da taxa + Copiar pós taxa Copy bytes Copiar bytes - - Copy priority - Copiar prioridade - Copy dust - Copiar pó + Copiar poeira Copy change @@ -2393,23 +2842,23 @@ https://www.transifex.com/projects/p/dash/ using - a usar + usando anonymous funds - fundos anónimos + fundos anônimos - (darksend requires this amount to be rounded up to the nearest %1). - (o darksend requer que esta quantia seja arredondada até ao %1 mais próximo). + (privatesend requires this amount to be rounded up to the nearest %1). + (O privatesend exige que esse valor seja arredondado para o próximo %1). - any available funds (not recommended) - quaisquer fundos disponíveis (não recomendado) + any available funds (not anonymous) + quaisquer fundos disponíveis (não anônimos) - and InstantX - e InstantX + and InstantSend + e EnvioInstantâneo %1 to %2 @@ -2417,67 +2866,71 @@ https://www.transifex.com/projects/p/dash/ Are you sure you want to send? - Tem a certeza que quer enviar? + Você tem certeza que deseja enviar? are added as transaction fee - são adicionadas como taxa de transacção + são adicionadas como taxas de transação Total Amount = <b>%1</b><br />= %2 Quantidade Total = <b>%1</b><br />= %2 - Confirm send coins - Confirmar envio de moedas - - - A fee %1 times higher than %2 per kB is considered an insanely high fee. - Uma taxa %1 vezes mais alta do que %2 por kB é considerada demasiado alta. - - - Estimated to begin confirmation within %n block(s). - Inicio de confirmação estimado dentro de %n bloco.Inicio de confirmação estimado dentro de %n blocos. + <b>(%1 of %2 entries displayed)</b> + <b>(%1 de %2 entradas exibidas)</b> - The recipient address is not valid, please recheck. - O endereço do destinatário é inválido, por favor verifique. + Confirm send coins + Confirme o envio de moedas - <b>(%1 of %2 entries displayed)</b> - <b>(%1 de %2 entradas mostradas)</b> + The recipient address is not valid. Please recheck. + Endereço de envio inváido. Favor checar. The amount to pay must be larger than 0. - A quantia a pagar tem que ser maior que 0. + A quantia à pagar deve ser maior que 0 The amount exceeds your balance. - A quantia excede o seu saldo. + A quantia excede o seu saldo The total exceeds your balance when the %1 transaction fee is included. - O total excede o seu saldo quando é incluído o valor %1 relativo a taxas de transacção. + O total excede o seu saldo quando a taxa da transação %1 é incluída - Duplicate address found, can only send to each address once per send operation. - Endereço duplicado encontrado, só pode enviar para um endereço uma vez por cada operação de envio. + Duplicate address found: addresses should only be used once each. + Encontrado endereço duplicado: os endereços deveriam ser usados apenas uma vez cada Transaction creation failed! - Falha ao criar transacção! + Criação de transação falha + + + The transaction was rejected with the following reason: %1 + A transação foi rejeitada com a seguinte razão: %1 - The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here. - A transacção foi rejeitada! Isto pode acontecer se algumas das moedas da sua carteira tiverem sido gastas, tal como se usou uma copia do ficheiro wallet.dat e moedas tiverem sido gastas nessa cópia mas não aqui. + A fee higher than %1 is considered an absurdly high fee. + Uma taxa maior que %1 é considerada uma taxa absurdamente alta. - Error: The wallet was unlocked only to anonymize coins. - Erro: A carteira foi somente desbloqueada para anonimização de moedas. + Payment request expired. + Pedido de pagamento expirado + + + %n block(s) + %n blocos%n blocos - Pay only the minimum fee of %1 - Pagar somente a taxa mínima de %1 + Pay only the required fee of %1 + Pagar somente a taxa requerida de %1 + + + Estimated to begin confirmation within %n block(s). + Estimativa para iniciar a confirmação em %n blocos.Estimativa para iniciar a confirmação em %n blocos. Warning: Invalid Dash address @@ -2485,7 +2938,15 @@ https://www.transifex.com/projects/p/dash/ Warning: Unknown change address - Aviso: Endereço de troco desconhecido + Aviso: Endereço de troco inválido + + + Confirm custom change address + Confirmar endereço de troco personalizado + + + The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure? + O endereço selecionado para o troco não pertence a esta carteira. Alguns ou todos os fundos da sua carteira modem ser mandados para esse endereço. Tem certeza? (no label) @@ -2500,15 +2961,15 @@ https://www.transifex.com/projects/p/dash/ Pay &To: - &Pagar a: + Pagar &Para: The Dash address to send the payment to - O endereço Dash para onde enviar o pagamento + O endereço Dash para enviar o pagamento a Choose previously used address - Escolher endereço usado previamente + Escolher endereço usado anteriormente Alt+A @@ -2516,7 +2977,7 @@ https://www.transifex.com/projects/p/dash/ Paste address from clipboard - Colar endereço da área de transferência + Colar o endereço da área de transferência Alt+P @@ -2532,11 +2993,19 @@ https://www.transifex.com/projects/p/dash/ Enter a label for this address to add it to the list of used addresses - Digite um rótulo para este endereço para adicioná-lo à lista de endereços usados + Digite um rótulo para este endereço para adicioná-lo no catálogo A&mount: - &Quantidade + Q&uantidade: + + + The fee will be deducted from the amount being sent. The recipient will receive a lower amount of Dash than you enter in the amount field. If multiple recipients are selected, the fee is split equally. + A taxa será deduzida do valor enviado. O destinatário receberá uma quantidade menor de Dash do que você insere no campo de valor. Se vários destinatários forem selecionados, a taxa será dividida igualmente. + + + S&ubtract fee from amount + &Retirar taxa da quantia Message: @@ -2544,61 +3013,68 @@ https://www.transifex.com/projects/p/dash/ A message that was attached to the dash: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Dash network. - Mensagem que foi anexada ao dash: URI e que será guardada com a transacção para sua referência. Nota: Esta mensagem não será enviada para a rede Dash. + A mensagem que foi anexada ao Dash: URI na qual será gravada na transação para sua referência. Nota: Essa mensagem não será gravada publicamente na rede Dash. + + + This is an unauthenticated payment request. + Esta é uma cobrança não autenticada. - This is an unverified payment request. - Este é um pedido de pagamento não verificado. + This is an authenticated payment request. + Esta é uma cobrança autenticada. Pay To: - Pagar a: + Pague Para: Memo: - Rótulo: + Memorizar: - This is a verified payment request. - Este é um pedido de pagamento verificado. + Enter a label for this address to add it to your address book + Digite um rótulo para este endereço para adicioná-lo ao catálogo de endereços + + + SendConfirmationDialog - Enter a label for this address to add it to your address book - Introduza um rótulo para este endereço para adicionar ao livro de endereços + Yes + Sim ShutdownWindow - Dash Core is shutting down... - O Dash Core está a desligar... + %1 is shutting down... + %1 está desligando... Do not shut down the computer until this window disappears. - Não desligue o computador enquanto está janela estiver visível. + Não desligue o computador até essa janela desaparecer. SignVerifyMessageDialog Signatures - Sign / Verify a Message - Assinaturas - Assinar / Verificar uma Mensagem + Assinaturas - Assinar / Verificar uma mensagem &Sign Message &Assinar Mensagem - You can sign messages with your addresses to prove you own them. Be careful not to sign anything vague, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. - Pode assinar mensagens com os seus endereços para provar que são seus. Tenha atenção para não assinar mensagens vagas, já que, ataques de phishing podem levá-lo a assinar a sua própria identidade para os atacantes. Apenas assine declarações detalhadas com as quais concorde. + You can sign messages/agreements with your addresses to prove you can receive Dash sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + Você pode assinar mensagens/acordos com seus endereços para provar que pode receber o Dash enviado para eles. Tenha cuidado para não assinar nada vago ou aleatório, pois os ataques de phishing podem tentar convencê-lo a assinar sua identidade para eles. Assine apenas declarações totalmente detalhadas com as quais você concorda. The Dash address to sign the message with - Endereço Dash com o qual deseja assinar a mensagem + O endereço Dash que assinará a mensagem Choose previously used address - Escolher endereço usado previamente + Escolha um endereço usado anteriormente Alt+A @@ -2606,7 +3082,7 @@ https://www.transifex.com/projects/p/dash/ Paste address from clipboard - Colar endereço da área de transferência + Colar o endereço da área de transferência Alt+P @@ -2614,7 +3090,7 @@ https://www.transifex.com/projects/p/dash/ Enter the message you want to sign here - Escreva aqui a mensagem que deseja assinar + Entre a mensagem que você quer assinar aqui Signature @@ -2622,11 +3098,11 @@ https://www.transifex.com/projects/p/dash/ Copy the current signature to the system clipboard - Copiar a assinatura actual para a área de transferência + Copiar a assinatura para a área de transferência do sistema Sign the message to prove you own this Dash address - Assinar a mensagem para provar que é o proprietário deste endereço Dash + Assinar mensagem para provar que você é dono deste endereço Dash Sign &Message @@ -2634,27 +3110,27 @@ https://www.transifex.com/projects/p/dash/ Reset all sign message fields - Repor todos os campos de assinatura de mensagem + Limpar todos os campos de assinatura da mensagem Clear &All - Limpar &Tudo + Limpar Tudo &Verify Message &Verificar Mensagem - Enter the signing address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. - Introduza o endereço de assinatura, mensagem (assegure-se que copia exactamente as quebras de linha, espaços, tabulações, etc) e assinatura abaixo para verificar a mensagem. Tenha atenção para não ler mais na assinatura do que o que estiver na mensagem assinada, para evitar ser enganado por um atacante que se encontre entre si e quem assinou a mensagem. + Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction! + Coloque o endereço do autor, a mensagem (certifique-se de copiar toda a mensagem, incluindo quebras de linha, espaços, tabulações, etc.) e a assinatura embaixo para verificar a mensagem. Cuidado para não ler mais da assinatura do que está assinado na mensagem, para evitar ser enganado pelo ataque man-in-the-middle. Note que isso somente prova a propriedade de um endereço, e não o remetende de qualquer transação. The Dash address the message was signed with - Endereço Dash com o qual a mensagem foi assinada + O endereço Dash que foi usado para assinar a mensagem Verify the message to ensure it was signed with the specified Dash address - Verificar a mensagem de forma a garantir que foi assinada com o endereço Dash especificado + Verifique a mensagem para se assegurar de que foi assinada pelo endereço Dash específicado Verify &Message @@ -2662,35 +3138,35 @@ https://www.transifex.com/projects/p/dash/ Reset all verify message fields - Repor todos os campos de verificação de mensagem + Limpar todos os campos de assinatura da mensagem Click "Sign Message" to generate signature - Prima "Assinar Mensagem" para gerar a assinatura + Clique em "Assinar mensagem" para gerar a assinatura The entered address is invalid. - O endereço inserido é inválido. + O endereço digitado é inválido Please check the address and try again. - Por favor verifique o endereço e tente de novo. + Favor checar o endereço e tente novamente The entered address does not refer to a key. - O endereço introduzido não corresponde a uma chave. + O endereço fornecido não se refere a uma chave. Wallet unlock was cancelled. - O desbloqueamento da carteira foi cancelado. + O desbloqueio da carteira foi cancelado Private key for the entered address is not available. - A chave privada correspondente ao endereço introduzido não está disponível. + A chave privada para o endereço disponibilizado não está disponível. Message signing failed. - A assinatura da mensagem falhou. + Assinatura da mensagem falhou. Message signed. @@ -2698,47 +3174,35 @@ https://www.transifex.com/projects/p/dash/ The signature could not be decoded. - A assinatura não pode ser descodificada. + A assinatura não pôde ser decodificada. Please check the signature and try again. - Por favor verifique a assinatura e tente de novo. + Por favor, cheque a assinatura e tente novamente. The signature did not match the message digest. - A assinatura não corresponde à compilação da mensagem. + A assinatura não corresponde a mensagem Message verification failed. - A verificação da mensagem falhou. + Falha na verificação da mensagem Message verified. - Mensagem verificada. + Mensagem verificada SplashScreen - Dash Core - Dash Core + [testnet] + [testnet] Version %1 Versão %1 - - The Bitcoin Core developers - Os programadores Bitcoin Core - - - The Dash Core developers - Os programadores Dash Core - - - [testnet] - [rede de testes] - TrafficGraphWidget @@ -2751,71 +3215,67 @@ https://www.transifex.com/projects/p/dash/ TransactionDesc Open for %n more block(s) - Aberta durante mais %n blocoAberta durante mais %n blocos + Aberto para %n mais blocosAberto para %n mais blocos Open until %1 - Aberta até %1 + Aberto até %1 conflicted - conflituosa + Conflitado - %1/offline (verified via instantx) - %1/desligado (verificado através de instantx) + %1/offline + %1/offline - %1/confirmed (verified via instantx) - %1/confirmado (verificado através de instantx) + 0/unconfirmed, %1 + 0/não confirmado, %1 - %1 confirmations (verified via instantx) - %1 confirmações (verificado através de instantx) + in memory pool + na memória - %1/offline - %1/desligada + not in memory pool + não na memóra + + + abandoned + abandonado %1/unconfirmed - %1/não confirmada + %1/não confirmado %1 confirmations %1 confirmações - %1/offline (InstantX verification in progress - %2 of %3 signatures) - %1/desligado (verificação InstantX em progresso - %2 de %3 assinaturas) + verified via InstantSend + verificado via InstantSend - %1/confirmed (InstantX verification in progress - %2 of %3 signatures ) - %1/confirmada (verificação InstantX em progresso - %2 de %3 assinaturas) + InstantSend verification in progress - %1 of %2 signatures + Confirmação InstantSend em andamento - %1 de %2 assinaturas - %1 confirmations (InstantX verification in progress - %2 of %3 signatures) - %1 confirmações (verificação InstantX em progresso - %2 de %3 assinaturas) + InstantSend verification failed + A confirmação do InstantSend falhou - %1/offline (InstantX verification failed) - %1/desligado (a verificação InstantX falhou) + Status + Status - %1/confirmed (InstantX verification failed) - %1/confirmada (a verificação InstantX falhou) - - - Status - Estado - - - , has not been successfully broadcast yet - , ainda não foi transmitida com sucesso + , has not been successfully broadcast yet + , ainda não foi propagada na rede com êxito. , broadcast through %n node(s) - , transmitida através de %n nó, transmitida através de %n nós + , transmissão por %n nós, transmissão por %n nós Date @@ -2823,11 +3283,11 @@ https://www.transifex.com/projects/p/dash/ Source - Origem + Fonte Generated - Gerada + Gerado From @@ -2847,23 +3307,23 @@ https://www.transifex.com/projects/p/dash/ watch-only - somente de visualização + Monitorado label - rótulo + Rótulo Credit - Credito + Crédito matures in %n more block(s) - maturidade em %n blocomaturidade em %n blocos + Maduro em %n blocosMaduro em %n blocos not accepted - rejeitada + não aceito Debit @@ -2875,11 +3335,11 @@ https://www.transifex.com/projects/p/dash/ Total credit - Credito total + Crédito total Transaction fee - Taxa de transacção + Taxa da transação Net amount @@ -2895,23 +3355,31 @@ https://www.transifex.com/projects/p/dash/ Transaction ID - ID de transacção + ID da transação + + + Output index + Index da saída + + + Transaction total size + Tamanho transacional total Merchant - Comerciante + Mercador Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. - As moedas geradas tem que amadurecer %1 blocos antes de poderem ser gastas. Quando gerou este bloco, este foi propagado para a rede de modo a ser incluído na cadeia de blocos. Se não foi incluído na cadeia, o seu estado será alterado para "rejeitado" e não será possível gastar as moedas. Isto pode acontecer ocasionalmente quando outro nó gera um bloco a poucos segundos do seu. + Moedas recém minerados precisam aguardar %1 blocos antes de serem gastos. Quando o bloco foi gerado, ele foi disseminado pela rede para ser adicionado à blockchain. Se ele falhar em ser inserido na cadeia, seu estado será modificado para "não aceito" e ele não poderá ser gasto. Isso pode acontecer eventualmente quando blocos são gerados quase que simultaneamente. Debug information - Informação de depuração + Depurar informação Transaction - Transacção + Transação Inputs @@ -2933,12 +3401,12 @@ https://www.transifex.com/projects/p/dash/ TransactionDescDialog - Transaction details - Detalhes da transação + This pane shows a detailed description of the transaction + Este painel mostra uma descrição detalhada da transação - This pane shows a detailed description of the transaction - Esta janela mostra uma descrição detalhada da transação + Details for %1 + Detalhes para %1 @@ -2952,131 +3420,139 @@ https://www.transifex.com/projects/p/dash/ Tipo - Address - Endereço + Address / Label + Endereço / Etiqueta Open for %n more block(s) - Aberta durante mais %n blocoAberta durante mais %n blocos + Aberto para %n mais blocosAberto para %n mais blocos Open until %1 - Aberta até %1 + Aberto até %1 Offline - Desligado + Offline Unconfirmed Não confirmado + + Abandoned + Abandonado + Confirming (%1 of %2 recommended confirmations) - A confirmar (%1 de %2 confirmações recomendadas) + Confirmando (%1 de %2 confirmações recomendadas) Confirmed (%1 confirmations) - Confirmada (%1 confirmações) + Confirmado (%1 confirmações) Conflicted - Conflituosa + Conflitante Immature (%1 confirmations, will be available after %2) - Imatura (%1 confirmações, estará disponível após %2) + Recém-criado (%1 confirmações, disponível somente após %2) This block was not received by any other nodes and will probably not be accepted! - Este bloco não foi recebido por nenhum outro nó e provavelmente será rejeitado! + Este bloco não foi recebido por nenhum outro participante da rede e provavelmente não será aceito! Generated but not accepted - Gerado mas rejeitado + Gerado mas não aceito Received with - Recebido com + Recebido Received from - Recebido de + Recebido - Received via Darksend - Recebido via Darksend + Received via PrivateSend + Recebido via EnvioPrivado Sent to - Enviado para + Enviado Payment to yourself - Pagamento ao próprio + Pagamento a si mesmo Mined - Minado + Minerado - Darksend Denominate - Denominação Darksend + PrivateSend Denominate + PrivateSend Denominate - Darksend Collateral Payment - Pagamento Colateral Darksend + PrivateSend Collateral Payment + PrivateSend Pagamento de Garantia - Darksend Make Collateral Inputs - Darksend Fazer Entradas Colaterais + PrivateSend Make Collateral Inputs + PrivateSend fazer insumos colaterais - Darksend Create Denominations - Darksend Criar Denominações + PrivateSend Create Denominations + PrivateSend Criar Denominações - Darksent - Darksent + PrivateSend + EnvioPrivado watch-only - somente de visualização + Monitorado (n/a) - (n/d) + (n/a) + + + (no label) + (sem rótulo) Transaction status. Hover over this field to show number of confirmations. - Estado da transacção. Passe o ponteiro do rato sobre este campo para mostrar o número de confirmações. + Status da transação. Passe o mouse sobre este campo para mostrar o número de confirmações. Date and time that the transaction was received. - Data e hora à qual a transacção foi recebida. + Data e hora que a transação foi recebida. Type of transaction. - Tipo de transacção. + Tipo de transação. Whether or not a watch-only address is involved in this transaction. - Se existe algum endereço somente de visualização envolvido nesta transacção. + Mostrar ou não endereços monitorados na lista de transações. - Destination address of transaction. - Endereço de destino da transacção. + User-defined intent/purpose of the transaction. + Intenção/Propósito definido pelo usuário para a transação Amount removed from or added to balance. - Quantia removida ou adicionada ao saldo. + Quantidade debitada ou creditada ao saldo. TransactionView All - Todas + Todos Today @@ -3084,19 +3560,19 @@ https://www.transifex.com/projects/p/dash/ This week - Esta semana + Essa semana This month - Este mês + Esse mês Last month - Mês anterior + Último mês This year - Este ano + Esse ano Range... @@ -3104,7 +3580,7 @@ https://www.transifex.com/projects/p/dash/ Most Common - Mais Comum + Mais comum Received with @@ -3115,45 +3591,49 @@ https://www.transifex.com/projects/p/dash/ Enviado para - Darksent - Darksent + PrivateSend + EnvioPrivado - Darksend Make Collateral Inputs - Darksend Fazer Entradas Colaterais + PrivateSend Make Collateral Inputs + PrivateSend fazer insumos colaterais - Darksend Create Denominations - Darksend Criar Denominações + PrivateSend Create Denominations + PrivateSend Criar Denominações - Darksend Denominate - Denominação Darksend + PrivateSend Denominate + PrivateSend Denominate - Darksend Collateral Payment - Pagamento Colateral Darksend + PrivateSend Collateral Payment + PrivateSend Pagamento de Garantia To yourself - Ao próprio + Para você mesmo Mined - Minado + Minerado Other - Outra + Outro Enter address or label to search - Introduza endereço ou rótulo a pesquisar + Procure um endereço ou etiqueta Min amount Quantia mínima + + Abandon transaction + Abandonar transação + Copy address Copiar endereço @@ -3164,11 +3644,19 @@ https://www.transifex.com/projects/p/dash/ Copy amount - Copiar quantia + Copias quantia Copy transaction ID - Copiar ID de transacção + Copiar ID da transação + + + Copy raw transaction + Copiar o raw da transação + + + Copy full transaction details + Copiar dados completos da transação Edit label @@ -3176,23 +3664,23 @@ https://www.transifex.com/projects/p/dash/ Show transaction details - Mostrar detalhes da transacção + Mostrar detalhes da transação Export Transaction History - Exportar histórico de transacções + Exportar histórico de transações Comma separated file (*.csv) - Ficheiro separado por vírgulas (*.csv) + Comma separated file (*.csv) Confirmed - Confirmada + Confirmado Watch-only - Somente de visualização + Monitorado Date @@ -3216,23 +3704,23 @@ https://www.transifex.com/projects/p/dash/ Exporting Failed - A exportação falhou + Falha na exportação There was an error trying to save the transaction history to %1. - Ocorreu um erro ao tentar gravar o histórico de transacções para %1. + Ocorreu um erro ao tentar salvar o histórico de transações em %1. Exporting Successful - Exportação Concluída com Sucesso + Exportação feita com êxito The transaction history was successfully saved to %1. - O histórico de transacções foi gravado com sucesso para %1. + O histórico de transação foi gravado com êxito em %1. Range: - Intervalo: + Intervalo: to @@ -3243,7 +3731,7 @@ https://www.transifex.com/projects/p/dash/ UnitDisplayStatusBarControl Unit to show amounts in. Click to select another unit. - Unidade das quantias. Clique para seleccionar outra unidade. + Unidade para mostrar. Clique para selecionar outra unidade. @@ -3257,11 +3745,15 @@ https://www.transifex.com/projects/p/dash/ WalletModel Send Coins - Enviar Moedas + Enviar moedas + + + InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH. + EnvioInstantâneo não suporta o envio de valores tão altos ainda. As transações são limitadas a %1 DASH. - InstantX doesn't support sending values that high yet. Transactions are currently limited to %1 DASH. - O InstantX não suporta valores tão elevados neste momento. As transacções estão limitadas a %1 DASH. + Used way too many inputs (>%1) for this InstantSend transaction, fees could be huge. + Usado demais entradas (>%1) para esta transação do InstantSend, as taxas podem ser enormes. @@ -3272,1112 +3764,1542 @@ https://www.transifex.com/projects/p/dash/ Export the data in the current tab to a file - Exportar os dados do separador actual para um ficheiro + Exportar os dados da guia atual para um arquivo Selected amount: - Quantia seleccionada: + Quantidade selecionada: Backup Wallet - Criar Cópia de Segurança da Carteira + Backup da carteira + + + Wallet Data (*.dat) + Dados da carteira (*.dat) + + + Backup Failed + Falha no backup + + + There was an error trying to save the wallet data to %1. + Ocorreu um erro ao tentar salvar os dados da carteira em %1. + + + Backup Successful + Backup bem sucedido + + + The wallet data was successfully saved to %1. + Os dados da carteira foram salvos com êxito em %1. + + + + dash-core + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + Vincular ao endereço fornecido e sempre escutar nele. Use a notação [host]:port para IPv6 + + + Error: Listening for incoming connections failed (listen returned error %s) + Erro: Aceitar conexões de entrada falhou (retornou erro %s) + + + Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message) + Executa um comando quando um alerta relevante é recebido ou vemos uma longa segregação (%s é substituída pela mensagem) + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + Executa um comando quando uma transação da carteira mudar (%s no comando será substituído por TxID) + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + Executa um comando quando o melhor bloco mudar (%s no comando será substituído pelo hash do bloco) + + + Name to construct url for KeePass entry that stores the wallet passphrase + Nome para construir url para a entrada do KeePass que armazena a senha da carteira + + + Set maximum size of high-priority/low-fee transactions in bytes (default: %d) + Definir tamanho máximo de transações de alta prioridade/baixa taxa em bytes (padrão: %d) + + + Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d) + Define o número de threads de verificação de script (%u a %d, 0 = automático, <0 = número de cores deixados livres, padrão: %d) + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + Este é um build de teste pré-lançamento - use por sua conta e risco - não use para mineração ou comércio. + + + Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues. + Atenção: A rede não parecem concordar plenamente! Alguns mineiros parecem estar enfrentando problemas. + + + Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade. + Atenção: Nós não parecemos concordar plenamente com nossos nós! Você pode precisar atualizar ou outros nós podem precisar atualizar. + + + You must specify a masternodeprivkey in the configuration. Please see documentation for help. + Você deve especificar um masternodeprivkey na configuração. Por favor, consulte a documentação para obter ajuda. + + + Accept command line and JSON-RPC commands + Aceitar linha de comando e comandos JSON-RPC + + + Add a node to connect to and attempt to keep the connection open + Adicionar um nó com o qual se conectar e tentar manter a conexão ativa + + + Allow DNS lookups for -addnode, -seednode and -connect + Permitir consultas DNS para -addnode, -seednode e -connect + + + Already have that input. + Já tem essa entrada. + + + Block creation options: + Opções de criação de bloco: + + + Cannot downgrade wallet + Não é possível fazer downgrade da carteira + + + Cannot write default address + Não foi possível escrever no endereço padrão + + + Collateral not valid. + Colateral inválido. + + + Connect to a node to retrieve peer addresses, and disconnect + Conectar a um nó para receber endereços de participantes, e desconectar. + + + Connection options: + Opções de conexão: + + + Corrupted block database detected + Detectada database de bloco corrompida + + + Debugging/Testing options: + Opções de depuração/teste: + + + Do not load the wallet and disable wallet RPC calls + Não carrega a carteira e desabilita as chamadas RPC para a carteira + + + Do you want to rebuild the block database now? + Você quer reconstruir o banco de dados de blocos agora? + + + Done loading + Carregamento completo + + + Entries are full. + Entradas estão cheias. + + + Error initializing block database + Erro ao inicializar a database do bloco + + + Error initializing wallet database environment %s! + Erro ao inicializar ambiente de banco de dados de carteira %s! + + + Error loading block database + Erro ao carregar banco de dados de blocos + + + Error opening block database + Erro ao abrir banco de dados de blocos + + + Error reading from database, shutting down. + Erro ao ler o banco de dados. Finalizando. + + + Error + Erro + + + Error: Disk space is low! + Erro: Pouco espaço em disco! + + + Failed to listen on any port. Use -listen=0 if you want this. + Falha ao escutar em qualquer porta. Use -listen=0 se você quiser isso. + + + (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data) + (1 = manter metadados tx e.g. informação do dono da conta e requisição de pagamente, 2 = descartar metadados tx) + + + -maxtxfee is set very high! Fees this large could be paid on a single transaction. + -maxtxfee é muito alto! Essa quantia poderia ser paga em uma única transação. + + + A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s) + A variação da taxa (em %s/kB) que será usada quando não houver dados suficientes para se estimar a taxa (default: %s) + + + Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d) + Aceita transações retransmitidas advindas de pares em lista branca, mesmo quando não estiver retransmitindo transações (padrão: %d) + + + Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times + Permitir conexões JSON-RPC de uma fonte específica. Válido para um único ip (ex. 1.2.3.4), até uma rede/máscara (ex. 1.2.3.4/255.255.255.0) ou uma rede/CIDR (ex. 1.2.3.4/24). Esta opção pode ser usada múltiplas vezes + + + Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6 + Vincular ao endereço fornecido e sempre escutar nele. Use a notação [host]:port para IPv6 + + + Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces) + Vincular a determinado endereço para atender as ligações JSON-RPC. Use [host]: Notação de porta para IPv6. Esta opção pode ser especificada várias vezes (padrão: ligam-se a todas as interfaces) + + + Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality) + Criar novos arquivos com permissões padrão do sistema, em vez de umask 077 (apenas efetivo com funcionalidade de carteira desabilitada) + + + Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup + Apaga todas as transações da carteira e somente recupera essas partes da blockchain usando o comando -rescan na inicialização + + + Error loading %s: You can't enable HD on a already existing non-HD wallet + Erro ao carregar %s: você não pode ativar o HD em uma carteira não HD já existente + + + Found unconfirmed denominated outputs, will wait till they confirm to continue. + Encontrado saídas não confirmadas, esperará até que confirme para continuar. + + + How thorough the block verification of -checkblocks is (0-4, default: %u) + Quão completa a verificação de blocos do -checkblocks é (0-4, padrão: %u) + + + If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u) + Se paytxfee não estiver definida, incluir comissão suficiente para que as transações comecem a ter confirmações em média dentro de N blocos (padrão %u) + + + 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: %s, testnet: %s) + Se este bloco está no blockchain, assume-se que ele e seus ancestrais são válidos e podem ignorar a verificação de scripts (0 para verificar todos, padrão: %s, testnet: %s) + + + Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions) + Valor inválido para -maxtxfee=<valor>: '%s' (precisa ser pelo menos a taxa mínima de %s para prevenir que a transação nunca seja confirmada) + + + Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u) + Mantém um índice completo de transações, usado pela chamada rpc getrawtransaction (padrão: %u) + + + Make sure to encrypt your wallet and delete all non-encrypted backups after you verified that wallet works! + Certifique-se de criptografar sua carteira e excluir todos os backups não criptografados depois de verificar se a carteira funciona! + + + Maximum size of data in data carrier transactions we relay and mine (default: %u) + Tamanho máximo de dados em transações de dados de operadora (padrão %u) + + + Number of seconds to keep misbehaving peers from reconnecting (default: %u) + Número de segundos para impedir que peers mal comportados reconectem (padrão %u) + + + Output debugging information (default: %u, supplying <category> is optional) + Informação de saída de debug (padrão: %u, definir <category> é opcional) + + + Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s) + Use um proxy SOCKS5 separado para alcançar participantes da rede via serviços ocultos Tor (padrão: %s) + + + User defined mnemonic for HD wallet (bip39). Only has effect during wallet creation/first start (default: randomly generated) + Usuário definido mnemonic para carteira HD (bip39). Somente tem efeito durante a criação/primeiro começa da carteira (padrão: gerado aleatoriamente) + + + User defined seed for HD wallet (should be in hex). Only has effect during wallet creation/first start (default: randomly generated) + Semente definida pelo usuário para carteira HD (deve estar em hex). Só tem efeito durante a criação / início da carteira (padrão: gerado aleatoriamente) + + + Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway + Peers permitidos não podem ser banidos do DoS e suas transações sempre são transmitidas, até mesmo se eles já estão no pool de memória, útil, por exemplo, para um gateway + + + You need to rebuild the database using -reindex-chainstate to change -txindex + Você precisa reconstruir o banco de dados usando -reindex-chainstate para alterar -txindex + + + (default: %s) + (padão: %s) + + + Accept public REST requests (default: %u) + Aceitar pedidos restantes públicas (padrão: %u) + + + Always query for peer addresses via DNS lookup (default: %u) + Sempre pergunte pelo endereço de peer via pesquisa DNS (padrão: %u) + + + Can't mix: no compatible inputs found! + Não é possível misturar: não foram encontradas entradas compatíveis! + + + Chain selection options: + Opções de seleção de cadeia: + + + Connect through SOCKS5 proxy + Conecte através do proxy SOCKS5 + + + Connect to KeePassHttp on port <port> (default: %u) + Conecte-se ao KeePassHttp na porta<port> (padrão: %u)  + + + Enable the client to act as a masternode (0-1, default: %u) + Ative o cliente para atuar como um masternode (0-1, padrão: %u) + + + Entry exceeds maximum size. + Entrada excede o tamanho máximo. + + + Error loading %s: You can't disable HD on a already existing HD wallet + Erro ao carregar %s: você não pode desativar o HD em uma carteira HD já existente + + + Failed to load fulfilled requests cache from + Falha ao carregar o cache de solicitações preenchidas de + + + Failed to load governance cache from + Falha ao carregar o cache de governança de + + + Failed to load masternode cache from + Falha ao carregar o cache masternode de + + + Failed to load masternode payments cache from + Falha ao carregar o cache de pagamentos do masternode + + + Found enough users, signing ( waiting %s ) + Encontrou usuários suficientes, assinando ( esperando %s ) + + + Found enough users, signing ... + Encontrou usuários suficientes, assinando ... + + + How many blocks to check at startup (default: %u, 0 = all) + Quantos blocos devem ser checados ao iniciar (padrão: %u, 0 = todos) + + + Importing... + Importando... + + + Include IP addresses in debug output (default: %u) + Incluir endereço IP na saída de depuração (padrão: %u) + + + Incompatible mode. + Modo incompatível. + + + Incompatible version. + Versão incompatível. + + + Incorrect or no genesis block found. Wrong datadir for network? + Bloco gênese incorreto ou não encontrado. Datadir errado para a rede? + + + Information + Informação + + + Input is not valid. + A entrada não é válida. + + + Insufficient funds. + Saldo insuficiente. + + + Invalid -onion address: '%s' + Endereço -onion inválido: '%s' + + + Invalid -proxy address: '%s' + Endereço - proxy inválido: '%s' + + + Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s) + Valor inválido para -paytxfee=<amount>: '%s' (precisa ser no mínimo %s) + + + Keypool ran out, please call keypoolrefill first + Erro na Keypool, favor executar keypoolrefill primeiro + + + Listen for JSON-RPC connections on <port> (default: %u or testnet: %u) + Escutar por conexões JSON-RPC na porta <port> (padrão: %u ou testnet: %u) + + + Listen for connections on <port> (default: %u or testnet: %u) + Aguardar por conexões na porta <port> (padrão: %u ou testnet: %u) + + + Loading banlist... + Carregando lista de banidos... + + + Loading fulfilled requests cache... + Carregando o cache de solicitações atendidas ... + + + Loading masternode cache... + Carregando o cache do masternode ... + + + Loading masternode payment cache... + Carregando o cache de pagamento do masternode ... + + + Lock is already in place. + Bloqueio já está no lugar. + + + Lock masternodes from masternode configuration file (default: %u) + Bloquear masternodes do arquivo de configuração do masternode (padrão: %u) + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: %u) + Buffer máximo de recebimento por conexão, <n>*1000 bytes (padrão: %u) + + + Maximum per-connection send buffer, <n>*1000 bytes (default: %u) + Buffer máximo de envio por conexão, <n>*1000 bytes (padrão: %u) + + + Mixing in progress... + Mixing em progresso... + + + Mnemonic passphrase is too long, must be at most 256 characters + Frase mnemotica é muito grande, deve ter ao máximo 256 caracteres + + + Need to specify a port with -whitebind: '%s' + Necessário informar uma porta com -whitebind: '%s' + + + No Masternodes detected. + Nenhum Masternode detectado. + + + No compatible Masternode found. + Nenhum Masternode compatível encontrado. + + + Not in the Masternode list. + Não está na lista de Masternode. + + + Only connect to nodes in network <net> (ipv4, ipv6 or onion) + Somente conectar a clientes na rede <net> (ipv4, ipv6 ou onion) - Wallet Data (*.dat) - Dados da Carteira (*.dat) + Port: %d + Porta: %d - Backup Failed - A Cópia de Segurança Falhou + Prepend debug output with timestamp (default: %u) + Adiciona timestamp como prefixo no debug (padrão: %u) - There was an error trying to save the wallet data to %1. - Ocorreu um erro ao tentar criar cópia de segurança da carteira para %1. + Rebuild chain state and block index from the blk*.dat files on disk + Reconstruir índice de cadeia de bloco a partir dos arquivos blk*.dat no disco - Backup Successful - Cópia de Segurança Criada com Sucesso + Rebuild chain state from the currently indexed blocks + Reconstruir estado a partir dos blocos indexados - The wallet data was successfully saved to %1. - Os dados da carteira foram gravados com sucesso em %1. + Send trace/debug info to debug.log file (default: %u) + Enviar informação de execução/depuração para o ficheiro debug.log (omissão: %u) - - - dash-core - Bind to given address and always listen on it. Use [host]:port notation for IPv6 - Vincular a um determinado endereço e ouvir sempre aí. Use a notação [endereço]:porta para IPv6 + Send transactions as zero-fee transactions if possible (default: %u) + Enviar com taxa-zero se possível (omissão: %u) - Cannot obtain a lock on data directory %s. Dash Core is probably already running. - Não foi possível obter acesso exclusivo à pasta de dados %s. Possivelmente o Dash Core já se encontra em execução. + Set key pool size to <n> (default: %u) + Defina o tamanho da chave para piscina<n> (padrão: %u) - Darksend uses exact denominated amounts to send funds, you might simply need to anonymize some more coins. - O Darksend usa denominações exactas para enviar fundos, pode necessitar simplesmente de anonimizar mais algumas moedas. + Set the number of threads to service RPC calls (default: %d) + Defina o número de threads para chamadas do serviço RPC (padrão: %d) - Enter regression test mode, which uses a special chain in which blocks can be solved instantly. - Entrar no modo de testes de regressão, que usa uma cadeia de blocos especial onde cada bloco é resolvido instantaneamente. + Specify configuration file (default: %s) + Especificar arquivo de configuração (padrão: %s) - Error: Listening for incoming connections failed (listen returned error %s) - Erro: A escuta por ligações de entrada falhou (a escuta devolveu o erro %s) + Specify connection timeout in milliseconds (minimum: 1, default: %d) + Especificar tempo para desistência de conexões, em mili segundos (mínimo: 1, padrão: %d) - Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message) - Executar comando quando é recebido um alerta ou vemos uma longa bifurcação na cadeia de blocos (%s no comando é substituído pela mensagem) + Specify masternode configuration file (default: %s) + Especificar ficheiro de configuração masternode (omissão: %s) - Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) - Executar comando quando muda uma transacção na carteira (%s no comando é substituído pelo TxID) + Specify pid file (default: %s) + Especificar arquivo pid (padrão: %s) - Execute command when the best block changes (%s in cmd is replaced by block hash) - Executar comando quando o melhor bloco muda (%s no comando é substituído pela hash do bloco) + Spend unconfirmed change when sending transactions (default: %u) + Gastar troco não confirmado quando enviar transações (padrão: %u) - In this mode -genproclimit controls how many blocks are generated immediately. - Neste modo o -genproclimit controla quantos blocos são gerados imediatamente. + Submitted following entries to masternode: %u / %d + Apresentadas as seguintes entradas ao masternode: %u / %d - InstantX requires inputs with at least 6 confirmations, you might need to wait a few minutes and try again. - O InstantX necessita de entradas com pelo menos 6 confirmações, pode ser necessário aguardar mais uns minutos e tentar de novo. + Submitted to masternode, waiting for more entries ( %u / %d ) %s + Apresentado ao masternode, esperando por mais entradas ( %u / %d ) %s - Name to construct url for KeePass entry that stores the wallet passphrase - Nome para construir o url para a entrada KeePass que guarda a palavra-passe da carteira + Submitted to masternode, waiting in queue %s + Enviado para o masternode, esperando na fila %s - Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect) - Questionar por endereços dos nós através de pesquisas DNS caso tenha poucos endereços (omissão: 1 excepto -connect) + Synchroning blockchain... + Sincronizando blockchain... - Set maximum size of high-priority/low-fee transactions in bytes (default: %d) - Definir tamanho máximo de transacções com alta-prioridade/taxa-reduzida em bytes (omissão: %d) + Synchronization failed + Sincronização falhou - Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d) - Definir número de processos de verificação de scripts (%u a %d, 0 = auto, <0 = deixar este número de cores livres, omissão: %d) + Synchronization finished + Sincronização finalizada - This is a pre-release test build - use at your own risk - do not use for mining or merchant applications - Esta é uma compilação prévia de teste - use por sua conta e risco - não use para minar nem em aplicações comerciais + Synchronizing masternodes... + Sincronizando masternodes... - Unable to bind to %s on this computer. Dash Core is probably already running. - Não foi possível vincular a %s neste computador. Provavelmente o Dash Core já está a ser executado. + This is not a Masternode. + Este não é um Masternode. - Unable to locate enough Darksend denominated funds for this transaction. - Não foi possível localizar fundos Darksend denominados suficientes para esta transacção. + Threshold for disconnecting misbehaving peers (default: %u) + Limite para desconectar peers mal comportados (padrão: %u) - Unable to locate enough Darksend non-denominated funds for this transaction that are not equal 1000 DASH. - Não foi possível localizar fundos Darksend não denominados suficientes para esta transacção que não sejam igual a 1000DRK. + Too many %f denominations, removing. + Muitas %f denominações, removendo. - Warning: -paytxfee is set very high! This is the transaction fee you will pay if you send a transaction. - Aviso: -paytxfee tem um valor muito elevado! Esta é a taxa de transacção que será paga se enviar uma transacção. + Tor control port password (default: empty) + Senha da porta de controle do Tor (padrão: vazio) - Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues. - Aviso: A rede não parece estar concordar! Parece haver alguns mineiros com problemas. + Tor control port to use if onion listening enabled (default: %s) + Porta de controle a ser usada se o monitoramento onion estiver habilitado (padrão: %s) - Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade. - Aviso: Parece que não estamos de acordo com outros nós! Talvez necessite actualizar a aplicação ou os outros nós necessitem actualizar. + Unable to start HTTP server. See debug log for details. + Não foi possível iniciar o servidor HTTP. Veja o log de debug para detalhes. - Warning: error reading wallet.dat! All keys read correctly, but transaction data or address book entries might be missing or incorrect. - Aviso: erro ao carregar wallet.dat! Todas as chaves estão correctas mas os dados das transacções ou as entradas do livro de endereços podem estar ausentes ou incorrectas. + Unknown response. + Resposta desconhecida. - Warning: wallet.dat corrupt, data salvaged! Original wallet.dat saved as wallet.{timestamp}.bak in %s; if your balance or transactions are incorrect you should restore from a backup. - Aviso: wallet.dar corrompido mas os dados foram recuperados! A carteira original foi gravada como wallet{data/hora}.bak in %s; se o seu saldo ou transacções forem incorrectos deverá recuperar a partir de uma cópia de segurança. + Unsupported argument -benchmark ignored, use -debug=bench. + Argument -benchmark não suportado e ignorado, use -debug=bench. - You must specify a masternodeprivkey in the configuration. Please see documentation for help. - Tem que especificar uma masternodeprivkey na configuração. Por favor verifique a documentação para ajuda. + Unsupported argument -debugnet ignored, use -debug=net. + Argument -debugnet não suportado e ignorado, use -debug=net. - (default: 1) - (omissão: 1) + Unsupported argument -tor found, use -onion. + O argumento -tor não é suportado, use -onion. - Accept command line and JSON-RPC commands - Aceitar comandos da linha de comandos e JSON-RPC + Upgrade wallet to latest format on startup + Atualizar carteira para o formato mais recente ao iniciar - Accept connections from outside (default: 1 if no -proxy or -connect) - Aceitar ligações do exterior (omissão: 1 se não tiver usado -proxy ou -connect) + Use KeePass 2 integration using KeePassHttp plugin (default: %u) + Utilize integração KeePass 2 usando plugin KeePassHttp (default: %u) - Add a node to connect to and attempt to keep the connection open - Adicionar um nó ao qual efectuar ligação e tentar manter a ligação aberta + Use UPnP to map the listening port (default: %u) + Usar UPnP para mapear a porta de entrada (padrão: %u) - Allow DNS lookups for -addnode, -seednode and -connect - Permitir pesquisas de DNS para -addnode, -deednode e -connect + Use the test chain + Use a cadeia de teste - Already have that input. - Entrada existente. + User Agent comment (%s) contains unsafe characters. + Comentário User Agent (%s) contém caracteres inseguros. - Attempt to recover private keys from a corrupt wallet.dat - Tentar recuperar as chaves privadas a partir de um ficheiro wallet.dat corrompido + Will retry... + Será feita nova tentativa... - Block creation options: - Opções de criação de blocos: + Invalid masternodeprivkey. Please see documenation. + masternodeprivkey inválida. Por favor, consulte a documentação. - Can't denominate: no compatible inputs left. - Não é possível denominar: não existem mais entradas compatíveis. + Can't find random Masternode. + Não é possível encontrar o Masternode aleatório. - Cannot downgrade wallet - Não é possível reverter para uma versão anterior da carteira + Can't mix while sync in progress. + Não é possível misturar enquanto a sincronização está em progresso. - Cannot resolve -bind address: '%s' - Não foi possível resolver endereço de vínculo: '%s' + Could not parse masternode.conf + Impossível avaliar masternode.conf - Cannot resolve -externalip address: '%s' - Não foi possível resolver endereço -externalip: '%s' + Invalid netmask specified in -whitelist: '%s' + Máscara de rede inválida detectada em -whitelist: '%s' - Cannot write default address - Não foi possível escrever endereço por omissão + Invalid port detected in masternode.conf + Porta inválida detectada em masternode.conf - Collateral not valid. - Colateral inválido. + Invalid script detected. + Detectado script inválido. - Connect only to the specified node(s) - Ligar apenas ao(s) nó(s) especificados + KeePassHttp id for the established association + KeePassHttp id para a associação estabelecida - Connect to a node to retrieve peer addresses, and disconnect - Efectuar ligação a um nó para obter os endereços de outros clientes e desligar + KeePassHttp key for AES encrypted communication with KeePass + Chave KeePassHttp para usar na comunicação cifrada AES com o KeePass - Connection options: - Opções de ligação: + Keep at most <n> unconnectable transactions in memory (default: %u) + Manter ao máximo <n> transações inconectáveis na memória (padrão: %u) - Corrupted block database detected - Detectada corrupção na base de dados dos blocos + Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) + Desabilitar todas as funcionalidades específicas da Dash (Masternodes, EnvioPrivado, EnvioInstantâneo, Governança) (0-1, default: %u) - Darksend options: - Opções Darksend: + %s file contains all private keys from this wallet. Do not share it with anyone! + %s arquivo contém todas as chaves privadas desta carteira. Não compartilhe com ninguém! - Debugging/Testing options: - Opções de Depuração/Teste + Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) + Descobrir o próprio IP (padrão: 1 enquanto aguardando conexões e sem -externalip ou -proxy) - Discover own IP address (default: 1 when listening and no -externalip) - Descobrir endereço de IP próprio (omissão: 1 quando em escuta e -externalip não definido) + Do not keep transactions in the mempool longer than <n> hours (default: %u) + Não manter transações na mempool por mais que <n> horas (padrão: %u) - Do not load the wallet and disable wallet RPC calls - Não carregar a carteira e desabilitar as chamadas RPC + Enable InstantSend, show confirmations for locked transactions (0-1, default: %u) + Activar InstantSend, mostrar confirmações para transacções bloqueadas (0-1, omissão: %u) - Do you want to rebuild the block database now? - Quer reconstruir agora a base de dados dos blocos? + Enable multiple PrivateSend mixing sessions per block, experimental (0-1, default: %u) + Habalitar múltiplas sessões de mistura para EnvioPrivado por bloco, experimental (0-1, padrão: %u) - Done loading - Carregamento completo + Enable use of automated PrivateSend for funds stored in this wallet (0-1, default: %u) + Activar o uso de PrivateSend automatizado para os fundos guardados nesta carteira (0-1, omissão: %u) - Entries are full. - Entradas completas. + Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID) + Execute comando quando uma transação EnvioInstantâneo numa carteira é fechada com sucesso (%s no cmd é substituído por TxID) - Error initializing block database - Erro ao inicializar a base de dados dos blocos + Extra transactions to keep in memory for compact block reconstructions (default: %u) + Transações extras para manter na memória para reconstruções do blocos compactos (padrão: %u) - Error initializing wallet database environment %s! - Erro ao inicializar o ambiente de base de dados da carteira %s! + Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. + Falha em criar o backup, o arquivo já existe! Isso pode ter acontecido se você resetou sua carteira em menos de 60 segundos. Você pode continuar se está ok com isso. - Error loading block database - Erro ao carregar base de dados de blocos + Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s) + Comissões (em %s/kB) menores serão consideradas como zero para relaying, mineração e criação de transação (padrão %s) - Error loading wallet.dat - Erro ao carregar wallet.dat + Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s) + Comissões (em %s/kB) menores serão consideradas como zero para criação de transação (padrão %s) - Error loading wallet.dat: Wallet corrupted - Erro ao carregar wallet.dat: A carteira está corrompida + If <category> is not supplied or if <category> = 1, output all debugging information. + Se <category> não for suprida ou se <category> = 1, mostrar toda informação de depuração. - Error opening block database - Erro ao abrir base de dados de blocos + InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH. + EnvioInstantâneo não suporta o envio de valores tão altos ainda. As transações são limitadas a %1 DASH. - Error reading from database, shutting down. - Erro ao ler da base de dados, a encerrar. + InstantSend requires inputs with at least %d confirmations, you might need to wait a few minutes and try again. + O InstantSend necessita de entradas com pelo menos %d confirmações, pode ser necessário aguardar mais uns minutos e tentar de novo. - Error recovering public key. - Erro ao recuperar chave pública. + Maintain a full address index, used to query for the balance, txids and unspent outputs for addresses (default: %u) + Mantenha um index completo de endereços, usados para inquirir sobre o saldo, txids e outpus não gastos para endereços (padrão: %u) - Error - Erro + Maintain a full spent index, used to query the spending txid and input index for an outpoint (default: %u) + Mantenha um index completo de gastos, usados para inquirir a txid de gastos e index de input para um outpoint (padrão: %u) - Error: Disk space is low! - Erro: Pouco espaço em disco! + Maintain a timestamp index for block hashes, used to query blocks hashes by a range of timestamps (default: %u) + Mantenha um index de marcadores de tempo para hashes de bloco, usados para inquirir hashes de blocos por alcance de marcadores de tempo (padrão: %u) - Error: Wallet locked, unable to create transaction! - Erro: Carteira bloqueada, não foi possível criar a transacção! + Maintain at most <n> connections to peers (temporary service connections excluded) (default: %u) + Mantenha no máximo<n>conexões a pares (conexões de serviço temporário excluídas) (padrão: %u) - Error: You already have pending entries in the Darksend pool - Erro: Já tem entradas pendentes no conjunto Darksend + PrivateSend uses exact denominated amounts to send funds, you might simply need to anonymize some more coins. + O PrivateSend usa denominações exactas para enviar fundos, pode necessitar simplesmente de anonimizar mais algumas moedas. - Failed to listen on any port. Use -listen=0 if you want this. - Falhou a escuta em qualquer porta. Use -listen=0 se é isto que deseja. + Prune configured below the minimum of %d MiB. Please use a higher number. + Prune configurado abaixo do mínimo de %d MiB. Por favor use um número mais alto. - Failed to read block - Falha ao ler bloco + Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node) + Prune: A ultima sincronização da carteira foi além do dado comprimido. Você precisa reindexar (fazer o download de toda a blockchain novamente) - If <category> is not supplied, output all debugging information. - Se a <category> não for fornecida, mostrar toda a informação de depuração. + Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u) + Gerar credenciais aleatórias para cada conexão por proxy. Isto habilita o isolamento de stream do Tor (padrão: %u) - (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data) - (1 = mantem meta dados das transacções ex. dono da conta e informação das solicitações de pagamento, 2 = elimina os meta dados das transacções) + Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again. + Rescans não são possíveis no modo prune. Você precisa usar -reindex, que irá fazer o download de toda a blockchain novamente. - An error occurred while setting up the RPC address %s port %u for listening: %s - Ocorreu um erro ao configurar o endereço RPC %s na porta %u para escutar: %s + Specify full path to directory for automatic wallet backups (must exist) + Especifique o caminho completo para o diretório de backups automáticos da carteira (deve existir) - Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6 - Vincular a um determinado endereço e ouvir sempre aí. Use a notação [host]:porta para IPv6 + Support filtering of blocks and transaction with bloom filters (default: %u) + Suportar filtragem de blocos e transações com filtros bloom (padrão: %u) - Continuously rate-limit free transactions to <n>*1000 bytes per minute (default:%u) - Continuamente limitar o número de transacções gratuitas por minuto a <n>*1000 bytes (default:%u) + The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct + O banco de dados de blocos contém um bloco que parece ser do futuro. Isso pode ser devido à data e hora do seu computador estarem configuradas incorretamente. Apenas reconstrua o banco de dados de blocos se você estiver certo de que a data e hora de seu computador estão corretas. - Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup - Apaga todas as transacções da carteira e só recupera essas partes da cadeia de blocos com -rescan no arranque + The transaction amount is too small to send after the fee has been deducted + O valor da transação é muito pequeno para ser enviado depois que a taxa foi deduzida - Enable instantx, show confirmations for locked transactions (bool, default: %s) - Activar instantx, mostrar confirmações para transacções bloqueadas (bool, default: %s) + Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. + O tamanho total da string de versão da rede (%i) excede o tamanho máximo (%i). Reduza o numero ou tamanho de uacomments. - Enable use of automated darksend for funds stored in this wallet (0-1, default: %u) - Activar o uso de darksend automatizado para os fundos guardados nesta carteira (0-1, default: %u) + Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index. + O índice de transação não pode ser desativado no modo completo. Inicie com o comutador na linha de comando -litemode ou ative o índice de transação. - Fees (in DASH/Kb) smaller than this are considered zero fee for relaying (default: %s) - Taxas (em DASH/Kb) inferiores a esta serão consideradas zero para retransmissão (default: %s) + Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) + Tenta manter tráfego fora dos limites dentro do alvo especificado (em MiB por 24h), 0 = sem limite (padrão: %d) - Flush database activity from memory pool to disk log every <n> megabytes (default: %u) - Consolidar a actividade de dados de memoria para disco a cada <n> megabytes (omissão: %u) + Unable to locate enough PrivateSend denominated funds for this transaction. + Não é possível localizar fundos denominados do PrivateSend suficientes para essa transação. - Found unconfirmed denominated outputs, will wait till they confirm to continue. - Foram encontradas saídas denominadas não confirmadas, a aguardar pela confirmação para prosseguir. + Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported. + Argumento inválido -socks encontrado. Definir a versão do SOCKS não é mais possível, somente proxys SOCK5 são suportados. - How thorough the block verification of -checkblocks is (0-4, default: %u) - Quão minuciosa é a verificação dos blocos do -checkblocks (0-4, omissão: %u) + Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay. + Argumento não suportado -whitelistalwaysrelay foi ignorado, utilize -whitelistrelay e/ou -whitelistforcerelay. - Log transaction priority and fee per kB when mining blocks (default: %u) - Salvar prioridade e taxa da transacção por kB quando minar blocos (omissão: %u) + Use UPnP to map the listening port (default: 1 when listening and no -proxy) + Usar UPnP para mapear a porta de entrada (padrão: 1 quando listening e no -proxy) - Number of seconds to keep misbehaving peers from reconnecting (default: %u) - Número de segundos a não permitir ligações de nós com comportamento inadequado (omissão: %u) + WARNING! Failed to replenish keypool, please unlock your wallet to do so. + ATENÇÃO! Falha ao repor o keypool, desbloqueie sua carteira para fazer isso. - Output debugging information (default: %u, supplying <category> is optional) - Informação de depuração de saída (omissão: %u, fornecer <category> é opcional) + Wallet is locked, can't replenish keypool! Automatic backups and mixing are disabled, please unlock your wallet to replenish keypool. + Carteira travada, impossível reabastecer o keypool. Backups e misturas automáticas estão desabilitadas, por favor, destrave sua carteira para reabastecer o keypool. - Provide liquidity to Darksend by infrequently mixing coins on a continual basis (0-100, default: %u, 1=very frequent, high fees, 100=very infrequent, low fees) - Providenciar liquidez para o Darksend misturando moedas continuamente com baixa frequência (0-100, omissão: %u, 1=muito frequente taxas elevadas, 100=pouco frequente, taxas reduzidas) + Warning: Unknown block versions being mined! It's possible unknown rules are in effect + Alerta: Versões de blocos desconhecidas mineradas! É possível que regras desconhecidas estejam ativas - Send trace/debug info to console instead of debug.log file (default: %u) - Enviar informação de execução/depuração para a consola em vez de enviar para o ficheiro debug.log (omissão: %u) + You are starting in lite mode, all Dash-specific functionality is disabled. + Você está começando no modo light, todas as funcionalidades específicas do Dash estão desativadas. - Set the number of threads for coin generation if enabled (-1 = all cores, default: %d) - Definir o limite de processadores quando a geração está activa (-1 = todos, omissão: %d) + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain + Você precisa reconstruir o banco de dados usando -reindex para sair do modo prune. Isso irá rebaixar todo o blockchain. - Show N confirmations for a successfully locked transaction (0-9999, default: %u) - Mostrar N confirmações para uma transacção bloqueada com sucesso (0-9999, omissão: %u) + (%d could be used only on mainnet) + (%d poderia ser usado somente na mainnet) - This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit <https://www.openssl.org/> and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard. - Este produto inclui software desenvolvido pelo Projecto OpenSSL para uso no OpenSSL Toolkit <https://www.openssl.org/>, software criptográfico escrito por Eric Young e software UPnP escrito por Thomas Bernard. + (default: %u) + (padrão: %u) - Unable to locate enough funds for this transaction that are not equal 1000 DASH. - Não foi possível localizar fundos suficientes para esta transacção que não sejam igual a 1000DRK. + (must be %d for mainnet) + (precisa ser %d para mainnet) - Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s) - Usar um proxy SOCKS5 diferente para ligar aos nós através dos serviços escondidos Tor (omissão: %s) + -maxmempool must be at least %d MB + -maxmempool deve ser pelo menos %d MB - Warning: -maxtxfee is set very high! Fees this large could be paid on a single transaction. - Aviso: -maxtxfee é demasiado alta! Taxas deste tamanho podem ser pagas em apenas uma transacção. + <category> can be: + <category> pode ser: - Warning: Please check that your computer's date and time are correct! If your clock is wrong Dash Core will not work properly. - Aviso: Por favor verifique que a data e a hora do computador está correcta! Se o relógio estiver errado o Dash Core não vai funcionar correctamente. + Append comment to the user agent string + Adicionar comentário à string user agent - (9999 could be used only on mainnet) - (9999 apenas pode ser usado para mainnet) + Automatic backups disabled + Backup automáticos desabilitados. - (default: %s) - (default: %s) + Automatically create Tor hidden service (default: %d) + Criar automaticamente serviços ocultos do Tor (padrão: %d) - <category> can be: - - <category> pode ser: - + ERROR! Failed to create automatic backup + ERRO! Falha ao criar o backup automático - Accept public REST requests (default: %u) - Aceitar REST requests (default: %u) públicos + Enable publish hash block in <address> + Abilitar a publicação da hash do block em <endereço> - Acceptable ciphers (default: %s) - Cifras aceitáveis (default: %s) + Enable publish hash of governance objects (like proposals) in <address> + Ativar o hash de publicação de objetos de controle (como propostas) em <address> - Always query for peer addresses via DNS lookup (default: %u) - Perguntar sempre por endereços de pares via DNS lookup (default: %u) + Enable publish hash of governance votes in <address> + Ativar o hash de publicação de votos da governança em <address> - Cannot resolve -whitebind address: '%s' - Incapaz de resolver -whitebind address: '%s' + Enable publish hash transaction (locked via InstantSend) in <address> + Ative a transação de hash de publicação (bloqueada via InstantSend) em<address> - Connect through SOCKS5 proxy - Conectar através de SOCKS5 proxy + Enable publish hash transaction in <address> + Abilitar a publicação da hash da transação em <endereço> - Connect to KeePassHttp on port <port> (default: %u) - Conectar a KeePassHttp na porta <port> (default: %u) + Enable publish raw block in <address> + Abilitar a publicação dos dados brutos do block em <endereço> - Copyright (C) 2009-%i The Bitcoin Core Developers - Copyright (C) 2009-%i The Bitcoin Core Developers + Enable publish raw transaction (locked via InstantSend) in <address> + Ativar publicação de transação bruta (bloqueada via InstantSend) em<address> - Copyright (C) 2014-%i The Dash Core Developers - Copyright (C) 2014-%i Dash Core Developers + Enable publish raw transaction in <address> + Abilitar a publicação dos dados brutos da transação em <endereço> - Could not parse -rpcbind value %s as network address - Incapaz de analizar -rpcbind value %s como endereço de rede + Enable transaction replacement in the memory pool (default: %u) + Habilita substituição de transação em memória (padrão: %u) - Darksend is idle. - Darksend está inactivo. + Error: A fatal internal error occurred, see debug.log for details + Erro: Um erro interno fatal ocorreu, veja debug.log para detalhes - Darksend request complete: - Pedido Darksend completo: + Failed to create backup %s! + Falhou ao criar backup %s! - Darksend request incomplete: - Pedido Darksend incompleto: + Failed to create backup, error: %s + Falha ao criar backup, erro: %s - Disable safemode, override a real safe mode event (default: %u) - Desactivar safemode, ultrapassar o evento de modo de segurança real (default: %u) + Failed to delete backup, error: %s + Falha ao apagar backup, erro: %s - Enable the client to act as a masternode (0-1, default: %u) - Permitir ao cliente actuar como um masternode (0-1, omissão: %u) + Failed to load InstantSend data cache from + Falha ao carregar o cache de dados do InstantSend de - Error connecting to Masternode. - Erro conectando ao Masternode + Failed to load sporks cache from + Falha ao carregar o cache de sporks de - Error loading wallet.dat: Wallet requires newer version of Dash Core - Erro carregando wallet.dat Carteira precisa de nova versão do Dash Core + Failed to parse host:port string + Falha ao avaliar a string host:port - Error: A fatal internal error occured, see debug.log for details - Erro: Um erro fatal interno ocorreu, veja debug.log para mais detalhes + Fee (in %s/kB) to add to transactions you send (default: %s) + Taxa (em %s/kB) a ser adicionada às transações que você mandar (padrão: %s) - Error: Can't select current denominated inputs - Erro: Incapaz de seleccionar inputs denominados correntes + Imports blocks from external blk000??.dat file on startup + Importar blocos a partir de arquivo externo blk000??.dat durante a inicialização - Error: Unsupported argument -tor found, use -onion. - Erro: Argument não suportado -tor encontrado, utilize -onion + InstantSend options: + Opções de EnvioInstantâneo: - Fee (in DASH/kB) to add to transactions you send (default: %s) - Taxas (em DASH/Kb) a adicionar às transacções que envia (omissão: %s) + Invalid amount for -fallbackfee=<amount>: '%s' + Valor inválido para -fallbackfee=<amount>: '%s' - Finalizing transaction. - Finalizando transacção. + Invalid input count. + Contagem de entrada inválida. - Force safe mode (default: %u) - Forçar safe mode (default: %u) + Keep the transaction memory pool below <n> megabytes (default: %u) + Mantenha a mempool de transações abaixo de <n> megabytes (padrão: %u) - Found enough users, signing ( waiting %s ) - Encontrados utilizadores suficientes, assinando ( waiting %s ) + Last PrivateSend was too recent. + O último PrivateSend foi muito recente. - Found enough users, signing ... - Encontrados utilizadores suficientes, assinando ... + Last successful PrivateSend action was too recent. + A última ação PrivateSend com sucesso foi muito recente. - Generate coins (default: %u) - Gerar moedas (default: %u) + Line: %d + Linha: %d - How many blocks to check at startup (default: %u, 0 = all) - Quanto blocos deve verificar no arranque (omissão: %u, 0 = all) + Loading InstantSend data cache... + Carregando o cache de dados do InstantSend ... - Importing... - A importar... + Loading addresses... + Carregando endereços... - Imports blocks from external blk000??.dat file - Importa blocos a partir de ficheiros blk000??.dat externos + Loading block index... + Carregando índice de blocos... - Include IP addresses in debug output (default: %u) - Incluir endereços IP no output do debug (default: %u) + Loading governance cache... + Sincronizando cache de governança .... - Incompatible mode. - Modo incompatível. + Loading sporks cache... + Carregando o cache de sporks ... - Incompatible version. - Versão incompatível. + Loading wallet... (%3.2f %%) + Carregando carteira... (%3.2f %%) - Incorrect or no genesis block found. Wrong datadir for network? - Bloco original não encontrado ou incorrecto. Pasta de dados errada para esta rede? + Loading wallet... + Carregando carteira... - Information - Informação + Location of the auth cookie (default: data dir) + Localização do cookie de autenticação (padrão: diretório de dados) - Initialization sanity check failed. Dash Core is shutting down. - A verificação de consistência no arranque falhou. O Dash Core está a encerrar. + Make the wallet broadcast transactions + Criar transações de transmissão de carteira - Input is not valid. - A entrada não é válida. + Masternode cache is empty, skipping payments and governance cache... + A cache masternode está vazia, pulando cache de pagamentos e governança... - InstantX options: - Opções InstantX + Masternode options: + Opções do Masternode: - Insufficient funds. - Fundos insuficientes. + Masternode queue is full. + A fila do masternode está cheia. - Invalid -onion address: '%s' - Endereço -onion inválido: '%s' + Masternode: + Masternode: - Invalid -proxy address: '%s' - Endereço -proxy inválido: '%s' + Minimum bytes per sigop in transactions we relay and mine (default: %u) + Mínimo de bytes por sigop nas transações que retransmitimos e mineramos (padrão: %u) - Invalid amount for -maxtxfee=<amount>: '%s' - Quantidade inválida para -maxtxfee=<amount>: '%s' + Missing input transaction information. + Falta informação da transação de entrada. - Invalid amount for -minrelaytxfee=<amount>: '%s' - Quantia inválida para -minrelaytxfee=<amount>: '%s' + No errors detected. + Nenhum erro detectado. - Invalid amount for -mintxfee=<amount>: '%s' - Quantia inválida para -mintxfee=<amount>: '%s' + No matching denominations found for mixing. + Nenhuma denominação correspondente encontrada para misturar. - Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s) - Quantia inválida para -paytxfee=<amount>: '%s' (must be at least %s) + Node relay options: + Opções de relé nó : - Invalid amount for -paytxfee=<amount>: '%s' - Quantia inválida para -paytxfee=<amount>: '%s' + Non-standard public key detected. + Nenhuma chave pública fora do padrão detectada. - Last successful Darksend action was too recent. - Ultima acção Darksend com sucesso é demasiado recente. + Not compatible with existing transactions. + Não é compatível com as transações existentes. - Limit size of signature cache to <n> entries (default: %u) - Limitar o tamanho da cache de assinaturas a <n> elementos (default: %u) + Not enough file descriptors available. + Não há descritores de arquivos disponíveis. - Listen for JSON-RPC connections on <port> (default: %u or testnet: %u) - Escutar por ligações JSON-RPC na porta <port> (default: %u or testnet: %u) + Not enough funds to anonymize. + Não há fundos suficientes para anonimo. - Listen for connections on <port> (default: %u or testnet: %u) - Escutar ligações na <port> (default: %u or testnet: %u) + Number of automatic wallet backups (default: %u) + Número de backups automáticos da carteira (padrão: %u) - Loading budget cache... - Carregando cache de orçamentos... + Options: + Opções: - Loading masternode cache... - A carregar a cache de masternodes... + Password for JSON-RPC connections + Senha para conexões JSON-RPC - Loading masternode payment cache... - Carregando cache de pagamento de maternodes... + Print version and exit + Versão de impressão e sair - Lock is already in place. - Bloqueio já efectuado. + PrivateSend is idle. + PrivateSend está inativo. - Lock masternodes from masternode configuration file (default: %u) - Bloquear masternode presentes no ficheiro de configuração (omissão: %u) + PrivateSend options: + Opções do PrivateSend: - Maintain at most <n> connections to peers (default: %u) - Manter no máximo <n> ligações a outros nós (omissão: %u) + PrivateSend request complete: + PrivateSend solicitação completa: - Maximum per-connection receive buffer, <n>*1000 bytes (default: %u) - Tamanho máximo da memória intermédia de recepção por ligação, <n>*1000 bytes (omissão: %u) + PrivateSend request incomplete: + PrivateSend solicitação incompleta: - Maximum per-connection send buffer, <n>*1000 bytes (default: %u) - Tamanho máximo da memória intermédia de envio por ligação, <n>*1000 bytes (omissão: %u) + Prune cannot be configured with a negative value. + O modo prune não pode ser configurado com um valor negativo. - Mixing in progress... - Mistura em progresso... + Prune mode is incompatible with -txindex. + O modo prune é incompatível com -txindex. - Need to specify a port with -whitebind: '%s' - Necessita especificar a porta com -whitebind: '%s' + Pruning blockstore... + Prunando os blocos existentes... - No Masternodes detected. - Nenhum Masternode detectado. + Rescan the block chain for missing wallet transactions on startup + Analise novamente o blockchain para ver se há transações da carteira em falta na inicialização - No compatible Masternode found. - Nenhum Masternode compatível encontrado. + The wallet will avoid paying less than the minimum relay fee. + A carteira irá evitar pagar menos que a taxa mínima de retransmissão. - Not in the Masternode list. - Não está na lista de Masternodes. + This is the minimum transaction fee you pay on every transaction. + Esta é a taxa mínima que você paga em todas as transação. - Number of automatic wallet backups (default: 10) - Número de backups automáticos da carteira (omissão: 10) + This is the transaction fee you will pay if you send a transaction. + Esta é a taxa que você irá pagar se enviar uma transação. - Only accept block chain matching built-in checkpoints (default: %u) - Somente aceitar pontos de inspecção apropriados presentes na cadeia de blocos (omissão: %u) + Transaction amounts must not be negative + As quantidades nas transações não podem ser negativas. - Only connect to nodes in network <net> (ipv4, ipv6 or onion) - Somente ligar a nós na rede <net> (IPv4, IPv6 ou onion) + Transaction has too long of a mempool chain + A transação demorou muito na memória - Prepend debug output with timestamp (default: %u) - Adicionar data/hora à informação de depuração (omissão: %u) + Transaction must have at least one recipient + A transação deve ter ao menos um destinatário - Run a thread to flush wallet periodically (default: %u) - Executar periodicamente um processo para consolidar a carteira (omissão: %u) + Transaction too large + Transação muito grande - Send trace/debug info to debug.log file (default: %u) - Enviar informação de execução/depuração para o ficheiro debug.log (omissão: %u) + Trying to connect... + Tentando se conectar... - Send transactions as zero-fee transactions if possible (default: %u) - Enviar com taxa-zero se possível (ex: %u) + Unable to bind to %s on this computer. %s is probably already running. + Impossível vincular a %s neste computador. O %s provavelmente já está rodando. - Server certificate file (default: %s) - Ficheiro de certificado do servidor (omissão: %s) + Upgrading UTXO database + Atualizando banco de dados UTXO - Server private key (default: %s) - Chave privada do servidor (default: %s) + Use devnet chain with provided name + Use a cadeia devnet com o nome fornecido - Set external address:port to get to this masternode (example: %s) - Definir endereço:porta externo para ligação a este masternode (ex: %s) + Wallet debugging/testing options: + Opções de depuração/teste da Carteira - Set key pool size to <n> (default: %u) - Define o tamanho do conjunto de chaves para <n> (omissão: %u) + Wallet is not initialized + Carteira não inicializada - Set minimum block size in bytes (default: %u) - Define o tamanho máximo do bloco em bytes (omissão %u) + Wallet needed to be rewritten: restart %s to complete + A Carteira precisa ser reescrita: reinicie o %s para completar - Set the number of threads to service RPC calls (default: %d) - Define o número de threads que processam os pedidos RPC (omissão: %d) + Warning: unknown new rules activated (versionbit %i) + Alerta: regras novas desconhecidas ativadas (versionbit %i) - Sets the DB_PRIVATE flag in the wallet db environment (default: %u) - Definir a flag DB_PRIVATE no ambiente db da carteira (default: %u) + Wasn't able to create wallet backup folder %s! + Não foi possível criar a pasta para backup da carteira %s! - Signing timed out. - Assinatura fora do tempo. + Whether to operate in a blocks only mode (default: %u) + Se é para operar em modo somente blocos (padrão: %u) - Specify configuration file (default: %s) - Especificar ficheiro de configuração (default: %s) + You can not start a masternode in lite mode. + Você não pode iniciar um masternode no modo light. - Specify connection timeout in milliseconds (minimum: 1, default: %d) - Especificar timeout da conexão em milisegundos (minimum: 1, default: %d) + ZeroMQ notification options: + ZeroMQ notificação opções - Specify masternode configuration file (default: %s) - Especificar ficheiro de configuração masternode (default: %s) + no mixing available. + mixing não disponível. - Specify pid file (default: %s) - Especificar ficheiro pid (default: %s) + see debug.log for details. + Consulte debug.log para obter detalhes. - Spend unconfirmed change when sending transactions (default: %u) - Gastar troco não confirmado quando enviando transacções (default: %u) + RPC server options: + Opções do servidor RPC: - Stop running after importing blocks from disk (default: %u) - Para de correr após importação de blocos do disco (default: %u) + Dash Core + Dash Core - Submitted following entries to masternode: %u / %d - Apresentadas as seguintes entradas ao masternode: %u / %d + The %s developers + Desenvolvedores do %s - Submitted to masternode, waiting for more entries ( %u / %d ) %s - Apresentado ao masternode, esperando por mais entradas ( %u / %d ) %s + Accept connections from outside (default: 1 if no -proxy or -connect/-noconnect) + Aceite conexões de fora (padrão: 1 senão -proxy ou -connect/-noconnect) - Submitted to masternode, waiting in queue %s - Apresentado ao masternode, esperando na fila %s + Cannot obtain a lock on data directory %s. %s is probably already running. + Não foi possível obter exclusividade de escrita no endereço %s. O %s provavelmente já está sendo executado. - Synchronizing budgets... - A sincronizar orçamentos... + Connect only to the specified node(s); -noconnect or -connect=0 alone to disable automatic connections + Conecte apenas a nó(s) especificados; -noconnect ou -connect=0 somente para desabilitar conexões automáticas - Synchronizing masternode winners... - A sincronizar masternodes vencedores... + Distributed under the MIT software license, see the accompanying file %s or %s + Distribuído sob a licença de software MIT, veja o arquivo %s ou %s - Synchronizing masternodes... - A sincronizar masternodes... + Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + Erro ao ler arquivo %s! Todas as chaves privadas foram lidas corretamente, mas os dados de transação ou o livro de endereços podem estar faltando ou incorretos. - Synchronizing sporks... - A sincronizar sporks... + Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d) + Força a retransmissão de transações de pares da lista branca, mesmo quando violam a política local de retransmissão (default: %d) - This is not a Masternode. - Este não é um Masternode + Incorrect or no devnet genesis block found. Wrong datadir for devnet specified? + Bloco gênese incorreto ou não encontrado. Datadir errado para a devnet rede? - Threshold for disconnecting misbehaving peers (default: %u) - Limiar para desconectar pares mal comportados (default: %u) + Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds) + Ajuste da média máxima permitida do time offset para um par. A perspectiva local de horário pode ser influenciada por pares à frente ou atrás neste montante. (padrão: %u segundos) - Use KeePass 2 integration using KeePassHttp plugin (default: %u) - Utilize integração KeePass 2 usando plugin KeePassHttp (default: %u) + Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s) + Total máximo de taxas (em %s) para usar em uma única transação de carteira ou transação crua; especificar um valor muito baixo pode abortar grandes transações (padrão: %s) - Use N separate masternodes to anonymize funds (2-8, default: %u) - Utilize N masternode distintos para anonimizar fundos (2-8, default: %u) + Override spork address. Only useful for regtest and devnet. Using this on mainnet or testnet will ban you. + Substituir endereço de spork. Útil apenas para regtest e devnet. Usando isto na mainnet ou testnet irá banir você. - Use UPnP to map the listening port (default: %u) - Utilize UPNP para mapear porta de escuta (default: %u) + Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly. + Por favor verifique se a data e o horário de seu computador estão corretos. Se o relógio de seu computador estiver incorreto, %s não funcionará corretamente. - Wallet needed to be rewritten: restart Dash Core to complete - Carteira necessita de ser re-escrita: reinicie Dash Core para completar + Please contribute if you find %s useful. Visit %s for further information about the software. + Por favor contribua se você entender que %s é útil. Visite %s para mais informações sobre o software. - Warning: Unsupported argument -benchmark ignored, use -debug=bench. - Aviso: Argumento não suportado -benchmark ignorado, use -debug=bench. + 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) + Providencia liquidez ao EnvioPrivado através da mistura não frequente de moedas em uma base contínua (%u-%u, padrão: %u, 1=muito frequente, altas taxas, %u=muito infrequente, taxas baixas) - Warning: Unsupported argument -debugnet ignored, use -debug=net. - Aviso: Argumento não suportado debugnet ignorado, use -debug=net. + Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect/-noconnect) + Consulta por endereços de pares via pesquisa de DNS, se baixa nos endereços (default: 1 unless -connect/-noconnect) - Will retry... - Tentar de novo... + Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB) + Reduz o requerimente de espaço habiitando a poda (apagando) de blocos antigos. Isso permite chamar o comando pruneblockchain via RPC para apagar blocos específicos, e habiita o poda automático de blocos antigos se o tamanho em MiB for atingido. Esse modo é incompatível com -txindex e -rescan. Aviso: Reverter essa configuração requer baixar novamente o blockchain inteiro. (padrão: 0 = desabilitado, 1 = permite a poda manual via RPC, >%u = poda os blocos para ficar abaixo do especificado, em MiB) - Invalid masternodeprivkey. Please see documenation. - masternodeprivkey inválida. Por favor reveja a documentação. + Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) + Especificar a taxa miníma (em %s/KiB) para transações para ser incluída na criação de blocos. (padrão: %s) - (must be 9999 for mainnet) - (deve ser 9999 para mainnet) + This is the transaction fee you may pay when fee estimates are not available. + Esta é a taxa que você deve pagar quando a taxa estimada não está disponível. - Can't find random Masternode. - Incapaz de encontrar Masternode aleatório. + This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit %s and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard. + Esse produto inclui um software desenvolvido pelo OpenSSL Project para uso na OpenSSL Toolkit %s e software criptográfico escrito por Eric Young e software UPnP escrito por Thomas Bernard. - Can't mix while sync in progress. - Incapaz de misturar enquanto sincronização está a decorrer. + Unable to locate enough PrivateSend non-denominated funds for this transaction. + Não é possível localizar fundos não-expressos do PrivateSend suficientes para essa transação. - Could not parse masternode.conf - Incapaz de analizar masternode.conf + Use N separate masternodes for each denominated input to mix funds (%u-%u, default: %u) + Use N masternodes separados para cada entrada denominada para misturar fundos (%u-%u, padrão: %u) - Invalid netmask specified in -whitelist: '%s' - Netmask especificado invalido na -whitelist: '%s' + Use hierarchical deterministic key generation (HD) after BIP39/BIP44. Only has effect during wallet creation/first start + Use a geração hierárquica de chaves determinísticas (HD) após o BIP39 / BIP44. Só tem efeito durante a criação / início da carteira - Invalid port detected in masternode.conf - Port inválido detectado em masternode.conf + User defined mnemonic passphrase for HD wallet (BIP39). Only has effect during wallet creation/first start (default: empty string) + Senha mnemônica definida pelo usuário para carteira HD (BIP39). Só tem efeito durante a criação da carteira / primeira inicialização (padrão: string vazia) - Invalid private key. - Chave privada inválida. + Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times + Nome de usuário e hash da senha para conexões JSON-RPC. O campo <userpw> vem com o formato: <USERNAME>:<SALT>$<HASH>. Um script python canônico é incluído em share/rpcuser. O cliente pode conectar normalmente usando o rpcuser=<USERNAME>/rpcpassword=<PASSWORD>. Esta opção pode ser especificado multiplas vezes - Invalid script detected. - Script inválido detectado. + Wallet will not create transactions that violate mempool chain limits (default: %u) + A carteira não irá criar transações que violem o limite da mempool chain (padrão: %u) - KeePassHttp id for the established association - Identificador KeePassHttp para a associação estabelecida + Warning: At least %d of %d masternodes are running on a newer software version. Please check latest releases, you might need to update too. + Aviso: Pelo menos %d de %d masternodes estão sendo executados em uma versão de software mais recente. Por favor, verifique os últimos lançamentos, talvez você precise atualizar também. - KeePassHttp key for AES encrypted communication with KeePass - Chave KeePassHttp para usar na comunicação cifrada AES com o KeePass + Warning: Every masternode (out of %d known ones) is running on a newer software version. Please check latest releases, it's very likely that you missed a major/critical update. + Aviso: Cada masternode (fora de %d conhecidos) está sendo executado em uma versão de software mais recente. Por favor, verifique os últimos lançamentos, é muito provável que você tenha perdido uma atualização importante / crítica. - Keep N DASH anonymized (default: %u) - Manter N DASH anonimizados (default: %u) + Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup. + Atenção: Arquivo da carteira corrompido, dados recuperados! Original %s salvo como %s em %s; se seu saldo ou transações estiverem incorretos, você deve restaurar o backup. - Keep at most <n> unconnectable transactions in memory (default: %u) - Manter no máximo <n> transacções não conectáveis em memória (omissão: %u) + Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times. + Inclui na lista branca pares conectando do dado endereço IP (ex: 1.2.3.4) ou rede em notação CIDR (ex: 1.2.3.0/24). Pode ser especificado várias vezes. - Last Darksend was too recent. - O último Darksend é muito recente. + %s corrupt, salvage failed + %s corrompido, recuperação falhou - Line: %d - Linha: %d + %s is not a valid backup folder! + %s não é uma pasta de backup válida! - Loading addresses... - A carregar endereços... + %s is set very high! + %s está muito alto! - Loading block index... - A carregar índice de blocos... + (press q to shutdown and continue later) + (pressione q para desligar e continuar depois) - Loading wallet... (%3.2f %%) - A carregar carteira... (%3.2f %%) + -devnet can only be specified once + -devnet só pode ser especificado uma vez - Loading wallet... - A carregar carteira... + -port must be specified when -devnet and -listen are specified + -rpcport deve ser especificado quando -devnet e -server são especificados - Masternode options: - Opções de masternode: + -rpcport must be specified when -devnet and -server are specified + -rpcport deve ser especificado quando -devnet e -server são especificados - Masternode queue is full. - A fila do masternode está cheia. + Allow RFC1918 addresses to be relayed and connected to (default: %u) + Permitir que os endereços RFC1918 sejam retransmitidos e conectados a (padrão: %u) - Masternode: - Masternode: + Attempt to recover private keys from a corrupt wallet on startup + Tenta recuperar as chaves privadas de uma carteira corrompida ao inicializar - Missing input transaction information. - A informação da transacção de entrada não foi encontrada. + Cannot resolve -%s address: '%s' + Não foi possível encontrar o endereço de -%s: '%s' - No funds detected in need of denominating. - Não foram detectados fundos a necessitar de denominação. + Change index out of range + Índice de mudança fora do intervalo - No matching denominations found for mixing. - Não foram encontradas denominações correspondentes para misturar. + Copyright (C) + Copyright - Node relay options: - Opções de relé do nódulo: + Error loading %s + Erro ao carregar %s - Non-standard public key detected. - Detectada chave pública fora do padrão. + Error loading %s: Wallet corrupted + Erro ao carregar %s Carteira corrompida - Not compatible with existing transactions. - Não é compatível com as transacções existentes. + Error loading %s: Wallet requires newer version of %s + Erro ao carregar %s A carteira requer a versão mais nova do %s - Not enough file descriptors available. - Número insuficiente de descritores de ficheiros disponíveis. + Error upgrading chainstate database + Erro ao atualizar banco de dados do chainstate - Options: - Opções: + Failed to find mixing queue to join + Não foi possível encontrar a fila de mistura para participar - Password for JSON-RPC connections - Palavra-passe para as ligações JSON-RPC + Failed to start a new mixing queue + Falha ao iniciar uma nova fila de mistura - RPC SSL options: (see the Bitcoin Wiki for SSL setup instructions) - Opções RPC SSL: (para instruções de configuração SSL dirija-se à Dash Wiki) + Initialization sanity check failed. %s is shutting down. + O teste de integridade de inicialização falhou. O %s está sendo desligado. - RPC server options: - Opções do servidor RPC: + Invalid amount for -%s=<amount>: '%s' + Quantidade inválida para -%s=<amount>: '%s' - RPC support for HTTP persistent connections (default: %d) - Suporte RPC para conexões persistentes HTTP (default: %d) + Invalid spork address specified with -sporkaddr + Endereço de spork inválido especificado com -sporkaddr - Randomly drop 1 of every <n> network messages - Ignorar aleatoriamente 1 de cada <n> mensagens da rede + Keep N DASH anonymized (%u-%u, default: %u) + Mantenha o N DASH anônimo (%u-%u, padrão: %u) - Randomly fuzz 1 of every <n> network messages - Esfiapar aleatoriamente 1 de cada <n> mensagens da rede + Print this help message and exit + Mostra essa mensagem de ajuda e sai - Rebuild block chain index from current blk000??.dat files - Reconstruir o índice da cadeia de blocos a partir dos ficheiros actuais blk000??.dat + Receive and display P2P network alerts (default: %u) + Receba e exiba alertas de rede P2P (default: %u) - Receive and display P2P network alerts (default: %u) - Receber e apresentar alertas da rede P2P (default: %u) + Reducing -maxconnections from %d to %d, because of system limitations. + Reduzindo -maxconnections de %d para %d, devido à limitações do sistema. Relay and mine data carrier transactions (default: %u) - Relé e mine data de transacções de suporte (default: %u) + Transações de dados de operadora (padrão: %u) Relay non-P2SH multisig (default: %u) - Relé não-P2SH multisig (default: %u) - - - Rescan the block chain for missing wallet transactions - Examinar novamente a cadeia de blocos para encontrar transacções inexistentes na carteira + Retransmitir P2SH não multisig (padrão: %u) Rescanning... - A examinar novamente... + Escaneando... Run in the background as a daemon and accept commands - Executar em segundo plano como um serviço e aceitar comandos + Executar em segundo plano como um daemon e aceitar comandos + + + Send trace/debug info to console instead of debug.log file + Envia informação de trace/debug para o console e não para o arquivo debug.log Session not complete! - Sessão incompleta! + Sessão não completa! Session timed out. - Esgotado o tempo de sessão. + A sessão expirou. Set database cache size in megabytes (%d to %d, default: %d) - Define o tamanho máximo da cache de dados em megabytes (%d até %d, omissão %d) + Define o tamanho da cache em megabytes (%d a %d, padrão: %d) Set maximum block size in bytes (default: %d) - Define o tamanho máximo do bloco em bytes (omissão %d) + Definir tamanho máximo do bloco em bytes (padrão: %d) Set the masternode private key - Define a chave privada do masternode + Definir a chave privada do masternode Show all debugging options (usage: --help -help-debug) - Mostrar todas as opções de depuração (uso: --help -help-debug) + Exibe todas as opçoes de debug (uso: --help -help-debug) Shrink debug.log file on client startup (default: 1 when no -debug) - Diminuir o ficheiro debug.log ao iniciar o cliente (omissão: 1 quando não usa opção -debug) - - - Signing failed. - A assinatura falhou. + Compactar arquivo debug.log ao executar o cliente (padrão: 1 sem -debug) Signing transaction failed - A assinatura da transacção falhou + A transação assinada falhou Specify data directory - Especificar a pasta de dados + Especificar diretório de dados Specify wallet file (within data directory) - Especificar o ficheiro da carteira (dentro da pasta de dados) + Especifique o arquivo da carteira (dentro do diretório de dados) Specify your own public address - Especificar o seu endereço público + Especificar seu próprio endereço público - This help message - Esta mensagem de ajuda + Starting network threads... + Iniciando threads de rede... - This is experimental software. - Este é um software experimental. + Synchronization pending... + Sincronização pendente - This is intended for regression testing tools and app development. - Esta opção destina-se a ferramentas de testes de regressão e desenvolvimento de aplicativos. + Synchronizing governance objects... + Sincronizando objetos de governança .... - Transaction amount too small - Quantia da transacção demasiado pequena + Synchronizing masternode payments... + Sincronizando pagamentos do masternode... - Transaction amounts must be positive - As quantias da transacção tem que ser positivas + The source code is available from %s. + O código fonte está disponível pelo %s. - Transaction created successfully. - Transacção criada com sucesso. + The transaction amount is too small to pay the fee + O valor da transação é muito pequeno para pagar a taxa - Transaction fees are too high. - As taxas da transacção são demasiado elevadas. + This is experimental software. + Este é um software experimental. - Transaction not valid. - A transacção não é válida. + Transaction amount too small + Quantia da transação muito pequena - Transaction too large for fee policy - Transacção demasiado grande para a política de taxas + Transaction created successfully. + Transação criada com sucesso. - Transaction too large - Transacção demasiado grande + Transaction fees are too high. + Taxa de transação muito alta. - Transmitting final transaction. - Transmitindo transacção final + Transaction not valid. + Transação inválida. + + + Transaction too large for fee policy + Transação muito grande para a política de tarifas Unable to bind to %s on this computer (bind returned error %s) - Não foi possível vincular a %s neste computador (o vínculo retornou o erro %s) + Impossível abrir %s neste computador (bind retornou o erro %s) Unable to sign spork message, wrong key? - Não foi possível assinar a mensagem spork, chave incorrecta? + Impossível assinar mensagem spork, chave incorreta? Unknown network specified in -onlynet: '%s' - Rede especificada desconhecida em -onlynet: '%s' + Rede desconhecida fornecida em -onlynet: '%s' Unknown state: id = %u Estado desconhecido: id = %u - - Upgrade wallet to latest format - Actualizar carteira para o formato mais recente - - - Use OpenSSL (https) for JSON-RPC connections - Usar OpenSSl (https) para as ligações JSON-RPC - - - Use UPnP to map the listening port (default: 1 when listening) - Usar UPnP para mapear a porta de entrada (omissão: 1 quando em escuta) - - - Use the test network - Usar a rede de testes - Username for JSON-RPC connections - Utilizador para as ligações JSON-RPC - - - Value more than Darksend pool maximum allows. - Valor mais elevado do que o máximo permitido pelo Darksend. + Nome de usuario para conexão JSON-RPC Verifying blocks... - A verificar blocos... + Verificando blocos... Verifying wallet... - A verificar carteira... + Verificando carteira... + + + Very low number of keys left: %d + Número muito baixo de chaves restantes: %d Wallet %s resides outside data directory %s - A carteira %s encontra-se fora da pasta de dados %s + Carteira %s está fora do diretório de dados %s Wallet is locked. - A carteira encontra-se bloqueada. + Carteira está bloqueada. Wallet options: @@ -4385,39 +5307,23 @@ https://www.transifex.com/projects/p/dash/ Wallet window title - Título da janela + Título da janela da carteira Warning - Aviso - - - Warning: This version is obsolete, upgrade required! - Aviso: Esta versão é obsoleta, actualização necessária! - - - You need to rebuild the database using -reindex to change -txindex - Necessita reconstruir a base de dados usando -reindex para alterar -txindex + Atenção Your entries added successfully. - Suas entradas adicionadas com sucesso. + Suas entradas foram adicionadas com êxito. Your transaction was accepted into the pool! - A sua transacção foi aceite na pool! + Sua transação foi aceita na pool! Zapping all transactions from wallet... - A apagar todas as transacções da carteira... - - - on startup - no arranque - - - wallet.dat corrupt, salvage failed - wallet.dat corrompido, recuperação falhou + Fazendo o Zapping de todas as transações da wallet... \ No newline at end of file diff --git a/src/qt/locale/dash_ru.ts b/src/qt/locale/dash_ru.ts index 61dad82d9d1b..120b9e299ebd 100644 --- a/src/qt/locale/dash_ru.ts +++ b/src/qt/locale/dash_ru.ts @@ -383,7 +383,7 @@ &Verify message... - П&роверить сообщение... + &Проверить сообщение... Verify messages to ensure they were signed with specified Dash addresses @@ -794,8 +794,8 @@ Пожалуйста, переключитесь в режим списка для использования этой функции. - Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again. - Выбраны неанонимизированные средства. <b>PrivateSend будет отключен.</b><br><br>Если Вы все-таки хотите использовать PrivateSend, пожалуйста, снимите выделение с со всех неанонимизированных средств и заново поставьте галочку напротив PrivateSend. + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. + Выбраны неанонимизированные средства. <b>PrivateSend будет отключен.</b><br><br>Если Вы все-таки хотите использовать PrivateSend, пожалуйста, снимите выделение со всех неанонимизированных средств и заново поставьте галочку напротив PrivateSend. (%1 locked) @@ -834,73 +834,6 @@ н/д - - DarksendConfig - - Configure PrivateSend - Настройте PrivateSend - - - Basic Privacy - Обычная приватность - - - High Privacy - Высокая приватность - - - Maximum Privacy - Максимальная приватность - - - Please select a privacy level. - Пожалуйста, выберите уровень приватности. - - - Use 2 separate masternodes to mix funds up to 1000 DASH - Использовать 2 отдельные мастерноды для перемешивания до 1000 DASH - - - Use 8 separate masternodes to mix funds up to 1000 DASH - Использовать 8 отдельных мастернод для перемешивания до 1000 DASH - - - Use 16 separate masternodes - Использовать 16 отдельных мастернод - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - Это самый быстрый вариант, анонимизация 1000 DASH будет стоить вам примерно 0.025 DASH - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - Это относительно быстрый вариант, анонимизация 1000 DASH будет стоить вам примерно 0.05 DASH - - - 0.1 DASH per 1000 DASH you anonymize. - 0.1 DASH за каждые анонимизированные 1000 DASH. - - - This is the slowest and most secure option. Using maximum anonymity will cost - Этот самый медленный и безопасный вариант. Анонимизация будет стоить - - - PrivateSend Configuration - Настройки PrivateSend - - - PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend успешно установлен в режим базовой приватности (%1 и 2 раунда). Вы можете изменить это в любое время, используя окно настроек. - - - PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend успешно установлен в режим высокой приватности (%1 и 8 раундов). Вы можете изменить это в любое время, используя окно настроек. - - - PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend успешно установлен в режим максимальной приватности (%1 и 16 раундов). Вы можете изменить это в любое время, используя окно настроек. - - EditAddressDialog @@ -1038,8 +971,8 @@ Информация о PrivateSend - <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more info see <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> - <h3>Основы PrivateSend</h3>PrivateSend позволяет Вам получить настоящую финансовую конфиденциальность за счет скрытия источников Ваших средств. Все Dash в Вашем кошельке состоят из различных ""входов", Вы можете думать о них как об отдельных монетах.<br>PrivateSend использует инновационный процесс для перемешивания Ваших входов со входами еще двоих человек, но при этом Ваши монеты никогда не покидают Ваш кошелек. Вы сохраняете контроль над Вашими деньгами на протяжении всего времени.<hr> <b>PrivateSend работает так:</b><ol type="1"> <li>PrivateSend начинается с разбиения Ваших входов транзакций на стандартные номиналы. Такими номиналами являются 0.01 DASH, 0.1 DASH, 1 DASH и 10 DASH -- что-то вроде купюр, которыми вы пользуетесь каждый день.</li> <li>Ваш кошелек затем отправляет запросы к особым образом настроенным сетевым узлам, называемым "мастернодами". Эти мастерноды знают только то, что Вы хотите перемешать определенные номиналы. Никакой идентифицирующей информации мастернодам не отправляется, так что они не знают "кто" Вы конкретно.</li> <li>Сессия перемешивания начинается тогда, когда еще двое человек отправляют схожее сообщение, подтверждающее, что они хотят перемешать точно такой же номинал. Мастернода перемешивает входы и просит кошельки всех пользователей осуществить платежи по ним. Ваш кошелек осуществляет выплату самому себе, но на другой адрес (тоже из вашего кошелька).</li> <li>Для скрытия Ваших средств кошелек должен повторить этот процесс несколько раз с каждым номиналом. Каждый раз, когда такой процесс завершается, называется "раундом". С каждым раундом PrivateSend становится экспоненциально сложнее определить откуда поступили средства.</li> <li>Процесс перемешивания выполняется в фоне, без участия пользователя. Когда Вы захотите провести транзакцию, средства уже будут анонимизированы. Дополнительно ждать не требуется.</li> </ol> <hr><b>ВАЖНО:</b> Ваш кошелек содержит 1000 адресов. Каждое перемешивание использует до 9 из этих адресов. Это означает, что 1000 адресов хватит примерно на 100 перемешиваний. Когда 900 адресов будут уже использованы, Ваш кошелек должен создать новые адреса. Однако, он может сделать это, только если у Вас включены автоматические резервные копии.<br>Соответственно, пользователям с отключенным резервным копированием PrivateSend не доступен. <hr>Дополнительная информация доступна по ссылке <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. + <h3>Основы PrivateSend</h3>PrivateSend позволяет Вам получить настоящую финансовую конфиденциальность за счет скрытия источников Ваших средств. Все Dash в Вашем кошельке состоят из различных "входов", Вы можете думать о них как об отдельных монетах.<br>PrivateSend использует инновационный процесс для перемешивания Ваших входов со входами еще двоих человек, но при этом Ваши монеты никогда не покидают Ваш кошелек. Вы сохраняете контроль над Вашими деньгами на протяжении всего времени.<hr> <b>PrivateSend работает так:</b><ol type="1"> <li>PrivateSend начинается с разбиения Ваших входов транзакций на стандартные номиналы. Такими номиналами являются 0.01 DASH, 0.1 DASH, 1 DASH и 10 DASH -- что-то вроде купюр, которыми вы пользуетесь каждый день.</li> <li>Ваш кошелек затем отправляет запросы к особым образом настроенным сетевым узлам, называемым "мастернодами". Эти мастерноды знают только то, что Вы хотите перемешать определенные номиналы. Никакой идентифицирующей информации мастернодам не отправляется, так что они не знают "кто" Вы конкретно.</li> <li>Сессия перемешивания начинается тогда, когда еще двое человек отправляют схожее сообщение, подтверждающее, что они хотят перемешать точно такой же номинал. Мастернода перемешивает входы и просит кошельки всех пользователей осуществить платежи по ним. Ваш кошелек осуществляет выплату самому себе, но на другой адрес (тоже из вашего кошелька).</li> <li>Для скрытия Ваших средств кошелек должен повторить этот процесс несколько раз с каждым номиналом. Каждый раз, когда такой процесс завершается, называется "раундом". С каждым раундом PrivateSend становится экспоненциально сложнее определить откуда поступили средства.</li> <li>Процесс перемешивания выполняется в фоне, без участия пользователя. Когда Вы захотите провести транзакцию, средства уже будут анонимизированы. Дополнительно ждать не требуется.</li> </ol> <hr><b>ВАЖНО:</b> Ваш кошелек содержит 1000 адресов. Каждое перемешивание использует до 9 из этих адресов. Это означает, что 1000 адресов хватит примерно на 100 перемешиваний. Когда 900 адресов будут уже использованы, Ваш кошелек должен создать новые адреса. Однако, он может сделать это, только если у Вас включены автоматические резервные копии.<br>Соответственно, пользователям с отключенным резервным копированием PrivateSend не доступен. <hr>Дополнительная информация доступна в <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">документации по PrivateSend</a> @@ -1097,7 +1030,7 @@ Note: Status of your masternodes in local wallet can potentially be slightly incorrect.<br />Always wait for wallet to sync additional data and then double check from another node<br />if your masternode should be running but you still do not see "ENABLED" in "Status" field. - Примечание: Статус Ваших мастернод в локальном кошельке может быть не совсем корректным.<br />Всегда дожидайтесь окончания синхронизации дополнительных данных и перепроверяйте с другой ноды,<br />если Ваша ьастернода должна быть активна, но Вы ее статус все равно не отображается как "ENABLED". + Примечание: Статус Ваших мастернод в локальном кошельке может быть не совсем корректным.<br />Всегда дожидайтесь окончания синхронизации дополнительных данных и перепроверяйте с другой ноды,<br />если Ваша мастернода должна быть активна, но ее статус все равно не отображается как "ENABLED". Alias @@ -4158,11 +4091,11 @@ https://www.transifex.com/projects/p/dash/ Failed to load masternode cache from - Ошибка загрузка кэша мастернод из + Ошибка загрузки кэша мастернод из Failed to load masternode payments cache from - Ошибка загрузка кэша выплат мастернодам из + Ошибка загрузки кэша выплат мастернодам из Found enough users, signing ( waiting %s ) @@ -4480,6 +4413,10 @@ https://www.transifex.com/projects/p/dash/ Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) Отключить всю функциональность, относящуюся к Dash (Мастерноды, PrivateSend, InstantSend, Governance) (0-1, по умолчанию: %u) + + %s file contains all private keys from this wallet. Do not share it with anyone! + %s файл содержит в себе все закрытые ключи для этого кошелька. Никому его не показывайте! + Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) Определить свой IP (по умолчанию: 1 при прослушивании и если не используется -externalip или -proxy) @@ -4504,6 +4441,10 @@ https://www.transifex.com/projects/p/dash/ Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID) Выполнить команду, когда транзакция InstantSend успешно заблокирована (%s в команде заменяется на TxID) + + Extra transactions to keep in memory for compact block reconstructions (default: %u) + Количество дополнительных транзакций, которые нужно держать в памяти для реконструкции компактных блоков (по умолчанию: %u) + Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. Не удалось создать резервную копию, т.к. файл уже существует! Такое могло случится, если вы перезапустили кошелек менее чем через 60 секунд после предыдущего запуска. Вы можете продолжить, если это допустимо. @@ -4584,6 +4525,10 @@ https://www.transifex.com/projects/p/dash/ Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. Длина строки сетевой версии (%i) превышает максимально допустимую (%i). Уменьшите количество или размер строк uacomment. + + Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index. + Индексирование транзакций нельзя отключить в полном режиме. Либо запустите кошелек с опцией -litemode, либо включите индексирование транзакций. + Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) Пытаться ограничить исходящий трафик в пределах установленного лимита (в МБ за 24ч), 0 = не ограничивать (по умолчанию: %d) @@ -4616,6 +4561,10 @@ https://www.transifex.com/projects/p/dash/ Warning: Unknown block versions being mined! It's possible unknown rules are in effect Внимание: создаются блоки неизвестной версии! Возможно активированы неизвестные правила + + You are starting in lite mode, all Dash-specific functionality is disabled. + Вы запускаете кошелек в облегченном режиме, все специфичные для Dash функции отключены. + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain Вам необходимо пересоздать базы данных, запустив клиент с ключом -reindex, чтобы вернуться в полный режим. Это приведет к повторному скачиванию всей цепи блоков. @@ -4660,6 +4609,14 @@ https://www.transifex.com/projects/p/dash/ Enable publish hash block in <address> Включить публикацию хэша блока на <address> + + Enable publish hash of governance objects (like proposals) in <address> + Включить публикацию хэша объектов управления (таких как бюджетные предложения) на <address> + + + Enable publish hash of governance votes in <address> + Включить публикацию хэша голосов за объекты управления на <address> + Enable publish hash transaction (locked via InstantSend) in <address> Включить публикацию хэша транзакции (заблокированной через InstantSend) на <address> @@ -4700,6 +4657,14 @@ https://www.transifex.com/projects/p/dash/ Failed to delete backup, error: %s Не удалось удалить резервную копию, ошибка: %s + + Failed to load InstantSend data cache from + Ошибка загрузки кэша InstantSend из + + + Failed to load sporks cache from + Ошибка загрузки кэша спорков из + Failed to parse host:port string Не удалось разбить строку host:port @@ -4720,6 +4685,10 @@ https://www.transifex.com/projects/p/dash/ Invalid amount for -fallbackfee=<amount>: '%s' Неверная сумма в параметре -fallbackfee=<amount>: '%s' + + Invalid input count. + Неверная количество входов. + Keep the transaction memory pool below <n> megabytes (default: %u) Держать размер пула памяти транзакций ниже <n> мегабайт (по умолчанию: %u) @@ -4736,6 +4705,10 @@ https://www.transifex.com/projects/p/dash/ Line: %d Строка: %d + + Loading InstantSend data cache... + Загрузка кэша InstantSend... + Loading addresses... Загрузка адресов... @@ -4748,6 +4721,10 @@ https://www.transifex.com/projects/p/dash/ Loading governance cache... Загрузка кэша управления... + + Loading sporks cache... + Загрузка кэша спорков... + Loading wallet... (%3.2f %%) Загрузка кошелька... (%3.2f %%) @@ -4888,6 +4865,10 @@ https://www.transifex.com/projects/p/dash/ Transaction must have at least one recipient У транзакции должен быть хотя бы один получатель + + Transaction too large + Транзакция слишком большая + Trying to connect... Попытка соединения... @@ -4908,6 +4889,10 @@ https://www.transifex.com/projects/p/dash/ Wallet debugging/testing options: Параметры отладки/тестирования кошелька: + + Wallet is not initialized + Кошелек не инициализирован + Wallet needed to be rewritten: restart %s to complete Необходимо перезаписать кошелёк: перезапустите %s для завершения операции @@ -4924,6 +4909,10 @@ https://www.transifex.com/projects/p/dash/ Whether to operate in a blocks only mode (default: %u) Работать в режиме обработки только блоков (по умолчанию: %u) + + You can not start a masternode in lite mode. + Вы не можете запустить мастерноду в облегченном режиме. + ZeroMQ notification options: Параметры оповещения ZeroMQ: @@ -5012,10 +5001,6 @@ https://www.transifex.com/projects/p/dash/ Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) Установить минимальную комиссию (в %s/kB) для добавления транзакций в блок (по умолчанию: %s) - - Show N confirmations for a successfully locked transaction (%u-%u, default: %u) - Показывать N подтверждений для успешно заблокированной транзакции (%u-%u, по умолчанию: %u) - This is the transaction fee you may pay when fee estimates are not available. Это комиссия, которую Вы заплатите при отправке транзакции, если расчет комиссии не доступен. diff --git a/src/qt/locale/dash_sk.ts b/src/qt/locale/dash_sk.ts index 91066589f143..7dcea13cb3c0 100644 --- a/src/qt/locale/dash_sk.ts +++ b/src/qt/locale/dash_sk.ts @@ -519,7 +519,7 @@ %n active connection(s) to Dash network - %n aktívne spojenie so sieťou Dash%n aktívne spojenia so sieťou Dash%n aktívnych spojení so sieťou Dash + %n aktívne spojenie so sieťou Dash%n aktívne spojenia so sieťou Dash%n aktívnych spojení so sieťou Dash%n aktívnych spojení so sieťou Dash Network activity disabled @@ -551,7 +551,7 @@ Processed %n block(s) of transaction history. - Spracovaný jeden blok transakčnej histórie.Spracované %n bloky transakčnej histórie.Spracovaných %n blokov transakčnej histórie. + Spracovaný jeden blok transakčnej histórie.Spracované %n bloky transakčnej histórie.Spracovaných %n blokov transakčnej histórie.Spracovaných %n blokov transakčnej histórie. %1 behind @@ -794,7 +794,7 @@ Pre použitie tejto funkcie prepnite na "Zoznamový mód". - Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again. + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. Vybratý ne-anonymizovaný vstup. <b>PrivateSend bude vypnutý.</b><br><br>Ak si stále želáte použiť PrivateSend, najskôr odznačte všetky ne-anonymizované vstupy a potom znova zaškrtnite PrivateSend políčko. @@ -834,73 +834,6 @@ nie je k dispozícii - - DarksendConfig - - Configure PrivateSend - Nastaviť PrivateSend - - - Basic Privacy - Základné súkromie - - - High Privacy - Vysoké súkromie - - - Maximum Privacy - Maximálne súkromie - - - Please select a privacy level. - Prosím vyberte úroveň súkromia. - - - Use 2 separate masternodes to mix funds up to 1000 DASH - Použiť 2 rozdielne masternode pre fixovanie prostriedkov až do 1000 DASH - - - Use 8 separate masternodes to mix funds up to 1000 DASH - Použiť 8 rozdielnych masternode pre fixovanie prostriedkov až do 1000 DASH - - - Use 16 separate masternodes - Použiť 16 rozdielnych masternode - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - Táto možnosť je najrýchlejšia a bude stáť približne 0.025 DASH pre anonimizáciu 1000 DASH - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - Táto možnosť je stredne rýchla bude stáť približne 0.05 DASH pre anonimizáciu 1000 DASH - - - 0.1 DASH per 1000 DASH you anonymize. - 0.1 DASH za 1000 DASH ktoré spravíte anonymnými. - - - This is the slowest and most secure option. Using maximum anonymity will cost - Toto je najpomalšia a najviac bezpečná možnosť. Použitie maximálnej anonymity bude stáť - - - PrivateSend Configuration - Nastavenie PrivateSend - - - PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - Darksend bol úspešne nastavený na základnú hodnotu (%1 a 2 kolá). Toto nastavenie môžete kedykoľvek zmeniť otvorením konfiguračnej obrazovky Dash. - - - PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend bol úspešne nastavený na základnú hodnotu (%1 a 8 kôl). Toto nastavenie môžete kedykoľvek zmeniť otvorením konfiguračnej obrazovky Dash. - - - PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend bol úspešne nastavený na maximálnu hodnotu (%1 a 16 kôl). Toto nastavenie môžete kedykoľvek zmeniť otvorením konfiguračnej obrazovky Dash. - - EditAddressDialog @@ -1038,8 +971,8 @@ Informácie o PrivateSend - <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more info see <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> - <h3>Základy PrivateSend</h3> PrivateSend Vám poskytuje skutočné finančné súkromie skrývaním pôvodu Vašich zdrojov. Všetky Dash vo vašej peňaženke pozostávajú z rôznych "vstupov", ktoré si môžete predstaviť ako rozdelené, which you can think of as separate, diskrétne mince.<br> PrivateSend používa zdokonalený proces pre miešanie Vašich vstupov so vstupmi iných ľudí bez toho. aby Vaše mince museli opustiť Vašu peňaženku. Počas cele doby máte kontrolu nad Vašimi peniazmi.<hr> <b>Proces PrivateSend funguje nasledovne:</b><ol type="1"> <li>PrivateSend začne rozdelením Vašich transakčných vstupov na štandardné časti, takzvané denominácie. Tieto denominácie sú 0.01 DASH, 0.1 DASH, 1 DASH a 10 DASH -- v podstate niečo ako papierové peniaze, ktoré používate každý deň.</li> <li>Vaša peňaženka potom pošle požiadavok špeciálne nastavenému softvérovému uzlu nazvaného "Masternode". Tieto Masternode sú potom informované, že máte záujem miešať určité denominácie. Masternode neobdrží žiadne informácie ktoré by Vás identifikovali, takže niekdy nevedia kto ste.</li> <li>Keď ďalší dvaja ľudia pošlú podobnú správu hovoriacu o tom že chcú miešať rovnakú denomináciu, začne sa samotné miešanie. Masternode zamieša vstupy a povie peňaženkám všetkých troch používateľov, aby zaplatili teraz už zmiešané vstupy sami sebe. Vaša peňaženka zaplatí tieto denominácie priamo sebe, ale už na inú adresu (nazývanú "meniaca adresa").</li> <li>Aby boli Vaše zdroje plne zakryté, Vaša peňaženka musí zopakovať tento proces niekoľko krát s každou denomináciou. Vždy keď je tento proces dokončený, je nazvaný "kolo". Každé kolo PrivateSend exponenciálne sťažuje určiť odkiaľ pochádzajú Vaše zdroje.</li> <li>Toto miešanie sa deje na pozadí bez nutnosti zásahov z Vašej strany. Keď si prajete uskutočniť transakciu, Vaše zdroje budú už anonýmne. Nie je nutné na nič čakať.</li> </ol> DÔLEŽITÉ:</b> Vaša peňaženka obsahuje iba 1000 týchto "meniacich adries". Vždy keď prebehne miešanie, použije sa maximálne až 9 Vašich adries. To znamená, že týchto 1000 adries vystačí zhruba na 100 miešaní. Keď sa použije 900 adries, Vaša peňaženka musí vytvoriť viac adries. Toto je však možné iba vtedy, keď máte zapnuté automatické zálohovanie.<br> V dôsledku toho, používatelia ktorí majú zálohovanie vypnuté, budú mať vypnutý aj PrivateSend. <hr>Viac informácií nájdete na <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. + <h3>Základy PrivateSend</h3> PrivateSend Vám poskytuje skutočné finančné súkromie skrývaním pôvodu Vašich zdrojov. Všetky Dash vo vašej peňaženke pozostávajú z rôznych "vstupov", ktoré si môžete predstaviť ako rozdelené, which you can think of as separate, diskrétne mince.<br> PrivateSend používa zdokonalený proces pre miešanie Vašich vstupov so vstupmi iných ľudí bez toho. aby Vaše mince museli opustiť Vašu peňaženku. Počas cele doby máte kontrolu nad Vašimi peniazmi.<hr> <b>Proces PrivateSend funguje nasledovne:</b><ol type="1"> <li>PrivateSend začne rozdelením Vašich transakčných vstupov na štandardné časti, takzvané denominácie. Tieto denominácie sú 0.01 DASH, 0.1 DASH, 1 DASH a 10 DASH -- v podstate niečo ako papierové peniaze, ktoré používate každý deň.</li> <li>Vaša peňaženka potom pošle požiadavok špeciálne nastavenému softvérovému uzlu nazvaného "Masternode". Tieto Masternode sú potom informované, že máte záujem miešať určité denominácie. Masternode neobdrží žiadne informácie ktoré by Vás identifikovali, takže niekdy nevedia kto ste.</li> <li>Keď ďalší dvaja ľudia pošlú podobnú správu hovoriacu o tom že chcú miešať rovnakú denomináciu, začne sa samotné miešanie. Masternode zamieša vstupy a povie peňaženkám všetkých troch používateľov, aby zaplatili teraz už zmiešané vstupy sami sebe. Vaša peňaženka zaplatí tieto denominácie priamo sebe, ale už na inú adresu (nazývanú "meniaca adresa").</li> <li>Aby boli Vaše zdroje plne zakryté, Vaša peňaženka musí zopakovať tento proces niekoľko krát s každou denomináciou. Vždy keď je tento proces dokončený, je nazvaný "kolo". Každé kolo PrivateSend exponenciálne sťažuje určiť odkiaľ pochádzajú Vaše zdroje.</li> <li>Toto miešanie sa deje na pozadí bez nutnosti zásahov z Vašej strany. Keď si prajete uskutočniť transakciu, Vaše zdroje budú už anonýmne. Nie je nutné na nič čakať.</li> </ol> <hr> <b>DÔLEŽITÉ:</b> Vaša peňaženka obsahuje iba 1000 týchto "meniacich adries". Vždy keď prebehne miešanie, použije sa maximálne až 9 Vašich adries. To znamená, že týchto 1000 adries vystačí zhruba na 100 miešaní. Keď sa použije 900 adries, Vaša peňaženka musí vytvoriť viac adries. Toto je však možné iba vtedy, keď máte zapnuté automatické zálohovanie.<br> V dôsledku toho, používatelia ktorí majú zálohovanie vypnuté, budú mať vypnutý aj PrivateSend. <hr><a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">Viac informácií nájdete v dokumentácii PrivateSend</a>. @@ -1783,7 +1716,7 @@ https://www.transifex.com/projects/p/dash/ %n Rounds - %n kolo%n kolá%n kôl + %n kolo%n kolá%n kôl%n kôl Found enough compatible inputs to anonymize %1 @@ -1811,7 +1744,7 @@ https://www.transifex.com/projects/p/dash/ Denominated inputs have %5 of %n rounds on average - Denominované vstup má v priemere %5 z %n kôlDenominované vstupy majú v priemere %5 z %n kôlDenominované vstupy majú v priemere %5 z %n kôl + Denominované vstup má v priemere %5 z %n kôlDenominované vstupy majú v priemere %5 z %n kôlDenominované vstupy majú v priemere %5 z %n kôlDenominované vstupy majú v priemere %5 z %n kôl keys left: %1 @@ -2047,27 +1980,27 @@ https://www.transifex.com/projects/p/dash/ %n second(s) - %n sekunda%n sekundy%n sekúnd + %n sekunda%n sekundy%n sekúnd%n sekúnd %n minute(s) - %n minúta%n minúty%n minút + %n minúta%n minúty%n minút%n minút %n hour(s) - %n hodina%n hodiny%n hodín + %n hodina%n hodiny%n hodín%n hodín %n day(s) - %n deň%n dni%n dní + %n deň%n dni%n dní%n dní %n week(s) - %n týždeň%n týždne%n týždňov + %n týždeň%n týždne%n týždňov%n týždňov %n year(s) - %n rok%n roky%n rokov + %n rok%n roky%n rokov%n rokov %1 and %2 @@ -2222,7 +2155,7 @@ https://www.transifex.com/projects/p/dash/ Current number of transactions - Aktuálny počet tranzakcií + Aktuálny počet transakcií Memory usage @@ -2366,7 +2299,7 @@ https://www.transifex.com/projects/p/dash/ Rescan blockchain files - Znova prehľadať reťazec blokov + Znova prehľadať blockchain Recover transactions 1 @@ -2394,11 +2327,11 @@ https://www.transifex.com/projects/p/dash/ -zapwallettxes=1: Recover transactions from blockchain (keep meta-data, e.g. account owner). - -zapwallettxes=1: Obnoviť transakcie z reťazca blokov (zachovať metaúdaje, napr. vlastníka účtu). + -zapwallettxes=1: Obnoviť transakcie z blockchainu (zachovať metaúdaje, napr. vlastníka účtu). -zapwallettxes=2: Recover transactions from blockchain (drop meta-data). - -zapwallettxes=2: Obnoviť transakcie z reťazca blokov (zahodiť metaúdaje). + -zapwallettxes=2: Obnoviť transakcie z blockchainu (zahodiť metaúdaje). -upgradewallet: Upgrade wallet to latest format on startup. (Note: this is NOT an update of the wallet itself!) @@ -2990,7 +2923,7 @@ https://www.transifex.com/projects/p/dash/ %n block(s) - %n blok%n bloky%n blokov + %n blok%n bloky%n blokov%n blokov Pay only the required fee of %1 @@ -2998,7 +2931,7 @@ https://www.transifex.com/projects/p/dash/ Estimated to begin confirmation within %n block(s). - Odhad pre začiatok potvrdzovania je %n blok.Odhad pre začiatok potvrdzovania sú %n bloky.Odhad pre začiatok potvrdzovania je %n blokov. + Odhad pre začiatok potvrdzovania je %n blok.Odhad pre začiatok potvrdzovania sú %n bloky.Odhad pre začiatok potvrdzovania je %n blokov.Odhad pre začiatok potvrdzovania je %n blokov. Warning: Invalid Dash address @@ -3283,7 +3216,7 @@ https://www.transifex.com/projects/p/dash/ TransactionDesc Open for %n more block(s) - Otvoriť pre %n ďalší blokOtvoriť pre %n ďalšie blokyOtvoriť pre %n ďalších blokov + Otvoriť pre %n ďalší blokOtvoriť pre %n ďalšie blokyOtvoriť pre %n ďalších blokovOtvoriť pre %n ďalších blokov Open until %1 @@ -3343,7 +3276,7 @@ https://www.transifex.com/projects/p/dash/ , broadcast through %n node(s) - , vysielaný cez %n uzol, vysielaný cez %n uzle, vysielaný cez %n uzlov + , vysielaný cez %n uzol, vysielaný cez %n uzle, vysielaný cez %n uzlov, vysielaný cez %n uzlov Date @@ -3387,7 +3320,7 @@ https://www.transifex.com/projects/p/dash/ matures in %n more block(s) - dozreje za %n ďalší blokdozreje za %n ďalšie blokydozreje za %n ďalších blokov + dozreje za %n ďalší blokdozreje za %n ďalšie blokydozreje za %n ďalších blokovdozreje za %n ďalších blokov not accepted @@ -3493,7 +3426,7 @@ https://www.transifex.com/projects/p/dash/ Open for %n more block(s) - Otvoriť pre %n ďalší blokOtvoriť pre %n ďalšie blokyOtvoriť pre %n ďalších blokov + Otvoriť pre %n ďalší blokOtvoriť pre %n ďalšie blokyOtvoriť pre %n ďalších blokovOtvoriť pre %n ďalších blokov Open until %1 @@ -4043,7 +3976,7 @@ https://www.transifex.com/projects/p/dash/ Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup - Vymazať všetky transakcie z peňaženky a pri spustení znova získať z reťazca blokov iba tie získané pomocou -rescan + Vymazať všetky transakcie z peňaženky a pri spustení znova získať z blockchainu iba tie získané pomocou -rescan Error loading %s: You can't enable HD on a already existing non-HD wallet @@ -4155,7 +4088,7 @@ https://www.transifex.com/projects/p/dash/ Failed to load governance cache from - Chyba pri načítaní vyrovnávajúcej pamäti dozoru z + Chyba pri načítaní vyrovnávajúcej pamäti správy z Failed to load masternode cache from @@ -4359,7 +4292,7 @@ https://www.transifex.com/projects/p/dash/ Synchroning blockchain... - Synchronizuje sa reťazec blokov... + Synchronizuje sa blockchain... Synchronization failed @@ -4481,6 +4414,10 @@ https://www.transifex.com/projects/p/dash/ Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) Vypnúť všetky funkcie špecifické pre Dash (Masternodes, PrivateSend, InstantSend, Vláda) (0-1, predvolené: %u) + + %s file contains all private keys from this wallet. Do not share it with anyone! + Súbor %s obsahuje všetky súkromné kľúče z tejto peňaženky. Nezdieľajte s nikým! + Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) Zisti vlastnú IP adresu (predvolené: 1 pre listen a -externalip alebo -proxy) @@ -4505,6 +4442,10 @@ https://www.transifex.com/projects/p/dash/ Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID) Spustiť príkaz keď je InstantSend zmení úspešne zamknutá (%s v cmd sa nahrádza TxID) + + Extra transactions to keep in memory for compact block reconstructions (default: %u) + Extra transakcie na uchovanie v pamäti, určené pre rekonštrukciu kompaktného bloku (predvolené: %u) + Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. Chyba pri vytváraní zálohy, lebo súbor už existuje! Toto môže nastať v prípade, ak ste reštartovali peňaženku za menej ako 60 sekúnd. Ak Vám to nevadí, môžete pokračovať. @@ -4551,11 +4492,11 @@ https://www.transifex.com/projects/p/dash/ Prune configured below the minimum of %d MiB. Please use a higher number. - Redukcia nastavená pod minimálnu hodnotu %d MiB. Prosím použite vyššiu hodnotu. + Orezanie nastavené pod minimálnu hodnotu %d MiB. Prosím použite vyššiu hodnotu. Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node) - Prerezávanie: posledná synchronizácia peňaženky prebehla pred už prerezanými dátami. Je treba previesť -reindex (v prípade prerezávacieho režimu stiahne znovu celý reťazec blokov) + Orezávanie: posledná synchronizácia peňaženky prebehla už pred orezanými dátami. Je treba spustiť -reindex (stiahne znovu celý blockchain v prípade orezaného uzlu) Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u) @@ -4563,7 +4504,7 @@ https://www.transifex.com/projects/p/dash/ Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again. - V prerezávaciom režime nie je možné reťazec blokov preskenovať. Musíte vykonať -reindex, čo znova stiahne celý reťaec blokov. + V orezanom režime nie je možné reťazec blokov pre-skenovať. Musíte vykonať -reindex, čo znova stiahne celý blockchain. Specify full path to directory for automatic wallet backups (must exist) @@ -4579,12 +4520,16 @@ https://www.transifex.com/projects/p/dash/ The transaction amount is too small to send after the fee has been deducted - Suma je príliš malá pre odoslanie tranzakcie + Po odpočítaní poplatku je suma transakcie príliš malá pre odoslanie Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. Celková dĺžka verzie sieťového reťazca (%i) prekračuje maximálnu dĺžku (%i). Znížte počet a veľkosť parametra uacomments. + + Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index. + Index transakcií nie je možné deaktivovať v plnom režime. Buď začnite s prepínačom príkazového riadka -litemode alebo aktivujte index transakcií. + Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) Sa snaží držať odchádzajúce prevádzku v rámci daného cieľa (v MB za 24h), 0 = žiadny limit (predvolený: %d) @@ -4617,9 +4562,13 @@ https://www.transifex.com/projects/p/dash/ Warning: Unknown block versions being mined! It's possible unknown rules are in effect Upozornenie: Ťaží sa neznáma verzia blokov! Je možné, že sú v platnosti neznáme pravidlá + + You are starting in lite mode, all Dash-specific functionality is disabled. + Začínate v režime Lite, všetky funkcie špecifické pre Dash sú deaktivované. + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain - K návratu k neprerezávaciemu režimu je treba prestavať databázu použitím -reindex. Tiež sa znova stiahne celý reťazec blokov + K návratu k neorezanému režimu je treba prestavať databázu použitím -reindex. Tiež sa znova stiahne celý blockchain (%d could be used only on mainnet) @@ -4661,13 +4610,21 @@ https://www.transifex.com/projects/p/dash/ Enable publish hash block in <address> Povoliť zverejneneie hash blokov pre <address> + + Enable publish hash of governance objects (like proposals) in <address> + Povoliť zverejnenie hash objektov správy (ako napr. návrhy) pre <address> + + + Enable publish hash of governance votes in <address> + Povoliť zverejnenie hash hlasovania správy pre <address> + Enable publish hash transaction (locked via InstantSend) in <address> Povoliť publikovanie transformačných transakcií (zamknutých cez InstantSend) v <address> Enable publish hash transaction in <address> - Povoliť zverejnenie hash tranzakcií pre <address> + Povoliť zverejnenie hash transakcií pre <address> Enable publish raw block in <address> @@ -4701,13 +4658,21 @@ https://www.transifex.com/projects/p/dash/ Failed to delete backup, error: %s Nepodarilo sa vymazať zálohu, chyba: %s + + Failed to load InstantSend data cache from + Chyba pri načítaní vyrovnávajúcej pamäti InstantSend dát z + + + Failed to load sporks cache from + Chyba pri načítaní vyrovnávajúcej pamäti sporks z + Failed to parse host:port string Chyba pri načítaní reťazca host:port Fee (in %s/kB) to add to transactions you send (default: %s) - Poplatok (za %s/kB) pridaný do tranzakcie, ktorú posielate (predvolené: %s) + Poplatok (v %s/kB) pridaný do transakcie, ktorú posielate (predvolené: %s) Imports blocks from external blk000??.dat file on startup @@ -4721,6 +4686,10 @@ https://www.transifex.com/projects/p/dash/ Invalid amount for -fallbackfee=<amount>: '%s' Neplatná suma pre -fallbackfee=<amount>: '%s' + + Invalid input count. + Neplatný počet vstupov. + Keep the transaction memory pool below <n> megabytes (default: %u) Udržovať zasobník transakcií menší než <n> megabajtov (predvolené: %u) @@ -4737,6 +4706,10 @@ https://www.transifex.com/projects/p/dash/ Line: %d Riadok: %d + + Loading InstantSend data cache... + Načíta sa vyrovnávacia pamäť InstantSend dát... + Loading addresses... Načítavanie adries... @@ -4747,7 +4720,11 @@ https://www.transifex.com/projects/p/dash/ Loading governance cache... - Načíta sa vyrovnávacia pamäť dozoru... + Načíta sa vyrovnávacia pamäť správy... + + + Loading sporks cache... + Načíta sa vyrovnávacia pamäť sporks... Loading wallet... (%3.2f %%) @@ -4767,7 +4744,7 @@ https://www.transifex.com/projects/p/dash/ Masternode cache is empty, skipping payments and governance cache... - Vyrovnávacia pamäť Masternode je prázdna, vynechávajú sa platby a vyrovnávacia pamäť dozoru... + Vyrovnávacia pamäť Masternode je prázdna, vynechávajú sa platby a vyrovnávacia pamäť správy... Masternode options: @@ -4851,11 +4828,11 @@ https://www.transifex.com/projects/p/dash/ Prune cannot be configured with a negative value. - Redukovanie nemôže byť nastavené na zápornú hodnotu. + Orezanie nemôže byť nastavené na zápornú hodnotu. Prune mode is incompatible with -txindex. - Redukovanie je nekompatibilné s -txindex. + Orezávanie nie je kompatibilné s -txindex. Pruning blockstore... @@ -4889,6 +4866,10 @@ https://www.transifex.com/projects/p/dash/ Transaction must have at least one recipient Transakcia musí mať aspoň jedného príjemcu + + Transaction too large + Transakcia je príliš veľká + Trying to connect... Pokúšame sa pripojiť... @@ -4909,6 +4890,10 @@ https://www.transifex.com/projects/p/dash/ Wallet debugging/testing options: Ladiace / testovacie možnosti peňaženky. + + Wallet is not initialized + Peňaženka nie je inicializovaná + Wallet needed to be rewritten: restart %s to complete Peňaženka musí byť prepísaná: pre dokončenie reštartujte %s @@ -4925,6 +4910,10 @@ https://www.transifex.com/projects/p/dash/ Whether to operate in a blocks only mode (default: %u) Či fungovat iba v čistom blokovom režime (predvolené: %u) + + You can not start a masternode in lite mode. + V režime Lite nemôžete spustiť masternode. + ZeroMQ notification options: Možnosti pripojenia ZeroMQ: @@ -5007,16 +4996,12 @@ https://www.transifex.com/projects/p/dash/ Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB) - Obmedziť nároky na úložný priestor prerezáváním (mazaním) starých blokov. Táto volba tiež umožní použiť RPC volanie pruneblockchain na zmazanie konkrétnych blokov a ďalej automatické prerezávanie starých blokov, ak je zadána cieľová velikosť súborov z blokmi v MiB. Tento režim nie je zlúčiteľný s -txindex ani -rescan. Upozornenie: opätovná zmena tohoto nastavenia bude vyžadovať nové stiahnutie celého reťazca blokov. (predvolené: 0 = bloky neprerezávať, 1 = povoliť ručné prerezávanie cez RPC, >%u = automatické prerezávanie blokov tak, aby bola udržaná cieľová velikosť súborov s blokmi v MiB) + Obmedziť nároky na úložný priestor orezávaním (mazaním) starých blokov. Táto voľba tiež umožní použiť RPC volanie pruneblockchain na zmazanie konkrétnych blokov a ďalej automatické orezávanie starých blokov, ak je zadaná cieľová veľkosť súborov z blokmi v MiB. Tento režim nie je zlúčiteľný s -txindex ani -rescan. Upozornenie: opätovná zmena tohoto nastavenia bude vyžadovať nové stiahnutie celého blockchainu . (predvolené: 0 = bloky neorezávať, 1 = povoliť ručné orezávanie cez RPC, >%u = automatické orezávanie blokov tak, aby bola udržaná cieľová veľkosť súborov s blokmi v MiB) Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) Nastaviť nejnižší akceptovateľný poplatok (v %s/kB) pre transakcie, ktoré majú byť zahrnutý do nových blokov. (predvolené: %s) - - Show N confirmations for a successfully locked transaction (%u-%u, default: %u) - Zobraziť N potvrdení pre úspešne uzamknuté transakcie (%u-%u, predvolené: %u) - This is the transaction fee you may pay when fee estimates are not available. Toto je poplatok za transakciu keď odhad poplatkov ešte nie je k dispozícii. diff --git a/src/qt/locale/dash_th.ts b/src/qt/locale/dash_th.ts new file mode 100644 index 000000000000..9cf7194e319d --- /dev/null +++ b/src/qt/locale/dash_th.ts @@ -0,0 +1,5329 @@ + + + AddressBookPage + + Right-click to edit address or label + คลิกขวาเพื่อแก้ไขที่อยู่ หรือป้ายชื่อ + + + Create a new address + สร้างที่อยู่ใหม่ + + + &New + &สร้างใหม่ + + + Copy the currently selected address to the system clipboard + คัดลอกที่อยู่ที่ถูกเลือกไปยัง คลิปบอร์ดของระบบ + + + &Copy + &คัดลอก + + + Delete the currently selected address from the list + ลบที่อยู่ที่เลือกไว้ในขณะนี้จากรายการ + + + &Delete + &ลบ + + + Export the data in the current tab to a file + ส่งออกข้อมูลที่อยู่ในแท็บไปที่ไฟล์ + + + &Export + &ส่งออก + + + C&lose + &ปิด + + + Choose the address to send coins to + เลือกที่อยู่เพื่อส่งเหรียญไปไว้ + + + Choose the address to receive coins with + เลือกที่อยู่เพื่อส่งเหรียญไปไว้ + + + C&hoose + เลือก + + + Sending addresses + ส่งที่อยู่ + + + Receiving addresses + ที่อยู่ผู้รับ + + + These are your Dash addresses for sending payments. Always check the amount and the receiving address before sending coins. + นี่คือที่อยู่ Dash ของคุณสำหรับการส่งการชำระเงิน โปรดตรวจสอบจำนวนเงินและที่อยู่ผู้รับก่อนที่จะส่งเหรียญ + + + These are your Dash addresses for receiving payments. It is recommended to use a new receiving address for each transaction. + นี่เป็นที่อยู่ Dash ของคุณสำหรับการรับการชำระเงิน แนะนำให้ใช้ที่อยู่รับใหม่สำหรับแต่ละธุรกรรม + + + &Copy Address + คัดลอกที่อยู่ + + + Copy &Label + คัดลอกป้ายชื่อ + + + &Edit + แก้ไข + + + Export Address List + ส่งออกรายการที่อยู่ + + + Comma separated file (*.csv) + Comma separated file (*.csv) + + + Exporting Failed + การส่งออกล้มเหลว + + + There was an error trying to save the address list to %1. Please try again. + เกิดข้อผิดพลาดขณะพยายามบันทึกรายการที่อยู่ไปยัง %1 กรุณาลองอีกครั้ง + + + + AddressTableModel + + Label + ชื่อ + + + Address + ที่อยู่ + + + (no label) + (ไม่มีชื่อ) + + + + AskPassphraseDialog + + Passphrase Dialog + ช่องสำหรับ รหัสผ่าน + + + Enter passphrase + ใส่รหัสผ่าน + + + New passphrase + รหัสผา่นใหม่ + + + Repeat new passphrase + กรุณากรอกรหัสผ่านใหม่อีกครั้งหนึ่ง + + + Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>. + ใส่วลีรหัสผ่านใหม่ลงใน Wallet <br/> โปรดใช้วลีรหัสผ่านที่มี<b>อักขระสุ่ม 10 ตัวขึ้นไป</b>, หรือ<b>มากกว่าหรือมากกว่า 8 คำ</b> + + + Encrypt wallet + กระเป๋าสตางค์ที่เข้ารหัส + + + This operation needs your wallet passphrase to unlock the wallet. + การดำเนินการนี้ต้องการรหัสวลีของคุณเพื่อปลดล็อค Wallet ของคุณ + + + Unlock wallet for mixing only + ปลดล็อค Wallet สำหรับการผสมเท่านั้น + + + Unlock wallet + ปลดล็อค Wallet + + + This operation needs your wallet passphrase to decrypt the wallet. + การดำเนินการนี้ต้องการรหัสวลีของคุณเพื่อถอดรหัส wallet + + + Decrypt wallet + ถอดรหัส Wallet + + + Change passphrase + เปลี่ยนรหัสผ่าน + + + Enter the old passphrase and new passphrase to the wallet. + ใส่รหัสผ่านเก่าและรหัสวลีใหม่ไปยัง wallet + + + Confirm wallet encryption + ยืนยันการเข้ารหัสกระเป๋าสตางค์ + + + Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR DASH</b>! + คำเตือน : ถ้าคุณเข้ารหัส Wallet ของคุณแล้วทำรหัสวลีหาย คุณจะ<b>สูญเสีย Dash ของคุณทั้งหมด</b>! + + + Are you sure you wish to encrypt your wallet? + คุณแน่ใจหรือไม่ว่าต้องการเข้ารหัสข้อมูล Wallet ของคุณ? + + + Wallet encrypted + Wallet ถูกเข้ารหัสเรียบร้อยแล้ว + + + %1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your funds from being stolen by malware infecting your computer. + %1 จะปิดตอนนี้เพื่อสิ้นสุดขั้นตอนการเข้ารหัส โปรดทราบว่าการเข้ารหัสข้อมูล Wallet ของคุณไม่สามารถป้องกันเงินจากการถูกโจรกรรมจากคอมพิวเตอร์ของคุณได้อย่างเต็มที่ + + + IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. Previous backups of the unencrypted wallet file contain the same HD seed and still have full access to all your funds just like the new, encrypted wallet. + ข้อมูลสำคัญ: การสำรองข้อมูลใดๆที่คุณได้สำรองจากไฟล์ Wallet จะถูกแทนที่ด้วยไฟล์ Wallet ที่เข้ารหัสที่สร้างใหม่ การสำรองข้อมูลก่อนหน้าของไฟล์ Wallet ที่ไม่ได้เข้ารหัสมี HD Seed เดียวกันและยังคงสามารถเข้าถึงกองทุนของคุณได้อย่างเต็มที่เช่นเดียวกับ Wallet ใหม่ที่มีการเข้ารหัส + + + IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet. + ข้อมูลสำคัญ: การสำรองข้อมูลก่อนหน้าใด ๆ ที่คุณทำจากกระเป๋าสตางค์ของคุณควรถูกแทนที่โดยใช้ไฟล์กระเป๋าสตางค์ที่เข้ารหัสลับใหม่ที่สร้างขึ้น เพื่อความปลอดภัยการสำรองข้อมูลก่อนหน้าของไฟล์ Wallet ที่ไม่ได้เข้ารหัสจะใช้งานไม่ได้เมื่อคุณเริ่มใช้ Wallet ที่เข้ารหัสใหม่ + + + Wallet encryption failed + การเข้ารหัสกระเป๋าสตางค์ผิดพลาด + + + Wallet encryption failed due to an internal error. Your wallet was not encrypted. + การเข้ารหัส Wallet ล้มเหลวเนื่องจากข้อผิดพลาดภายใน Wallet ของคุณไม่ได้รับการเข้ารหัส + + + The supplied passphrases do not match. + รหัสผ่านที่คุณกรอกไม่ตรงกัน + + + Wallet unlock failed + ปลดล็อค Wallet ล้มเหลว + + + The passphrase entered for the wallet decryption was incorrect. + รหัสผ่านวลีสำหรับเข้าถึง Wallet ไม่ถูกต้อง + + + Wallet decryption failed + การเข้าถึง Wallet ล้มเหลว + + + Wallet passphrase was successfully changed. + เปลี่ยนรหัสผ่านวลีเรียบร้อยแล้ว + + + Warning: The Caps Lock key is on! + คำเตือน: ปุ่ม Caps Lock เปิดอยู่ + + + + BanTableModel + + IP/Netmask + IP/Netmask (ตัวกรอง IP) + + + Banned Until + ห้ามใช้จนกว่า + + + + BitcoinGUI + + A fatal error occurred. Dash Core can no longer continue safely and will quit. + เกิดข้อผิดพลาดร้ายแรงขึ้น Dash Core ไม่สามารถดำเนินการต่อได้อย่างปลอดภัยและจะออกจากระบบ + + + Dash Core + Dash Core + + + Wallet + Wallet + + + Node + Node/โหนด + + + &Overview + &ภาพรวม + + + Show general overview of wallet + แสดงภาพรวมทั่วไปของกระเป๋าเงิน + + + &Send + &ส่ง + + + Send coins to a Dash address + ส่ง เหรียญ ไปยัง ที่อยู่ Dash + + + &Receive + &รับ + + + Request payments (generates QR codes and dash: URIs) + เรียกเก็บ การชำระเงิน (สร้าง QR codes และ Dash: URIs) + + + &Transactions + การทำธุรกรรม + + + Browse transaction history + เรียกดูประวัติการทำธุรกรรม + + + &Masternodes + Masternodes + + + Browse masternodes + Browse masternodes + + + E&xit + &ออก + + + Quit application + ออกจากโปรแกรม + + + Show information about Dash Core + แสดงข้อมูลเกี่ยวกับ Dash Core + + + About &Qt + เกี่ยวกับ &Qt + + + Show information about Qt + แสดงข้อมูล เกี่ยวกับ Qt + + + &Options... + &ตัวเลือก... + + + &About %1 + &เกี่ยวกับ %1 + + + Modify configuration options for %1 + ปรับปรุง ข้อมูลการตั้งค่าตัวเลือก สำหรับ %1 + + + &Show / Hide + &แสดง / ซ่อน + + + Show or hide the main Window + แสดง หรือ ซ่อน หน้าหลัก + + + &Encrypt Wallet... + เข้ารหัส Wallet + + + Encrypt the private keys that belong to your wallet + เข้ารหัส private keys สำหรับ Wallet ของท่าน + + + &Backup Wallet... + &สำรอง Wallet ... + + + Backup wallet to another location + สำรอง กระเป๋าเงินไปยัง ที่เก็บอื่น + + + &Change Passphrase... + &เปลี่ยนรหัสผ่าน... + + + Change the passphrase used for wallet encryption + เปลี่ยนรหัสผ่านที่ใช้สำหรับการเข้ารหัสกระเป๋าเงิน + + + &Unlock Wallet... + ปลดล็อค Wallet + + + Unlock wallet + ปลดล็อค Wallet + + + &Lock Wallet + ล็อค Wallet + + + Sign &message... + เซ็นต์ชื่อด้วย &ข้อความ... + + + Sign messages with your Dash addresses to prove you own them + เซ็นชื่อด้วยข้อความ ที่เก็บ Dash เพื่อแสดงว่าท่านเป็นเจ้าของ Dash นี้จริง + + + &Verify message... + &ยืนยันข้อความ... + + + Verify messages to ensure they were signed with specified Dash addresses + ตรวจสอบ ข้อความ เพื่อให้แน่ใจว่า การเซ็นต์ชื่อ ด้วยที่เก็บ Dash แล้ว + + + &Information + ข้อมูล + + + Show diagnostic information + แสดงข้อมูลการวินิจฉัย + + + &Debug console + คอนโซลการแก้ไขจุดบกพร่อง + + + Open debugging console + เปิดคอนโซลการแก้ไขจุดบกพร่อง + + + &Network Monitor + ตัวตรวจสอบเครือข่าย + + + Show network monitor + แสดงตัวตรวจสอบเครือข่าย + + + &Peers list + รายชื่อ Peers + + + Show peers info + แสดงข้อมูล Peers + + + Wallet &Repair + ซ่อมแซม Wallet + + + Show wallet repair options + แสดงตัวเลือกการซ่อมแซม Wallet + + + Open Wallet &Configuration File + เปิดไฟล์การกำหนดค่า Wallet + + + Open configuration file + เปิดไฟล์การกำหนดค่า + + + Open &Masternode Configuration File + เปิดไฟล์การกำหนดค่า Masternode + + + Open Masternode configuration file + เปิดไฟล์การกำหนดค่า Masternode + + + Show Automatic &Backups + แสดงการสำรองข้อมูลอัตโนมัติ + + + Show automatically created wallet backups + แสดงการสำรองข้อมูล Wallet ที่สร้างขึ้นโดยอัตโนมัติ + + + &Sending addresses... + &ที่เก็บเงิน ที่จะส่ง bitcoin + + + Show the list of used sending addresses and labels + แสดงรายการ ที่เก็บเงินที่จะส่ง bitcoin ออก และป้ายชื่อ ที่ใช้ไปแล้ว + + + &Receiving addresses... + ที่อยู่ผู้รับ + + + Show the list of used receiving addresses and labels + แสดงรายการที่อยู่ผู้รับและป้ายชื่อที่ใช้ไปแล้ว + + + Open &URI... + เปิด URI + + + Open a dash: URI or payment request + เปิด Dash: URI หรือคำขอการชำระเงิน + + + &Command-line options + ตัวเลือก Command-line + + + Show the %1 help message to get a list with possible Dash command-line options + แสดง %1 ข้อความช่วยเหลือ เพื่อแสดงรายการ ตัวเลือกที่เป็นไปได้สำหรับ Dash command-line + + + %1 client + %1 ไคลเอนต์ + + + &PrivateSend information + ข้อมูล PrivateSend + + + Show the PrivateSend basic information + แสดงข้อมูลพื้นฐานของ PrivateSend + + + &File + ไฟล์ + + + &Settings + การตั้งค่า + + + &Tools + เครื่องมือ + + + &Help + ช่วยเหลือ + + + Tabs toolbar + แถบเครื่องมือ + + + %n active connection(s) to Dash network + %n เชื่อมต่อใช้งานกับเครือข่าย Dash + + + Network activity disabled + ปิดการใช้งานเครือข่ายแล้ว + + + Syncing Headers (%1%)... + กำลังซิงค์ส่วนหัว (%1%) ... + + + Synchronizing with network... + กำลังซิงค์กับเครือข่าย ... + + + Indexing blocks on disk... + การกำลังสร้างดัชนีของบล็อก ในดิสก์... + + + Processing blocks on disk... + กำลังดำเนินการกับบล็อกในดิสก์... + + + Reindexing blocks on disk... + กำลังทำดัชนี ที่เก็บบล็อก ใหม่ ในดิสก์... + + + Connecting to peers... + เชื่อมต่อกับ Peers + + + Processed %n block(s) of transaction history. + ประมวลผล %n บล็อคของประวัติการทำธุรกรรม + + + %1 behind + %1 ตามหลัง + + + Catching up... + กำลังอัพเดต + + + Last received block was generated %1 ago. + บล็อกสุดท้ายที่ได้รับ สร้างขึ้นเมื่อ %1 มาแล้ว + + + Transactions after this will not yet be visible. + รายการหลังจากนี้ จะไม่แสดงให้เห็น + + + Up to date + ปัจจุบัน + + + Synchronizing additional data: %p% + กำลังซิงค์ข้อมูลเพิ่มเติม: %p% + + + Error + ข้อผิดพลาด + + + Warning + คำเตือน + + + Information + ข้อมูล + + + Date: %1 + + วันที่: %1 + + + + Amount: %1 + + จำนวน: %1 + + + + Type: %1 + + รูปแบบ: %1 + + + + Label: %1 + + ป้ายชื่อ: %1 + + + + Address: %1 + + ที่อยู่: %1 + + + + Sent transaction + รายการที่ส่ง + + + Incoming transaction + การทำรายการขาเข้า + + + HD key generation is <b>enabled</b> + <b>เปิดใช้งาน</b>การสร้างคีย์ HD แล้ว + + + HD key generation is <b>disabled</b> + การสร้างคีย์ HD ถูก<b>ปิดใช้งาน</b> + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> + ระเป๋าเงินถูก <b>เข้ารหัส</b> และในขณะนี้ <b>ปลดล็อคแล้ว</b> + + + Wallet is <b>encrypted</b> and currently <b>unlocked</b> for mixing only + Wallet <b>เข้ารหัสแล้ว</b>และขณะนี้<b>ปลดล็อก</b>สำหรับการผสมเท่านั้น + + + Wallet is <b>encrypted</b> and currently <b>locked</b> + กระเป๋าเงินถูก <b>เข้ารหัส</b> และในปัจจุบัน <b>ล็อค </b> + + + + ClientModel + + Total: %1 (PS compatible: %2 / Enabled: %3) + ทั้งหมด: %1 (รองรับ PS: %2 / เปิดใช้งาน: %3) + + + Network Alert + การแจ้งเตือนเครือข่าย + + + + CoinControlDialog + + Quantity: + จำนวน: + + + Bytes: + ไบต์: + + + Amount: + จำนวน: + + + Fee: + ค่าธรรมเนียม: + + + Coin Selection + การเลือก Coin + + + Dust: + เศษ: + + + After Fee: + ส่วนที่เหลือจากค่าธรรมเนียม: + + + Change: + เงินทอน: + + + (un)select all + (ไม่)เลือกทั้งหมด + + + toggle lock state + สลับสถานะล็อค + + + Tree mode + โหมดแบบต้นไม้ + + + List mode + โหมดแบบรายการ + + + (1 locked) + (1 ล็อค) + + + Amount + จำนวน + + + Received with label + รับโดยป้ายชื่อ (label) + + + Received with address + รับโดยที่เก็บ + + + PS Rounds + PS รอบ + + + Date + วันที่ + + + Confirmations + การยืนยัน + + + Confirmed + ยืนยันแล้ว + + + Copy address + คัดลอกที่อยู่ + + + Copy label + คัดลอกป้ายชื่อ + + + Copy amount + คัดลอกจำนวน + + + Copy transaction ID + คัดลอก ID ธุรกรรม + + + Lock unspent + Lock unspent + + + Unlock unspent + Unlock unspent + + + Copy quantity + ปริมาณการคัดลอก + + + Copy fee + ค่าธรรมเนียมการคัดลอก + + + Copy after fee + ค่าธรรมเนียมหลังการคัดลอก + + + Copy bytes + คัดลอกไบต์ + + + Copy dust + คัดลอก dust + + + Copy change + คัดลอกการเปลี่ยนแปลง + + + Please switch to "List mode" to use this function. + โปรดเปลี่ยนเป็น List mode เพื่อใช้ฟังก์ชั่นนี้ + + + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. + อินพุตแบบไม่ระบุตัวตนอินที่ถูกเลือก <b> PrivateSend จะถูกปิดการใช้งาน </b><br><br> หากคุณยังต้องการใช้ PrivateSend โปรดยกเลิกการเลือกอินพุตที่ไม่ระบุตัวตนทั้งหมดก่อนแล้วจึงเช็คในช่อง PrivateSend อีกครั้ง + + + (%1 locked) + (%1 ล็อค) + + + yes + ใช่ + + + no + ไม่ + + + This label turns red if any recipient receives an amount smaller than the current dust threshold. + ป้ายนี้จะเปลี่ยนเป็นสีแดงถ้าตัวรับใด ๆ ได้รับจำนวนเงินน้อยกว่าขั้นต่ำในปัจจุบัน + + + Can vary +/- %1 duff(s) per input. + สามารถเปลี่ยนแปลงได้ + / - %1 duff (s) ต่อรายการ + + + (no label) + (ไม่มีชื่อ) + + + change from %1 (%2) + เปลี่ยนจาก %1 (%2) + + + (change) + (เปลี่ยน) + + + n/a + n/a + + + + EditAddressDialog + + Edit Address + แก้ไขที่อยู่ + + + &Label + &ป้ายชื่อ + + + The label associated with this address list entry + รายการแสดง ป้ายชื่อที่เกี่ยวข้องกับที่เก็บนี้ + + + &Address + &ที่เก็บ + + + The address associated with this address list entry. This can only be modified for sending addresses. + ที่เก็บที่เกี่ยวข้องกับ ที่เก็บที่แสดงรายการนี้ การปรับปรุงนี้ทำได้สำหรับ ที่เก็บเงินที่จะใช่ส่งเงิน เท่านั้น + + + New receiving address + ที่อยู่ผู้รับใหม่ + + + New sending address + ที่อยู่ผู้ส่งใหม่ + + + Edit receiving address + แก้ไขที่อยู่ผู้รับ + + + Edit sending address + แก้ไขที่อยู่ผู้ส่ง + + + The entered address "%1" is not a valid Dash address. + ที่อยู่ที่ป้อน "%1" ไม่ใช่ที่อยู่ Dash ที่ถูกต้อง + + + The entered address "%1" is already in the address book. + ที่อยู่ที่ป้อน "%1" อยู่ในสมุดที่อยู่แล้ว + + + Could not unlock wallet. + ไม่สามารถปลดล็อค Wallet ได้ + + + New key generation failed. + สร้างคีย์ใหม่ไม่สำเร็จ + + + + FreespaceChecker + + A new data directory will be created. + ไดเร็กทอรี่ใหม่ที่ใช้เก็บข้อมูลจะถูกสร้างขึ้นมา + + + name + ชื่อ + + + Directory already exists. Add %1 if you intend to create a new directory here. + ไดเร็กทอรี่มีอยู่แล้ว ใส่เพิ่ม %1 หากท่านต้องการสร้างไดเร็กทอรี่ใหม่ที่นี่ + + + Path already exists, and is not a directory. + พาธ มีอยู่แล้ว พาธนี่ไม่ใช่ไดเร็กทอรี่ + + + Cannot create data directory here. + ไม่สามารถสร้างไดเร็กทอรี่ข้อมูลที่นี่ + + + + HelpMessageDialog + + version + เวอร์ชั่น + + + (%1-bit) + (%1-บิท) + + + About %1 + เกี่ยวกับ %1 + + + Command-line options + ตัวเลือก Command-line + + + Usage: + วิธีใช้งาน: + + + command-line options + ตัวเลือก Command-line + + + UI Options: + ตัวเลือก UI: + + + Choose data directory on startup (default: %u) + เลือกไดเร็กทอรี่ข้อมูลตั้งแต่เริ่มต้นสตาร์ทอัพ (ค่าเริ่มต้น: %u) + + + Set language, for example "de_DE" (default: system locale) + ตั้งค่าภาษา ยกตัวอย่าง "de_DE" (ค่าเริ่มต้น: ภาษาท้องถิ่นของระบบ) + + + Start minimized + เริ่มต้นมินิไมซ์ + + + Set SSL root certificates for payment request (default: -system-) + ตั้งค่า SSL root certificates สำหรับเรียกการชำระเงิน (ค่าเริ่มต้น: -system-) + + + Show splash screen on startup (default: %u) + แสดง splash screen ตอนเริ่มต้น (ค่าเริ่มต้น: %u) + + + Reset all settings changed in the GUI + รีเซตการเปลี่ยนการตั้งค่าทั้งหมดใน GUI + + + PrivateSend information + ข้อมูล PrivateSend + + + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. + <h3>ข้อมูลพื้นฐานเกี่ยวกับ PrivateSend PrivateSend ช่วยให้คุณมีความเป็นส่วนตัวทางการเงินอย่างแท้จริงโดยปิดบังแหล่งที่มาของเงินทุนของคุณ Dash ทั้งหมดใน wallet ของคุณประกอบด้วย "อินพุต" ที่แตกต่างกันซึ่งคุณสามารถแยกเหรียญกันได้ <br> PrivateSend ใช้กระบวนการใหม่ในการผสมผสานอินพุตของคุณเข้ากับอินพุตของคนอื่นอีกสองคนโดยไม่ต้องมีเหรียญออกจาก wallet คุณสามารถควบคุมเงินได้ตลอดเวลา<hr><b>กระบวนการ PrivateSend ทำงานได้ดังนี้:</b><ol type="1"><li>PrivateSend เริ่มต้นด้วยการทำลายอินพุตการทำธุรกรรมของคุณลงไปเป็นหน่วยเงินมาตรฐาน หน่วยเงินเหล่านี้คือ 0.01 DASH, 0.1 DASH, 1 DASH และ 10 DASH - เหมือนกับเงินกระดาษที่คุณใช้ทุกวัน</li><li> Wallet ของคุณจะส่งคำขอไปยังโหนดซอฟต์แวร์ที่กำหนดค่าไว้เป็นพิเศษในเครือข่ายที่ เรียกว่า "masternodes" โดย masternodes เหล่านี้จะแจ้งให้ทราบแล้วว่าคุณสนใจที่จะผสมหน่วยเงินนั้นๆ ไม่มีข้อมูลที่สามารถระบุตัวได้ถูกส่งไปยัง masternodes ดังนั้นพวกเขาจึงไม่เคยรู้จักว่าคุณเป็นใคร </li><li>เมื่อมีคนอีกสองคนส่งข้อความที่คล้ายกันแสดงว่าพวกเขาต้องการที่จะผสมผสานหน่วยเงินเดียวกันเซสชันการผสมจะเริ่มขึ้น Masternode จะผสมผสานอินพุตเข้าด้วยกันและสั่งให้ wallet ของผู้ใช้ทั้ง 3 รายชำระค่าป้อนข้อมูลที่เปลี่ยนไปแล้วกลับคืนสู่ตัวเอง Wallet ของคุณจ่ายเงินให้กับตัวเองโดยตรง แต่ในที่อยู่อื่น (เรียกว่าที่อยู่เปลี่ยน)</li><li>เพื่อเป็นการปิดบังเงินทุนของคุณ wallet ของคุณจะต้องทำซ้ำขั้นตอนนี้ซ้ำหลายครั้งโดยใช้แต่ละหน่วยเงิน ทุกครั้งที่ดำเนินการเสร็จสิ้นระบบจะเรียกว่า "รอบ ๆ " รอบ PrivateSend ในแต่ละครั้งทำให้การระบุแหล่งเงินทุนของคุณเกิดขึ้นยากขึ้นอย่างมาก</li><li>กระบวนการผสมนี้เกิดขึ้นโดยไม่มีการก้าวก่ายในส่วนของคุณ เมื่อคุณต้องการทำธุรกรรม เงินของคุณจะถูกซ่อนไว้ ไม่ต้องการเพิ่มเติม </li></ol><hr><b>สำคัญ:</b>Wallet ของคุณมีเพียง 1,000 ที่อยู่ในการเปลี่ยนแปลงเท่านั้น ทุกครั้งที่เกิดเหตุการณ์ผสมเกิดขึ้น 9 ที่อยู่ของคุณจะถูกนำมาใช้ ซึ่งหมายความว่า 1000 ที่อยู่ล่าสุดสำหรับการผสมประมาณ 100 เหตุการณ์ เมื่อมีการใช้ 900 ราย Wallet ของคุณต้องสร้างที่อยู่เพิ่มเติม สามารถทำเช่นนี้ได้หากคุณเปิดใช้งานการสำรองข้อมูลอัตโนมัติไว้<br>ดังนั้นผู้ใช้ที่มีการสำรองข้อมูลถูกปิดใช้งานจะมีการปิดใช้ PrivateSend <hr>สำหรับข้อมูลเพิ่มเติม<a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">โปรดดูที่เอกสาร PrivateSend </a> + + + + Intro + + Welcome + ยินดีต้อนรับ + + + Welcome to %1. + ยินดีต้องรับสู่ %1 + + + As this is the first time the program is launched, you can choose where %1 will store its data. + นี่เป็นการรันโปรแกรมครั้งแรก ท่านสามารถเลือก ว่าจะเก็บข้อมูลไว้ที่ %1 + + + %1 will download and store a copy of the Dash block chain. At least %2GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory. + %1 จะดาวน์โหลดและเก็บสำเนาของ Dash block chain อย่างน้อย %2GB ของข้อมูลจะถูกเก็บไว้ในไดเรกทอรีนี้และจะเติบโตขึ้นเมื่อเวลาผ่านไป Wallet จะถูกจัดเก็บไว้ในไดเรกทอรีนี้ + + + Use the default data directory + ใช้ไดเร็กทอรี่ข้อมูล ที่เป็นค่าเริ่มต้น + + + Use a custom data directory: + ใช้ไดเร็กทอรี่ข้อมูลที่ตั้งค่าเอง: + + + Error: Specified data directory "%1" cannot be created. + ข้อผิดพลาด: ไดเร็กทอรี่ข้อมูลที่ต้องการ "%1" ไม่สามารถสร้างได้ + + + Error + ข้อผิดพลาด + + + %1 GB of free space available + %1 GB พื้นที่ว่าง + + + (of %1 GB needed) + (ต้องใช้ %1 GB) + + + + MasternodeList + + Form + รูป + + + My Masternodes + Masternodes ของฉัน + + + Note: Status of your masternodes in local wallet can potentially be slightly incorrect.<br />Always wait for wallet to sync additional data and then double check from another node<br />if your masternode should be running but you still do not see "ENABLED" in "Status" field. + หมายเหตุ: สถานะของ Masternodes ของคุณใน Wallet ภายในประเทศอาจไม่ถูกต้องเล็กน้อย<br />ต้องรอทุกครั้งให้ Wallet ซิงค์ข้อมูลเพิ่มเติมจากนั้นตรวจสอบอีกครั้งจากโหนดอื่น <br /> คุณจะเห็น Masternode ของคุณควรทำงานอยู่ แต่คุณยังไม่เห็น "เปิดการใช้งาน" ใน"สถานะ" + + + Alias + นามแฝง + + + Address + ที่อยู่ + + + Protocol + โปรโตคอล + + + Status + สถานะ + + + Active + กำลังใช้งาน + + + Last Seen + เห็นครั้งสุดท้าย + + + Payee + ผู้รับเงิน + + + S&tart alias + เริ่มนามแฝง + + + Start &all + เริ่มต้นทั้งหมด + + + Start &MISSING + เริ่มต้น MISSING + + + &Update status + อัพเดตสถานะ + + + Show additional Masternode information + แสดงข้อมูล Masternode เพิ่มเติม + + + Show &Info... + แสดงข้อมูล + + + Status will be updated automatically in (sec): + สถานะจะอัพเดตอัตโนมัติภายใน (วินาที) : + + + 0 + 0 + + + All Masternodes + Masternodes ทั้งหมด + + + Filter List: + Filter List: + + + Filter masternode list + Filter masternode list + + + Node Count: + จำนวนโหนด: + + + Start alias + เริ่มนามแฝง + + + Confirm masternode start + ยืนยันว่า Masternodes เริ่มทำงาน + + + Are you sure you want to start masternode %1? + คุณแน่ใจหรือไม่ว่าต้องการให้ masternode %1 เริ่มทำงาน? + + + Confirm all masternodes start + ยืนยันว่า Masternodes ทั้งหมดเริ่มทำงาน + + + Are you sure you want to start ALL masternodes? + คุณแน่ใจหรือไม่ว่าต้องการให้ masternode ทั้งหมดเริ่มทำงาน? + + + Command is not available right now + คำสั่งไม่พร้อมใช้งานในขณะนี้ + + + You can't use this command until masternode list is synced + คุณไม่สามารถใช้คำสั่งนี้ได้จนกว่าจะมีการซิงค์รายการ masternode + + + Confirm missing masternodes start + ยืนยันการเริ่มต้น masternodes ที่หายไป + + + Are you sure you want to start MISSING masternodes? + คุณแน่ใจหรือไม่ว่าต้องการเริ่มต้น masternodes ที่หายไป? + + + Additional information for Masternode %1 + ข้อมูลเพิ่มเติมสำหรับ Masternode %1 + + + Masternode Private Key + Masternode Private Key + + + Private Key + Private Key + + + Collateral + ประกอบ + + + IP + IP + + + Version + เวอร์ชั่น + + + Unknown + ไม่ทราบ + + + Sentinel + Sentinel + + + + ModalOverlay + + Form + รูป + + + Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the Dash network, as detailed below. + การทำธุรกรรมล่าสุดอาจยังไม่สามารถเห็นได้และทำให้ยอดคงเหลือของ wallet ของคุณไม่ถูกต้อง ข้อมูลนี้จะถูกต้องก่อนที่ Wallet ของคุณจะเสร็จสิ้นการซิงค์กับเครือข่าย Dash ตามรายละเอียดด้านล่างนี้ + + + Attempting to spend Dash that are affected by not-yet-displayed transactions will not be accepted by the network. + เครือข่ายจะไม่ยอมรับความพยายามที่จะใช้ Dash ที่ได้รับผลกระทบจากการทำธุรกรรมที่ยังไม่ได้แสดงผล + + + Number of blocks left + จำนวนบล็อกที่เหลือ + + + Unknown... + ไม่ทราบ... + + + Last block time + เวลาบล็อคล่าสุด + + + Progress + ความคืบหน้า + + + Progress increase per hour + เพิ่มความคืบหน้าต่อชั่วโมง + + + calculating... + กำลังคำนวณ... + + + Estimated time left until synced + เวลาโดยประมาณที่เหลือจนกว่าจะซิงค์ + + + Hide + ซ่อน + + + Unknown. Syncing Headers (%1)... + ไม่ทราบ กำลังซิงค์ส่วนหัว (%1) ... + + + + OpenURIDialog + + Open URI + เปิด URI + + + Open payment request from URI or file + เปิด การเรียกการชำระเงิน จาก URI หรือ ไฟล์ + + + URI: + URI: + + + Select payment request file + เลือก ไฟล์คำขอการชำระเงิน + + + Select payment request file to open + เลือกไฟล์คำขอชำระเงินเพื่อเปิด + + + + OptionsDialog + + Options + ตัวเลือก + + + &Main + &หลัก + + + Size of &database cache + ขนาดของ &database cache + + + MB + MB + + + Number of script &verification threads + จำนวนของสคริปท์ &verification threads + + + (0 = auto, <0 = leave that many cores free) + (0 = อัตโนมัติ, <0 = ปล่อย คอร์ อิสระ) + + + Amount of Dash to keep anonymized + จำนวน Dash เพื่อไม่ระบุชื่อ + + + W&allet + Wallet + + + Show additional tab listing all your masternodes in first sub-tab<br/>and all masternodes on the network in second sub-tab. + แสดงแท็บเพิ่มเติมแสดง masternodes ทั้งหมดของคุณในแท็บย่อยแรก <br/>และ masternodes ทั้งหมดบนเครือข่ายในแท็บย่อยที่สอง + + + Show Masternodes Tab + แสดงแถบ Masternodes + + + Show additional information and buttons for PrivateSend on overview screen. + แสดงข้อมูลเพิ่มเติมและปุ่มสำหรับ PrivateSend บนหน้าจอ + + + Enable advanced PrivateSend interface + เปิดใช้งานอินเทอร์เฟซขั้นสูงของ PrivateSend + + + Show warning dialog when PrivateSend detects that wallet has very low number of keys left. + แสดงข้อความแจ้งเตือนเมื่อ PrivateSend ตรวจพบว่า Wallet มีจำนวนคีย์ที่เหลืออยู่ต่ำมาก + + + Warn if PrivateSend is running out of keys + แจ้งเตือนหากคีย์ PrivateSend กำลังจะหมด + + + Whether to use experimental PrivateSend mode with multiple mixing sessions per block.<br/>Note: You must use this feature carefully.<br/>Make sure you always have recent wallet (auto)backup in a safe place! + ไม่ว่าจะใช้โหมด PrivateSend แบบทดลองกับการผสมหลายเซสชันต่อบล็อกหรือไม่<br/>หมายเหตุ: คุณต้องใช้คุณลักษณะนี้อย่างระมัดระวัง<br/>ตรวจสอบให้แน่ใจว่าคุณได้สำรองข้อมูล wallet (อัตโนมัติ) ไว้ในที่ปลอดภัยทุกครั้ง! + + + Enable PrivateSend &multi-session + เปิดใช้งาน PrivateSend และหลายเซสชัน + + + If you disable the spending of unconfirmed change, the change from a transaction<br/>cannot be used until that transaction has at least one confirmation.<br/>This also affects how your balance is computed. + ถ้าหากคุณปิดการใช้จ่ายในการเปลี่ยนแปลงที่ไม่ได้รับการยืนยัน การเปลี่ยนแปลงจากธุรกรรม<br/>จะไม่สามารถใช้ได้จนกว่ารายการดังกล่าวจะมีการยืนยันอย่างน้อยหนึ่งรายการ<br/>นอกจากนี้ยังมีผลต่อการคำนวณยอดคงเหลือของคุณอีกด้วย + + + PrivateSend rounds to use + จำนวนรอบ PrivateSend ที่ใช้ + + + This amount acts as a threshold to turn off PrivateSend once it's reached. + จำนวนนี้ทำหน้าที่เป็นเกณฑ์เพื่อปิดการใช้งาน PrivateSend เมื่อถึงขีดจำกัดแล้ว + + + Automatically open the Dash Core client port on the router. This only works when your router supports UPnP and it is enabled. + เปิดพอร์ต Dash Core client บนเราเตอร์โดยอัตโนมัติ การทำงานนี้ใช้ได้เฉพาะเมื่อเราเตอร์ของคุณรองรับ UPnP และเปิดใช้งานแล้ว + + + Accept connections from outside + ยอมรับการเชื่อมต่อจากภายนอก + + + Allow incoming connections + อนุญาตการเชื่อมต่อขาเข้า + + + Connect to the Dash network through a SOCKS5 proxy. + เชื่อมต่อกับเครือข่าย Dash ผ่านพร็อกซี่แบบ SOCKS5 + + + &Connect through SOCKS5 proxy (default proxy): + &เชื่อมต่อผ่าน พร็อกซี่ SOCKS5 (พร็อกซี่เริ่มต้น): + + + Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu. + มินิไมซ์แอพ แทนการออกจากแอพพลิเคชั่น เมื่อวินโดว์ได้รับการปิด เมื่อเลือกตัวเลือกนี้ แอพพลิเคชั่น จะถูกปิด ก็ต่อเมื่อ มีการเลือกเมนู Exit/ออกจากระบบ เท่านั้น + + + Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items.<br/>%s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. + URL ของบุคคลที่สาม (เช่น explorer บล็อก) ที่ปรากฏในแท็บธุรกรรมเป็นรายการในเมนู<br/> %s ใน URL ถูกแทนที่ด้วย hash การทำธุรกรรม หลาย URL ถูกแยกด้วยแถบแนวตั้ง + + + Expert + ผู้เชี่ยวชาญ + + + This setting determines the amount of individual masternodes that an input will be anonymized through.<br/>More rounds of anonymization gives a higher degree of privacy, but also costs more in fees. + การกำหนดค่านี้จะกำหนดจำนวนของ masternodes แต่ละตัวที่จะสามารถระบุได้โดยไม่ระบุตัวตน.<br/>การเก็บข้อมูลแบบไม่ระบุตัวตนทำให้มีระดับความเป็นส่วนตัวสูงขึ้น แต่ยังมีค่าใช้จ่ายเพิ่มขึ้นด้วย + + + Whether to show coin control features or not. + ไม่ว่าจะแสดงคุณสมบัติการควบคุมหยอดเหรียญหรือไม่ + + + Automatically start %1 after logging in to the system. + เริ่มต้น %1 โดยอัตโนมัติหลังจากเข้าสู่ระบบ + + + &Start %1 on system login + &เริ่ม %1 ในการล็อกอินระบบ + + + Enable coin &control features + เปิดใช้ coin & รูปแบบการควบคุม + + + &Spend unconfirmed change + &ใช้เงินทอนที่ยังไม่ยืนยัน + + + &Network + &เน็ตเวิร์ก + + + Map port using &UPnP + จองพอร์ต โดยใช้ &UPnP + + + Proxy &IP: + พร็อกซี่ &IP: + + + IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1) + IP แอดเดส ของ proxy (เช่น IPv4: 127.0.0.1 / IPv6: ::1) + + + &Port: + &พอร์ต + + + Port of the proxy (e.g. 9050) + พอร์ตของพร็อกซี่ (ตัวอย่าง 9050) + + + Used for reaching peers via: + ใช้ในการเข้าถึงอีกฝ่ายหนึ่ง peer โดย: + + + Shows, if the supplied default SOCKS5 proxy is used to reach peers via this network type. + แสดง หากพร็อกซี SOCKS5 เป็นค่าเริ่มต้นที่ใช้เพื่อเข้าถึง Peers ผ่านเครือข่ายประเภทนี้ + + + IPv4 + IPv4 + + + IPv6 + IPv6 + + + Tor + Tor + + + Connect to the Dash network through a separate SOCKS5 proxy for Tor hidden services. + เชื่อมต่อกับ เครือข่าย Dash ผ่านทาง พร้อกซี่ SOCKS5 แยกต่างหาก สำหรับ Tor เซอร์วิส + + + Use separate SOCKS5 proxy to reach peers via Tor hidden services: + ใช้พร็อกซี SOCKS5 แยกเพื่อเข้าถึงเพื่อนร่วมทางผ่านบริการที่ซ่อนไว้ของ Tor: + + + &Window + &วันโดว์ + + + &Hide the icon from the system tray. + ซ่อนไอคอนจาก System tray + + + Hide tray icon + ซ่อนไอคอน tray + + + Show only a tray icon after minimizing the window. + แสดงเทรย์ไอคอน หลังมืนิไมส์วินโดว์ เท่านั้น + + + &Minimize to the tray instead of the taskbar + ลดขนาด Tray ลงไปยังแถบ taskbar + + + M&inimize on close + ปิดหน้าต่าง + + + &Display + แสดง + + + User Interface &language: + User Interface language + + + The user interface language can be set here. This setting will take effect after restarting %1. + สามารถตั้งค่า User interface language ได้ที่นี่ การตั้งค่านี้จะมีผลหลังจากรีสตาร์ท %1 + + + Language missing or translation incomplete? Help contributing translations here: +https://www.transifex.com/projects/p/dash/ + ภาษาขาดหายไปหรือการแปลไม่สมบูรณ์ใช่หรือไม่? สามารถช่วยแปลเพิ่มเติมได้ที่นี่: +https://www.transifex.com/projects/p/dash/ + + + User Interface Theme: + User Interface Theme: + + + &Unit to show amounts in: + หน่วยที่จะแสดงจำนวนเงิน: + + + Choose the default subdivision unit to show in the interface and when sending coins. + เลือกรายละเอียดค่าเริ่มต้นสำหรับจำนวนเงินที่แสดงในอินเทอร์เฟซและเมื่อส่งเหรียญ + + + Decimal digits + ตัวเลขทศนิยม + + + Third party transaction URLs + URL ธุรกรรมของบุคคลที่สาม + + + Active command-line options that override above options: + ตัวเลือก command-line แอกทีฟอยู่นี้ จะแทนที่ ตัวเลือกด้านบนนี้: + + + Reset all client options to default. + รีเซต ไคลเอ็นออพชั่น กลับไปเป็นค่าเริ่มต้น + + + &Reset Options + &รีเซต ออพชั่น + + + &OK + โอเค + + + &Cancel + ยกเลิก + + + default + ค่าเริ่มต้น + + + none + ไม่มี + + + Confirm options reset + ยืนยันการรีเซ็ตตัวเลือก + + + Client restart required to activate changes. + รีสตาร์ทเครื่องไคลเอ็นต์เพื่อเปิดใช้งานการเปลี่ยนแปลง + + + Client will be shut down. Do you want to proceed? + ไคลเอนต์จะถูกปิด คุณต้องการดำเนินการต่อหรือไม่? + + + This change would require a client restart. + การเปลี่ยนแปลงนี้จะทำให้ไคลเอนต์รีสตาร์ท + + + The supplied proxy address is invalid. + ที่อยู่พร็อกซีที่ระบุไม่ถูกต้อง + + + + OverviewPage + + Form + รูป + + + The displayed information may be out of date. Your wallet automatically synchronizes with the Dash network after a connection is established, but this process has not completed yet. + ข้อมูลที่แสดงอาจล้าสมัย Wallet ของคุณจะทำข้อมูลให้ตรงกับเครือข่าย Dash โดยอัตโนมัติหลังจากที่มีการเชื่อมต่อแล้ว แต่กระบวนการนี้ยังไม่เสร็จสมบูรณ์ + + + Available: + ที่สามารถใช้ได้: + + + Your current spendable balance + ยอดคงเหลือที่สามารถชำระได้ในปัจจุบันของคุณ + + + Pending: + รอดำเนินการ: + + + Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance + ยอดรวมของธุรกรรมที่ยังไม่ได้รับการยืนยันและยังไม่นับรวมยอดที่สามารถใช้จ่ายได้ + + + Immature: + ไม่สามารถใช้ได้: + + + Mined balance that has not yet matured + ยอดดุลที่ขุดยังไม่ครบ + + + Balances + ยอดคงเหลือ + + + Unconfirmed transactions to watch-only addresses + การทำธุรกรรมที่ไม่ได้รับการยืนยันใช้สำหรับดูที่อยู่เท่านั้น + + + Mined balance in watch-only addresses that has not yet matured + ยอดคงเหลือที่ขุดในที่อยู่สำหรับดูอย่างเดียวที่ยังไม่สามารถใช้งานได้ + + + Total: + ยอดรวม: + + + Your current total balance + ยอดรวมปัจจุบันของคุณ + + + Current total balance in watch-only addresses + จำนวนยอดรวมปัจจุบันในที่อยู่สำหรับดูอย่างเดียว + + + Watch-only: + สำหรับดูอย่างเดียว + + + Your current balance in watch-only addresses + ยอดเงินปัจจุบันของคุณในที่อยู่สำหรับดูอย่างเดียว + + + Spendable: + ที่สามารถใช้ได้: + + + PrivateSend + PrivateSend + + + Status: + สถานะ : + + + Enabled/Disabled + เปิด / ปิด + + + Completion: + เสร็จสิ้น: + + + Try to manually submit a PrivateSend request. + พยายามส่งคำขอ PrivateSend ด้วยตนเอง + + + Reset the current status of PrivateSend (can interrupt PrivateSend if it's in the process of Mixing, which can cost you money!) + รีเซ็ตสถานะปัจจุบันของ PrivateSend (สามารถขัดจังหวะการทำงานของ PrivateSend หากอยู่ในระหว่างการผสม ซึ่งสามารถทำให้คุณเสียเงินได้!) + + + Information about PrivateSend and Mixing + ข้อมูลเกี่ยวกับ PrivateSend และการผสม + + + Info + ข้อมูล + + + Amount and Rounds: + จำนวนและรอบ: + + + 0 DASH / 0 Rounds + 0 DASH / 0 รอบ + + + Submitted Denom: + ส่งหน่วยเงิน: + + + n/a + n / a + + + Recent transactions + ธุรกรรมล่าสุด + + + Start/Stop Mixing + เริ่ม / หยุดการผสม + + + PrivateSend Balance: + PrivateSend ยอดคงเหลือ : + + + The denominations you submitted to the Masternode.<br>To mix, other users must submit the exact same denominations. + หน่วยเงินที่คุณส่งไปยัง Masternode. <br> ในการผสมนั้นผู้ใช้อื่นจะต้องส่งหน่วยเงินที่เหมือนกัน + + + (Last Message) + (ข้อความล่าสุด) + + + Try Mix + ลอง Mix + + + Reset + รีเซ็ต + + + out of sync + ออกจากซิงค์ + + + Automatic backups are disabled, no mixing available! + การสำรองข้อมูลอัตโนมัติถูกปิดใช้งานโดยไม่มีการผสม! + + + Start Mixing + เริ่มต้นการผสม + + + Stop Mixing + หยุดการผสม + + + No inputs detected + ไม่พบข้อมูล + + + %n Rounds + %n รอบ + + + Found enough compatible inputs to anonymize %1 + พบข้อมูลที่เข้ากันได้มากพอที่จะไม่ระบุชื่อ %1 + + + Not enough compatible inputs to anonymize <span style='color:red;'>%1</span>,<br>will anonymize <span style='color:red;'>%2</span> instead + ข้อมูลไม่เพียงพอที่จะไม่ระบุชื่อ <span style='color:red;'>%1</span> <br>จะไม่ระบุชื่อ<span style='color:red;'>%2</span> แทน + + + Overall progress + ความคืบหน้าโดยรวม + + + Denominated + สกุลเงิน + + + Mixed + ผสม + + + Anonymized + ไม่ระบุชื่อ + + + Denominated inputs have %5 of %n rounds on average + อินพุตที่เป็นสกุลเงินมีค่าเฉลี่ย %5 ของรอบ %n + + + keys left: %1 + คีย์เหลืออยู่: %1 + + + Disabled + ปิดการใช้งาน + + + Very low number of keys left since last automatic backup! + จำนวนคีย์ที่เหลือต่ำมากตั้งแต่การสำรองข้อมูลอัตโนมัติครั้งล่าสุด! + + + We are about to create a new automatic backup for you, however <span style='color:red;'> you should always make sure you have backups saved in some safe place</span>! + เรากำลังจะสร้างการสำรองข้อมูลอัตโนมัติใหม่ให้กับคุณ แต่อย่างไรก็ตาม<span style='color:red;'>คุณควรตรวจสอบให้แน่ใจเสมอว่าคุณได้สำรองข้อมูลไว้ในที่ปลอดภัยแล้ว</span>! + + + Note: You can turn this message off in options. + หมายเหตุ: คุณสามารถปิดข้อความนี้ได้ + + + WARNING! Something went wrong on automatic backup + คำเตือน! เกิดข้อผิดพลาดในการสำรองข้อมูลอัตโนมัติ + + + ERROR! Failed to create automatic backup + ข้อผิดพลาด! ไม่สามารถสร้างการสำรองข้อมูลอัตโนมัติ + + + Mixing is disabled, please close your wallet and fix the issue! + ปิดใช้งานการผสม โปรดปิด Wallet ของคุณและแก้ไขปัญหา! + + + Enabled + เปิดการใช้งาน + + + see debug.log for details. + ดูรายละเอียดใน debug.log + + + WARNING! Failed to replenish keypool, please unlock your wallet to do so. + คำเตือน! ไม่สามารถเติม keypool ได้ โปรดปลดล็อก Wallet ของคุณ + + + Last PrivateSend message: + + ข้อความ PrivateSend ล่าสุด: + + + + N/A + N/A + + + PrivateSend was successfully reset. + รีเซ็ต PrivateSend สำเร็จแล้ว + + + If you don't want to see internal PrivateSend fees/transactions select "Most Common" as Type on the "Transactions" tab. + หากคุณไม่ต้องการเห็นค่าธรรมเนียม PrivateSend / ธุรกรรม ให้เลือก "Most Common" เป็นประเภท "Transactions" + + + PrivateSend requires at least %1 to use. + PrivateSend ต้องการอย่างน้อย %1 เพื่อใช้งาน + + + Wallet is locked and user declined to unlock. Disabling PrivateSend. + Wallet ถูกล็อกและผู้ใช้ปฏิเสธที่จะปลดล็อก กำลังปิดใช้งาน PrivateSend + + + + PaymentServer + + Payment request error + คำขอการชำระเงินผิดพลาด + + + Cannot start dash: click-to-pay handler + ไม่สามารถเริ่มต้น Dash: คลิกเพื่อจ่ายเงิน + + + URI handling + การจัดการ URI + + + Payment request fetch URL is invalid: %1 + URL การเรียกร้องขอการชำระเงินไม่ถูกต้อง: %1 + + + Invalid payment address %1 + ที่อยู่การชำระเงินไม่ถูกต้อง %1 + + + URI cannot be parsed! This can be caused by an invalid Dash address or malformed URI parameters. + ไม่สามารถประมวลผล URI ได้สำเร็จ ! ซึ่งอาจเกิดจากที่อยู่ Dash ไม่ถูกต้องหรือพารามิเตอร์ URI ที่มีรูปแบบไม่ถูกต้อง + + + Payment request file handling + การจัดการไฟล์คำขอชำระเงิน + + + Payment request file cannot be read! This can be caused by an invalid payment request file. + ไม่สามารถอ่านไฟล์คำขอชำระเงิน! ซึ่งอาจเกิดจากไฟล์คำขอชำระเงินที่ไม่ถูกต้อง + + + Payment request rejected + คำขอชำระเงินถูกปฏิเสธ + + + Payment request network doesn't match client network. + เครือข่ายคำขอการชำระเงินไม่ตรงกับไคลเอนต์เครือข่าย + + + Payment request expired. + คำขอชำระเงินหมดอายุแล้ว + + + Payment request is not initialized. + ไม่ได้เริ่มต้นการร้องขอการชำระเงิน + + + Unverified payment requests to custom payment scripts are unsupported. + ไม่สนับสนุนคำขอการชำระเงินที่ไม่ได้ชำระเงินไปยังสคริปต์การชำระเงินที่กำหนดเอง + + + Invalid payment request. + คำขอการชำระเงินไม่ถูกต้อง + + + Requested payment amount of %1 is too small (considered dust). + จำนวนเงินที่เรียกร้องของ %1 น้อยเกินไป (ถือว่าเป็น dust) + + + Refund from %1 + การคืนเงินจาก %1 + + + Payment request %1 is too large (%2 bytes, allowed %3 bytes). + คำขอชำระเงิน %1 ใหญ่เกินไป (%2 ไบต์ อนุญาต %3 ไบต์) + + + Error communicating with %1: %2 + เกิดข้อผิดพลาดในการสื่อสารกับ %1: %2 + + + Payment request cannot be parsed! + คำขอชำระเงินไม่สามารถประมวลผลได้! + + + Bad response from server %1 + การตอบสนองที่ไม่ถูกต้องจากเซิร์ฟเวอร์ %1 + + + Network request error + คำขอเครือข่ายผิดพลาด + + + Payment acknowledged + ยอมรับการชำระเงินแล้ว + + + + PeerTableModel + + NodeId + โหนด Id + + + Node/Service + โหนด / บริการ + + + User Agent + ตัวแทนผู้ใช้ + + + Ping + Ping + + + + QObject + + Dash Core + Dash Core + + + %1 didn't yet exit safely... + %1 ยังไม่สามารถออกจากระบบได้อย่างปลอดภัย ... + + + Error reading masternode configuration file: %1 + เกิดข้อผิดพลาดในการอ่านไฟล์การกำหนดค่า masternode: %1 + + + Amount + จำนวน + + + Enter a Dash address (e.g. %1) + ป้อนที่อยู่ Dash (เช่น %1) + + + %1 d + %1 d + + + %1 h + %1 h + + + %1 m + %1 m + + + %1 s + %1 s + + + None + ไม่มี + + + N/A + N/A + + + %1 ms + %1 ms + + + %n second(s) + %n วินาที + + + %n minute(s) + %n นาที + + + %n hour(s) + %n ชั่วโมง + + + %n day(s) + %n วัน + + + %n week(s) + %n สัปดาห์ + + + %n year(s) + %n ปี + + + %1 and %2 + %1 และ %2 + + + unknown + ไม่ทราบ + + + + QObject::QObject + + Error: Specified data directory "%1" does not exist. + ข้อผิดพลาด: ไม่มีไดเรกทอรีข้อมูลพิเศษ "%1" ที่ระบุ + + + Error: Cannot parse configuration file: %1. Only use key=value syntax. + ข้อผิดพลาด: ไม่สามารถวิเคราะห์แยกไฟล์ได้: %1 ใช้คีย์เท่านั้น = value syntax + + + Error: %1 + ข้อผิดพลาด: %1 + + + + QRDialog + + QR-Code Title + ชื่อโค้ด QR + + + QR Code + โค้ด QR + + + &Save Image... + บันทึกรูปภาพ + + + Error creating QR Code. + เกิดข้อผิดพลาดในการสร้าง QR Code + + + + QRGeneralImageWidget + + &Save Image... + บันทึกรูปภาพ + + + &Copy Image + คัดลอกรูปภาพ + + + Save QR Code + บันทึกโค้ด QR + + + PNG Image (*.png) + PNG Image (*.png) + + + + QRImageWidget + + &Save Image... + บันทึกรูปภาพ + + + &Copy Image + คัดลอกรูปภาพ + + + Save QR Code + บันทึกโค้ด QR + + + PNG Image (*.png) + PNG Image (*.png) + + + + RPCConsole + + Tools window + หน้าต่างเครื่องมือ + + + &Information + ข้อมูล + + + General + ทั่วไป + + + Name + ชื่อ + + + N/A + N/A + + + Number of connections + จำนวนการเชื่อมต่อ + + + &Open + เปิด + + + Startup time + เวลาเริ่มต้น + + + Network + เครือข่าย + + + Last block time + เวลาบล็อคล่าสุด + + + Debug log file + ไฟล์บันทึกข้อผิดพลาด + + + Current number of blocks + จำนวนบล็อคปัจจุบัน + + + Client version + ไคลเอนต์เวอร์ชั่น + + + Using BerkeleyDB version + ใช้ BerkeleyDB เวอร์ชัน + + + Block chain + Block chain + + + Number of Masternodes + จำนวน Masternodes + + + Memory Pool + หน่วยความจำ Pool + + + Current number of transactions + จำนวนธุรกรรมปัจจุบัน + + + Memory usage + การใช้ความจำ + + + &Console + คอนโซล + + + Clear console + ล้างคอนโซล + + + &Network Traffic + &Network Traffic + + + &Clear + ลบล้าง + + + Totals + ทั้งหมด + + + Received + ได้รับ + + + Sent + ส่ง + + + &Peers + Peers + + + Banned peers + peers ที่ต้องห้าม + + + Select a peer to view detailed information. + เลือก Peer เพื่อดูรายละเอียด + + + Whitelisted + รายการที่อนุญาต + + + Direction + ทิศทาง + + + Version + เวอร์ชั่น + + + Starting Block + เริ่มต้นบล็อค + + + Synced Headers + ส่วนหัวที่ซิงโครไนซ์ + + + Synced Blocks + บล็อคที่ซิงโครไนซ์ + + + Wallet Path + เส้นทาง wallet + + + User Agent + ตัวแทนผู้ใช้ + + + Datadir + Datadir + + + Open the %1 debug log file from the current data directory. This can take a few seconds for large log files. + เปิดแฟ้มบันทึกข้อผิดพลาด %1 จากไดเร็กทอรีข้อมูลปัจจุบัน อาจใช้เวลาสักครู่สำหรับไฟล์บันทึกขนาดใหญ่ + + + Decrease font size + ลดขนาดตัวอักษร + + + Increase font size + เพิ่มขนาดตัวอักษร + + + Services + บริการ + + + Ban Score + Ban Score + + + Connection Time + เวลาในการเชื่อมต่อ + + + Last Send + ส่งล่าสุด + + + Last Receive + ได้รับล่าสุด + + + Ping Time + สัญญาณ Ping + + + The duration of a currently outstanding ping. + ระยะเวลาของ ping ที่โดดเด่นในปัจจุบัน + + + Ping Wait + รอ Ping + + + Min Ping + Min Ping + + + Time Offset + เวลาชดเชย + + + &Wallet Repair + ซ่อม Wallet + + + Salvage wallet + กู้คืน Wallet + + + Rescan blockchain files + สแกน blockchain ไฟล์ใหม่ + + + Recover transactions 1 + กู้คืนการทำธุรกรรม 1 + + + Recover transactions 2 + กู้คืนการทำธุรกรรม 2 + + + Upgrade wallet format + อัพเกรดรูปแบบ wallet + + + The buttons below will restart the wallet with command-line options to repair the wallet, fix issues with corrupt blockhain files or missing/obsolete transactions. + ปุ่มด้านล่างจะเริ่มต้น wallet ด้วยตัวเลือก command-line เพื่อซ่อมแซม wallet และแก้ไขปัญหาเกี่ยวกับไฟล์ blockhain ที่เสียหายหรือการทำธุรกรรมที่ขาดหายไป / ล้าสมัย + + + -salvagewallet: Attempt to recover private keys from a corrupt wallet.dat. + -salvagewallet: พยายามกู้คืนคีย์ส่วนตัวของ wallet.dat ที่เสียหาย + + + -rescan: Rescan the block chain for missing wallet transactions. + -rescan: สแกน blockchain ใหม่ สำหรับธุรกรรม wallet ที่ขาดหายไป + + + -zapwallettxes=1: Recover transactions from blockchain (keep meta-data, e.g. account owner). + -zapwallettxes=1: กู้คืนธุรกรรมจาก blockchain (เก็บ meta-data เช่น เจ้าของบัญชี) + + + -zapwallettxes=2: Recover transactions from blockchain (drop meta-data). + -zapwallettxes=2: กู้คืนธุรกรรมจาก blockchain (ทิ้ง meta-data) + + + -upgradewallet: Upgrade wallet to latest format on startup. (Note: this is NOT an update of the wallet itself!) + -upgradewallet: อัปเดต wallet เป็นรูปแบบสุดท้ายเมื่อเริ่มต้น (หมายเหตุ: นี่ไม่ใช่การปรับปรุงตัว wallet เอง!) + + + Wallet repair options. + ตัวเลือกการซ่อมแซม Wallet + + + Rebuild index + สร้างดัชนีใหม่ + + + -reindex: Rebuild block chain index from current blk000??.dat files. + -reindex: สร้างดัชนี blockchain ใหม่จากไฟล์ blk000??.dat ปัจจุบัน + + + &Disconnect + ยกเลิกการเชื่อมต่อ + + + Ban for + Ban สำหรับ + + + 1 &hour + 1 & ชั่วโมง + + + 1 &day + 1 & วัน + + + 1 &week + 1 &สัปดาห์ + + + 1 &year + 1 & ปี + + + &Unban + &Unban + + + Welcome to the %1 RPC console. + ยินดีต้อนรับสู่คอนโซล %1 RPC + + + Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. + ใช้ลูกศรขึ้นและลงเพื่อเลื่อนดูประวัติ, และ <b>Ctrl-L</b>เพื่อเคลียร์หน้าจอ + + + Type <b>help</b> for an overview of available commands. + พิมพ์ <b>help</b>สำหรับภาพรวมของคำสั่งที่พร้อมใช้งาน + + + WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramification of a command. + คำเตือน: สแกมเมอร์กำลังใช้งานอยู่, กำลังบอกให้ผู้ใช้พิมพ์คำสั่งที่นี่, ขโมยเนื้อหาใน wallet ได้ ห้ามใช้คอนโซลนี้โดยไม่เข้าใจส่วนย่อยของคำสั่ง + + + In: + ใน: + + + Out: + นอก​: + + + Network activity disabled + ปิดการใช้งานเครือข่ายแล้ว + + + %1 B + %1 B + + + %1 KB + %1 KB + + + %1 MB + %1 MB + + + %1 GB + %1 GB + + + (node id: %1) + (node id: %1) + + + via %1 + via %1 + + + never + ไม่เคย + + + Inbound + ขาเข้า + + + Outbound + ขาออก + + + Yes + ใช่ + + + No + ไม่ + + + Unknown + ไม่ทราบ + + + + ReceiveCoinsDialog + + Reuse one of the previously used receiving addresses.<br>Reusing addresses has security and privacy issues.<br>Do not use this unless re-generating a payment request made before. + ใช้ที่อยู่ที่ได้รับก่อนหน้านี้อีกครั้งนึง<br> การใช้ที่อยู่ซ้ำมีปัญหาด้านความปลอดภัยและความเป็นส่วนตัว<br>อย่าใช้ข้อมูลนี้เว้นแต่สร้างคำขอชำระเงินที่ทำขึ้นมาใหม่มาก่อน + + + R&euse an existing receiving address (not recommended) + ใช้ที่อยู่รับที่มีอยู่ (ไม่แนะนำ) + + + An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Dash network. + ข้อความเสริมที่จะแนบไปกับคำขอการชำระเงินซึ่งจะแสดงขึ้นเมื่อมีการเปิดคำขอ หมายเหตุ: ข้อความจะไม่ถูกส่งพร้อมกับการชำระเงินผ่านเครือข่าย Dash + + + &Message: + ข้อความ: + + + An optional label to associate with the new receiving address. + ป้ายกำกับเพิ่มเติมสำหรับเชื่อมโยงกับที่อยู่รับใหม่ + + + An optional message to attach to the payment request, which will be displayed when the request is opened.<br>Note: The message will not be sent with the payment over the Dash network. + ข้อความเสริมที่จะแนบไปกับคำขอการชำระเงินซึ่งจะแสดงขึ้นเมื่อมีการเปิดคำขอ<br> หมายเหตุ: ข้อความจะไม่ถูกส่งพร้อมกับการชำระเงินผ่านเครือข่าย Dash + + + Use this form to request payments. All fields are <b>optional</b>. + ใช้แบบฟอร์มนี้เพื่อขอการชำระเงิน ฟิลด์ทั้งหมดเป็นตัวเลือกเพิ่มเติม + + + &Label: + &ชื่อ: + + + An optional amount to request. Leave this empty or zero to not request a specific amount. + จำนวนขอเพิ่มเติม เว้นว่างไว้หรือศูนย์เพื่อไม่ให้มีการร้องขอจำนวนเงินที่ระบุ + + + &Amount: + จำนวน: + + + &Request payment + ขอชำระเงิน + + + Clear all fields of the form. + ล้างข้อมูลทั้งหมดของฟอร์ม + + + Clear + ลบล้าง + + + Request InstantSend + ขอ InstantSend + + + Requested payments history + ประวัติการชำระเงินที่ขอ + + + Show the selected request (does the same as double clicking an entry) + แสดงคำขอที่เลือก (ไม่เหมือนกับการดับเบิลคลิกรายการ) + + + Show + แสดง + + + Remove the selected entries from the list + ลบรายการที่เลือกออก + + + Remove + ลบออก + + + Copy URI + คัดลอก URI + + + Copy label + คัดลอกป้ายชื่อ + + + Copy message + คัดลอกข้อความ + + + Copy amount + คัดลอกจำนวน + + + + ReceiveRequestDialog + + QR Code + โค้ด QR + + + Copy &URI + คัดลอก URI + + + Copy &Address + คัดลอกที่อยู่ + + + &Save Image... + บันทึกรูปภาพ + + + Request payment to %1 + ขอชำระเงินถึง %1 + + + Payment information + ข้อมูลการชำระเงิน + + + URI + URI + + + Address + ที่อยู่ + + + Amount + จำนวน + + + Label + ชื่อ + + + Message + ข้อความ + + + InstantSend + InstantSend + + + Yes + ใช่ + + + No + ไม่ + + + Resulting URI too long, try to reduce the text for label / message. + ผลลัพธ์ URI ยาวเกินไปพยายามลดข้อความสำหรับป้ายกำกับ / ข้อความ + + + Error encoding URI into QR Code. + เกิดข้อผิดพลาดในการเข้ารหัส URI ไปยัง QR Code + + + + RecentRequestsTableModel + + Date + วันที่ + + + Label + ชื่อ + + + Message + ข้อความ + + + (no label) + (ไม่มีชื่อ) + + + (no message) + (ไม่มีข้อความ) + + + (no amount requested) + (ไม่ต้องขอวงเงิน) + + + Requested + ร้องขอ + + + + SendCoinsDialog + + Send Coins + ส่งเหรียญ + + + Coin Control Features + คุณสมบัติการควบคุมเหรียญ + + + Inputs... + ปัจจัยการผลิต... + + + automatically selected + เลือกโดยอัตโนมัติ + + + Insufficient funds! + เงินทุนไม่เพียงพอ + + + Quantity: + จำนวน: + + + Bytes: + ไบต์: + + + Amount: + จำนวน: + + + Fee: + ค่าธรรมเนียม: + + + Dust: + เศษ: + + + After Fee: + ส่วนที่เหลือจากค่าธรรมเนียม: + + + Change: + เงินทอน: + + + If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address. + หากมีการเปิดใช้งาน แต่การเปลี่ยนแปลงที่อยู่ว่างเปล่าหรือไม่ถูกต้องการเปลี่ยนแปลงจะถูกส่งไปยังที่อยู่ที่สร้างใหม่ + + + Custom change address + กำหนดการเปลี่ยนที่อยู่เอง + + + Transaction Fee: + ค่าธรรมเนียมการทำธุรกรรม: + + + Choose... + เลือก... + + + Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until your have validated the complete chain. + การใช้ fallbackfee อาจส่งผลให้การส่งธุรกรรมที่ต้องใช้เวลาหลายชั่วโมงหรือหลายวัน (หรือไม่) เพื่อยืนยัน พิจารณาเลือกค่าธรรมเนียมด้วยตนเองหรือรอจนกว่าคุณจะได้ตรวจสอบความสมบูรณ์ของสายโซ่ + + + Warning: Fee estimation is currently not possible. + คำเตือน: ปัจจุบันการประมาณค่าใช้จ่ายไม่สามารถทำได้ + + + collapse fee-settings + ยุบค่าธรรมเนียมการตั้งค่า + + + Confirmation time target: + การยืนยันเวลาเป้าหมาย + + + PrivateSend + PrivateSend + + + InstantSend + InstantSend + + + If the custom fee is set to 1000 duffs and the transaction is only 250 bytes, then "per kilobyte" only pays 250 duffs in fee,<br />while "at least" pays 1000 duffs. For transactions bigger than a kilobyte both pay by kilobyte. + หากค่าธรรมเนียมที่กำหนดเองถูกตั้งค่าเป็น 1000 duffs และการทำธุรกรรมนี้มีเพียง 250 ไบต์ ซึ่ง "ต่อกิโลไบต์" จะจ่ายเพียง 250 duffs ในขณะที่ "อย่างน้อย" จ่าย 1000 duffs สำหรับธุรกรรมที่มีขนาดใหญ่กว่ากิโลไบต์จะต้องจ่ายเป็นกิโลไบต์ + + + If the custom fee is set to 1000 duffs and the transaction is only 250 bytes, then "per kilobyte" only pays 250 duffs in fee,<br />while "total at least" pays 1000 duffs. For transactions bigger than a kilobyte both pay by kilobyte. + หากค่าธรรมเนียมที่กำหนดเองถูกตั้งค่าเป็น 1000 duffs และการทำธุรกรรมนี้มีเพียง 250 ไบต์ ซึ่ง "ต่อกิโลไบต์" จะจ่ายเพียง 250 duffs ในขณะที่ "อย่างน้อยทั้งหมด" จ่าย 1000 duffs สำหรับธุรกรรมที่มีขนาดใหญ่กว่ากิโลไบต์จะต้องจ่ายเป็นกิโลไบต์ + + + Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks.<br />But be aware that this can end up in a never confirming transaction once there is more demand for dash transactions than the network can process. + การจ่ายเงินเพียงค่าขั้นต่ำก็ทำได้ดีตราบเท่าที่ปริมาณธุรกรรมมีน้อยกว่าพื้นที่ในบล็อก<br /> แต่โปรดทราบว่าการจ่ายนี้จะสิ้นสุดลงในธุรกรรมที่ไม่เคยมีการยืนยัน ความต้องการใช้การทำธุรกรรมแบบ Dash มีมากกว่าที่เครือข่ายสามารถดำเนินการได้ + + + per kilobyte + ต่อกิโลไบต์ + + + Hide + ซ่อน + + + total at least + รวมอย่างน้อย + + + (read the tooltip) + (อ่านคำแนะนำ) + + + Recommended: + แนะนำ: + + + Custom: + กำหนดเอง: + + + (Smart fee not initialized yet. This usually takes a few blocks...) + (ค่าธรรมเนียมยังไม่ถูกเก็บ โดยปกติจะใช้สองสามบล็อค ... ) + + + normal + ปกติ + + + fast + เร็ว + + + Confirm the send action + ยืนยันการส่ง + + + S&end + ส่ง + + + Clear all fields of the form. + ล้างข้อมูลทั้งหมดของฟอร์ม + + + Clear &All + ล้างทั้งหมด + + + Send to multiple recipients at once + ส่งไปยังผู้รับหลายรายพร้อมกัน + + + Add &Recipient + เพิ่มผู้รับ + + + Balance: + ยอดคงเหลือ: + + + Copy quantity + คัดลอกปริมาณ + + + Copy amount + คัดลอกจำนวน + + + Copy fee + คัดลอกค่าธรรมเนียม + + + Copy after fee + คัดลอกหลังหักค่าธรรมเนียม + + + Copy bytes + คัดลอกไบต์ + + + Copy dust + คัดลอก dust + + + Copy change + คัดลอกการเปลี่ยนแปลง + + + using + การใช้ + + + anonymous funds + กองทุนที่ไม่ระบุตัวตน + + + (privatesend requires this amount to be rounded up to the nearest %1). + (privatesend ต้องใช้จำนวนเงินนี้เพื่อปัดเศษขึ้นให้อยู่ใกล้ที่สุด %1) + + + any available funds (not anonymous) + เงินทุนที่มีอยู่ (ไม่ระบุชื่อ) + + + and InstantSend + และ InstantSend + + + %1 to %2 + %1 ถึง %2 + + + Are you sure you want to send? + คุณแน่ใจว่าคุณต้องการส่งใช่หรือไม่? + + + are added as transaction fee + จะถูกเพิ่มเป็นค่าธรรมเนียมการทำธุรกรรม + + + Total Amount = <b>%1</b><br />= %2 + จำนวนเงินทั้งหมด = <b>%1</b> <br />= %2 + + + <b>(%1 of %2 entries displayed)</b> + <b>(%1 of %2 รายการที่แสดง)</b> + + + Confirm send coins + ยืนยันการส่งเหรียญ + + + The recipient address is not valid. Please recheck. + ที่อยู่ผู้รับไม่ถูกต้อง โปรดตรวจสอบอีกครั้ง + + + The amount to pay must be larger than 0. + จำนวนเงินที่จ่ายต้องมีมากกว่า 0 + + + The amount exceeds your balance. + จำนวนเงินเกินยอดดุลของคุณ + + + The total exceeds your balance when the %1 transaction fee is included. + ยอดทั้งหมดเกินกว่ายอดรวมของคุณเมื่อรวมกับค่าธรรมเนียมธุรรมกรรม %1 + + + Duplicate address found: addresses should only be used once each. + พบที่อยู่ซ้ำ: ควรใช้ที่อยู่เพียงครั้งเดียวเท่านั้น + + + Transaction creation failed! + การสร้างธุรกรรมล้มเหลว! + + + The transaction was rejected with the following reason: %1 + การทำธุรกรรมถูกปฏิเสธด้วยเหตุผลดังต่อไปนี้: %1 + + + A fee higher than %1 is considered an absurdly high fee. + ค่าธรรมเนียมสูงกว่า %1 ถือว่าเป็นค่าธรรมเนียมสูงอย่างไร้เหตุผล + + + Payment request expired. + คำขอชำระเงินหมดอายุแล้ว + + + %n block(s) + %n บล็อค + + + Pay only the required fee of %1 + จ่ายเฉพาะค่าธรรมเนียมที่ต้องการจาก %1 + + + Estimated to begin confirmation within %n block(s). + คาดว่าจะเริ่มการยืนยันภายใน %n บล็อค + + + Warning: Invalid Dash address + คำเตือน: ที่อยู่ Dash ไม่ถูกต้อง + + + Warning: Unknown change address + คำเตือน: ที่อยู่ที่ไม่รู้จัก + + + Confirm custom change address + ยืนยันที่อยู่เปลี่ยนแบบกำหนดเอง + + + The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure? + ที่อยู่ที่คุณเลือกสำหรับการเปลี่ยนแปลงไม่ใช่ส่วนหนึ่งของ wallet เงินบางส่วนหรือทั้งหมดใน wallet ของคุณอาจจะถูกส่งไปยังที่อยู่นี้ คุณแน่ใจหรือไม่? + + + (no label) + (ไม่มีชื่อ) + + + + SendCoinsEntry + + This is a normal payment. + นี่เป็นการชำระเงินปกติ + + + Pay &To: + จ่ายไปยัง: + + + The Dash address to send the payment to + ที่อยู่ Dash เพื่อส่งการชำระเงินไปที่ + + + Choose previously used address + เลือกที่อยู่ที่ใช้ก่อนหน้านี้ + + + Alt+A + Alt+A + + + Paste address from clipboard + วางที่อยู่จากคลิปบอร์ด + + + Alt+P + Alt+P + + + Remove this entry + นำรายการนี้ออก + + + &Label: + &ชื่อ: + + + Enter a label for this address to add it to the list of used addresses + ใส่ป้ายกำกับสำหรับที่อยู่นี้เพื่อเพิ่มลงในรายการที่อยู่ที่ใช้ + + + A&mount: + จำนวน: + + + The fee will be deducted from the amount being sent. The recipient will receive a lower amount of Dash than you enter in the amount field. If multiple recipients are selected, the fee is split equally. + ค่าธรรมเนียมจะถูกหักออกจากจำนวนเงินที่ส่ง ผู้รับจะได้รับ Dash น้อยกว่าที่คุณป้อนในฟิลด์ หากมีการเลือกผู้รับหลายรายค่าธรรมเนียมจะแบ่งเท่า ๆ กัน + + + S&ubtract fee from amount + หักค่าบริการจากจำนวนเงิน + + + Message: + ข้อความ: + + + A message that was attached to the dash: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Dash network. + ข้อความที่แนบมาไปยัง Dash: URI ซึ่งจะถูกเก็บไว้กับธุรกรรมเพื่อเป็นข้อมูลอ้างอิงของคุณ หมายเหตุ: ข้อความนี้จะถูกส่งไปยังเครือข่าย Dash + + + This is an unauthenticated payment request. + นี่คือคำขอชำระเงินที่ไม่ได้รับการพิสูจน์ตัวตน + + + This is an authenticated payment request. + นี่คือคำขอชำระเงินที่ได้รับการพิสูจน์ตัวตน + + + Pay To: + จ่ายไปยัง: + + + Memo: + Memo: + + + Enter a label for this address to add it to your address book + ป้อนป้ายกำกับสำหรับที่อยู่นี้เพื่อเพิ่มลงในสมุดที่อยู่ของคุณ + + + + SendConfirmationDialog + + Yes + ใช่ + + + + ShutdownWindow + + %1 is shutting down... + %1 กำลังปิด... + + + Do not shut down the computer until this window disappears. + อย่าปิดคอมพิวเตอร์จนกว่าหน้าต่างนี้จะหายไป + + + + SignVerifyMessageDialog + + Signatures - Sign / Verify a Message + ลายเซ็น - ลงชื่อ / ยืนยันข้อความ + + + &Sign Message + ลงนามข้อความ + + + You can sign messages/agreements with your addresses to prove you can receive Dash sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. + คุณสามารถลงนามในข้อความ / ข้อตกลงกับที่อยู่ของคุณเพื่อพิสูจน์ว่าคุณสามารถรับ Dash ส่งถึงพวกเขาได้ ระวังอย่าเซ็นชื่ออะไรที่กำกวม เนื่องจากการโจมตีแบบฟิชชิ่งอาจพยายามหลอกล่อให้คุณเซ็นชื่อตัวคุณเอง เพียงลงนามในคำชี้แจงอย่างละเอียดที่คุณเห็นด้วยเท่านั้น + + + The Dash address to sign the message with + ที่อยู่ Dash เพื่อเซ็นชื่อ + + + Choose previously used address + เลือกที่อยู่ที่ใช้ก่อนหน้านี้ + + + Alt+A + Alt+A + + + Paste address from clipboard + วางที่อยู่จากคลิปบอร์ด + + + Alt+P + Alt+P + + + Enter the message you want to sign here + ป้อนข้อความที่คุณต้องการลงชื่อเข้าใช้ที่นี่ + + + Signature + ลายเซ็น + + + Copy the current signature to the system clipboard + คัดลอกลายเซ็นปัจจุบันไปยังคลิปบอร์ดของระบบ + + + Sign the message to prove you own this Dash address + ลงนามข้อความเพื่อพิสูจน์ว่าคุณเป็นเจ้าของที่อยู่ Dash นี้ + + + Sign &Message + ลงนามข้อความ + + + Reset all sign message fields + รีเซ็ตช่องข้อความทั้งหมด + + + Clear &All + ลบทั้งหมด + + + &Verify Message + ยืนยันข้อความ + + + Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction! + ป้อนที่อยู่ของผู้รับข้อความ (ตรวจสอบให้แน่ใจว่าคุณได้คัดลอกเว้นวรรค ช่องว่าง ย่อหน้า ฯลฯ ) และลายเซ็นด้านล่างเพื่อยืนยันข้อความ โปรดระวังอย่าใช้ข้อมูลมากกว่าข้อความที่เซ็นชื่อไว้เพื่อหลีกเลี่ยงการฉ้อโกงโดยอาศัยการโจมตีแบบ man-in-the-middle โปรดทราบว่าสิ่งนี้พิสูจน์ให้เห็นว่าบุคคลที่ลงนามได้รับพร้อมกับที่อยู่แล้วไม่สามารถพิสูจน์ว่ามีผู้ส่งรายการใด ๆ ! + + + The Dash address the message was signed with + ข้อความที่อยู่ Dash ถูกเซ็นชื่อด้วย + + + Verify the message to ensure it was signed with the specified Dash address + ยืนยันข้อความเพื่อให้แน่ใจว่าข้อความถูกเซ็นชื่อด้วยที่อยู่ Dash ที่ระบุ + + + Verify &Message + ยืนยันข้อความ + + + Reset all verify message fields + รีเซ็ตข้อความยืนยันทั้งหมด + + + Click "Sign Message" to generate signature + คลิก "ลงนามข้อความ" เพื่อสร้างลายเซ็น + + + The entered address is invalid. + ที่อยู่ที่ป้อนไม่ถูกต้อง + + + Please check the address and try again. + โปรดตรวจสอบที่อยู่และลองอีกครั้ง + + + The entered address does not refer to a key. + ที่อยู่ที่ป้อนไม่ตรงกับคีย์ + + + Wallet unlock was cancelled. + ยกเลิกการปลดล็อก Wallet แล้ว + + + Private key for the entered address is not available. + คีย์ส่วนตัวสำหรับที่อยู่ที่ป้อนไปไม่สามารถใช้งานได้ + + + Message signing failed. + การลงชื่อข้อความล้มเหลว + + + Message signed. + ข้อความที่ลงชื่อแล้ว + + + The signature could not be decoded. + ลายเซ็นไม่สามารถอ่านได้ + + + Please check the signature and try again. + โปรดตรวจสอบลายเซ็นและลองอีกครั้ง + + + The signature did not match the message digest. + ลายเซ็นไม่ตรงกับข้อความ + + + Message verification failed. + การยืนยันข้อความล้มเหลว + + + Message verified. + ข้อความได้รับการยืนยัน + + + + SplashScreen + + [testnet] + [testnet] + + + Version %1 + เวอร์ชั่น %1 + + + + TrafficGraphWidget + + KB/s + KB/s + + + + TransactionDesc + + Open for %n more block(s) + เปิดสำหรับ %n บล็อกเพิ่มเติม + + + Open until %1 + เปิดจนถึง %1 + + + conflicted + ขัดแย้ง + + + %1/offline + %1 / ออฟไลน์ + + + 0/unconfirmed, %1 + 0 / ยังไม่ได้ยืนยัน %1 + + + in memory pool + ในหน่วยความจำ pool + + + not in memory pool + ไม่อยู่ในหน่วยความจำ pool + + + abandoned + ถูกละทิ้ง + + + %1/unconfirmed + %1 / ยังไม่ได้ยืนยัน + + + %1 confirmations + %1 ยืนยัน + + + verified via InstantSend + ยืนยันผ่าน InstantSend + + + InstantSend verification in progress - %1 of %2 signatures + การยืนยันของ InstantSend อยู่ระหว่างดำเนินการ - ลายเซ็น %1 ของ %2 + + + InstantSend verification failed + การยืนยัน InstantSend ล้มเหลว + + + Status + สถานะ + + + , has not been successfully broadcast yet + ยังถ่ายทอดไม่สำเร็จ + + + , broadcast through %n node(s) + , ถ่ายทอดผ่าน %n โหนด + + + Date + วันที่ + + + Source + ซอร์ส + + + Generated + สร้าง + + + From + จาก + + + unknown + ไม่ทราบ + + + To + ถึง + + + own address + ที่อยู่ของตัวเอง + + + watch-only + ดูเท่านั้น + + + label + ชื่อ + + + Credit + เครดิต + + + matures in %n more block(s) + ครบกำหนดในอีก %n บล็อค + + + not accepted + ไม่ยอมรับ + + + Debit + เดบิต + + + Total debit + เดบิตทั้งหมด + + + Total credit + เครดิตทั้งหมด + + + Transaction fee + ค่าธรรมเนียมการทำธุรกรรม + + + Net amount + ปริมาณสุทธิ + + + Message + ข้อความ + + + Comment + ความคิดเห็น + + + Transaction ID + ไอดีธุรกรรม + + + Output index + ดัชนีผลผลิต + + + Transaction total size + ขนาดธุรกรรมทั้งหมด + + + Merchant + ร้านค้า + + + Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. + เหรียญที่สร้างต้องครบกำหนด %1 ของบล็อกก่อนที่จะสามารถใช้จ่ายได้ เมื่อสร้างบล็อคนี้จะถูกส่งไปยังเครือข่ายเพื่อเพิ่มเข้าไปในบล็อก หากคุณไม่สามารถเข้าสู่สถานะนี้ มันจะเปลี่ยนเป็น "ไม่ยอมรับ" และไม่สามารถใช้จ่ายได้อีกต่อไป กรณีนี้อาจเกิดขึ้นเป็นครั้งคราวหากโหนดอื่นสร้างบล็อกไม่กี่วินาที + + + Debug information + ข้อมูล Debug + + + Transaction + ธุรกรรม + + + Inputs + อินพุต + + + Amount + จำนวน + + + true + จริง + + + false + เท็จ + + + + TransactionDescDialog + + This pane shows a detailed description of the transaction + หน้าต่างนี้จะแสดงข้อมูลโดยละเอียดเกี่ยวกับการทำธุรกรรม + + + Details for %1 + รายละเอียดสำหรับ %1 + + + + TransactionTableModel + + Date + วันที่ + + + Type + ประเภท + + + Address / Label + ที่อยู่ / ป้ายชื่อ + + + Open for %n more block(s) + เปิดอีก %n บล็อค + + + Open until %1 + เปิดจนถึง %1 + + + Offline + ออฟไลน์ + + + Unconfirmed + ไม่มีการยืนยัน + + + Abandoned + ละทิ้ง + + + Confirming (%1 of %2 recommended confirmations) + ยืนยัน (%1 จาก %2 แนะนำการยืนยัน) + + + Confirmed (%1 confirmations) + ยืนยันแล้ว (ยืนยัน %1) + + + Conflicted + ขัดแย้ง + + + Immature (%1 confirmations, will be available after %2) + ยังไม่สมบูรณ์ (การยืนยัน %1 จะพร้อมใช้งานหลังจาก %2) + + + This block was not received by any other nodes and will probably not be accepted! + บล็อกนี้ไม่ได้รับโดยโหนดอื่น ๆ และอาจจะไม่ได้รับการยอมรับ! + + + Generated but not accepted + สร้างแล้ว แต่ไม่ยอมรับ + + + Received with + ได้รับด้วย + + + Received from + ได้รับจาก + + + Received via PrivateSend + ได้รับผ่าน PrivateSend + + + Sent to + ส่งถึง + + + Payment to yourself + ชำระเงินด้วยตัวคุณเอง + + + Mined + Mined + + + PrivateSend Denominate + PrivateSend Denominate + + + PrivateSend Collateral Payment + หลักประกันการชำระเงิน PrivateSend  + + + PrivateSend Make Collateral Inputs + PrivateSend สร้างการป้อนข้อมูลหลักประกัน + + + PrivateSend Create Denominations + PrivateSend สร้างหน่วยเงิน + + + PrivateSend + PrivateSend + + + watch-only + ดูเท่านั้น + + + (n/a) + (n/a) + + + (no label) + (ไม่มีชื่อ) + + + Transaction status. Hover over this field to show number of confirmations. + สถานะของธุรกรรม เลื่อนเมาส์ไปที่ฟิลด์นี้เพื่อดูจำนวนการยืนยัน + + + Date and time that the transaction was received. + วันที่และเวลาที่ได้รับธุรกรรม + + + Type of transaction. + ประเภทของธุรกรรม + + + Whether or not a watch-only address is involved in this transaction. + ไม่ว่าจะเป็นที่อยู่สำหรับดูอย่างเดียวหรือไม่ก็ตามก็เกี่ยวข้องกับธุรกรรมนี้ + + + User-defined intent/purpose of the transaction. + เจตนา / วัตถุประสงค์ที่กำหนดโดยผู้ใช้ของธุรกรรม + + + Amount removed from or added to balance. + จำนวนเงินที่ลบออกจากยอดคงเหลือหรือเพิ่มลงในยอดคงเหลือ + + + + TransactionView + + All + ทั้งหมด + + + Today + วันนี้ + + + This week + สัปดาห์นี้ + + + This month + เดือนนี้ + + + Last month + เดือนที่แล้ว + + + This year + ปีนี้ + + + Range... + ช่วง... + + + Most Common + ใช้บ่อยที่สุด + + + Received with + ได้รับด้วย + + + Sent to + ส่งถึง + + + PrivateSend + PrivateSend + + + PrivateSend Make Collateral Inputs + PrivateSend สร้างการป้อนข้อมูลหลักประกัน + + + PrivateSend Create Denominations + PrivateSend สร้างหน่วยเงิน + + + PrivateSend Denominate + PrivateSend Denominate + + + PrivateSend Collateral Payment + PrivateSend หลักประกันการชำระเงิน + + + To yourself + ถึงของคุณ + + + Mined + ขุด + + + Other + อื่นๆ + + + Enter address or label to search + ป้อนที่อยู่หรือป้ายชื่อที่ต้องการค้นหา + + + Min amount + จำนวนเงินขั้นต่ำ + + + Abandon transaction + ยกเลิกรายการ + + + Copy address + คัดลอกที่อยู่ + + + Copy label + คัดลอกป้ายชื่อ + + + Copy amount + คัดลอกจำนวน + + + Copy transaction ID + คัดลอก ID ธุรกรรม + + + Copy raw transaction + คัดลอกธุรกรรมดิบ + + + Copy full transaction details + คัดลอกรายละเอียดธุรกรรมทั้งหมด + + + Edit label + แก้ไขป้ายชื่อ + + + Show transaction details + แสดงรายละเอียดธุรกรรม + + + Export Transaction History + ส่งออกประวัติการทำธุรกรรม + + + Comma separated file (*.csv) + ไฟล์ที่คั่นด้วยจุลภาค (* .csv) + + + Confirmed + ยืนยันแล้ว + + + Watch-only + ดูเท่านั้น + + + Date + วันที่ + + + Type + รูปแบบ + + + Label + ชื่อ + + + Address + ที่อยู่ + + + ID + ID + + + Exporting Failed + การส่งออกล้มเหลว + + + There was an error trying to save the transaction history to %1. + เกิดข้อผิดพลาดขณะพยายามบันทึกประวัติการทำธุรกรรมเป็น %1 + + + Exporting Successful + การส่งออกสำเร็จ + + + The transaction history was successfully saved to %1. + บันทึกประวัติการทำธุรกรรมสำเร็จแล้วที่ %1 + + + Range: + ช่วง: + + + to + ถึง + + + + UnitDisplayStatusBarControl + + Unit to show amounts in. Click to select another unit. + หน่วยเพื่อแสดงจำนวนเงิน คลิกเพื่อเลือกหน่วยอื่น + + + + WalletFrame + + No wallet has been loaded. + ไม่ได้มีการโหลด wallet + + + + WalletModel + + Send Coins + ส่งเหรียญ + + + InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH. + InstantSend ไม่สนับสนุนการส่งค่าที่สูงเกินไป ธุรกรรมปัจจุบันถูก จำกัด ไว้ที่ %1 DASH + + + Used way too many inputs (>%1) for this InstantSend transaction, fees could be huge. + จำนวนอินพุทที่ใช้มากเกินไป (>%1) สำหรับการทำธุรกรรม InstantSend นี้ ค่าธรรมเนียมค่อนข้างสูง + + + + WalletView + + &Export + &ส่งออก + + + Export the data in the current tab to a file + ส่งออกข้อมูลที่อยู่ในแท็บไปที่ไฟล์ + + + Selected amount: + จำนวนที่เลือก: + + + Backup Wallet + สำรอง Wallet + + + Wallet Data (*.dat) + Wallet Data (*.dat) + + + Backup Failed + สำรองข้อมูลล้มเหลว + + + There was an error trying to save the wallet data to %1. + เกิดข้อผิดพลาดขณะพยายามบันทึกข้อมูล wallet ไปที่ %1 + + + Backup Successful + สำรองข้อมูลสำเร็จ + + + The wallet data was successfully saved to %1. + บันทึกข้อมูล wallet สำเร็จแล้วที่ %1 + + + + dash-core + + Bind to given address and always listen on it. Use [host]:port notation for IPv6 + เชื่อมโยงไปยังที่อยู่ที่กำหนดและฟังเสมอ โปรดใช้รูปแบบ [host]:port สำหรับ IPv6 + + + Error: Listening for incoming connections failed (listen returned error %s) + ข้อผิดพลาด: ฟังการเชื่อมต่อขาเข้าล้มเหลว (ฟังข้อผิดพลาด %s) + + + Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message) + ดำเนินการคำสั่งเมื่อได้รับการแจ้งเตือนที่เกี่ยวข้องหรือเราเห็น fork ที่ยาวมาก (%s ในคำสั่งจะถูกแทนที่ด้วยข้อความ) + + + Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) + ดำเนินการคำสั่งเมื่อมีการเปลี่ยนแปลงธุรกรรม wallet (%s ใน cmd จะถูกแทนที่ด้วย TxID) + + + Execute command when the best block changes (%s in cmd is replaced by block hash) + ดำเนินการคำสั่งเมื่อมีการเปลี่ยนแปลงที่ดีที่สุดของบล็อก (%s ใน cmd ถูกแทนที่ด้วย block hash) + + + Name to construct url for KeePass entry that stores the wallet passphrase + ตั้งชื่อเพื่อสร้าง URL สำหรับเข้า KeePass ที่จัดเก็บวลีรหัสผ่าน wallet + + + Set maximum size of high-priority/low-fee transactions in bytes (default: %d) + ตั้งค่าขนาดสูงสุดของธุรกรรมที่มีความสำคัญสูง / ค่าธรรมเนียมต่ำเป็นไบต์ (ค่าเริ่มต้น: %d) + + + Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d) + กำหนดจำนวนของเธรดสคริปต์การตรวจสอบ (%u ถึง %d, 0 = อัตโนมัติ, <0 = ปล่อยให้คอร์อิสระ , ค่าเริ่มต้น: %d) + + + This is a pre-release test build - use at your own risk - do not use for mining or merchant applications + นี่คือการสร้างการทดสอบก่อนวางจำหน่าย - ใช้ได้แต่ต้องรับผิดชอบความเสี่ยงด้วยตัวเอง- ไม่ใช้แอพลิเคชั่นสำหรับการทำขุดหรือร้านค้า + + + Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues. + คำเตือน: เครือข่ายไม่ปรากฏ! ไมเนอร์บางคนกำลังประสบปัญหา + + + Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade. + คำเตือน: เราไม่ปรากฎกับ peer ของเรา! คุณอาจต้องอัพเกรดหรือโหนดอื่น ๆ อาจต้องอัพเกรด + + + You must specify a masternodeprivkey in the configuration. Please see documentation for help. + คุณต้องระบุ masternodeprivkey ในการกำหนดค่า โปรดดูเอกสารประกอบการช่วยเหลือ + + + Accept command line and JSON-RPC commands + ยอมรับ command line และคำสั่ง JSON-RPC + + + Add a node to connect to and attempt to keep the connection open + เพิ่มโหนดเพื่อเชื่อมต่อและพยายามทำให้เปิดการเชื่อมต่อ + + + Allow DNS lookups for -addnode, -seednode and -connect + อนุญาต DNS ค้นหาสำหรับ -addnode, -seednode และ -connect + + + Already have that input. + มีข้อมูลดังกล่าวแล้ว + + + Block creation options: + ตัวเลือกการสร้างบล็อก: + + + Cannot downgrade wallet + ไม่สามารถลดระดับ wallet ได้ + + + Cannot write default address + ไม่สามารถเขียนที่อยู่เริ่มต้นได้ + + + Collateral not valid. + หลักประกันไม่ถูกต้อง + + + Connect to a node to retrieve peer addresses, and disconnect + เชื่อมต่อกับโหนดเพื่อดึงข้อมูลที่อยู่ของ peer และยกเลิกการเชื่อมต่อ + + + Connection options: + ตัวเลือกการเชื่อมต่อ: + + + Corrupted block database detected + ตรวจพบฐานข้อมูลบล็อกที่เสียหาย + + + Debugging/Testing options: + การแก้จุดบกพร่อง / ตัวเลือกการทดสอบ: + + + Do not load the wallet and disable wallet RPC calls + ห้ามโหลด wallet และปิดการใช้งาน wallet RPC + + + Do you want to rebuild the block database now? + คุณต้องการสร้างฐานข้อมูลบล็อกใหม่หรือไม่? + + + Done loading + โหลดเรียบร้อยแล้ว + + + Entries are full. + รายการเต็ม + + + Error initializing block database + เกิดข้อผิดพลาดในการเริ่มต้นฐานข้อมูลบล็อก + + + Error initializing wallet database environment %s! + เริ่มต้นข้อผิดพลาดด้านสภาพแวดล้อมของฐานข้อมูล Wallet %s! + + + Error loading block database + เกิดข้อผิดพลาดในการโหลดฐานข้อมูลบล็อก + + + Error opening block database + เกิดข้อผิดพลาดในการเปิดฐานข้อมูลบล็อก + + + Error reading from database, shutting down. + เกิดข้อผิดพลาดในการอ่านจากฐานข้อมูล, กำลังปิดเครื่อง + + + Error + ข้อผิดพลาด + + + Error: Disk space is low! + ข้อผิดพลาด: พื้นที่ดิสก์ต่ำ! + + + Failed to listen on any port. Use -listen=0 if you want this. + เกิดข้อผิดพลาดในการฟังพอร์ตใด ๆ ใช้ -listen = 0 ถ้าต้องการ + + + (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data) + (1 = เก็บ tx meta data เช่น ชื่อเจ้าของบัญชีและข้อมูลการชำระเงิน, 2 = ทิ้ง tx meta data) + + + -maxtxfee is set very high! Fees this large could be paid on a single transaction. + -maxtxfee ตั้งค่าไว้สูงมาก! ค่าธรรมเนียมที่มีขนาดใหญ่นี้สามารถจ่ายได้เมื่อทำธุรกรรมครั้งเดียว + + + A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s) + อัตราค่าธรรมเนียม (%s / kB) ที่จะใช้เมื่อการประมาณค่าใช้จ่ายมีข้อมูลไม่เพียงพอ (ค่าเริ่มต้น: %s) + + + Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d) + ยอมรับการถ่ายทอดธุรกรรมที่ได้รับจาก whitelisted peers แม้ว่าจะไม่ได้ทำธุรกรรมแบบเดิมก็ตาม (ค่าเริ่มต้น: %d) + + + Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times + อนุญาตให้ JSON-RPC เชื่อมต่อจากแหล่งที่ระบุ สามารถใช้ได้สำหรับ <ip> คือ IP เดียว (เช่น 1.2.3.4) network / netmask (เช่น 1.2.3.4/255.255.255.0) หรือ network / CIDR (เช่น 1.2.3.4/24) สามารถระบุตัวเลือกนี้ได้หลายครั้ง + + + Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6 + เชื่อมโยงไปยังที่อยู่ที่กำหนดและ whitelist peers ที่เชื่อมต่ออยู่ ใช้ [โฮสต์]: สัญลักษณ์พอร์ตสำหรับ IPv6 + + + Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces) + เชื่อมโยงไปยังที่อยู่ที่กำหนดเพื่อรับฟังการเชื่อมต่อ JSON-RPC ใช้ [host]:port พอร์ตสำหรับ IPv6 ตัวเลือกนี้สามารถระบุได้หลายครั้ง (ค่าดีฟอลต์: ผูกกับอินเทอร์เฟซทั้งหมด) + + + Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality) + สร้างไฟล์ใหม่ด้วยระบบสิทธิ์ดีฟอลต์แทนที่จะเป็น umask 077 (มีผลกับการทำงานของกระเป๋าสตางค์เท่านั้น) + + + Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup + ลบการทำธุรกรรมทั้งหมดของ wallet และกู้เฉพาะส่วนของ blockchain ผ่านการสแกนใหม่เมื่อเริ่มต้น + + + Error loading %s: You can't enable HD on a already existing non-HD wallet + เกิดข้อผิดพลาดในการโหลด %s: คุณไม่สามารถเปิดใช้งาน HD บน wallet ที่ไม่ใช่ของ HD ที่มีอยู่แล้ว + + + Found unconfirmed denominated outputs, will wait till they confirm to continue. + พบเอาท์พุตที่เป็นสกุลเงินที่ไม่ได้รับการยืนยัน จะรอจนกว่าพวกเขาจะยืนยันเพื่อดำเนินการต่อ + + + How thorough the block verification of -checkblocks is (0-4, default: %u) + การตรวจสอบบล็อกอย่างละเอียดด้วย -checkblocks (0-4 ค่าดีฟอลต์: %u) + + + If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u) + หากไม่ได้ตั้งค่า paytxfee ให้รวมค่าคอมมิชชั่นสำหรับการทำธุรกรรมเพื่อเริ่มต้นการยืนยันโดยเฉลี่ยในบล็อก n (ค่าเริ่มต้น: %u) + + + 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: %s, testnet: %s) + ถ้าบล็อกนี้อยู่ในสายโซ่ สมมติว่าสายโซ่และรุ่นก่อนนี้ถูกต้องและเป็นไปได้ที่จะข้ามการตรวจสอบความถูกต้องของสคริปต์ (0 เพื่อยืนยันทุกอย่าง, เริ่มต้น: %s, testnet: %s) + + + Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions) + จำนวนเงินไม่ถูกต้องสำหรับ -maxtxfee = <amount>'%s' (ต้องมีอย่างน้อยค่าธรรมเนียงการส่งของ %s เพื่อป้องกันธุรกรรมที่ติดค้าง) + + + Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u) + รักษาดัชนีการทำรายการทั้งหมดโดยใช้การเรียก gettransaction rpc (ค่าเริ่มต้น: %u) + + + Make sure to encrypt your wallet and delete all non-encrypted backups after you verified that wallet works! + ตรวจสอบให้แน่ใจว่าได้เข้ารหัส wallet ของคุณและลบสำเนาสำรองที่ไม่ได้เข้ารหัสทั้งหมดหลังจากยืนยันว่า wallet ของคุณทำงานได้! + + + Maximum size of data in data carrier transactions we relay and mine (default: %u) + ขนาดสูงสุดของข้อมูลในการทำธุรกรรมผู้ให้บริการข้อมูลที่เราโอนและขุด (ค่าเริ่มต้น: %u) + + + Number of seconds to keep misbehaving peers from reconnecting (default: %u) + จำนวนวินาทีที่ peer ทำงานไม่ถูกต้องจากการเชื่อมต่อใหม่ (ค่าเริ่มต้น: %u) + + + Output debugging information (default: %u, supplying <category> is optional) + ข้อมูลการแก้ไขจุดบกพร่องของ Output (ค่าดีฟอลต์: %u <category> เป็นตัวเลือกเพิ่มเติม) + + + Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s) + ใช้พร็อกซี SOCKS5 แยกเพื่อเข้าถึงเพื่อนร่วมทางผ่านบริการที่ซ่อนไว้ของ Tor (ค่าดีฟอลต์: %s) + + + User defined mnemonic for HD wallet (bip39). Only has effect during wallet creation/first start (default: randomly generated) + ผู้ใช้กำหนดเทคนิคช่วยจำสำหรับ wallet HD (bip39) จะมีผลเฉพาะระหว่างการสร้าง wallet / การเริ่มครั้งแรก (ค่าเริ่มต้น: สร้างแบบสุ่ม) + + + User defined seed for HD wallet (should be in hex). Only has effect during wallet creation/first start (default: randomly generated) + ผู้ใช้กำหนด seed สำหรับ wallet HD (ควรเป็นเลขฐายสิบหก) มีผลเฉพาะระหว่างการสร้างกระเป๋าสตางค์ / การเริ่มครั้งแรก (ค่าเริ่มต้น: สร้างแบบสุ่ม) + + + Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway + Whitelisted peers ไม่สามารถถูกห้ามโดย DoS และการทำธุรกรรมของพวกเขาจะถูกส่งถึงแม้ว่าจะมีอยู่ในกลุ่ม mempool แล้วก็ตามเช่น สำหรับเกตเวย์ + + + You need to rebuild the database using -reindex-chainstate to change -txindex + คุณจำเป็นต้องสร้างฐานข้อมูลโดยใช้ -reindex-chainstate เพื่อเปลี่ยน -txindex + + + (default: %s) + (ค่าดีฟอลต์: %s) + + + Accept public REST requests (default: %u) + ยอมรับคำขอ REST สาธารณะ (ค่าเริ่มต้น: %u) + + + Always query for peer addresses via DNS lookup (default: %u) + ค้นหา peer address ผ่านการค้นหา DNS เสมอ (ค่าดีฟอลต์: %u) + + + Can't mix: no compatible inputs found! + ไม่สามารถผสมได้: ไม่พบอินพุตที่เข้ากันได้! + + + Chain selection options: + ตัวเลือกการเลือก Chain: + + + Connect through SOCKS5 proxy + เชื่อมต่อผ่าน SOCKS5 proxy + + + Connect to KeePassHttp on port <port> (default: %u) + เชื่อมต่อกับ KeePassHttp บนพอร์ต <port> (ค่าเริ่มต้น: %u) + + + Enable the client to act as a masternode (0-1, default: %u) + เปิดใช้งานไคลเอ็นต์เพื่อทำหน้าที่เป็น masternode (0-1 ค่าดีฟอลต์: %u) + + + Entry exceeds maximum size. + รายการมีขนาดสูงเกินไป + + + Error loading %s: You can't disable HD on a already existing HD wallet + เกิดข้อผิดพลาดในการโหลด %s: คุณไม่สามารถปิดใช้งาน HD ใน wallet HD ที่มีอยู่แล้ว + + + Failed to load fulfilled requests cache from + โหลดคำขอแคชล้มเหลว + + + Failed to load governance cache from + ไม่สามารถโหลดแคชกำกับดูแลจาก + + + Failed to load masternode cache from + ไม่สามารถโหลดแคช masternode ได้ + + + Failed to load masternode payments cache from + ไม่สามารถโหลดแคชการชำระเงิน masternode ได้จาก + + + Found enough users, signing ( waiting %s ) + พบผู้ใช้เพียงพอ, กำลังลงนาม... ( กำลังรอ %s ) + + + Found enough users, signing ... + พบผู้ใช้เพียงพอ, กำลังลงนาม... + + + How many blocks to check at startup (default: %u, 0 = all) + จำนวนบล็อกที่ถูกตรวจสอบเมื่อเริ่มต้น (ค่าเริ่มต้น: %u, 0 = ทั้งหมด) + + + Importing... + กำลังนำเข้า... + + + Include IP addresses in debug output (default: %u) + รวมทั้งที่อยู่ IP ในผลลัพธ์การแก้ปัญหา (ค่าดีฟอลต์: %u) + + + Incompatible mode. + โหมดที่เข้ากันไม่ได้ + + + Incompatible version. + รุ่นที่เข้ากันไม่ได้ + + + Incorrect or no genesis block found. Wrong datadir for network? + ไม่ถูกต้องหรือไม่พบต้นกำเนิดบล็อค Datadir สำหรับเครือข่ายผิดหรือไม่? + + + Information + ข้อมูล + + + Input is not valid. + ข้อมูลไม่ถูกต้อง + + + Insufficient funds. + เงินทุนไม่เพียงพอ + + + Invalid -onion address: '%s' + ที่อยู่ - onion ไม่ถูกต้อง: '%s' + + + Invalid -proxy address: '%s' + ที่อยู่ - proxy ไม่ถูกต้อง: '%s' + + + Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s) + จำนวนที่ไม่ถูกต้องสำหรับ -paytxfee = <amount>: '%s' (ต้องมีอย่างน้อย %s) + + + Keypool ran out, please call keypoolrefill first + Keypool หมดแล้ว โปรดเติม Keypool ก่อน + + + Listen for JSON-RPC connections on <port> (default: %u or testnet: %u) + ฟังเพื่อการเชื่อมต่อ JSON-RPC <port> (ค่าเริ่มต้น: %u หรือ testnet: %u) + + + Listen for connections on <port> (default: %u or testnet: %u) + ฟังเพื่อการเชื่อมต่อ <port> (ค่าเริ่มต้น: %u หรือ testnet: %u) + + + Loading banlist... + กำลังโหลดรายการต้องห้าม + + + Loading fulfilled requests cache... + กำลังโหลดแคชของคำขอ... + + + Loading masternode cache... + กำลังโหลดแคช masternode ... + + + Loading masternode payment cache... + กำลังโหลดแคชการชำระเงิน masternode ... + + + Lock is already in place. + ล็อกอยู่ในตำแหน่งแล้ว + + + Lock masternodes from masternode configuration file (default: %u) + ล็อค masternodes จากไฟล์การกำหนดค่า masternode (ค่าดีฟอลต์: %u) + + + Maximum per-connection receive buffer, <n>*1000 bytes (default: %u) + สูงสุดต่อการเชื่อมต่อได้รับบัฟเฟอร์ <n> * 1000 ไบต์ (ค่าเริ่มต้น: %u) + + + Maximum per-connection send buffer, <n>*1000 bytes (default: %u) + สูงสุดบัฟเฟอร์ต่อการส่งต่อ <n> * 1000 ไบต์ (ค่าดีฟอลต์: %u) + + + Mixing in progress... + อยู่ระหว่างการผสม... + + + Mnemonic passphrase is too long, must be at most 256 characters + รหัสผ่านของ Mnemonic ยาวเกินไป จะต้องมีไม่เกิน 256 ตัว + + + Need to specify a port with -whitebind: '%s' + จำเป็นต้องระบุพอร์ตด้วย -whitebind: '%s' + + + No Masternodes detected. + ไม่พบ Masternodes + + + No compatible Masternode found. + ไม่พบ Masternode ที่เข้ากันได้ + + + Not in the Masternode list. + ไม่อยู่ในรายการ Masternode + + + Only connect to nodes in network <net> (ipv4, ipv6 or onion) + เชื่อมต่อกับโหนดในเครือข่ายเท่านั้น <net> (ipv4, ipv6 หรือ onion) + + + Port: %d + พอร์ต: %d + + + Prepend debug output with timestamp (default: %u) + แสดงผลลัพธ์ดีบักด้วย timestamp (ค่าดีฟอลต์: %u) + + + Rebuild chain state and block index from the blk*.dat files on disk + สร้างสถานะ chain และ block index จากไฟล์ blk * .dat บนดิสก์ + + + Rebuild chain state from the currently indexed blocks + สร้างสถานะ chain จากบล็อกที่จัดทำดัชนีไว้ในขณะนี้ + + + Send trace/debug info to debug.log file (default: %u) + ส่งข้อมูล trace / debug ไปยังไฟล์ debug.log (ค่าดีฟอลต์: %u) + + + Send transactions as zero-fee transactions if possible (default: %u) + ถ้าเป็นไปได้ส่งรายการเป็นธุรกรรมที่เป็น zero-fee (ค่าเริ่มต้น: %u) + + + Set key pool size to <n> (default: %u) + ตั้งค่าขนาด key pool เป็น <n> (ค่าเริ่มต้น: %u) + + + Set the number of threads to service RPC calls (default: %d) + กำหนดจำนวน threads เพื่อให้บริการการโทร RPC (ค่าเริ่มต้น: %d) + + + Specify configuration file (default: %s) + ระบุไฟล์การกำหนดค่า (ค่าดีฟอลต์: %s) + + + Specify connection timeout in milliseconds (minimum: 1, default: %d) + ระบุระยะหมดเวลาการเชื่อมต่อเป็นมิลลิวินาที (ต่ำสุด: 1 ค่าเริ่มต้น: %d) + + + Specify masternode configuration file (default: %s) + ระบุไฟล์การกำหนดค่า Masternode (ค่าดีฟอลต์: %s) + + + Specify pid file (default: %s) + ระบุไฟล์ pid (ค่าดีฟอลต์: %s) + + + Spend unconfirmed change when sending transactions (default: %u) + ใช้การเปลี่ยนแปลงยกเลิกการยืนยันเมื่อกำลังส่งธุรกรรม (ค่าเริ่มต้น: %u) + + + Submitted following entries to masternode: %u / %d + ส่งรายการต่อไปนี้ไปยัง masternode: %u / %d + + + Submitted to masternode, waiting for more entries ( %u / %d ) %s + ส่งไปยัง Masternode รอรายการเพิ่มเติม (%u / %d) %s + + + Submitted to masternode, waiting in queue %s + ส่งไปยัง masternode กำลังรอคิว %s + + + Synchroning blockchain... + กำลังซิงโครไนซ์ Blockchain... + + + Synchronization failed + การซิงโครไนซ์ล้มเหลว + + + Synchronization finished + การซิงโครไนซ์สิ้นเสร็จ + + + Synchronizing masternodes... + กำลังซิงโครไนซ์ Masternodes... + + + This is not a Masternode. + นี่ไม่ใช่ Masternodes + + + Threshold for disconnecting misbehaving peers (default: %u) + เกณฑ์สำหรับการยกเลิกการเชื่อมต่อ peer ที่ทำงานผิดปกติ (ค่าเริ่มต้น: %u) + + + Too many %f denominations, removing. + จำนวนเงิน %f มากเกินไป, กำลังลบออก + + + Tor control port password (default: empty) + รหัสผ่านพอร์ตควบคุม Tor (ค่าเริ่มต้น: ว่าง) + + + Tor control port to use if onion listening enabled (default: %s) + เปิดพอร์ตควบคุม Tor เมื่อฟังการเชื่อมต่อ onion (ค่าดีฟอลต์: %s) + + + Unable to start HTTP server. See debug log for details. + ไม่สามารถเริ่มต้นเซิร์ฟเวอร์ HTTPได้ ดูบันทึกดีบักเพื่อดูรายละเอียด + + + Unknown response. + การตอบสนองที่ไม่รู้จัก + + + Unsupported argument -benchmark ignored, use -debug=bench. + อาร์กิวเมนต์ที่ไม่ได้รับการสนับสนุน -benchmark ถูกละเว้นใช้ -debug = bench + + + Unsupported argument -debugnet ignored, use -debug=net. + อาร์กิวเมนต์ที่ไม่ได้รับการสนับสนุน -debugnet ถูกละเว้นใช้ -debug = net + + + Unsupported argument -tor found, use -onion. + อาร์กิวเมนต์ที่ไม่ได้รับการสนับสนุน พบ -tor , ใช้ -onion. + + + Upgrade wallet to latest format on startup + อัปเดต wallet เป็นรูปแบบล่าสุดเมื่อเริ่มต้น + + + Use KeePass 2 integration using KeePassHttp plugin (default: %u) + ใช้การรวม KeePass 2 โดยใช้ปลั๊กอิน KeePassHttp (ค่าเริ่มต้น: %u) + + + Use UPnP to map the listening port (default: %u) + ใช้ UPnP เพื่อวางแผนพอร์ตการฟัง (ค่าเริ่มต้น: %u) + + + Use the test chain + ใช้สายโซ่ทดสอบ + + + User Agent comment (%s) contains unsafe characters. + ตัวแทนผู้ใช้แสดงความคิดเห็น (%s) มีอักขระที่ไม่ปลอดภัย + + + Will retry... + จะลองใหม่ ... + + + Invalid masternodeprivkey. Please see documenation. + masternodeprivkey ไม่ถูกต้อง โปรดดูเอกสารประกอบ + + + Can't find random Masternode. + ไม่พบ Masternode + + + Can't mix while sync in progress. + ไม่สามารถผสมระหว่างอยู่ในดำเนินการซิงค์ได้ + + + Could not parse masternode.conf + ไม่สามารถวิเคราะห์ masternode.conf + + + Invalid netmask specified in -whitelist: '%s' + netmask ไม่ถูกต้องที่ระบุใน -whitelist: '%s' + + + Invalid port detected in masternode.conf + ตรวจพบพอร์ตที่ไม่ถูกต้องใน masternode.conf + + + Invalid script detected. + ตรวจพบสคริปต์ที่ไม่ถูกต้อง + + + KeePassHttp id for the established association + รหัส KeePassHttp สำหรับสมาคมที่จัดตั้งขึ้น + + + KeePassHttp key for AES encrypted communication with KeePass + คีย์ KeePassHttp สำหรับการสื่อสารที่เข้ารหัสด้วย AES กับ KeePass + + + Keep at most <n> unconnectable transactions in memory (default: %u) + เก็บให้ได้มากที่สุด<n>รายการที่ไม่สามารถยกเลิกการทำธุรกรรมในหน่วยความจำ (ค่าเริ่มต้น: %u) + + + Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) + ปิดใช้ฟังก์ชันการทำงานเฉพาะ Dash ทั้งหมด (Masternodes, PrivateSend, InstantSend, Governance) (0-1, ค่าดีฟอลต์: %u) + + + %s file contains all private keys from this wallet. Do not share it with anyone! + ไฟล์ %s มี private keys ทั้งหมดจาก wallet นี้ ห้ามเผยแพร่กับคนอื่น + + + Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) + ค้นพบที่อยู่ IP ของตัวเอง (ค่าเริ่มต้น: 1 เมื่อฟังและไม่มี -externalip หรือ -proxy) + + + Do not keep transactions in the mempool longer than <n> hours (default: %u) + อย่าเก็บธุรกรรมไว้ใน mempool นานกว่า <n> ชั่วโมง (ค่าเริ่มต้น: %u) + + + Enable InstantSend, show confirmations for locked transactions (0-1, default: %u) + เปิดใช้งาน InstantSend แสดงการยืนยันสำหรับธุรกรรมที่ล็อกไว้ (0-1 ค่าดีฟอลต์: %u) + + + Enable multiple PrivateSend mixing sessions per block, experimental (0-1, default: %u) + เปิดใช้งานเซสชันการผสม PrivateSend ต่อบล็อค (0-1 ค่าดีฟอลต์: %u) + + + Enable use of automated PrivateSend for funds stored in this wallet (0-1, default: %u) + เปิดใช้งาน PrivateSend โดยอัตโนมัติสำหรับเก็บเงินไว้ใน wallet นี้ (0-1 ค่าเริ่มต้น: %u) + + + Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID) + ดำเนินการคำสั่งเมื่อทำธุรกรรมแบบ InstantSend ล็อคเรียบร้อยแล้ว (%s ใน cmd จะถูกแทนที่ด้วย TxID) + + + Extra transactions to keep in memory for compact block reconstructions (default: %u) + การทำธุรกรรมพิเศษเพื่อเก็บไว้ในหน่วยความจำสำหรับการสร้างบล็อคใหม่ทำที่มีขนาดกะทัดรัด (ค่าดีฟอลต์: %u) + + + Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. + ไม่สามารถสร้างไฟล์สำรองข้อมูลได้แล้ว! กรณีนี้อาจเกิดขึ้นหากคุณรีสตาร์ท wallet ภายในเวลาไม่ถึง 60 วินาที คุณสามารถดำเนินการต่อได้หากคุณต้องการ + + + Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s) + ค่าธรรมเนียม (ใน %s / kB) เล็กกว่านี้ถือว่าเป็นศูนย์สำหรับการโอนย้าย การขุดและการทำธุรกรรม (ค่าดีฟอลต์: %s) + + + Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s) + ค่าธรรมเนียม (ใน %s / kB) เล็กกว่านี้ถือว่าเป็นศูนย์สำหรับการทำธุรกรรม (ค่าดีฟอลต์: %s) + + + If <category> is not supplied or if <category> = 1, output all debugging information. + ถ้าไม่มี <category>หรือถ้า <category>= 1 , ผลลัพธ์ก็จะเกิดจากข้อมูลการดีบักทั้งหมด + + + InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH. + InstantSend ยังไม่สนับสนุนการส่งค่าที่สูงเกินไป ธุรกรรมปัจจุบันถูกจำกัดไว้ที่ %1 DASH + + + InstantSend requires inputs with at least %d confirmations, you might need to wait a few minutes and try again. + InstantSend ต้องการอินพุตที่มีการยืนยันอย่างน้อย %d คุณอาจต้องรออีกสักครู่แล้วลองอีกครั้ง + + + Maintain a full address index, used to query for the balance, txids and unspent outputs for addresses (default: %u) + รักษาดัชนีที่อยู่แบบเต็มใช้ในการค้นหายอดคงเหลือ, txids และผลลัพธ์ที่ไม่ได้ระบุสำหรับที่อยู่ (ค่าดีฟอลต์: %u) + + + Maintain a full spent index, used to query the spending txid and input index for an outpoint (default: %u) + รักษาดัชนีที่ใช้เต็มรูปแบบ ใช้เพื่อสอบถามค่าใช้จ่าย txid และดัชนีการป้อนข้อมูลสำหรับ outpoint (ค่าเริ่มต้น: %u) + + + Maintain a timestamp index for block hashes, used to query blocks hashes by a range of timestamps (default: %u) + รักษาดัชนี timestamps สำหรับแฮชบล็อค ใช้ในการค้นหาแฮชบล็อคโดยใช้ช่วงของ timestamps (default: %u) + + + Maintain at most <n> connections to peers (temporary service connections excluded) (default: %u) + รักษาบริการเชื่อมต่อแบบ peer อย่างน้อย <n> (ไม่รวมบริการเชื่อมต่อชั่วคราว) (ค่าเริ่มต้น: %u) + + + PrivateSend uses exact denominated amounts to send funds, you might simply need to anonymize some more coins. + PrivateSend ใช้จำนวนเงินตามสกุลเงินที่แน่นอนในการส่งเงิน คุณอาจเพียงต้องการระบุชื่อเหรียญเพิ่มเติม + + + Prune configured below the minimum of %d MiB. Please use a higher number. + Prune มีการกำหนดค่าขั้นต่ำ %d MiB โปรดใช้หมายเลขที่สูงกว่า + + + Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node) + การซิงโครไนซ์ wallet ครั้งล่าสุดเหนือกว่าข้อมูล pruned คุณต้อง -reindex (ดาวน์โหลด blockchain ทั้งหมดอีกครั้งในกรณีของ pruned โหนด) + + + Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u) + สุ่มข้อมูลตัวอย่างรับรองสำหรับการเชื่อมต่อพร็อกซีทุกครั้ง ซึ่งจะช่วยให้การแยก Tor สตรีม (ค่าเริ่มต้น: %u) + + + Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again. + Rescans ไม่สามารถทำได้ในโหมด pruned คุณจะต้องใช้ -reindex ซึ่งจะดาวน์โหลด blockchain ทั้งหมดอีกครั้ง + + + Specify full path to directory for automatic wallet backups (must exist) + ระบุแพทเต็มรูปแบบไปยังไดเรกทอรีสำหรับการสำรองข้อมูล wallet อัตโนมัติ (ต้องมีอยู่) + + + Support filtering of blocks and transaction with bloom filters (default: %u) + สนับสนุนการกรองบล็อคและการทำธุรกรรมด้วยตัวกรองของบลูม หรือ bloom filters (ค่าดีฟอลต์: %u) + + + The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct + ฐานข้อมูลบล็อกประกอบด้วยบล็อกที่เหมือนจะมาจากอนาคต อาจเป็นเพราะวันที่และเวลาของคอมพิวเตอร์ของคุณไม่ถูกต้อง สร้างฐานข้อมูลบล็อกใหม่เท่านั้น ถ้าคุณแน่ใจว่าวันที่และเวลาของคอมพิวเตอร์ถูกต้อง + + + The transaction amount is too small to send after the fee has been deducted + จำนวนเงินที่ทำธุรกรรมน้อยเกินเกินไปที่จะส่ง หลังจากหักค่าธรรมเนียมแล้ว + + + Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. + ความยาวทั้งหมดของสตริงเวอร์ชันเครือข่าย (%i) เกินความยาวสูงสุด (%i) ลดจำนวนหรือขนาดของ uacomments + + + Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index. + ไม่สามารถปิดใช้ดัชนีรายการในโหมดเต็มได้ นอกจากจะเริ่มต้นด้วยสวิตช์คำสั่ง - litemode หรือเปิดใช้งานรายการดัชนี + + + Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) + พยายามให้การรับส่งข้อมูลขาออกตามเป้าหมายที่กำหนด (ใน MiB ต่อ 24 ชั่วโมง), 0 = ไม่มีขีด จำกัด (ค่าเริ่มต้น: %d) + + + Unable to locate enough PrivateSend denominated funds for this transaction. + ไม่สามารถหาเงินได้เพียงพอสำหรับ PrivateSend สำหรับธุรกรรมนี้ + + + Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported. + พบอาร์กิวเมนต์ที่ไม่สนับสนุน -socks การตั้งค่าเวอร์ชัน BB ไม่สามารถทำได้อีกต่อไป สนับสนุนพร็อกซี SOCKS5 เท่านั้น + + + Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay. + เพิกเฉยอาร์กิวเมนต์ที่ไม่สนับสนุน - whitelistalwaysrelay ใช้ -whitelistrelay และ/หรือ -whitelistforcerelay. + + + Use UPnP to map the listening port (default: 1 when listening and no -proxy) + ใช้ UPnP เพื่อวางแผนพอร์ตการฟัง (ค่าเริ่มต้น: 1 เมื่อกำลังฟังและไม่มี -proxy) + + + WARNING! Failed to replenish keypool, please unlock your wallet to do so. + คำเตือน! ไม่สามารถเติม keypool ได้ โปรดปลดล็อก wallet ของคุณเพื่อทำเช่นนั้น + + + Wallet is locked, can't replenish keypool! Automatic backups and mixing are disabled, please unlock your wallet to replenish keypool. + Wallet ถูกล็อคไม่สามารถเติม keypool! ปิดใช้งานการสำรองข้อมูลและการผสมข้อมูลอัตโนมัติ โปรดปลดล็อกเพื่อใส่พวง keypool + + + Warning: Unknown block versions being mined! It's possible unknown rules are in effect + คำเตือน: ไม่สามารถหาเงิน PrivateSend ที่ระบุได้เพียงพอสำหรับธุรกรรมนี้ + + + You are starting in lite mode, all Dash-specific functionality is disabled. + คุณกำลังเริ่มต้นใน Lite Mode ฟังก์ชันการทำงานเฉพาะ Dash ทั้งหมดจะถูกปิดใช้งาน + + + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain + คุณจำเป็นต้องสร้างฐานข้อมูลโดยใช้ -reindex ที่จะกลับไปที่โหมด unpruned และนี่จะดาวน์โหลด blockchain ที่เหลือใหม่ทั้งหมด + + + (%d could be used only on mainnet) + (%d สามารถใช้ได้เฉพาะกับ mainnet) + + + (default: %u) + (ค่าเริ่มต้น: %u) + + + (must be %d for mainnet) + (ต้องเป็น %d สำหรับ mainnet) + + + -maxmempool must be at least %d MB + -maxmempool ต้องมีอย่างน้อย %d MB + + + <category> can be: + <category>เป็นไปได้: + + + Append comment to the user agent string + เพิ่มความคิดเห็นไปยังสตริงตัวแทนผู้ใช้ + + + Automatic backups disabled + ปิดการสำรองข้อมูลอัตโนมัติแล้ว + + + Automatically create Tor hidden service (default: %d) + สร้างบริการ Tor ที่ซ่อนไว้โดยอัตโนมัติ (ค่าเริ่มต้น: %d) + + + ERROR! Failed to create automatic backup + ข้อผิดพลาด! ไม่สามารถสร้างการสำรองข้อมูลอัตโนมัติ + + + Enable publish hash block in <address> + เปิดใช้งานบล็อคแฮชใน <address> + + + Enable publish hash of governance objects (like proposals) in <address> + เปิดใช้งานแฮชการเผยแพร่เนื้อหาการกำกับดูแล (เช่น ข้อเสนอ) ใน <address> + + + Enable publish hash of governance votes in <address> + เปิดใช้งานแฮชการโหวตเกี่ยวกับการกำกับดูแลใน <address> + + + Enable publish hash transaction (locked via InstantSend) in <address> + เปิดใช้งานการทำธุรกรรมแฮช (ล็อกผ่าน InstantSend) ใน<address> + + + Enable publish hash transaction in <address> + เปิดใช้งานการธุรกรรมแฮชใน<address> + + + Enable publish raw block in <address> + เปิดใช้งานบล็อกแบบดิบใน<address> + + + Enable publish raw transaction (locked via InstantSend) in <address> + เปิดใช้งานการทำธุรกรรมดิบ (ล็อกผ่าน InstantSend) ใน<address> + + + Enable publish raw transaction in <address> + เปิดใช้งานการทำธุรกรรมดิบใหม่ใน <address> + + + Enable transaction replacement in the memory pool (default: %u) + เปิดใช้การแทนที่ธุรกรรมในหน่วยความจำ pool (ค่าดีฟอลต์: %u) + + + Error: A fatal internal error occurred, see debug.log for details + ข้อผิดพลาด: มีข้อผิดพลาดร้ายแรงภายในเกิดขึ้น โปรดดูที่ debug.log เพื่อดูรายละเอียด + + + Failed to create backup %s! + ไม่สามารถสร้างการสำรองข้อมูลได้ %s! + + + Failed to create backup, error: %s + ไม่สามารถสร้างข้อมูลสำรอง ผิดพลาด: %s + + + Failed to delete backup, error: %s + ไม่สามารถลบข้อมูลสำรอง ผิดพลาด: %s + + + Failed to load InstantSend data cache from + โหลดแคชข้อมูล InstantSend  ล้มเหลว + + + Failed to load sporks cache from + โหลดแคช sporks ล้มเหลว + + + Failed to parse host:port string + แยกวิเคราะห์ host:port ล้มเหลว + + + Fee (in %s/kB) to add to transactions you send (default: %s) + ค่าธรรมเนียม (ใน %s / kB) เพื่อเพิ่มธุรกรรมที่คุณส่ง (ค่าเริ่มต้น: %s) + + + Imports blocks from external blk000??.dat file on startup + นำเข้าบล็อคจากภายนอก blk000 ??.dat ไฟล์เมื่อเริ่มต้น + + + InstantSend options: + ตัวเลือก InstantSend : + + + Invalid amount for -fallbackfee=<amount>: '%s' + จำนวนเงินที่ไม่ถูกต้องสำหรับ -fallbackfee = 1: '%s' + + + Invalid input count. + จำนวนการป้อนข้อมูลไม่ถูกต้อง + + + Keep the transaction memory pool below <n> megabytes (default: %u) + เก็บหน่วยความจำธุรกรรมไว้ต่ำกว่า 1 เมกะไบต์ (ค่าเริ่มต้น: %u) + + + Last PrivateSend was too recent. + PrivateSend ล่าสุดเป็นข้อมูลล่าสุด + + + Last successful PrivateSend action was too recent. + การกระทำ PrivateSend ที่ประสบความสำเร็จล่าสุดเป็นข้อมูลล่าสุด + + + Line: %d + Line: %d + + + Loading InstantSend data cache... + กำลังโหลดแคชข้อมูล InstantSend ... + + + Loading addresses... + กำลังโหลดที่อยู่ ... + + + Loading block index... + กำลังโหลดดัชนีบล็อก ... + + + Loading governance cache... + โหลดแคชกำกับดูแล ... + + + Loading sporks cache... + กำลังโหลดแคช sporks... + + + Loading wallet... (%3.2f %%) + กำลังโหลด wallet... (%3.2f %%) + + + Loading wallet... + กำลังโหลด Wallet ... + + + Location of the auth cookie (default: data dir) + ตำแหน่งของคุกกี้ที่ได้รับรองความถูกต้อง (ค่าดีฟอลต์: data dir) + + + Make the wallet broadcast transactions + ดำเนินการในการเผยแพร่ธุรกรรม wallet + + + Masternode cache is empty, skipping payments and governance cache... + แคช Masternode ว่างเปล่า ละเว้นการชำระเงินและแคชการจัดการ... + + + Masternode options: + ตัวเลือก Masternode: + + + Masternode queue is full. + คิว Masternode เต็ม + + + Masternode: + Masternode: + + + Minimum bytes per sigop in transactions we relay and mine (default: %u) + ไบต์ขั้นต่ำต่อ sigop ในธุรกรรมที่เราส่งต่อและขุด (ค่าเริ่มต้น: %u) + + + Missing input transaction information. + อินพุตข้อมูลธุรกรรมขาดหายไป + + + No errors detected. + ไม่พบข้อผิดพลาด + + + No matching denominations found for mixing. + ไม่พบสกุลเงินที่เหมาะสมสำหรับการผสม + + + Node relay options: + ตัวเลือกการถ่ายทอดโหนด: + + + Non-standard public key detected. + ตรวจพบคีย์สาธารณะที่ไม่เป็นไปตามมาตรฐาน + + + Not compatible with existing transactions. + ไม่สามารถใช้งานร่วมกับธุรกรรมที่มีอยู่ได้ + + + Not enough file descriptors available. + มีคำอธิบายไฟล์เพียงพอ + + + Not enough funds to anonymize. + เงินไม่เพียงพอที่จะไม่ระบุชื่อ + + + Number of automatic wallet backups (default: %u) + จำนวนการสำรองข้อมูล Wallet อัตโนมัติ (ค่าเริ่มต้น: %u) + + + Options: + ตัวเลือก: + + + Password for JSON-RPC connections + รหัสผ่านสำหรับการเชื่อมต่อ JSON-RPC + + + Print version and exit + พิมพ์เวอร์ชันแล้วออกโปรแกรม + + + PrivateSend is idle. + PrivateSend ไม่ได้ใช้งาน + + + PrivateSend options: + ตัวเลือก PrivateSend: + + + PrivateSend request complete: + คำขอ PrivateSend สมบูรณ์: + + + PrivateSend request incomplete: + คำขอ PrivateSend ไม่สมบูรณ์: + + + Prune cannot be configured with a negative value. + Prune ไม่สามารถกำหนดค่าได้ด้วยค่าลบ + + + Prune mode is incompatible with -txindex. + โหมด Prune ไม่สามารถใช้ได้กับ -txtindex ได้ + + + Pruning blockstore... + กำลังตัด blockstore ... + + + Rescan the block chain for missing wallet transactions on startup + สแกน Block chain ใหม่สำหรับธุรกรรม Wallet ที่หายไปเมื่อเริ่มต้น + + + The wallet will avoid paying less than the minimum relay fee. + Wallet จะหลีกเลี่ยงการจ่ายเงินน้อยกว่าค่าโอนขั้นต่ำ + + + This is the minimum transaction fee you pay on every transaction. + นี่คือค่าธรรมเนียมการทำธุรกรรมขั้นต่ำที่คุณต้องจ่ายในทุกรายการ + + + This is the transaction fee you will pay if you send a transaction. + นี่คือค่าธรรมเนียมการทำธุรกรรมที่คุณจะจ่ายหากคุณส่งธุรกรรม + + + Transaction amounts must not be negative + ธุรกรรมจะต้องไม่เป็นลบ + + + Transaction has too long of a mempool chain + ธุรกรรมมี Mempool chain ที่ยาวเกินไป + + + Transaction must have at least one recipient + ธุรกรรมต้องมีผู้รับอย่างน้อยหนึ่งราย + + + Transaction too large + ธุรกรรมมีขนาดใหญ่เกินไป + + + Trying to connect... + กำลังพยายามเชื่อมต่อ ... + + + Unable to bind to %s on this computer. %s is probably already running. + ไม่สามารถผูกกับ %s คอมพิวเตอร์เครื่องนี้ได้ %s อาจกำลังทำงานอยู่แล้ว + + + Upgrading UTXO database + การอัพเกรดฐานข้อมูล UTXO + + + Use devnet chain with provided name + ใช้ devnet chain พร้อมชื่อที่ระบุ + + + Wallet debugging/testing options: + การแก้จุดบกพร่อง / ตัวเลือกการทดสอบ Wallet: + + + Wallet is not initialized + ยังไม่ได้ลงชื่อ wallet + + + Wallet needed to be rewritten: restart %s to complete + Wallet ต้องการพิมพ์ใหม่: รีสตาร์ท %s ให้เสร็จสมบูรณ์ + + + Warning: unknown new rules activated (versionbit %i) + คำเตือน: กฎใหม่ที่ไม่รู้จักถูกเปิดใช้งาน (เวอร์ชั่นบิท %i) + + + Wasn't able to create wallet backup folder %s! + ไม่สามารถสร้างโฟลเดอร์สำรอง Wallet ได้ %s! + + + Whether to operate in a blocks only mode (default: %u) + ใช้งานในโหมดบล็อกเฉพาะ (ค่าเริ่มต้น: %u) + + + You can not start a masternode in lite mode. + คุณไม่สามารถเริ่มต้น masternode ใน Lite mode + + + ZeroMQ notification options: + ตัวเลือกการแจ้งเตือน ZeroMQ: + + + no mixing available. + ไม่มีการผสม + + + see debug.log for details. + ดูรายละเอียดได้ใน debug.log + + + RPC server options: + ตัวเลือกเซิร์ฟเวอร์ RPC: + + + Dash Core + Dash Core + + + The %s developers + %s นักพัฒนา + + + Accept connections from outside (default: 1 if no -proxy or -connect/-noconnect) + ยอมรับการเชื่อมต่อจากภายนอก (ค่าเริ่มต้น: 1 ถ้าไม่มี -proxy หรือ - เชื่อมต่อ / -ไม่เชื่อมต่อ) + + + Cannot obtain a lock on data directory %s. %s is probably already running. + ไม่สามารถรับการล็อกไดเรกทอรีข้อมูล %s ได้ %s มันอาจทำงานอยู่แล้ว + + + Connect only to the specified node(s); -noconnect or -connect=0 alone to disable automatic connections + เชื่อมต่อกับโหนดที่ระบุเท่านั้น -noconnect หรือ -connect = 0 เพื่อยกเลิกการเชื่อมต่ออัตโนมัติ + + + Distributed under the MIT software license, see the accompanying file %s or %s + เผยแพร่ภายใต้ใบอนุญาตซอฟต์แวร์ MIT โปรดดูที่ไฟล์ที่แนบมา %s หรือ %s + + + Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect. + เกิดข้อผิดพลาดในการอ่าน %s! คีย์ทั้งหมดอ่านได้อย่างถูกต้อง แต่ข้อมูลธุรกรรมหรือรายการสมุดที่อยู่อาจหายไปหรือไม่ถูกต้อง + + + Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d) + บังคับให้การถ่ายทอดของธุรกรรมจาก whitelisted peers แม้ว่าจะละเมิดนโยบายการถ่ายทอดข้อมูลท้องถิ่น (ค่าเริ่มต้น: %d) + + + Incorrect or no devnet genesis block found. Wrong datadir for devnet specified? + ไม่พบหรือไม่พบแหล่งกำเนิด devnet genesis ระบุข้อมูลที่ผิดสำหรับ devnet หรือไม่ ? + + + Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds) + การปรับชดเชยชดเชยเวลาของค่ามัธยฐานได้สูงสุด มุมมองท้องถิ่นของเวลาอาจได้รับอิทธิพลจาก Peer ข้างหน้าหรือข้างหลังด้วยจำนวนนี้ (ค่าเริ่มต้น: %u วินาที) + + + Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s) + ค่าธรรมเนียมรวมสูงสุด (เป็น %s) ที่จะใช้ในธุรกรรม Wallet เดียวหรือธุรกรรมแบบดั้งเดิม การตั้งค่านี้ต่ำเกินไปอาจยกเลิกธุรกรรมที่มีขนาดใหญ่ (ค่าเริ่มต้น: %s) + + + Override spork address. Only useful for regtest and devnet. Using this on mainnet or testnet will ban you. + ยกเลิกที่อยู่ spork เป็นประโยชน์สำหรับ regtest และ devnet การใช้สิ่งนี้ใน mainnet หรือ testnet จะห้ามคุณไม่ให้ใช้ + + + Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly. + โปรดตรวจสอบว่าวันที่และเวลาของคอมพิวเตอร์ของคุณถูกต้อง! ถ้านาฬิกาของคุณไม่ตรง %s มันจะทำงานได้ไม่ถูกต้อง + + + Please contribute if you find %s useful. Visit %s for further information about the software. + โปรดให้การสนับสนุนหากคุณพบ %s ที่เป็นประโยชน์ ไปที่ %s เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับซอฟต์แวร์ + + + 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) + ให้สภาพคล่องแก่ PrivateSend โดยการผสมเหรียญเป็นระยะ ๆ อย่างต่อเนื่อง (%u-%u, ค่าเริ่มต้น: %u, 1 = บ่อยครั้งมากค่าธรรมเนียมสูง,%u = ไม่บ่อยมากค่าธรรมเนียมต่ำ) + + + Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect/-noconnect) + ข้อสงสัยสำหรับที่อยู่ของ peer ค้นหาได้ทาง DNS หากมีที่อยู่ต่ำ (ค่าเริ่มต้น: 1 เว้นแต่ -connect / -noconnect) + + + Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB) + ลดข้อกำหนดในการจัดเก็บโดยการตัดแต่ง (การลบ) บล็อกเก่า ซึ่งจะช่วยให้สามารถเรียกใช้ RPM pruneblockchain เพื่อลบบล็อคที่ต้องการและช่วยให้การตัดบล็อกเก่าโดยอัตโนมัติหากมีการกำหนดขนาดเป้าหมายไว้ใน MiB โหมดนี้ไม่สามารถใช้ได้กับ -txtindex และ -rescan คำเตือน: การคืนค่าการตั้งค่านี้จะต้องดาวน์โหลดใหม่ทั้งหมด blockchain (ค่าเริ่มต้น: 0 = ปิดใช้งานการตัดบล็อค, 1 = อนุญาตการตัดด้วยตนเองผ่านทาง RPC, >%u = อัตโนมัติตัดไฟล์บล็อคเพื่อให้อยู่ภายใต้ขนาดเป้าหมายที่ระบุใน MiB) + + + Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) + กำหนดอัตราค่าธรรมเนียมต่ำสุด (%s / kB) สำหรับธุรกรรมที่ถูกรวมในการสร้างบล็อค (ค่าดีฟอลต์: %s) + + + This is the transaction fee you may pay when fee estimates are not available. + นี่คือค่าธรรมเนียมการทำธุรกรรมที่คุณอาจจะต้องจ่าย เมื่อประมาณการค่าบริการไม่พร้อมใช้งาน + + + This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit %s and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard. + ผลิตภัณฑ์นี้รวมถึงซอฟต์แวร์ที่พัฒนาโดย OpenSSL Project เพื่อใช้ใน OpenSSL Toolkit %s และซอฟต์แวร์เข้ารหัสที่เขียนขึ้นโดย Eric Young และซอฟต์แวร์ UPnP ที่เขียนโดย Thomas Bernard + + + Unable to locate enough PrivateSend non-denominated funds for this transaction. + ไม่สามารถหาเงินทุนที่ไม่ใช่สกุลเงินที่ระบุ PrivateSend ได้มากพอสำหรับธุรกรรมนี้ + + + Use N separate masternodes for each denominated input to mix funds (%u-%u, default: %u) + ใช้ N masternodes แยกต่างหากสำหรับแต่ละสกุลเงินที่ป้อนเพื่อผสมเงิน (%u-%u, ค่าเริ่มต้น: %u) + + + Use hierarchical deterministic key generation (HD) after BIP39/BIP44. Only has effect during wallet creation/first start + ใช้คีย์ที่กำหนดตามลำดับชั้น (HD) หลังจาก BIP39 / BIP44 มีผลเฉพาะระหว่างการสร้าง / การเริ่มต้น wallet ครั้งแรก เท่านั้น + + + User defined mnemonic passphrase for HD wallet (BIP39). Only has effect during wallet creation/first start (default: empty string) + ผู้ใช้กำหนดรหัสผ่านสำหรับการจดจำรหัสผ่านสำหรับ HD Wallet (BIP39) มีผลเฉพาะระหว่างการสร้าง Wallet / การเริ่มครั้งแรก (ค่าเริ่มต้น: ว่างเปล่า) + + + Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times + ชื่อผู้ใช้และ hashed password สำหรับการเชื่อมต่อ JSON-RPC ฟิลด์<userpw>มาในรูปแบบ:<USERNAME>: <SALT>$<HASH> สคริปต์จะรวมอยู่ใน share / rpcuser ลูกค้าเชื่อมต่อโดยปกติโดยใช้ rpcuser = <USERNAME> / rpcpassword = <PASSWORD> คู่อาร์กิวเมนต์ สามารถระบุตัวเลือกนี้ได้หลายครั้ง + + + Wallet will not create transactions that violate mempool chain limits (default: %u) + Wallet จะไม่สร้างธุรกรรมที่ละเมิดข้อจำกัดของ Mempool chain (ค่าเริ่มต้น: %u) + + + Warning: At least %d of %d masternodes are running on a newer software version. Please check latest releases, you might need to update too. + คำเตือน: อย่างน้อย %d จาก %d masternodes กำลังทำงานอยู่ในเวอร์ชันซอฟต์แวร์ใหม่กว่า โปรดตรวจสอบเวอร์ชันล่าสุดคุณอาจต้องอัปเดตด้วย + + + Warning: Every masternode (out of %d known ones) is running on a newer software version. Please check latest releases, it's very likely that you missed a major/critical update. + คำเตือน: ทุก masternode (จาก %d คนที่รู้จัก) กำลังทำงานในเวอร์ชั่นซอฟต์แวร์ที่ใหม่กว่า โปรดตรวจสอบเวอร์ชันล่าสุด เป็นไปได้ที่คุณพลาดการอัปเดตที่สำคัญ + + + Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup. + คำเตือน: ไฟล์ Wallet เสียหาย, ข้อมูลได้ถูกกู้! %s จากเดิมถูกบันทึกเป็น %s ใน %s; หากยอดคงเหลือหรือธุรกรรมของคุณไม่ถูกต้องคุณควรเรียกคืนจากสำเนาสำรอง + + + Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times. + Whitelist peers กำลังเชื่อมต่อจากที่อยู่ IP ที่ระบุ (เช่น 1.2.3.4) หรือเครือข่ายที่ได้รับการจดบันทึก CIDR (เช่น 1.2.3.0/24) สามารถระบุได้หลายครั้ง + + + %s corrupt, salvage failed + %s เสียหาย, กู้ไม่สำเร็จ + + + %s is not a valid backup folder! + %s ไม่ใช่โฟลเดอร์สำรองที่ถูกต้อง! + + + %s is set very high! + %s ถูกตั้งค่าไว้สูงมาก! + + + (press q to shutdown and continue later) + (กด q เพื่อปิดเครื่องและดำเนินการต่อในภายหลัง) + + + -devnet can only be specified once + --devnet สามารถระบุได้เพียงครั้งเดียวเท่านั้น + + + -port must be specified when -devnet and -listen are specified + --port ควรระบุเฉพาะเมื่อระบุ -devnet และ -listen + + + -rpcport must be specified when -devnet and -server are specified + -rpcport ควรระบุเฉพาะเมื่อระบุ -devnet และ -server + + + Allow RFC1918 addresses to be relayed and connected to (default: %u) + อนุญาตให้ที่อยู่ RFC1918 ถูกส่งต่อและเชื่อมต่อกับ (ค่าเริ่มต้น: %u) + + + Attempt to recover private keys from a corrupt wallet on startup + พยายามกู้คืน Privatekeys จาก wallet ที่เสียหายเมื่อเริ่มต้น + + + Cannot resolve -%s address: '%s' + ไม่สามารถแก้ไขได้ -%s ที่อยู่: '%s' + + + Change index out of range + ปรับเปลี่ยนดัชนีนอกช่วง + + + Copyright (C) + ลิขสิทธิ์ (C) + + + Error loading %s + เกิดข้อผิดพลาดในการโหลด %s + + + Error loading %s: Wallet corrupted + เกิดข้อผิดพลาดในการโหลด %s: Wallet เสียหาย + + + Error loading %s: Wallet requires newer version of %s + เกิดข้อผิดพลาดในการโหลด %s: Wallet ต้องใช้เวอร์ชันที่ใหม่กว่า %s + + + Error upgrading chainstate database + เกิดข้อผิดพลาดในการอัพเกรดฐานข้อมูล chainstate + + + Failed to find mixing queue to join + ไม่สามารถหาคิวการผสมที่จะเข้าร่วมได้ + + + Failed to start a new mixing queue + ไม่สามารถเริ่มคิวการผสมใหม่ + + + Initialization sanity check failed. %s is shutting down. + การตรวจสอบความถูกต้องล้มเหลว %s กำลังจะปิด + + + Invalid amount for -%s=<amount>: '%s' + จำนวนเงินไม่ถูกต้องสำหรับ -%s = <amount>: '%s' + + + Invalid spork address specified with -sporkaddr + ที่อยู่ spork ที่ระบุด้วย -sporkaddr ไม่ถูกต้อง + + + Keep N DASH anonymized (%u-%u, default: %u) + เก็บ N DASH ไว้โดยไม่ระบุชื่อ (%u-%u, ค่าดีฟอลต์: %u) + + + Print this help message and exit + พิมพ์ข้อความช่วยเหลือนี้และออกจากโปรแกรม + + + Receive and display P2P network alerts (default: %u) + รับและแสดงการแจ้งเตือนของเครือข่าย P2P (ค่าเริ่มต้น: %u) + + + Reducing -maxconnections from %d to %d, because of system limitations. + ลดการเชื่อมต่อสูงสุดจาก %d ถึง %d เนื่องจากข้อจำกัดของระบบ + + + Relay and mine data carrier transactions (default: %u) + ส่งผ่านและขุดธุรกรรมข้อมูลผู้ให้บริการ (ค่าเริ่มต้น: %u) + + + Relay non-P2SH multisig (default: %u) + ส่งผ่าน non-P2SH multisig (ค่าดีฟอลต์: %u) + + + Rescanning... + กำลังสแกนใหม่... + + + Run in the background as a daemon and accept commands + เรียกใช้ในพื้นหลังเป็น daemon และยอมรับคำสั่ง + + + Send trace/debug info to console instead of debug.log file + ส่งข้อมูล trace / debug ไปยัง console แทนไฟล์ debug.log + + + Session not complete! + เซสชันไม่สมบูรณ์! + + + Session timed out. + เซสชันหมดอายุแล้ว + + + Set database cache size in megabytes (%d to %d, default: %d) + ตั้งค่าขนาดฐานข้อมูลเป็นเมกะไบต์ (%d ถึง %d ค่าเริ่มต้น: %d) + + + Set maximum block size in bytes (default: %d) + กำหนดขนาดบล็อกสูงสุดเป็นไบต์ (ค่าดีฟอลต์: %d) + + + Set the masternode private key + ตั้งค่าคีย์ส่วนตัว Masternode + + + Show all debugging options (usage: --help -help-debug) + แสดงการแก้จุดบกพร่องทั้งหมด (การใช้งาน: --help - help-debug) + + + Shrink debug.log file on client startup (default: 1 when no -debug) + ลดขนาดไฟล์ debug.log ในการเริ่มต้นของไคลเอ็นต์ (ค่าเริ่มต้น: 1 เมื่อไม่มี debug) + + + Signing transaction failed + ลงชื่อการทำธุรกรรมล้มเหลว + + + Specify data directory + ระบุไดเร็กทอรีข้อมูล + + + Specify wallet file (within data directory) + ระบุไฟล์ wallet (ภายในไดเรกทอรีข้อมูล) + + + Specify your own public address + กำหนดที่อยู่สาธารณะของคุณเอง + + + Starting network threads... + เริ่มต้นเธรดเครือข่าย .. + + + Synchronization pending... + กำลังรอการซิงค์ + + + Synchronizing governance objects... + กำลังปรับเทียบออบเจคการกำกับ ... + + + Synchronizing masternode payments... + กำลังซิงค์การชำระเงิน Masternode ... + + + The source code is available from %s. + ซอร์สโค้ดที่ใช้ได้จาก %s + + + The transaction amount is too small to pay the fee + จำนวนเงินที่ทำธุรกรรมมีน้อยเกินไปที่จะจ่ายค่าธรรมเนียม + + + This is experimental software. + นี่เป็นซอฟต์แวร์ทดลอง + + + Transaction amount too small + จำนวนธุรกรรมน้อยเกินไป + + + Transaction created successfully. + ทำธุรกรรมสำเร็จแล้ว + + + Transaction fees are too high. + ค่าธรรมเนียมการทำธุรกรรมสูงเกินไป + + + Transaction not valid. + ธุรกรรมไม่ถูกต้อง + + + Transaction too large for fee policy + ธุรกรรมมีขนาดใหญ่เกินไปสำหรับนโยบายค่าธรรมเนียม + + + Unable to bind to %s on this computer (bind returned error %s) + ไม่สามารถ bind กับ %s บนคอมพิวเตอร์เครื่องนี้ได้ (bind ผิดพลาด %s) + + + Unable to sign spork message, wrong key? + ไม่สามารถลงชื่อเข้าใช้ข้อความ spork ได้ คีย์ผิดหรือไม่? + + + Unknown network specified in -onlynet: '%s' + ไม่รู้จักเครือข่ายที่ระบุไว้ใน -onlynet: '%s' + + + Unknown state: id = %u + ไม่ทราบสถานะ : ไอดี = %u + + + Username for JSON-RPC connections + ชื่อผู้ใช้สำหรับการเชื่อมต่อ JSON-RPC + + + Verifying blocks... + กำลังตรวจสอบบล็อค ... + + + Verifying wallet... + กำลังตรวจสอบ Wallet... + + + Very low number of keys left: %d + จำนวนคีย์ที่เหลืออยู่ต่ำมาก: %d + + + Wallet %s resides outside data directory %s + Wallet %s อยู่นอกไดเรกทอรีข้อมูล %s + + + Wallet is locked. + ล็อค Wallet + + + Wallet options: + ตัวเลือก Wallet : + + + Wallet window title + ชื่อหน้าต่าง Wallet + + + Warning + คำเตือน + + + Your entries added successfully. + เพิ่มรายการของคุณเรียบร้อยแล้ว + + + Your transaction was accepted into the pool! + การทำธุรกรรมของคุณได้รับการยอมรับเข้าสู่ pool แล้ว + + + Zapping all transactions from wallet... + กำลังลบการทำธุรกรรมทั้งหมดจาก wallet... + + + \ No newline at end of file diff --git a/src/qt/locale/dash_pt_BR.ts b/src/qt/locale/dash_tr.ts similarity index 51% rename from src/qt/locale/dash_pt_BR.ts rename to src/qt/locale/dash_tr.ts index e525f2994a58..9b7cd22cd699 100644 --- a/src/qt/locale/dash_pt_BR.ts +++ b/src/qt/locale/dash_tr.ts @@ -1,249 +1,249 @@ - + AddressBookPage Right-click to edit address or label - Clique com o botão direito para editar o endereço ou o rótulo + Adres veya etiketi düzenlemek için sağ tıklayın. Create a new address - Criar um novo endereço + Yeni bir adres oluştur &New - &Novo + &Yeni Copy the currently selected address to the system clipboard - Copiar o endereço selecionado para a área de transferência do sistema + Şu anda seçili olan adresi sistem panosuna kopyala &Copy - &Copiar + &Kopyala Delete the currently selected address from the list - Excluir os endereços selecionados da lista + Seçili adresi listeden sil &Delete - E&xcluir + &Sil Export the data in the current tab to a file - Exportar os dados na aba atual para um arquivo + Güncel sekmedeki verileri bir dosyaya aktar &Export - &Exportar + &Dışa aktar C&lose - &Fechar + K&apat Choose the address to send coins to - Escolha o endereço no qual receberá as moedas + Parayı göndermek istediğiniz adresi seçiniz Choose the address to receive coins with - Escolha o endereço para receber moedas com ele + Parayı almak istediğiniz adresi seçiniz C&hoose - E&scolha + S&eçiniz Sending addresses - Endereços de envio + Gönderilen adresler Receiving addresses - Endereços de recebimento + Alım adresleri These are your Dash addresses for sending payments. Always check the amount and the receiving address before sending coins. - Esses são os seus endereços para enviar pagamentos. Sempre cheque a quantia e o endereço de recebimento antes de enviar moedas, + Bunlar ödemeleri göndermek için kullanacağınız Dash adreslerinizdir. Dash yollamadan önce tutarı ve alıcının alım adresini her zaman kontrol ediniz. These are your Dash addresses for receiving payments. It is recommended to use a new receiving address for each transaction. - Estes são os seus endereços para receber pagamentos. É recomendado usar um novo para cada transação. + Bunlar ödemeleri almak için kullanacağınız Dash adreslerinizdir. Her işlem için yeni bir alım adresi kullanmanız tavsiye edilir. &Copy Address - &Copiar Endereço + &Adresi Kopyala Copy &Label - Copiar &Rótulo + &Etiketi Kopyala &Edit - &Editar + &Değiştir Export Address List - Exportar lista de endereço + Adres Listesini Dışarı Aktar Comma separated file (*.csv) - Arquivo separado por vírgulas (*. csv) + Virgülle ayrılmış değerler dosyası (*.csv) Exporting Failed - Falha ao exportar + Dışarı aktarmada hata There was an error trying to save the address list to %1. Please try again. - Houve um erro ao tentar salvar a lista de endereços em %1. Por favor tente novamente. + Adres listesinin %1 konumuna kaydedilmesi sırasında bir hata meydana geldi. Lütfen tekrar deneyin. AddressTableModel Label - Rótulo + Etiket Address - Endereço + Adres (no label) - (sem rótulo) + (etiket yok) AskPassphraseDialog Passphrase Dialog - Janela da Frase de Segurança + Parola diyaloğu Enter passphrase - Digite a frase de segurança + Parolayı giriniz New passphrase - Nova frase de segurança + Yeni parola Repeat new passphrase - Repita a nova frase de segurança + Yeni parolayı tekrarlayınız Enter the new passphrase to the wallet.<br/>Please use a passphrase of <b>ten or more random characters</b>, or <b>eight or more words</b>. - Insira a nova senha para a carteira.<br/>Favor usar uma senha com <b>dez ou mais caracteres aleatórios</b>, ou <b>oito ou mais palavras</b>. + Cüzdan için yeni parolayı giriniz.<br/>Lütfen <b>on ya da daha fazla rastgele karakter</b> veya <b>sekiz ya da daha fazla kelime</b> içeren bir parola kullanınız. Encrypt wallet - Criptografar carteira + Cüzdanı şifrele This operation needs your wallet passphrase to unlock the wallet. - Esta operação precisa da sua senha para desbloquear a carteira. + Bu eylem cüzdan kilidini açmak için cüzdan parolanızı gerektirir. Unlock wallet for mixing only - Desbloquear carteira appenas para misturar + Cüzdanın kilidini sadece karışım için aç Unlock wallet - Desbloquear carteira + Cüzdan kilidini kaldır This operation needs your wallet passphrase to decrypt the wallet. - Esta operação precisa da sua senha para descriptografar a carteira + Bu eylem, cüzdan şifresini çözmek için cüzdan parolanıza ihtiyaç duyuyor. Decrypt wallet - Descriptografar carteira + Cüzdanın şifrelemesini aç Change passphrase - Alterar frase de segurança + Parola değiştir Enter the old passphrase and new passphrase to the wallet. - Insira a senha antiga e a nova para a carteira. + Eski ve yeni parolanızı cüzdana giriniz. Confirm wallet encryption - Confirmar criptografia da carteira + Cüzdan şifrelemesini onayla Warning: If you encrypt your wallet and lose your passphrase, you will <b>LOSE ALL OF YOUR DASH</b>! - Aviso: Se você encriptar sua wallet e perder sua senha, você vai <b>PERDER TODAS AS SUAS DASH</b>! + Uyarı: Eğer cüzdanınızı şifreler ve parolanızı unutursanız <b>TÜM DASHİNİZİ KAYBEDERSİNİZ</b>! Are you sure you wish to encrypt your wallet? - Tem certeza de que deseja criptografar sua carteira? + Cüzdanınızı şifrelemek istediğinizden emin misiniz? Wallet encrypted - Carteira criptografada + Cüzdan şifrelendi %1 will close now to finish the encryption process. Remember that encrypting your wallet cannot fully protect your funds from being stolen by malware infecting your computer. - A %1 irá fechar agora para terminar o processo de encriptação. Lembre-se que encriptar sua carteira não fará com que seus Dash estejam totalmente a salvo de serem roubados por malwares que infectem seu computador. + %1 şimdi şifreleme işlemini bitirmek için kapanacak. Unutmayın, cüzdanınızı şifrelemek sizi bilgisayarınıza bulaşmış olan kötü amaçlı yazılımlar tarafından paranızın çalınmasına karşı tamamen korumaz. IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. Previous backups of the unencrypted wallet file contain the same HD seed and still have full access to all your funds just like the new, encrypted wallet. - IMPORTANTE: Todos os backups anteriores feitos do seu arquivo de carteira devem ser substituídos pelo arquivo de carteira criptografado recém-gerado. Os backups anteriores do arquivo de carteira não criptografada contêm a mesma semente HD e ainda têm acesso total a todos os seus fundos, assim como a nova carteira criptografada. + ÖNEMLİ: Cüzdanınızın eskiden yapılmış yedekleri yeni oluşturulmuş, şifreli cüzdan dosyası ile değiştirilmelidir. Şifresiz cüzdan dosyasının önceki yedekleri aynı HD kaynağını içerir ve yeni, şifreli cüzdanınız gibi tüm bakiyenize erişim sağlar. IMPORTANT: Any previous backups you have made of your wallet file should be replaced with the newly generated, encrypted wallet file. For security reasons, previous backups of the unencrypted wallet file will become useless as soon as you start using the new, encrypted wallet. - IMPORTANTE: Qualquer backup prévio que você tenha feito da sua carteira deve ser substituído pelo novo e encriptado arquivo gerado. Por razões de segurança, qualquer backup do arquivo não criptografado se tornará inútil assim que você começar a usar uma nova carteira criptografada. + ÖNEMLİ: Önceden yapmış olduğunuz cüzdan dosyası yedeklemelerinin yeni oluşturulan şifrelenmiş cüzdan dosyası ile değiştirilmeleri gerekir. Güvenlik nedenleriyle yeni, şifrelenmiş cüzdanı kullanmaya başladığınızda eski şifrelenmemiş cüzdan dosyaları işe yaramaz hale gelecektir. Wallet encryption failed - A criptografia da carteira falhou + Cüzdan şifreleme başarısız Wallet encryption failed due to an internal error. Your wallet was not encrypted. - Falha na criptografia devido a um erro inerno. Sua carteira não foi criptografada. + Dahili bir hata yüzünden cüzdan şifrelemesi başarısız oldu. Cüzdanın şifrelenmedi. The supplied passphrases do not match. - A frase de segurança fornecida não confere. + Girilen parolalar birbiriyle eşleşmiyor. Wallet unlock failed - O desbloqueio da carteira falhou + Cüzdan kilidini kaldırma başarısız oldu The passphrase entered for the wallet decryption was incorrect. - A senha inserida para descriptografar a carteira está incorreta. + Cüzdan şifresinin açılması için girilen parola yanlıştı. Wallet decryption failed - A descriptografia da carteira falhou + Cüzdan şifresinin açılması başarısız oldu Wallet passphrase was successfully changed. - A frase de segurança da carteira foi alterada com êxito. + Cüzdan parolası başarılı bir şekilde değiştirildi. Warning: The Caps Lock key is on! - Atenção: A tecla Caps Lock está ligada! + Uyarı: Caps Lock tuşu etkin durumda! BanTableModel IP/Netmask - IP/Máscara de rede + IP/Ağ Maskesi Banned Until - Banido até + Şu zamana kadar yasaklı: BitcoinGUI A fatal error occurred. Dash Core can no longer continue safely and will quit. - Ocorreu um erro fatal. A Dash não pode mais continuar de forma segura, e irá fechar + Kritik bir hata oluştu. Dash Core artık güvenli şekilde devam edemez ve kapanacak. Dash Core @@ -251,739 +251,672 @@ Wallet - carteira + Cüzdan Node - + Düğüm &Overview - &Visão geral + &Genel bakış Show general overview of wallet - Mostrar visão geral da carteira + Cüzdana genel bakışı göster &Send - &Enviar + &Gönder Send coins to a Dash address - Envie moedas para um endereço Dash + Bir Dash adresine coin gönder &Receive - &Receber + &Al Request payments (generates QR codes and dash: URIs) - Solicitações de pagamentos (gera códigos QR e Dash: URIs) + Ödeme talep et (QR kodu ve Dash URI'si oluşturur) &Transactions - &Transações + &İşlemler Browse transaction history - Navegar pelo histórico de transações + İşlem geçmişine gözat &Masternodes - &Masternodes + &Ana düğümler Browse masternodes - Exibir masternodes + Ana düğümlere gözat E&xit - S&air + Ç&ık Quit application - Sair da aplicação + Uygulamadan çık Show information about Dash Core - Exibe informações sobre Dash Core + Dash Core hakkında bilgileri göster About &Qt - Sobre &Qt + &Qt Hakkında Show information about Qt - Exibe informações sobre Qt + Qt hakkında bilgi göster &Options... - &Opções... + &Seçenekler... &About %1 - &Sobre %1 + %1 &Hakkında Modify configuration options for %1 - Modificar opções de configuração para o %1 + %1 için yapılandırma ayarlarını değiştir &Show / Hide - &Exibir/Ocultar + &Göster / Gizle Show or hide the main Window - Mostrar ou esconder a Janela Principal. + Ana pencereyi göster ya da gizle &Encrypt Wallet... - &Criptografar Carteira... + &Cüzdanı Şifrele... Encrypt the private keys that belong to your wallet - Criptografar as chaves privadas que pertencem à sua carteira + Cüzdanınıza ait özel anahtarları şifreleyin &Backup Wallet... - Fazer &Backup da Carteira... + &Cüzdanı Yedekle... Backup wallet to another location - Faça o Backup da carteira para outro local + Cüzdanı diğer bir konumda yedekle &Change Passphrase... - Alterar frase de segurança + &Parolayı Değiştir... Change the passphrase used for wallet encryption - Mudar a frase de segurança utilizada na criptografia da carteira + Cüzdan şifrelemesi için kullanılan parolayı değiştir &Unlock Wallet... - &Desbloquear carteira... + Cüzdanı &Kilitle... Unlock wallet - Desbloquear carteira + Cüzdan kilidini kaldır &Lock Wallet - Bloquear carteira + Cüzdanı &Kilitle Sign &message... - Assinar &Mensagem... + &İleti imzala... Sign messages with your Dash addresses to prove you own them - Assine mensagens com seus endereços Dash para provar que você é dono delas + İletileri adreslerin size ait olduğunu ispatlamak için Dash adresleri ile imzala &Verify message... - &Verificar mensagem... + İletiyi &kontrol et... Verify messages to ensure they were signed with specified Dash addresses - Verifique as mensagens para ter certeza de que elas foram assinadas com o endereço da Dash especificado + Belirtilen Dash adresleri ile imzalandıklarından emin olmak için iletileri kontrol et &Information - &Informação + &Bilgi Show diagnostic information - Mostrar informação de diagnóstico + Tanı bilgilerini göster &Debug console - &Console de depuração + &Debug konsolu Open debugging console - Abrir o console de depuração + Debug konsolunu aç &Network Monitor - &Monitor de Rede + &Ağ Monitörü Show network monitor - Exibe o monitor de rede + Ağ monitörünü göster &Peers list - &Peers + &Eş listesi Show peers info - mostrar informações dos peers + Eş bilgisini göster Wallet &Repair - &Reparar carteira + Cüzdan &Tamir Show wallet repair options - Mostrar opções de reparação da carteira + Cüzdan tamir seçeneklerini göster Open Wallet &Configuration File - Abrir arquivo de &Configuração da carteira + Cüzdan &Ayar Dosyasını Aç Open configuration file - Abrir arquivo de configuração + Yapılandırma Dosyasını Aç Open &Masternode Configuration File - Abrir arquivo de configuração do &Masternode + &Ana Düğüm Ayar Dosyasını Aç Open Masternode configuration file - Abrir arquivo de configuração de Masternode + Ana düğüm ayar dosyasını aç Show Automatic &Backups - Mostrar &backups automáticos + Otomatik &Yedekleri Göster Show automatically created wallet backups - Mostrar backups da carteira criados automaticamente + Otomatik oluşturulan cüzdan yedeklerini göster &Sending addresses... - Endereço&s de envio... + &Gönderme adresleri... Show the list of used sending addresses and labels - Mostrar a lista de endereços de envio e rótulos usados + Kullanılmış gönderme adresleri ve etiketlerin listesini göster &Receiving addresses... - Endereços de &recebimento... + &Alma adresleri... Show the list of used receiving addresses and labels - Mostrar a lista de endereços de recebimento usados ​​e rótulos + Kullanılmış alım adresleri ve etiketlerin listesini göster Open &URI... - Abrir &URI... + &URI Aç... Open a dash: URI or payment request - Abrir um Dash: URI ou cobrança + Bir Dash: bağlantısı ya da ödeme talebi aç &Command-line options - Opções de linha de &comando + &Komut satırı seçenekleri Show the %1 help message to get a list with possible Dash command-line options - Mostrar a mensagem de ajuda do %1 para obter uma lista com possíveis opções de linha de comando Dash + Olası Dash komut satırı seçeneklerinin listesini görmek için %1 yardım mesajını göster %1 client - %1 cliente + %1 istemci &PrivateSend information - Informação de Envio&Privado + &Özel Gönder bilgisi Show the PrivateSend basic information - Mostra informações básicas de EnvioPrivado + Özel Gönder temel bilgilerini göster &File - &Arquivo + &Dosya &Settings - &Configurações + &Ayarlar &Tools - &Ferramentas + &Araçlar &Help - &Ajuda + &Yardım Tabs toolbar - Barra de ferramentas + Sekme araç çubuğu %n active connection(s) to Dash network - %n conexões ativas com a rede Dash%n conexões ativas com a rede Dash + Dash ağına %n aktif bağlantıDash ağına %n aktif bağlantı Network activity disabled - Atividade da rede disativada + Ağ etkinliği devre dışı bırakıldı Syncing Headers (%1%)... - Sincronizando cabeçahos (%1%)... + Üstbilgiler Senkronize Ediliyor (%1%)... Synchronizing with network... - Sincronizando com a rede... + Ağ ile senkronize ediliyor... Indexing blocks on disk... - Indexando blocos no disco... + Bloklar diske indeksleniyor... Processing blocks on disk... - Processando blocos no disco... + Bloklar diske işleniyor... Reindexing blocks on disk... - Reindexando blocos no disco... + Diskteki bloklar yeniden indeksleniyor... Connecting to peers... - Conectando... + Eşlere bağlanılıyor... Processed %n block(s) of transaction history. - Processados %n blocos do histórico de transações.Processados %n blocos do histórico de transações. + İşlem geçmişindeki %n blok işlendi.İşlem geçmişindeki %n blok işlendi. %1 behind - %1 atrás + %1 geride Catching up... - Recuperando o atraso ... + Aralık kapatılıyor... Last received block was generated %1 ago. - Último bloco recebido foi gerado %1 atrás. + Son alınan blok %1 önce oluşturulmuştu. Transactions after this will not yet be visible. - Transações após isso ainda não estão visíveis. + Bundan sonraki işlemler henüz görüntülenemez. Up to date - Atualizado + Güncel Synchronizing additional data: %p% - Sincronizando dados adicionais: %p% + Ek veri eşleniyor: %p% Error - Erro + Hata Warning - Atenção + Uyarı Information - Informação + Bilgi Date: %1 - Data: %1 + Tarih: %1 Amount: %1 - Quantia: %1 + Tutar: %1 Type: %1 - Tipo: %1 + Tür: %1 Label: %1 - Rótulo: %1 + Etiket: %1 Address: %1 - Endereço: %1 + Adres: %1 Sent transaction - Transação enviada + İşlem gönderildi Incoming transaction - Transação recebida + Gelen işlem HD key generation is <b>enabled</b> - Geração de chave HD está <b>ativada</b> + HD anahtar oluşturma <b>etkin</b> HD key generation is <b>disabled</b> - Geração de chave HD está <b>desativada</b> + HD anahtar oluşturma <b>devre dışı</b> Wallet is <b>encrypted</b> and currently <b>unlocked</b> - Carteira está <b>criptografada</b> e atualmente <b>desbloqueada</b> + Cüzdan <b>şifrelenmiştir</b> ve şu anda <b>kilidi açıktır</b> Wallet is <b>encrypted</b> and currently <b>unlocked</b> for mixing only - A carteira está <b>encriptada</b> e atualmente <b>desbloqueada</b> apenas para anonimização + Cüzdan <b>şifrelenmiştir</b> ve şu anda kilidi sadece karışım için <b>açıktır</b> Wallet is <b>encrypted</b> and currently <b>locked</b> - Carteira está <b>criptografada</b> e atualmente <b>bloqueada</b> + Cüzdan <b>şifrelenmiştir</b> ve şu anda <b>kilitlidir</b> ClientModel Total: %1 (PS compatible: %2 / Enabled: %3) - Total: %1 (Compatível c/ EP: %2 / Habilitado: %3) + Toplam %1 (ÖG uyumlu: %2 / Etkin: %3) Network Alert - Alerta da Rede + Ağ Uyarısı CoinControlDialog Quantity: - Quantidade: + Miktar: Bytes: - Bytes: + Bayt: Amount: - Quantia: + Meblağ: Fee: - Taxa: + Ücret: Coin Selection - Seleção da Moeda + Bitcoin Seçimi Dust: - Poeira: + Toz: After Fee: - Depois da taxa: + Ücretten sonra: Change: - trocar + Para üstü: (un)select all - (de)selecionar tudo + tümünü seç(me) toggle lock state - alternar estado de bloqueio + kilit durumunu aç/kapa Tree mode - Modo árvore + Ağaç kipi List mode - Modo lista + Liste kipi (1 locked) - (1 travado) + (1 kilitli) Amount - Quantidade + Meblağ Received with label - Recebido com rótulo + Şu etiketle alındı Received with address - Recebido com endereço + Şu adresle alındı PS Rounds - Rounds de EP + ÖG Turları Date - Data + Tarih Confirmations - Confirmações + Doğrulamalar Confirmed - Confirmado + Doğrulandı Copy address - Copie endereço + Adres kopyala Copy label - Copiar rótulo + Etiket kopyala Copy amount - Copie quantia + Tutarı kopyala Copy transaction ID - Copie ID da transação + İşlem ID'sini kopyala Lock unspent - Bloquear não gasto + Harcanmamışı kilitle Unlock unspent - Desbloquear não gasto + Harcanmamışın kilidini aç Copy quantity - Copiar quantidade + Miktarı kopyala Copy fee - Copie taxa + Ücreti kopyala Copy after fee - Copiar pós taxa + Ücretten sonrasını kopyala Copy bytes - Copiar bytes + Baytları kopyala Copy dust - Copiar poeira + Tozu kopyala Copy change - Copiar troco + Para üstünü kopyala Please switch to "List mode" to use this function. - Por favor alterne para "Modo lista" para usar essa função. + Bu fonksiyonu kullanmak için lütfen "Liste modu"na geçin. - Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again. - Entrada não anônima selecionada. <b>O PrivateSend será desativado.</b> <br> <br> Se você ainda quiser usar o PrivateSend, desmarque primeiro todas as entradas não modificadas e marque a caixa de seleção PrivateSend novamente. + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. + Anonim hale getirilmemiş girdi seçildi. <b>Özel Gönder devre dışı olacak</b><br><br>Yine de Özel Gönder kullanmak istiyorsanız, lütfen önce anonim hale getirilmemiş girdilerin seçimini kaldırın sonra Özel Gönder kutusunu işaretleyin. (%1 locked) - (%1 bloqueado) + (%1 kilitlendi) yes - sim + Evet no - não + Hayır This label turns red if any recipient receives an amount smaller than the current dust threshold. - Este texto fica vermelho se qualquer destinatário receber uma quantidade menor que que o dust. + Eğer herhangi bir alıcı mevcut toz eşiğinden daha düşük bir tutar alırsa bu etiket kırmızıya dönüşür. Can vary +/- %1 duff(s) per input. - Pode variar +/- %1 duff(s) por entrada. + Girdi başına +/- %1 duff değişebilir. (no label) - (sem rótulo) + (etiket yok) change from %1 (%2) - troco de %1 (%2) + %1 ögesinden para üstü (%2) (change) - (troco) + (para üstü) n/a - n/d - - - - DarksendConfig - - Configure PrivateSend - Configurar ProvateSend - - - Basic Privacy - Privacidade Básica - - - High Privacy - Alta Privacidade - - - Maximum Privacy - Privacidade Máxima - - - Please select a privacy level. - Por favor, escolha um nível de privacidade. - - - Use 2 separate masternodes to mix funds up to 1000 DASH - Usar 2 Masternodes separados para fazer a mixagem de fundos até 1000 DASH - - - Use 8 separate masternodes to mix funds up to 1000 DASH - Usar 8 Masternodes separados para fazer a mixagem de fundos até 1000 DASH - - - Use 16 separate masternodes - Usar 16 Masternodes separados - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - Esta é a opção mais rápida e custará em torno de ~0,025 DASH para anonimizar 1000 DASH - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - Esta opção é moderadamente rápida e custará em torno de ~0,05 DASH para anonimizar 1000 DASH - - - 0.1 DASH per 1000 DASH you anonymize. - 0,1 DASH por cada 1000 DASH que você anonimizar. - - - This is the slowest and most secure option. Using maximum anonymity will cost - Esta é a opção mais lenta e mais segura. Usar o máximo de anonimidade custará - - - PrivateSend Configuration - Configuração EnvioPrivado - - - PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - O PrivateSend foi ajustado com sucesso para básico (%1 e 2 rodadas). Você pode alterar esse ajuste a qualquer momento, abrindo a tela de configuração da Dash. - - - PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - O PrivateSend foi ajustado com sucesso para alto(%1 e 8 rodadas). Você pode alterar esse ajuste a qualquer momento, abrindo a tela de configuração da Dash. - - - PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - O PrivateSend foi ajustado com sucesso para máximo (%1 e 16 rodadas). Você pode alterar esse ajuste a qualquer momento, abrindo a tela de configuração da Dash. + Mevcut değil EditAddressDialog Edit Address - Editar Endereço + Adresi düzenle &Label - &Rótulo + &Etiket The label associated with this address list entry - O rótulo associado a esta entrada na lista de endereços + Bu adres listesi girdisi ile ilişkili etiket &Address - &Endereço + &Adres The address associated with this address list entry. This can only be modified for sending addresses. - O endereço associado a esta lista de endereços de entrada. Isso só pode ser modificado para o envio de endereços. + Bu adres listesi girdisi ile ilişkili adres. Sadece gönderme adresleri için değiştirilebilir. New receiving address - Novo endereço de recebimento + Yeni alım adresi New sending address - Novo endereço de envio + Yeni gönderi adresi Edit receiving address - Editar endereço de recebimento + Alım adresini düzenle Edit sending address - Editar endereço de envio + Gönderi adresini düzenle The entered address "%1" is not a valid Dash address. - O endereço digitado "%1" não é um endereço válido. + Girilen %1 adresi, geçerli bir Dash adresi değildir. The entered address "%1" is already in the address book. - O endereço digitado "%1" já se encontra no catálogo de endereços. + Girilen "%1" adresi zaten adres defterinde mevcuttur. Could not unlock wallet. - Não foi possível desbloquear a carteira. + Cüzdan kilidi açılamadı. New key generation failed. - Falha na geração de nova chave. + Yeni anahtar oluşturulması başarısız oldu. FreespaceChecker A new data directory will be created. - Um novo diretório de dados será criado. + Yeni bir veri klasörü oluşturulacaktır. name - nome + İsim Directory already exists. Add %1 if you intend to create a new directory here. - O diretório já existe. Adicione %1 se você pretende criar um novo diretório aqui. + Klasör zaten mevcuttur. Burada yeni bir klasör oluşturmak istiyorsanız, %1 ekleyiniz. Path already exists, and is not a directory. - Esta pasta já existe, e não é um diretório. + Erişim yolu zaten mevcuttur ve klasör değildir. Cannot create data directory here. - Não é possível criar um diretório de dados aqui + Burada veri klasörü oluşturulamaz. HelpMessageDialog version - versão + Sürüm (%1-bit) @@ -991,165 +924,169 @@ About %1 - Sobre %1 + %1 Hakkında Command-line options - opções da linha de comando + komut satırı seçenekleri Usage: - Uso: + Kullanım: command-line options - opções da linha de comando + komut satırı seçenekleri UI Options: - opções da UI + Arayüz Seçenekleri: Choose data directory on startup (default: %u) - Escolher diretório de dados na inicialização (padrão: %u) + Başlangıçta veri klasörü seç (varsayılan: %u) Set language, for example "de_DE" (default: system locale) - Definir idioma, por exemplo "de_DE" (padrão: idioma do sistema) + Lisan belirt, mesela "de_De" (varsayılan: sistem dili) Start minimized - Iniciar minimizado + Küçültülmüş olarak başlat Set SSL root certificates for payment request (default: -system-) - Definir certificados de root SSL para requisições de pagamento (padrão: -sistema-) + Ödeme talebi için SSL kök sertifikalarını belirle (varsayılan: -system-) Show splash screen on startup (default: %u) - Exibir tela de abertura na inicialização (padrão: %u) + Başlatıldığında başlangıç ekranını göster (varsayılan: %u) Reset all settings changed in the GUI - Resetar todas as configuraçãoes do GUI + Grafik arayüzde yapılan tüm seçenek değişikliklerini sıfırla PrivateSend information - Informação de Envio&Privado + Özel Gönder bilgisi + + + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. + <h3>Özel Gönder Temel Bilgiler</h3> Özel Gönder size paranızın gerçek kaynağını gizleyerek tam bir gizlilik imkanı sağlar. Cüzdanınızdaki Dash farklı "girdiler"den oluşur, bunu ayrı gizli paralar olarak düşünebilirsiniz.<br> Özel Gönder sizin girdilerinizi başka iki kişinin girdileri ile karıştırmak için yenilikçi bir işlem kullanır ve bu sırada paranın cüzdanınızdan çıkmasına gerek kalmaz. Her an paranızın kontrolü sizdedir.<hr> <b>Özel Gönder işlemi şöyle işler:</b><ol type="1"> <li>Özel Gönder işlem girdilerinizi standart birimlere bölerek başlar. Bu birimler 0.01 DASH, 0.1 DASH, 1 DASH ve 10 DASH'dir -- her gün kullandığınız kağıt para gibi düşünebilirsiniz.</li> <li>Sonra cüzdanınız ağdaki özel ayarlanmış yazılım düğümlerine talepler gönderir, bunşara "ana düğümler" denir. Bu ana düğümlere sizin belli bir miktar birimi karıştırmak istediğiniz bilgisi gider. Ana düğümlere kimliğinizi açık edecek bir bilgi gönderilmez, bu yüzden "kim" olduğunuzu bilmezler.</li> <li>Aynı birimleri karıştırmak istediğini belirten iki başka kişi daha benzer mesajlar gönderince, karıştırma işlemi başlar. Ana düğüm giridleri karıştırır ve üç kullanıcının da cüzdanına şimdi dönüştürülmüş olan girdiyi kendilerne ödemelerini emreder. Cüzdanınız bu birimleri doğrudan kendisine öder ama farklı bir adres kullanır (buna değişim adresi denir).</li> <li>Paranızı tamamen gizlemek için cüzdanınız bu işlemi her birim için birkaç defa tekrar etmelidir. Her işlem tamamlandığına buna bir "tur" denir. Her bir Özel Gönder turu paranızın kaynağının bulunmasını üstel olarak zorlaştırır.</li> <li>Bu karışım işlemi arkaplanda sizin tarafınızdan bir müdahale olmadan gerçekleşir. Bir işlem yapmak istediğinizde bakiyeniz zaten anonimleştirilmiş olacaktır. Ek bir beklemeye gerek kalmaz.</li> </ol> <hr><b>ÖNEMLİ:</b> Cüzdanınızda bu "değişim adreslerinden" sadece 1000 tane vardır. Her bir karışım işleminde bu adreslerden 9 taneye kadar kullanılır. Bu da 1000 adresin yaklaşık 100 karışım işlemine yeteceği anlamına gelir. 900 tanesi kullanıldığı zaman, cüzdanınızın daha fazla adres oluşturması gerekir. Yalnız bunu ancak otomatik yedekleme etkinse yapabilir.<br> Sonuç olarak yedeklemeyi kapatan kullanıcılar aynı zamanda Özel Gönderi de kapatmış olurlar. <hr>Daha fazla bilgi için lütfen <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">Özel Gönder dökümantasyonuna</a> göz atın. - + Intro Welcome - Bem-vindo + Hoş geldiniz Welcome to %1. - Bem vindo ao %1 + %1'a hoş geldiniz. As this is the first time the program is launched, you can choose where %1 will store its data. - Como essa é a primeira vez que o programa é executado, você pode escolher onde %1 armazenará seus dados. + Bu programın ilk kez başlatılmasından dolayı %1 yazılımının verilerini nerede saklayacağını seçebilirsiniz. %1 will download and store a copy of the Dash block chain. At least %2GB of data will be stored in this directory, and it will grow over time. The wallet will also be stored in this directory. - %1 fará o download e armazenará uma cópia da cadeia de blocos do Dash. Pelo menos %2GB de dados serão armazenados neste diretório e crescerão com o tempo. A carteira também será armazenada neste diretório. + %1 Dash blok zincirinin bir kopyasını indirecek ve depolayacak. En az %2GB veri bu klasörde depolanacak ve zaman içinde büyüyecek. Cüzdan da aynı zamanda bu klasörde depolanacak. Use the default data directory - Use o diretório de dados padrão + Varsayılan veri klasörünü kullan Use a custom data directory: - Use um diretório de dados personalizado: + Özel bir veri klasörü kullan: Error: Specified data directory "%1" cannot be created. - Erro: Diretório de dados "%1" não pode ser criado. + Hata: belirtilen "%1" veri klasörü oluşturulamaz. Error - Erro + Hata %1 GB of free space available - %1 GB de espaço livre disponível + %1 GB boş alan mevcut (of %1 GB needed) - (Mais de %1GB necessário) + (%1 GB gerekli) MasternodeList Form - Formulário + Form My Masternodes - Meus Masternodes + Ana Düğümlerim Note: Status of your masternodes in local wallet can potentially be slightly incorrect.<br />Always wait for wallet to sync additional data and then double check from another node<br />if your masternode should be running but you still do not see "ENABLED" in "Status" field. - Nota: O status de seus masternodes na carteira local pode potencialmente ser ligeiramente incorreto. <br />Sempre espere que a carteira sincronize dados adicionais e depois cheque novamente de outro nó <br />, se o seu masternode estiver em execução, mas você ainda não vê "ATIVADO" no campo "Status". + Not: Yerel cüzdanınızdaki ana düğüm durumları biraz yanlış olabilir.<br />Eğer ana düğümünüz çalışıyor durumda olmalı ama "Durum" alanında "ETKİN" olarak görünmüyorsa<br />her zaman cüzdanın ek bilgiyi eşleştirmesini bekleyip başka bir düğümü kontrol edin. Alias - Apelido + Adlar Address - Endereço + Adres Protocol - Protocolo + Protokol Status - Status + Durum Active - Ativo + Etkin Last Seen - visto pela última vez + Son Görülme Payee - Payee + Alacaklı S&tart alias - Iniciar Apelido + A&d Başlat Start &all - Iniciar tudo + &Hepsini başlat Start &MISSING - Iniciar &FALTANTES + &KAYIP başlat &Update status - &Update status + &Güncelleme durumu Show additional Masternode information - Mostrar informações adicionais do Masternode + Ek Ana düğüm bilgisi göster Show &Info... - Mostrar &Info... + &Bilgi göster... Status will be updated automatically in (sec): - O estado será atualizado automaticamente em (seg): + Şu kadar (san) içinde durum güncellenecek: 0 @@ -1157,71 +1094,71 @@ All Masternodes - Todos os Masternodes + Tüm Ana Düğümler Filter List: - Filtrar Lista: + Filtre Listesi: Filter masternode list - Filtrar lista de masternodes + Ana düğüm listesini filtrele Node Count: - Contagem de Masternodes + Düğüm Sayısı: Start alias - Iniciar apelido + Ad başlat Confirm masternode start - Confirmar início de masternode + Ana düğüm başlangıcını onayla Are you sure you want to start masternode %1? - Tem certeza de que deseja iniciar o masternode %1? + Ana düğüm %1 başlatmak istediğinize emin misiniz? Confirm all masternodes start - Confirmar inicialização de todos masternodes + Tüm ana düğüm başlatmalarını onayla Are you sure you want to start ALL masternodes? - Tem certeza que quer inicializar TODOS os masternodes? + TÜM ana düğümleri başlatmak istediğinize emin misiniz? Command is not available right now - Esse comando não está disponível nesse momento + Komut şu an mevcut değil You can't use this command until masternode list is synced - Você não pode usar este comando até que a lista do masternode seja sincronizada + Ana düğüm listesi eşleşene kadar bu komutu kullanamazsınız Confirm missing masternodes start - Confirmar inicialização dos masternodes faltantes + Kayıp ana düğümleri başlatmayı onayla Are you sure you want to start MISSING masternodes? - Tem certeza que quer inicializar os masternodes FALTANTES? + KAYIP ana düğümleri başlatmak istediğinize emin misiniz? Additional information for Masternode %1 - Informações adicionais para o Masternode %1 + Ana Düğüm %1 için ek bilgi Masternode Private Key - Chave Privada Masternode + Ana Düğüm Özel Anahtarı Private Key - Chave Privada + Özel Anahtar Collateral - Garantia + Teminat IP @@ -1229,11 +1166,11 @@ Version - Versão + Sürüm Unknown - Desconhecido + Bilinmiyor Sentinel @@ -1244,62 +1181,62 @@ ModalOverlay Form - Forma + Form Recent transactions may not yet be visible, and therefore your wallet's balance might be incorrect. This information will be correct once your wallet has finished synchronizing with the Dash network, as detailed below. - Transações recentes podem não estar visíveis ainda, portanto o seu saldo pode estar incorreto. Esta informação será corrigida assim que sua carteira for sincronizada com a rede, como detalhado abaixo. + Son işlemler henüz görünmeyebilir ve bu nedenle cüzdanınızın bakiyesi yanlış olabilir. Bu bilgiler, aşağıda detaylandırıldığı gibi, cüzdanınız Dash ağı ile senkronizasyonunu tamamladığında doğru olacaktır. Attempting to spend Dash that are affected by not-yet-displayed transactions will not be accepted by the network. - A tentativa de gastar o Dash que é afetado por transações ainda não exibidas não será aceita pela rede. + Daha görüntülenmemiş işlemler tarafından etkilenen Dash'i harcama teşebbüsü ağ tarafından kabul edilmez. Number of blocks left - Número de blocos que faltam + Kalan blok sayısı Unknown... - Desconhecido... + Bilinmiyor... Last block time - Hora do último bloco + Son blok zamanı Progress - Progresso + İlerleme Progress increase per hour - Aumento do progresso por hora + Saat başı ilerleme artışı calculating... - calculando + hesaplanıyor... Estimated time left until synced - Tempo estimado para sincronizar + Senkronize edilene kadar kalan tahmini süre Hide - Esconder + Gizle Unknown. Syncing Headers (%1)... - Desconhecido. Sincronizando cabeçalhos (%1)... + Bilinmeyen. Üstbilgiler Senkronize Ediliyor (%1)... OpenURIDialog Open URI - Abrir URI + URI aç Open payment request from URI or file - Cobrança aberta de URI ou arquivo + Dosyadan veya URI'den ödeme talebi aç URI: @@ -1307,26 +1244,26 @@ Select payment request file - Selecione o arquivo de cobrança + Ödeme talebi dosyasını seç Select payment request file to open - Selecione o arquivo de cobrança para ser aberto + Açılacak ödeme talebi dosyasını seç OptionsDialog Options - Opções + Seçenekler &Main - Principal + &Esas ayarlar Size of &database cache - Tamanho do banco de &dados do cache + &Veritabanı tamponunun boyutu MB @@ -1334,151 +1271,151 @@ Number of script &verification threads - Número de threads do script de &verificação + İş parçacıklarını &denetleme betiği sayısı (0 = auto, <0 = leave that many cores free) - (0 = automático, <0 = número de cores deixados livres) + (0 = otomatik, <0 = bu kadar çekirdeği kullanma) Amount of Dash to keep anonymized - Quantidade de dashs para manter anonimizado + Anonim tutulacak Dash tutarı W&allet - C&arteira + &Cüzdan Show additional tab listing all your masternodes in first sub-tab<br/>and all masternodes on the network in second sub-tab. - Mostre uma guia adicional listando todos os seus masternodes na primeira subposição <br/> e todos os masternodes na rede na segunda sub-guia. + İlk alt sekmede tüm ana düğümlerinizi gösteren ve<br/>ikinci alt sekmede tüm ana düğümleri gösteren ek bir sekme göster. Show Masternodes Tab - Mostrar a aba Masternodes + Ana Düğümler Sekmesini Göster Show additional information and buttons for PrivateSend on overview screen. - Mostre informações adicionais e botões para PrivateSend na tela. + Genel bakış ekranında Özel Gönder için ek bilgi ve düğmeler göster. Enable advanced PrivateSend interface - Ativar interface avançada PrivateSend + Gelişmiş Özel Gönder arayüzünü etkinleştir Show warning dialog when PrivateSend detects that wallet has very low number of keys left. - Mostrar mensagem de aviso quando o PrivateSend detectar que a carteira tem um número muito baixo de chaves restantes. + Özel Gönder çok az sayıda anahtar kaldığını tespit ettiğinde bir uyarı göster. Warn if PrivateSend is running out of keys - Avisar se o PrivateSend estiver sem as chaves + Özel Gönderin anahtarı bitiyorsa uyar Whether to use experimental PrivateSend mode with multiple mixing sessions per block.<br/>Note: You must use this feature carefully.<br/>Make sure you always have recent wallet (auto)backup in a safe place! - Se você deseja usar o modo PrivateSend com várias sessões de mixagem por bloco. <br/> Nota: Você deve usar este recurso com cuidado. <br/> Certifique-se de ter sempre um backup recente de sua carteira em um local seguro. + Blok başına birden fazla karışım yapmak için deneysel Özel Gönder modunu kullanıp kullanmayacağınızı belirler.<br/>Not: Bu özelliği dikkatli kullanmalısınız.<br/>Cüzdanınızın son (oto)yedeğinin güvenli bir yerde olduğundan her zaman emin olun! Enable PrivateSend &multi-session - Ativar PrivateSend &multi-sessão + Özel Gönder &çoklu oturumu etkinleştir If you disable the spending of unconfirmed change, the change from a transaction<br/>cannot be used until that transaction has at least one confirmation.<br/>This also affects how your balance is computed. - Se você desabilitar o gasto de um troco não confirmado, o troco da transação<br/>não poderá ser utilizado até a transação ter pelo menos uma confirmação.<br/>Isso também afeta seu saldo computado. + Teyit edilmemiş para üstünü harcamayı devre dışı bırakırsanız<br/>bir işlemin para üstü bu işlem için en az bir teyit olana dek harcanamaz.<br/>Bu, aynı zamanda bakiyenizin nasıl hesaplandığını da etkiler. PrivateSend rounds to use - PrivateSend rodadas para usar + Kullanılacak Özel Gönder turları This amount acts as a threshold to turn off PrivateSend once it's reached. - Esse valor atua como um limite para desativar o PrivateSend assim que ele for atingido. + Bu tutar ulaşıldığında Özel Gönderi kapatacak bir eşik olarak çalışır. Automatically open the Dash Core client port on the router. This only works when your router supports UPnP and it is enabled. - Abre automaticamente a porta para o cliente Dash Core no roteador. Essa função apenas funciona se o seu roteador oferece suporte a UPnP e a opção estiver habilitada. + Router'da otomatik olarak Dash Core istemcisi portu aç. Bu sadece router'ınız UPnP destekliyorsa ve etkinse çalışır. Accept connections from outside - Aceitar conexões externas + Dışarıdan bağlantı kabul et Allow incoming connections - Permitir conexões de entrada + Gelen bağlantılara izin ver Connect to the Dash network through a SOCKS5 proxy. - Conecta à rede Dash através de um proxy SOCKS5. + Dash ağına bir SOCKS5 vekil sunucusu aracılığıyla bağlan. &Connect through SOCKS5 proxy (default proxy): - &Conectar via proxy SOCKS5 (proxy padrão): + SOCKS5 vekil sunucusu aracılığıyla &bağlan (varsayılan vekil sunucusu): Minimize instead of exit the application when the window is closed. When this option is enabled, the application will be closed only after selecting Exit in the menu. - Minimizar em vez de sair do aplicativo quando a janela for fechada. Quando esta opção está ativada, o aplicativo só será fechado selecionando Sair no menu. + Pencere kapatıldığında uygulamadan çıkmak yerine uygulamayı küçültür. Bu seçenek etkinleştirildiğinde, uygulama sadece menüden çıkış seçildiğinde kapanacaktır. Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items.<br/>%s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |. - As URLs de terceiros (por exemplo, um explorador de blocos) que aparecem na guia de transações como itens de menu de contexto. <br/>%s na URL são substituídos por hash de transação. Vários URLs são separados pela barra vertical |. + İşlemler sekmesinde bağlam menüsü ögeleri gibi görünen üçüncü parti URLleri (ör. bir blok tarayıcısı).<br/>URL'deki %s işlem hashi ile değiştirilir. Birden fazla URL dikey çizgi | ile ayrılır. Expert - Avançado + Gelişmiş This setting determines the amount of individual masternodes that an input will be anonymized through.<br/>More rounds of anonymization gives a higher degree of privacy, but also costs more in fees. - Esta configuração determina a quantidade de Masternodes individuais através dos quais um input será anonimizado. <br/>Mais rodadas de anonimização garantem um maior grau de privacidade, mas também custarão mais em fees. + Bu ayar, bir girdinin anonim hale getirilmesi için kullanılacak ana düğüm sayısını belirler.<br/>Daha çok anonimleştirme turu daha yüksek bir gizlilik sağlar ama daha maliyetli olur. Whether to show coin control features or not. - Mostrar ou não opções de controle da moeda. + Para kontrol özelliklerinin gösterilip gösterilmeyeceğini ayarlar. Automatically start %1 after logging in to the system. - Executar o %1 automaticamente ao iniciar o sistema. + Sistemde oturum açıldığında %1 programını otomatik olarak başlat. &Start %1 on system login - &Iniciar %1 ao fazer login no sistema + &Açılışta %1 açılsın Enable coin &control features - Habilitar opções de &controle de moedas + Para &kontrolü özelliklerini etkinleştir &Spend unconfirmed change - Ga&star mudança não confirmada + Teyit edilmemiş para üstünü &harca &Network - Rede + &Şebeke Map port using &UPnP - Mapear porta usando &UPnP + Portları &UPnP kullanarak haritala Proxy &IP: - &IP do proxy: + Vekil &İP: IP address of the proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1) - Endereço de IP do proxy (e.g. IPv4: 127.0.0.1 / IPv6: ::1) + Vekil sunucusunun IP adresi (mesela IPv4: 127.0.0.1 / IPv6: ::1) &Port: - &Porta: + &Port: Port of the proxy (e.g. 9050) - Porta do serviço de proxy (ex. 9050) + Vekil sunucunun portu (mesela 9050) Used for reaching peers via: - Usado para alcançar peers através de: + Eşlere ulaşmak için kullanılır, şu üzerinden: Shows, if the supplied default SOCKS5 proxy is used to reach peers via this network type. - Mostra, se o proxy SOCKS5 padrão fornecido for usado para alcançar pares através deste tipo de rede. + Bu ağ türünde eşlere ulaşmak için varsayılan SOCKS5 proxy'nin kullanılıp kullanılmadığını gösterir. IPv4 @@ -1494,496 +1431,496 @@ Connect to the Dash network through a separate SOCKS5 proxy for Tor hidden services. - Conecte-se à rede Dash através de um proxy SOCKS5 separado para serviços ocultos Tor + Dash ağına gizli Tor servisleri için ayrı bir SOCKS5 vekil sunucusu aracılığıyla bağlan. Use separate SOCKS5 proxy to reach peers via Tor hidden services: - Usar proxy SOCKS5 separado para alcançar pares via serviços ocultos Tor: + Eşlere Tor gizli servislerini kullanarak ulaşmak için ayrı SOCKS5 vekilleri kullan: &Window - &Janela + &Pencere &Hide the icon from the system tray. - &Ocultar o ícone da bandeja do sistema. + Simgeyi sistem çubuğunda &gizle. Hide tray icon - Ocultar ícone da bandeja + Sistem çubuğu simgesini gizle Show only a tray icon after minimizing the window. - Mostrar apenas um ícone na bandeja ao minimizar a janela. + Küçültüldükten sonra sadece çekmece ikonu göster. &Minimize to the tray instead of the taskbar - &Minimizar para a bandeja em vez da barra de tarefas. + İşlem çubuğu yerine sistem çekmecesine &küçült M&inimize on close - M&inimizar ao sair + Kapatma sırasında k&üçült &Display - &Mostrar + &Görünüm User Interface &language: - &Língua da interface com usuário: + Kullanıcı arayüzü &lisanı: The user interface language can be set here. This setting will take effect after restarting %1. - O idioma da interface pode ser definido aqui. Essa configuração terá efeito após reiniciar o %1. + Kullanıcı arayüzünün dili burada belirtilebilir. Bu ayar %1 tekrar başlatıldığında etkinleşecektir. Language missing or translation incomplete? Help contributing translations here: https://www.transifex.com/projects/p/dash/ - Idioma inexistente ou tradução incompleta? Contribua com a tradução aqui: + Diliniz mevcut değil veya çeviri eksik mi? Buradan çevirilere katkıda bulunun: https://www.transifex.com/projects/p/dash/ User Interface Theme: - Tema da Interface do usuário: + Kullanıcı Arayüzü Teması: &Unit to show amounts in: - &Unidade usada para mostrar quantidades: + Meblağları göstermek için &birim: Choose the default subdivision unit to show in the interface and when sending coins. - Escolha a unidade padrão de subdivisão para interface mostrar quando enviar bitcoins. + Bitcoin gönderildiğinde arayüzde gösterilecek varsayılan alt birimi seçiniz. Decimal digits - Dígitos decimais + Ondalık haneler Third party transaction URLs - URLs da transação de terceiros + Üçüncü taraf muamele URL'leri Active command-line options that override above options: - Ativa as opções de linha de comando que sobrescreve as opções acima: + Yukarıdaki seçeneklerin yerine geçen faal komut satırı seçenekleri: Reset all client options to default. - Redefinir todas as opções do cliente para opções padrão. + İstemcinin tüm seçeneklerini varsayılan değerlere geri al. &Reset Options - &Redefinir opções + Seçenekleri Sıfı&rla &OK - &OK + &Tamam &Cancel - &Cancelar + &İptal default - padrão + varsayılan none - Nenhum + Boş Confirm options reset - Confirmar redefinição de opções + Seçeneklerin sıfırlanmasını teyit et Client restart required to activate changes. - Reinicialização do aplicativo necessária para efetivar alterações. + Değişikliklerin uygulanması için istemcinin yeniden başlatılması lazımdır. Client will be shut down. Do you want to proceed? - O programa será encerrado. Deseja continuar? + İstemci kapanacaktır. Devam etmek istiyor musunuz? This change would require a client restart. - Essa mudança requer uma reinicialização do aplicativo. + Bu değişiklik istemcinin tekrar başlatılmasını gerektirir. The supplied proxy address is invalid. - O endereço proxy fornecido é inválido. + Girilen vekil sunucu adresi geçersizdir. OverviewPage Form - Formulário + Form The displayed information may be out of date. Your wallet automatically synchronizes with the Dash network after a connection is established, but this process has not completed yet. - A informação exibida pode estar desatualizada. Sua wallet é sincronizada automaticamente com a rede Dash tão logo uma conexão se estabeleça, entrtanto esse processo ainda não está completo. + Görüntülenen bilgiler güncel olmayabilir. Bağlantı kurulduğunda cüzdanınız otomatik olarak Dash ağı ile senkronize olur ancak bu işlem henüz tamamlanmamıştır. Available: - Disponível: + Mevcut: Your current spendable balance - Seu saldo atual spendable + Güncel harcanabilir bakiyeniz Pending: - Pendente: + Beklemede: Total of transactions that have yet to be confirmed, and do not yet count toward the spendable balance - Total de transações que ainda têm de ser confirmados, e ainda não contam para o equilíbrio spendable + Henüz teyit edilmemiş ve harcanabilir bakiyeye eklenmemiş muamelelerin toplamı Immature: - Imaturo: + Olgunlaşmamış: Mined balance that has not yet matured - Saldo minerado que ainda não maturou + Oluşturulan bakiye henüz olgunlaşmamıştır Balances - Saldos + Bakiyeler Unconfirmed transactions to watch-only addresses - Transações não confirmadas para endereços apenas para exibição + Sadece izlenen adreslere gelen doğrulanmamış işlemler Mined balance in watch-only addresses that has not yet matured - Saldo minerado em endereços somente para observação que ainda não maturou + Sadece izlenen adreslerin henüz olgunlaşmamış oluşturulan bakiyeleri Total: - Total: + Toplam: Your current total balance - Seu saldo total atual + Güncel toplam bakiyeniz Current total balance in watch-only addresses - Saldo total atual em endereços de monitoração + Sadece izlenen adreslerdeki güncel toplam bakiye Watch-only: - Observar apenas: + Sadece-izlenen: Your current balance in watch-only addresses - Seu saldo atual em endereços somente de monitoração + Sadece izlenen adreslerdeki güncel bakiyeniz Spendable: - Saldo disponível: + Harcanabilir: PrivateSend - PrivateSend + Özel Gönder Status: - Status: + Durum: Enabled/Disabled - Ativado/Desativado + Açık/Kapalı Completion: - Conclusão: + Tamamlanma: Try to manually submit a PrivateSend request. - Tente enviar manualmente uma solicitação PrivateSend. + Bir Özel Gönder isteğini elle göndermeyi dene. Reset the current status of PrivateSend (can interrupt PrivateSend if it's in the process of Mixing, which can cost you money!) - Reseta o estado atual do PrivateSend (pode interromper o PrivateSend se este estiver em processo de Mixing, isso pode te custar dinheiro!) + Özel Gönderin mevcut durumunu sıfırla (eğer bir Karışım işlemi devam ediyorsa Özel Gönderi yarıda keser, bu da para kaybetmenize neden olabilir!) Information about PrivateSend and Mixing - Informações sobre PrivateSend e Mixing + Özel Gönder ve Karışım hakkında bilgi Info - Info + Bilgi Amount and Rounds: - Quantia e Rodadas: + Tutar ve Turlar: 0 DASH / 0 Rounds - 0 DASH / 0 Rodadas + 0 DASH / 0 Tur Submitted Denom: - Denom Submetido: + Gönderilen Birim: n/a - n/a + Mevcut değil Recent transactions - Transações recentes + Son işlemler Start/Stop Mixing - Iniciar/Interronper o Mixing + Karışımı Başlat/Durdur PrivateSend Balance: - Saldo PrivateSend + Özel Gönder Bakiyesi: The denominations you submitted to the Masternode.<br>To mix, other users must submit the exact same denominations. - As denominações submetidas por você ao Masternode.<br/>Para a realização do mixing, outros usuários precisam submeter exatamente as mesmas denominações. + Ana düğüme gönderdiğiniz birimler.<br>Karıştırmak için diğer kullanıcılar da tam olarak aynı birimleri göndermelidir. (Last Message) - (Última Mensagem) + (Son Mesaj) Try Mix - Tentar o Mixing + Karışım Dene Reset - Resetar + Sıfırla out of sync - Sem sincronia + eşleşme dışı Automatic backups are disabled, no mixing available! - Backups automáticos estão desabilitados, Mixing não disponível! + Otomatik yedekleme kapalı, karışım mevcut değil! Start Mixing - Iniciar o Mixing + Karışıma Başla Stop Mixing - Interromper o Mixing + Karışımı Durdur No inputs detected - Nenhuma entrada detectada + Hiç girdi tespit edilmedi %n Rounds - %n Rodadas%n Rodadas + %n Tur%n Tur Found enough compatible inputs to anonymize %1 - Encontrou entradas compatíveis para anonimizar %1 + %1 anonim hale getirmek için yeterli uyumlu girdi bulundu Not enough compatible inputs to anonymize <span style='color:red;'>%1</span>,<br>will anonymize <span style='color:red;'>%2</span> instead - Não há entradas compatíveis suficientes para anonimizar <span style='color:red;'>%1</span>,<br> vamos anonimizar <span style='color:red;'>%2</span> em vez disso + <span style='color:red;'>%1</span> anonim hale getirmek için yeterli uyumlu girdi yok,<br>onun yerine<span style='color:red;'>%2</span> anonim hale getirilecek Overall progress - Progresso geral + Genel ilerleme Denominated - Denominado + Birimlendirildi Mixed - Misturado + Karıştırıldı Anonymized - Anonimizado + Anonim Yapıldı Denominated inputs have %5 of %n rounds on average - Os insumos denominados têm %5 de %n rodadas em médiaOs insumos denominados têm %5 de %n rodadas em média + Birimlendirilmiş girdiler ortalama %5 / %n tura sahipBirimlendirilmiş girdiler ortalama %5 / %n tura sahip keys left: %1 - Chaves restantes: %1 + kalan anahtarlar: %1 Disabled - Desativado + Kapalı Very low number of keys left since last automatic backup! - Número muito baixo de chaves deixadas desde o último backup automático! + Son otomatik yedekten beri çok az sayıda anahtar kaldı! We are about to create a new automatic backup for you, however <span style='color:red;'> you should always make sure you have backups saved in some safe place</span>! - Estamos prestes a criar um novo backup automático para você, no entanto<span style='color:red;'>, você deve sempre garantir que você tenha backups salvos em algum lugar seguro</span>! + Sizin için yeni bir otomatik yedek oluşturmak üzereyiz ancak <span style='color:red;'> yedeklerinizin her zaman güvenli bir yerde taşındığından emin olmalısınız</span>! Note: You can turn this message off in options. - Nota: Você desliga esta mensagem nas opções. + Not: Bu mesajı seçeneklerden kapatabilirsiniz. WARNING! Something went wrong on automatic backup - ALERTA! Algo de errado ocorreu com o backup automático + UYARI! Otomatik yedeklemede bir sorun çıktı ERROR! Failed to create automatic backup - ERRO! Falha ao criar o backup automático + HATA! Otomatik yedek oluşturulamadı Mixing is disabled, please close your wallet and fix the issue! - Mixing está desabilitado, por favor feche sua carteira e corrija o problema! + Karışım devredışı, lütfen cüzdanınızı kapatıp sorunu çözün! Enabled - Ativado + Açık see debug.log for details. - Consulte debug.log para obter detalhes. + ayrıntılar için debug.log dosyasına bakın. WARNING! Failed to replenish keypool, please unlock your wallet to do so. - ATENÇÃO! Falha ao repor o keypool, desbloqueie sua carteira para fazer isso. + UYARI! Anahtar havuzu yenilenemedi, lütfen devam etmek için cüzdanınızın kilidini açın. Last PrivateSend message: - Última mensagem do PrivateSend: + Son Özel Gönder mesajı: N/A - N/A + Mevcut değil PrivateSend was successfully reset. - PrivateSend foi reiniciado com sucesso. + Özel Gönder başarıyla sıfırlandı. If you don't want to see internal PrivateSend fees/transactions select "Most Common" as Type on the "Transactions" tab. - Se você não quiser ver as taxas / transações internas do PrivateSend selecione "Mais comum" como Tipo na guia "Transações". + Eğer Özel Gönderin iç ücretlerini/işlemlerini görmek istemiyorsanız "İşlemler" sekmesinde Tür olarak "En Sıkı" seçin. PrivateSend requires at least %1 to use. - PrivateSend requer pelo menos %1 para usar. + Özel Gönder en az %1 gerektiriyor. Wallet is locked and user declined to unlock. Disabling PrivateSend. - A Carteira está bloqueada e o usuário recusou a desbloquear. Desativando o PrivateSend. + Cüzdan kilitli ve kullanıcı kilidi açmadı. Özel Gönder devredışı bırakılıyor. PaymentServer Payment request error - Erro no pedido de pagamento + Ödeme talebi hatası Cannot start dash: click-to-pay handler - Não foi possível iniciar Dash: manipulador click-to-pay + Dash başlatılamadı: tıkla-ve-öde yöneticisi URI handling - Manipulação de URI + URI yönetimi Payment request fetch URL is invalid: %1 - URL de cobrança é inválida: %1 + Ödeme talebini alma URL'i geçersiz: %1 Invalid payment address %1 - Endereço de pagamento inválido %1 + %1 ödeme adresi geçersizdir URI cannot be parsed! This can be caused by an invalid Dash address or malformed URI parameters. - A URI não pode ser analisada! Isto pode ser causado por um endereço inválido ou um parâmetro URI malformado. + URI ayrıştırılamıyor! Bunun nedeni geçersiz bir Dash adresi veya hatalı biçimlendirilmiş URI değişkenleri olabilir. Payment request file handling - Manipulação de arquivo de cobrança + Ödeme talebi dosyası yönetimi Payment request file cannot be read! This can be caused by an invalid payment request file. - Arquivo de pedido de pagamento não pode ser lido! Isto pode ser causado por uma requisição de pagamento inválida. + Ödeme talebi dosyası okunamıyor! Bunun nedeni geçersiz bir ödeme talebi dosyası olabilir. Payment request rejected - Pedido de pagamento rejeitado + Ödeme talebi reddedildi Payment request network doesn't match client network. - Rede do pedido de pagamento não corresponde rede do cliente. + Ödeme talebi ağı, istemci ağıyla eşleşmiyor. Payment request expired. - Pedido de pagamento expirado. + Ödeme talebinin geçerlilik süresi bitti. Payment request is not initialized. - A requisição do pagamento não foi inicializada. + Ödeme talebi başlatılmadı. Unverified payment requests to custom payment scripts are unsupported. - Pedidos de pagamento não verificados para scripts de pagamento personalizados não são suportados. + Özel ödeme betiklerine, doğrulanmamış ödeme talepleri desteklenmez. Invalid payment request. - Pedido de pagamento inválido + Geçersiz ödeme talebi. Requested payment amount of %1 is too small (considered dust). - Valor do pagamento solicitado de %1 é muito pequeno (Considerado poeira). + Talep edilen %1 ödeme tutarı çok küçüktür (toz olarak kabul edilir). Refund from %1 - Reembolso de %1 + %1 adresinden geri ödeme Payment request %1 is too large (%2 bytes, allowed %3 bytes). - Pedido de pagamento %1 é muito grande (%2 bytes, permitido %3 bytes). + %1 ödeme talebi çok büyük (%2 bayt, üst sınır %3 bayt). Error communicating with %1: %2 - Erro na comunicação com %1: %2 + %1 ile iletişimde hata: %2 Payment request cannot be parsed! - Pedido de pagamento não pode ser analisado! + Ödeme talebi ayrıştırılamaz! Bad response from server %1 - Má resposta do servidor %1 + %1 sunucusundan hatalı yanıt Network request error - Erro de solicitação de rede + Ağ talebi hatası Payment acknowledged - Pagamento reconhecido + Ödeme kabul edildi PeerTableModel NodeId - ID do nó + Düğüm ID'si Node/Service - Nó/Serviço + Düğüm/Servis User Agent - User Agent + Kullanıcı Yazılımı Ping @@ -1998,31 +1935,31 @@ https://www.transifex.com/projects/p/dash/ %1 didn't yet exit safely... - %1 ainda não terminou com segurança... + %1 henüz güvenli bir şekilde çıkış yapmamıştır... Error reading masternode configuration file: %1 - Erro ao ler o arquivo de configuração do masternode: %1 + Ana düğüm ayar dosyası okuma hatası: %1 Amount - Quantia + Tutar Enter a Dash address (e.g. %1) - Informe um endereço Dash (ex: %1) + Bir Dash adresi giriniz (mesela %1) %1 d - %1 d + %1 g %1 h - %1 h + %1 s %1 m - %1 m + %1 d %1 s @@ -2030,11 +1967,11 @@ https://www.transifex.com/projects/p/dash/ None - Nenhum + Boş N/A - N/A + Mevcut değil %1 ms @@ -2042,430 +1979,430 @@ https://www.transifex.com/projects/p/dash/ %n second(s) - %n segundos%n segundos + %n saniye%n saniye %n minute(s) - %n minutos%n minutos + %n dakika%n dakika %n hour(s) - %n horas%n horas + %n saat%n saat %n day(s) - %n dias%n dias + %n gün%n gün %n week(s) - %n semanas%n semanas + %n hafta%n hafta %n year(s) - %n anos%n anos + %n yıl%n yıl %1 and %2 - %1 e %2 + %1 ve %2 unknown - desconhecido + bilinmiyor QObject::QObject Error: Specified data directory "%1" does not exist. - Erro: Diretório de dados especificado "%1" não existe. + Hata: Belirtilen "%1" veri klasörü yoktur. Error: Cannot parse configuration file: %1. Only use key=value syntax. - Erro: Não foi possível interpretar arquivo de configuração: %1. Utilize apenas a sintaxe chave=valor. + Hata: %1 yapılandırma dosyası ayrıştırılamadı. Sadece anahtar=değer dizimini kullanınız. Error: %1 - Erro: %1 + Hata: %1 QRDialog QR-Code Title - QR-Code Titulo + QR Kod Başlığı QR Code - Código QR + QR Kodu &Save Image... - &Salvar Imagem... + Resmi ka&ydet... Error creating QR Code. - Erro ao criar o QR Code. + QR Kod oluşturma hatası. QRGeneralImageWidget &Save Image... - &Salvar Imagem... + Resmi ka&ydet... &Copy Image - &Copiar imagem + Resmi &Kopyala Save QR Code - Salvar código QR + QR Kodu Kaydet PNG Image (*.png) - Imagem PNG (*.png) + PNG Resim (*.png) QRImageWidget &Save Image... - &Salvar Imagem... + Resmi ka&ydet... &Copy Image - &Copiar imagem + Resmi &Kopyala Save QR Code - Salvar código QR + QR Kodu Kaydet PNG Image (*.png) - Imagem PNG (*.png) + PNG Resim (*.png) RPCConsole Tools window - Janela de ferramentas + Araçlar penceresi &Information - &Informação + &Malumat General - Geral + Genel Name - Nome + İsim N/A - N/A + Mevcut değil Number of connections - Número de conexões + Bağlantı sayısı &Open - &Abrir + &Aç Startup time - Horário de inicialização + Başlama zamanı Network - Rede + Şebeke Last block time - Horário do último bloco + Son blok zamanı Debug log file - Arquivo de log de Depuração + Hata ayıklama kütük dosyası Current number of blocks - Quantidade atual de blocos + Güncel blok sayısı Client version - Versão do cliente + İstemci sürümü Using BerkeleyDB version - Usando BerkeleyDB versão + Kullanılan BerkeleyDB sürümü Block chain - Corrente de blocos + Blok zinciri Number of Masternodes - Número de Masternodes + Ana düğüm sayısı Memory Pool - Pool de memória + Bellek Alanı Current number of transactions - Número atual de transações + Güncel işlem sayısı Memory usage - Uso de memória + Bellek kullanımı &Console - &Console + &Konsol Clear console - Limpar console + Konsolu temizle &Network Traffic - Tráfico de Rede + &Şebeke trafiği &Clear - &Limpar + &Temizle Totals - Totais + Toplamlar Received - Recebido + Alınan Sent - Enviado + Yollanan &Peers - &Peers + &Eşler Banned peers - Nós banidos + Yasaklı eşler Select a peer to view detailed information. - Selecione um ponto para ver informações detalhadas. + Ayrıntılı bilgi görmek için bir eş seçin. Whitelisted - Lista branca + Beyaz listedekiler Direction - Direção + Yön Version - Versão + Sürüm Starting Block - Bloco inicial + Başlangıç Bloku Synced Headers - Cabeçalhos Sincronizados + Eşleşmiş Üstbilgiler Synced Blocks - Blocos Sincronizados + Eşleşmiş Bloklar Wallet Path - Caminho da carteira + Cüzdan Yolu User Agent - Agente de Usuário + Kullanıcı Yazılımı Datadir - Datadir + Veri konumu Open the %1 debug log file from the current data directory. This can take a few seconds for large log files. - Abrir o arquivo de log de depuração do %1 localizado no diretório atual de dados. Isso pode levar alguns segundos para arquivos de log grandes. + Güncel veri klasöründen %1 hata ayıklama kütük dosyasını açar. Büyük kütük dosyaları için bu birkaç saniye alabilir. Decrease font size - Diminuir o tamanho da fonte + Font boyutunu küçült Increase font size - Aumentar o tamanho da fonte + Yazıtipi boyutunu büyült Services - Serviços + Servisler Ban Score - Banir pontuação + Yasaklama Skoru Connection Time - Tempo de conexão + Bağlantı Süresi Last Send - Último envio + Son Gönderme Last Receive - Último recebido + Son Alma Ping Time - Tempo de Ping + Ping Süresi The duration of a currently outstanding ping. - A duração de um ping excepcional no momento. + Güncel olarak göze çarpan bir ping'in süresi. Ping Wait - Espera de ping + Ping Beklemesi Min Ping - Ping min + En Düşük Ping Time Offset - Offset de tempo + Saat Farkı &Wallet Repair - &Reparar carteira + &Cüzdan Tamiri Salvage wallet - Salvar wallet + Cüzdanı kurtar Rescan blockchain files - reescanear o blockchain + Blok zinciri dosyalarını yeniden tara Recover transactions 1 - Recuperar transações 1 + İşlemleri geri al 1 Recover transactions 2 - Recuperar transações 2 + İşlemleri geri al 2 Upgrade wallet format - Atualizar formato da carteira + Cüzdan biçimini yükselt The buttons below will restart the wallet with command-line options to repair the wallet, fix issues with corrupt blockhain files or missing/obsolete transactions. - Os botões abaixo reiniciarão a carteira com opções de linha de comando para reparar a carteira, corrigir problemas com arquivos blockhain corrompidos ou transações ausentes/obsoletas. + Aşağıdaki düğmeler cüzdanı tamir etmek, bozuk blok zinciri dosyalarıyla veya kayıp/geçersiz işlemlerle ilgili sorunları çözmek için cüzdanı komut istemi seçenekleri ile yeniden başlatacak. -salvagewallet: Attempt to recover private keys from a corrupt wallet.dat. - -salvagewallet: tenta recuperar chaves privadas de um wallet.dat corrompido. + -salvagewallet: Bozuk bir wallet.dat dosyasında özel anahtarları kurtarma denemesi. -rescan: Rescan the block chain for missing wallet transactions. - -rescan: Reinicia a cadeia de bloco para transações de carteira ausentes. + -rescan: Kayıp cüzdan işlemleri için blok zincirini yeniden tarama. -zapwallettxes=1: Recover transactions from blockchain (keep meta-data, e.g. account owner). - -zapwallettxes = 1: Recupere as transações do blockchain (mantenha metadados, por exemplo, proprietário da conta). + -zapwallettxes=1: Blok zincirinden işlemleri kurtarma (meta-veri tutulur, ör. hesap sahibi). -zapwallettxes=2: Recover transactions from blockchain (drop meta-data). - -zapwallettxes = 2: Recupere as transações do blockchain (elimine metadados). + -zapwallettxes=2: Blok zincirinden işlemleri kurtarma (meta-veri atılır). -upgradewallet: Upgrade wallet to latest format on startup. (Note: this is NOT an update of the wallet itself!) - -upgradewallet: Atualize a carteira para o formato mais recente na inicialização. (Nota: esta não é uma atualização da carteira em si!) + -upgradewallet: Açılışta cüzdanı en son biçime yükselt. (Not: Bu cüzdanın kendisinin güncellenmesi DEĞİLDİR!) Wallet repair options. - Opções de reparação da carteira. + Cüzdan tamir seçenekleri. Rebuild index - Reconstrução de Índice + Dizini yeniden oluştur -reindex: Rebuild block chain index from current blk000??.dat files. - -reindex: Reconstrói o índice de cadeia de blocos a partir dos arquivos atuais de dados blk000??.dat . + -reindex: Blok zinciri dizinini mevcut blk000??.dat dosyalarından yeniden oluşturma. &Disconnect - &Desconectar + &Bağlantıyı Kes Ban for - Banir por + Yasakla 1 &hour - 1 &hora + 1 &saat 1 &day - 1 &dia + 1 &gün 1 &week - 1 &semana + 1 &hafta 1 &year - 1 &ano + 1 &yıl &Unban - &Desbanir + &Yasaklamayı Kaldır Welcome to the %1 RPC console. - Bem-vindo ao console RPC do %1. + %1 RPC konsoluna hoş geldiniz. Use up and down arrows to navigate history, and <b>Ctrl-L</b> to clear screen. - Use as setas para cima e para baixo para navegar pelo histórico, e <b>Ctrl-L</b> para limpar a tela. + Tarihçede gezinmek için imleç tuşlarını kullanınız, <b>Ctrl-L</b> ile de ekranı temizleyebilirsiniz. Type <b>help</b> for an overview of available commands. - Digite <b>help</b> para uma visão geral dos comandos disponíveis. + Mevcut komutların listesi için <b>help</b> yazınız. WARNING: Scammers have been active, telling users to type commands here, stealing their wallet contents. Do not use this console without fully understanding the ramification of a command. - AVISO: Os golpistas estão ativos, dizendo aos usuários para digitar comandos aqui, roubando o conteúdo da carteira. Não use este console sem compreender totalmente o contexto de um comando. + UYARI: Kullanıcıların cüzdan içeriğini çalmak için buraya komut girmenizi söyleyen dolandırıcılar olabilir. Bir komutun ne anlama geldiğini tam olarak bilmiyorsanız bu konsolu kullanmayın. In: - Entrada: + İçeri: Out: - Saída: + Dışarı: Network activity disabled - Atividade da rede disativada + Ağ etkinliği devre dışı bırakıldı %1 B @@ -2485,157 +2422,157 @@ https://www.transifex.com/projects/p/dash/ (node id: %1) - (id do nó: %1) + (düğüm kimliği: %1) via %1 - por %1 + %1 vasıtasıyla never - nunca + asla Inbound - Entrada + Gelen Outbound - Saída + Giden Yes - sim + Evet No - não + Hayır Unknown - Desconhecido + bilinmiyor ReceiveCoinsDialog Reuse one of the previously used receiving addresses.<br>Reusing addresses has security and privacy issues.<br>Do not use this unless re-generating a payment request made before. - Reutilize um dos endereços de recebimento usados anteriormente.<br> Os endereços que estão sendo usados têm problemas de segurança e privacidade. <br>Não use isso, a menos que você gere novamente uma solicitação de pagamento feita anteriormente. + Öneden kullanılmış bir alım adresini tekrar kullan.<br>Adresleri tekrar kullanmak güvenlik ve gizlilik sorunlarına neden olur.<br>Eğer daha önce yapılmış bir ödeme talebi tekrar oluşturulmadıysa bunu kullanmayın. R&euse an existing receiving address (not recommended) - R&eutilize um endereço de recebimento (não recomendado) + &Hâlihazırda bulunan bir alım adresini kullan (önerilmez) An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Dash network. - Uma mensagem opcional que será anexada na cobrança e será mostrada quando ela for aberta. Nota: A mensagem não será enviada com o pagamento pela rede Dash. + Talep açıldığında gösterilecek, isteğinize dayalı, ödeme talebi ile ilişkilendirilecek bir ileti. Not: Bu ileti ödeme ile birlikte Dash ağı üzerinden gönderilmeyecektir. &Message: - &Mensagem + Me&saj: An optional label to associate with the new receiving address. - Um marcador opcional para associar ao novo endereço de recebimento. + Yeni alım adresi ile ilişkili, seçiminize dayalı etiket. An optional message to attach to the payment request, which will be displayed when the request is opened.<br>Note: The message will not be sent with the payment over the Dash network. - Uma mensagem opcional para anexar à solicitação de pagamento, que será exibida quando a solicitação for aberta. <br>Observação: a mensagem não será enviada com o pagamento pela rede do Dash. + Ödeme talebine eklenecek isteğe bağlı bir mesaj, talep açıldığında gösterilecektir.<br>Not: Mesaj ödeme ile birlikte Dash ağı üzerinden gönderilmez. Use this form to request payments. All fields are <b>optional</b>. - Use esse formulário para fazer cobranças. Todos os campos são <b>opcionais</b>. + Ödeme talep etmek için bu formu kullanın. Tüm alanlar <b>seçime dayalıdır</b>. &Label: - &Rótulo: + &Etiket: An optional amount to request. Leave this empty or zero to not request a specific amount. - Uma quantia opcional para cobrar. Deixe vazio ou em branco se o pagador puder especificar a quantia. + Seçiminize dayalı talep edilecek meblağ. Belli bir meblağ talep etmemek için bunu boş bırakın veya sıfır değerini kullanın. &Amount: - Qu&antia: + &Meblağ: &Request payment - &Requisitar Pagamento + Ödeme &talep et Clear all fields of the form. - Limpa todos os campos do formulário. + Formdaki tüm alanları temizle. Clear - Limpar + Temizle Request InstantSend - Solicitar o InstantSend + Anında Gönder Talep Et Requested payments history - Histórico de cobranças + Talep edilen ödemelerin tarihçesi Show the selected request (does the same as double clicking an entry) - Mostra a cobrança selecionada (o mesmo que clicar duas vezes em um registro) + Seçilen talebi göster (bir unsura çift tıklamakla aynı anlama gelir) Show - Mostrar + Göster Remove the selected entries from the list - Remove o registro selecionado da lista + Seçilen unsurları listeden kaldır Remove - Remover + Kaldır Copy URI - Copiar URI + URI'yi kopyala Copy label - Copiar rótulo + Etiket kopyala Copy message - Copiar mensagem + İletiyi kopyala Copy amount - Copiar quantia + Tutarı kopyala ReceiveRequestDialog QR Code - Código QR + QR Kodu Copy &URI - Copiar &URI + &URI'yi kopyala Copy &Address - &Copiar Endereço + &Adresi kopyala &Save Image... - &Salvar Imagem... + Resmi ka&ydet... Request payment to %1 - Pedido de pagamento para %1 + %1 unsuruna ödeme talep et Payment information - Informação do pagamento + Ödeme bilgisi URI @@ -2643,189 +2580,201 @@ https://www.transifex.com/projects/p/dash/ Address - Endereço + Adres Amount - Quantia + Tutar Label - Rótulo + Etiket Message - mensagem + İleti InstantSend - EnvioInstantâneo + Anında Gönder Yes - Sim + Evet No - Não + Hayır Resulting URI too long, try to reduce the text for label / message. - URI resultante muito longa. Tente reduzir o texto do rótulo ou da mensagem. + Sonuç URI çok uzun, etiket ya da ileti metnini kısaltmayı deneyiniz. Error encoding URI into QR Code. - Erro ao codigicar o URI em código QR + URI'nin QR koduna kodlanmasında hata oluştu. RecentRequestsTableModel Date - Data + Tarih Label - Rótulo + Etiket Message - Mensagem + İleti (no label) - (sem rótulo) + (etiket yok) (no message) - (sem mensagem) + (ileti yok) (no amount requested) - (nenhuma quantia solicitada) + (tutar talep edilmedi) Requested - Solicitado + Talep edilen SendCoinsDialog Send Coins - Enviar dinheiro + Bitcoin yolla Coin Control Features - Opções de Controle da Moeda + Para kontrolü özellikleri Inputs... - Entradas... + Girdiler... automatically selected - automaticamente selecionado + otomatik seçilmiş Insufficient funds! - Saldo insuficiente! + Yetersiz fon! Quantity: - Quantidade: + Miktar: Bytes: - Bytes: + Bayt: Amount: - Quantia: + Meblağ: Fee: - Taxa: + Ücret: Dust: - Poeira: + Toz: After Fee: - Depois da taxa: + Ücretten sonra: Change: - trocar + Para üstü: If this is activated, but the change address is empty or invalid, change will be sent to a newly generated address. - Se isso estiver ativo e o endereço de troco estiver vazio ou inválido, o troco será enviado a um novo endereço gerado na hora. + Bu etkinleştirildiyse fakat para üstü adresi boş ya da geçersizse para üstü yeni oluşturulan bir adrese gönderilecektir. Custom change address - Endereço específico de troco + Özel para üstü adresi Transaction Fee: - Taxa de transação + İşlem ücreti: Choose... - Escolha... + Seç... Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until your have validated the complete chain. - Usar o fallbackfee pode resultar no envio de uma transação que levará várias horas ou dias (ou nunca) para confirmação. Considere escolher sua taxa manualmente ou espere até que você tenha validado a cadeia completa. + Fallbackfee kullanmak, bir işlemin teyit edilmesinin satler veya günler almasına (ve hiçbir zaman teyit edilememesine) neden olabilir. Ücreti elle seçmeyi veya tüm zincirin onaylanmasını beklemeyi göz önünde bulundurun. Warning: Fee estimation is currently not possible. - Atenção: Estimativa de taxa não disponível no momento + Uyarı: Ücret tahmini şu anda mümkün değildir. collapse fee-settings - Ocultar painel + ücret-ayarlarını-küçült Confirmation time target: - Tempo alvo de confirmação: + Doğrulama süresi hedefi: PrivateSend - EnvioPrivado + Özel Gönder InstantSend - EnvioInstantâneo + Anında Gönder + + + If the custom fee is set to 1000 duffs and the transaction is only 250 bytes, then "per kilobyte" only pays 250 duffs in fee,<br />while "at least" pays 1000 duffs. For transactions bigger than a kilobyte both pay by kilobyte. + Eğer özel ücret 1000 duff olarak belirlendiyse ve işlem sadece 250 bayt ise "kilobayt başına" ücret olarak sadece 250 duff ödersiniz,<br />bu da "en az" 1000 duff öder. Bir kilobayttan büyük işlemler için ikisi de kilobayt başına ödeme yapar. + + + If the custom fee is set to 1000 duffs and the transaction is only 250 bytes, then "per kilobyte" only pays 250 duffs in fee,<br />while "total at least" pays 1000 duffs. For transactions bigger than a kilobyte both pay by kilobyte. + Eğer özel ücret 1000 duff olarak belirlendiyse ve işlem sadece 250 bayt ise "kilobayt başına" ücret olarak sadece 250 duff ödersiniz,<br />bu da "toplam en az" 1000 duff öder. Bir kilobayttan büyük işlemler için ikisi de kilobayt başına ödeme yapar. + + + Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks.<br />But be aware that this can end up in a never confirming transaction once there is more demand for dash transactions than the network can process. + Bloklardaki alandan daha düşük işlem hacmi var olduğu sürece sadece minimum ücreti ödemek yeterli olur.<br />Ama ağın işleyebileceğinden daha fazla dash işlemi talebi olursa, bunun işlemlerin asla teyit edilememesi gibi bir sonucu olabileceğini unutmayın. per kilobyte - por kilobyte + kilobayt başı Hide - Esconder + Gizle total at least - total pelo menos + toplam en az (read the tooltip) - (Leia o popup) + (bilgi balonunu oku) Recommended: - Recomendado + Tavsiye edilen: Custom: - Personalizado: + Özel: (Smart fee not initialized yet. This usually takes a few blocks...) - (Smart fee não iniciado. Isso requer alguns blocos...) + (Zeki ücret henüz başlatılmadı. Bu genelde birkaç blok alır...) normal @@ -2833,194 +2782,194 @@ https://www.transifex.com/projects/p/dash/ fast - rápido + hızlı Confirm the send action - Confirmar o envio + Yollama etkinliğini teyit ediniz S&end - Enviar + G&önder Clear all fields of the form. - Limpar todos os campos do formulário. + Formdaki tüm alanları temizle. Clear &All - Limpar Tudo + Tümünü &temizle Send to multiple recipients at once - Enviar para vários destinatários de uma só vez + Birçok alıcıya aynı anda gönder Add &Recipient - Adicionar destinatário + &Alıcı ekle Balance: - Saldo: + Bakiye: Copy quantity - Copiar quantidade + Miktarı kopyala Copy amount - Copiar quantia + Tutarı kopyala Copy fee - Copiar taxa + Ücreti kopyala Copy after fee - Copiar pós taxa + Ücretten sonrasını kopyala Copy bytes - Copiar bytes + Baytları kopyala Copy dust - Copiar poeira + Tozu kopyala Copy change - Copiar troco + Para üstünü kopyala using - usando + bunu kullanarak: anonymous funds - fundos anônimos + anonim bakiye (privatesend requires this amount to be rounded up to the nearest %1). - (O privatesend exige que esse valor seja arredondado para o próximo %1). + (özel gönder için bu tutarın en yakın %1 tutarına yuvarlanması gerekiyor). any available funds (not anonymous) - quaisquer fundos disponíveis (não anônimos) + tüm mevcut bakiye (anonim değil) and InstantSend - e EnvioInstantâneo + ve Anında Gönder %1 to %2 - %1 a %2 + %1 ögesinden %2 unsuruna Are you sure you want to send? - Você tem certeza que deseja enviar? + Göndermek istediğinizden emin misiniz? are added as transaction fee - são adicionadas como taxas de transação + işlem ücreti olarak eklendi Total Amount = <b>%1</b><br />= %2 - Quantidade Total = <b>%1</b><br />= %2 + Toplam Tutar = <b>%1</b><br />= %2 <b>(%1 of %2 entries displayed)</b> - <b>(%1 de %2 entradas exibidas)</b> + <b>(%1 / %2 girdi gösteriliyor)</b> Confirm send coins - Confirme o envio de moedas + Bitcoin gönderimini onaylayın The recipient address is not valid. Please recheck. - Endereço de envio inváido. Favor checar. + Alıcı adresi geçerli değildir. Lütfen tekrar kontrol ediniz. The amount to pay must be larger than 0. - A quantia à pagar deve ser maior que 0 + Ödeyeceğiniz tutarın 0'dan yüksek olması gerekir. The amount exceeds your balance. - A quantia excede o seu saldo + Tutar bakiyenizden yüksektir. The total exceeds your balance when the %1 transaction fee is included. - O total excede o seu saldo quando a taxa da transação %1 é incluída + Toplam, %1 işlem ücreti eklendiğinde bakiyenizi geçmektedir. Duplicate address found: addresses should only be used once each. - Encontrado endereço duplicado: os endereços deveriam ser usados apenas uma vez cada + Tekrarlayan adres bulundu: adresler sadece bir kez kullanılmalıdır. Transaction creation failed! - Criação de transação falha + İşlem oluşturma başarısız! The transaction was rejected with the following reason: %1 - A transação foi rejeitada com a seguinte razão: %1 + İşlem şu nedenden dolayı reddedildi: %1 A fee higher than %1 is considered an absurdly high fee. - Uma taxa maior que %1 é considerada uma taxa absurdamente alta. + %1 tutarından yüksek bir ücret saçma derecede yüksek bir ücret olarak kabul edilir. Payment request expired. - Pedido de pagamento expirado + Ödeme talebinin geçerlilik süresi bitti. %n block(s) - %n blocos%n blocos + %n blok%n blok Pay only the required fee of %1 - Pagar somente a taxa requerida de %1 + Sadece asgari ücret olan %1 tutarını öde Estimated to begin confirmation within %n block(s). - Estimativa para iniciar a confirmação em %n blocos.Estimativa para iniciar a confirmação em %n blocos. + %n blok içinde onaylamanın başlayacağı tahmin ediliyor.%n blok içinde onaylamanın başlayacağı tahmin ediliyor. Warning: Invalid Dash address - Aviso: Endereço Dash inválido + UYARI: Geçersiz Dash adresi Warning: Unknown change address - Aviso: Endereço de troco inválido + Uyarı: Bilinmeyen para üstü adresi Confirm custom change address - Confirmar endereço de troco personalizado + Özel para üstü adresini onayla The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure? - O endereço selecionado para o troco não pertence a esta carteira. Alguns ou todos os fundos da sua carteira modem ser mandados para esse endereço. Tem certeza? + Para üstü için seçtiğiniz adres bu cüzdanın bir parçası değil. Cüzdanınızdaki bir miktar veya tüm para bu adrese gönderilebilir. Emin misiniz? (no label) - (sem rótulo) + (etiket yok) SendCoinsEntry This is a normal payment. - Este é um pagamento normal. + Bu, normal bir ödemedir. Pay &To: - Pagar &Para: + &Şu adrese öde: The Dash address to send the payment to - O endereço Dash para enviar o pagamento a + Ödemenin yollanacağı Dash adresi Choose previously used address - Escolher endereço usado anteriormente + Önceden kullanılmış adres seç Alt+A @@ -3028,7 +2977,7 @@ https://www.transifex.com/projects/p/dash/ Paste address from clipboard - Colar o endereço da área de transferência + Panodan adres yapıştır Alt+P @@ -3036,96 +2985,96 @@ https://www.transifex.com/projects/p/dash/ Remove this entry - Remover esta entrada + Bu unsuru kaldır &Label: - &Rótulo: + &Etiket: Enter a label for this address to add it to the list of used addresses - Digite um rótulo para este endereço para adicioná-lo no catálogo + Kullanılmış adres listesine eklemek için bu adrese bir etiket girin A&mount: - Q&uantidade: + Mebla&ğ: The fee will be deducted from the amount being sent. The recipient will receive a lower amount of Dash than you enter in the amount field. If multiple recipients are selected, the fee is split equally. - A taxa será deduzida do valor enviado. O destinatário receberá uma quantidade menor de Dash do que você insere no campo de valor. Se vários destinatários forem selecionados, a taxa será dividida igualmente. + Ücret gönderilen tutardan düşülecek. Alıcı sizin tutar alanına girdiğinizden daha düşük miktarda Dash alacak. Eğer birden fazla alıcı seçilirse, ücret eşit bölünür. S&ubtract fee from amount - &Retirar taxa da quantia + Ücreti tutardan düş Message: - Mensagem: + Mesaj: A message that was attached to the dash: URI which will be stored with the transaction for your reference. Note: This message will not be sent over the Dash network. - A mensagem que foi anexada ao Dash: URI na qual será gravada na transação para sua referência. Nota: Essa mensagem não será gravada publicamente na rede Dash. + Referans için Dash: URI'siyle iliştirilmiş işlemle birlikte depolanacak bir ileti. Not: Bu mesaj Dash ağı üzerinden gönderilmeyecektir. This is an unauthenticated payment request. - Esta é uma cobrança não autenticada. + Bu, kimliği doğrulanmamış bir ödeme talebidir. This is an authenticated payment request. - Esta é uma cobrança autenticada. + Bu, kimliği doğrulanmış bir ödeme talebidir. Pay To: - Pague Para: + Şu adrese öde: Memo: - Memorizar: + Not: Enter a label for this address to add it to your address book - Digite um rótulo para este endereço para adicioná-lo ao catálogo de endereços + Adres defterinize eklemek için bu adrese bir etiket giriniz SendConfirmationDialog Yes - Sim + Evet ShutdownWindow %1 is shutting down... - %1 está desligando... + %1 kapanıyor... Do not shut down the computer until this window disappears. - Não desligue o computador até essa janela desaparecer. + Bu pencere kalkıncaya dek bilgisayarı kapatmayınız. SignVerifyMessageDialog Signatures - Sign / Verify a Message - Assinaturas - Assinar / Verificar uma mensagem + İmzalar - Mesaj İmzala / Kontrol et &Sign Message - &Assinar Mensagem + Mesaj &imzala You can sign messages/agreements with your addresses to prove you can receive Dash sent to them. Be careful not to sign anything vague or random, as phishing attacks may try to trick you into signing your identity over to them. Only sign fully-detailed statements you agree to. - Você pode assinar mensagens/acordos com seus endereços para provar que pode receber o Dash enviado para eles. Tenha cuidado para não assinar nada vago ou aleatório, pois os ataques de phishing podem tentar convencê-lo a assinar sua identidade para eles. Assine apenas declarações totalmente detalhadas com as quais você concorda. + Onlara gönderilen Dash'i alabildiğinizi ispatlamak için mesajları/anlaşmaları adresleriniz ile imzalayabilirsiniz. Muallak ya da rastgele bir şeyi imzalamamaya dikkat edin çünkü kimlik avı saldırıları ile sizi kimliğinizi açık etmek için dolandırabilirler. Sadece anlaştığınız tam ayrıntılı beyanları imzalayın. The Dash address to sign the message with - O endereço Dash que assinará a mensagem + İletinin imzalanmasında kullanılacak Dash adresi Choose previously used address - Escolha um endereço usado anteriormente + Önceden kullanılmış adres seç Alt+A @@ -3133,7 +3082,7 @@ https://www.transifex.com/projects/p/dash/ Paste address from clipboard - Colar o endereço da área de transferência + Panodan adres yapıştır Alt+P @@ -3141,107 +3090,107 @@ https://www.transifex.com/projects/p/dash/ Enter the message you want to sign here - Entre a mensagem que você quer assinar aqui + İmzalamak istediğiniz mesajı burada giriniz Signature - Assinatura + İmza Copy the current signature to the system clipboard - Copiar a assinatura para a área de transferência do sistema + Güncel imzayı sistem panosuna kopyala Sign the message to prove you own this Dash address - Assinar mensagem para provar que você é dono deste endereço Dash + Bu Dash adresine sahip olduğunuzu ispatlamak için mesajı imzala Sign &Message - Assinar &Mensagem + &Mesajı imzala Reset all sign message fields - Limpar todos os campos de assinatura da mensagem + Tüm mesaj alanlarını sıfırla Clear &All - Limpar Tudo + Tümünü &temizle &Verify Message - &Verificar Mensagem + Mesaj &kontrol et Enter the receiver's address, message (ensure you copy line breaks, spaces, tabs, etc. exactly) and signature below to verify the message. Be careful not to read more into the signature than what is in the signed message itself, to avoid being tricked by a man-in-the-middle attack. Note that this only proves the signing party receives with the address, it cannot prove sendership of any transaction! - Coloque o endereço do autor, a mensagem (certifique-se de copiar toda a mensagem, incluindo quebras de linha, espaços, tabulações, etc.) e a assinatura embaixo para verificar a mensagem. Cuidado para não ler mais da assinatura do que está assinado na mensagem, para evitar ser enganado pelo ataque man-in-the-middle. Note que isso somente prova a propriedade de um endereço, e não o remetende de qualquer transação. + Alıcının adresini, iletiyi (satır sonları, boşluklar, sekmeler vs. karakterleri tam olarak kopyaladığınızdan emin olunuz) ve imzayı aşağıya giriniz. Bir ortadaki adam saldırısı tarafından kandırılmaya engel olmak için imzadan, imzalı iletinin içeriğini aşan bir anlam çıkarmamaya dikkat ediniz. Bunun sadece imzalayan tarafın adres ile alım yapabildiğini ispatladığını ve herhangi bir işlemin gönderi tarafını kanıtlayamayacağını unutmayınız! The Dash address the message was signed with - O endereço Dash que foi usado para assinar a mensagem + İletinin imzalanmasında kullanılan Dash adresi Verify the message to ensure it was signed with the specified Dash address - Verifique a mensagem para se assegurar de que foi assinada pelo endereço Dash específicado + Belirtilen Dash adresi ile imzalandığını doğrulamak için iletiyi kontrol et Verify &Message - Verificar &Mensagem + &Mesaj kontrol et Reset all verify message fields - Limpar todos os campos de assinatura da mensagem + Tüm mesaj kontrolü alanlarını sıfırla Click "Sign Message" to generate signature - Clique em "Assinar mensagem" para gerar a assinatura + İmzayı oluşturmak için "İletiyi İmzala"ya tıklayın The entered address is invalid. - O endereço digitado é inválido + Girilen adres geçersizdir. Please check the address and try again. - Favor checar o endereço e tente novamente + Lütfen adresi kontrol edip tekrar deneyiniz. The entered address does not refer to a key. - O endereço fornecido não se refere a uma chave. + Girilen adres herhangi bir anahtara işaret etmemektedir. Wallet unlock was cancelled. - O desbloqueio da carteira foi cancelado + Cüzdan kilidinin açılması iptal edildi. Private key for the entered address is not available. - A chave privada para o endereço disponibilizado não está disponível. + Girilen adres için özel anahtar mevcut değildir. Message signing failed. - Assinatura da mensagem falhou. + İleti imzalaması başarısız oldu. Message signed. - Mensagem assinada. + İleti imzalandı. The signature could not be decoded. - A assinatura não pôde ser decodificada. + İmzanın kodu çözülemedi. Please check the signature and try again. - Por favor, cheque a assinatura e tente novamente. + Lütfen imzayı kontrol edip tekrar deneyiniz. The signature did not match the message digest. - A assinatura não corresponde a mensagem + İmza iletinin özeti ile eşleşmedi. Message verification failed. - Falha na verificação da mensagem + İleti doğrulaması başarısız oldu. Message verified. - Mensagem verificada + İleti doğrulandı. @@ -3252,7 +3201,7 @@ https://www.transifex.com/projects/p/dash/ Version %1 - Versão %1 + Sürüm %1 @@ -3266,488 +3215,488 @@ https://www.transifex.com/projects/p/dash/ TransactionDesc Open for %n more block(s) - Aberto para %n mais blocosAberto para %n mais blocos + %n daha blok için açık%n daha blok için açık Open until %1 - Aberto até %1 + %1 değerine dek açık conflicted - Conflitado + Uyuşmadı %1/offline - %1/offline + %1/çevrim dışı 0/unconfirmed, %1 - 0/não confirmado, %1 + 0/doğrulanmamış, %1 in memory pool - na memória + bellek alanında not in memory pool - não na memóra + bellek alanında değil abandoned - abandonado + Terk edilmiş %1/unconfirmed - %1/não confirmado + %1/doğrulanmadı %1 confirmations - %1 confirmações + %1 doğrulama verified via InstantSend - verificado via InstantSend + Anında Gönder ile onaylanmış InstantSend verification in progress - %1 of %2 signatures - Confirmação InstantSend em andamento - %1 de %2 assinaturas + Anında Gönder onaylaması sürüyor - %1 / %2 imza InstantSend verification failed - A confirmação do InstantSend falhou + Anında Gönder onaylaması başarısız Status - Status + Durum , has not been successfully broadcast yet - , ainda não foi propagada na rede com êxito. + , henüz başarılı bir şekilde yayınlanmadı , broadcast through %n node(s) - , transmissão por %n nós, transmissão por %n nós + , %n düğümden yayın, %n düğümden yayın Date - Data + Tarih Source - Fonte + Kaynak Generated - Gerado + Oluşturuldu From - De + Gönderen unknown - desconhecido + bilinmiyor To - Para + Alıcı own address - endereço próprio + kendi adresiniz watch-only - Monitorado + Sadece izlenen label - Rótulo + Etiket Credit - Crédito + Alınan Tutar matures in %n more block(s) - Maduro em %n blocosMaduro em %n blocos + %n blok sonra olgunlaşacak%n blok sonra olgunlaşacak not accepted - não aceito + kabul edilmedi Debit - Débito + Çekilen Tutar Total debit - Débito total + Toplam çekilen tutar Total credit - Crédito total + Toplam alınan tutar Transaction fee - Taxa da transação + İşlem ücreti Net amount - Valor líquido + Net tutar Message - Mensagem + İleti Comment - Comentário + Yorum Transaction ID - ID da transação + İşlem ID'si Output index - Index da saída + Çıktı indeksi Transaction total size - Tamanho transacional total + İşlemin toplam boyutu Merchant - Mercador + Tüccar Generated coins must mature %1 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, its state will change to "not accepted" and it won't be spendable. This may occasionally happen if another node generates a block within a few seconds of yours. - Moedas recém minerados precisam aguardar %1 blocos antes de serem gastos. Quando o bloco foi gerado, ele foi disseminado pela rede para ser adicionado à blockchain. Se ele falhar em ser inserido na cadeia, seu estado será modificado para "não aceito" e ele não poderá ser gasto. Isso pode acontecer eventualmente quando blocos são gerados quase que simultaneamente. + Oluşturulan bitcoin'lerin harcanabilmelerinden önce %1 blok beklemeleri gerekmektedir. Bu blok, oluşturduğunuzda, blok zincirine eklenmesi için ağda yayınlandı. Zincire eklenmesi başarısız olursa, durumu "kabul edilmedi" olarak değiştirilecek ve harcanamayacaktır. Bu, bazen başka bir düğüm sizden birkaç saniye önce ya da sonra blok oluşturursa meydana gelebilir. Debug information - Depurar informação + Hata ayıklama bilgisi Transaction - Transação + İşlem Inputs - Entradas + Girdiler Amount - Quantia + Tutar true - verdadeiro + doğru false - falso + yanlış TransactionDescDialog This pane shows a detailed description of the transaction - Este painel mostra uma descrição detalhada da transação + Bu pano muamelenin ayrıntılı açıklamasını gösterir Details for %1 - Detalhes para %1 + %1 için ayrıntılar TransactionTableModel Date - Data + Tarih Type - Tipo + Tür Address / Label - Endereço / Etiqueta + Adres / Etiket Open for %n more block(s) - Aberto para %n mais blocosAberto para %n mais blocos + %n daha blok için açık%n daha blok için açık Open until %1 - Aberto até %1 + %1 değerine dek açık Offline - Offline + Çevrim dışı Unconfirmed - Não confirmado + Doğrulanmamış Abandoned - Abandonado + Terk edilmiş Confirming (%1 of %2 recommended confirmations) - Confirmando (%1 de %2 confirmações recomendadas) + Doğrulanıyor (%1 kere doğrulandı, önerilen doğrulama sayısı %2) Confirmed (%1 confirmations) - Confirmado (%1 confirmações) + Doğrulandı (%1 doğrulama) Conflicted - Conflitante + Uyuşmadı Immature (%1 confirmations, will be available after %2) - Recém-criado (%1 confirmações, disponível somente após %2) + Olgunlaşmamış (%1 doğrulama, %2 doğrulama sonra kullanılabilir olacaktır) This block was not received by any other nodes and will probably not be accepted! - Este bloco não foi recebido por nenhum outro participante da rede e provavelmente não será aceito! + Bu blok başka hiçbir düğüm tarafından alınmamıştır ve muhtemelen kabul edilmeyecektir! Generated but not accepted - Gerado mas não aceito + Oluşturuldu ama kabul edilmedi Received with - Recebido + Şununla alındı Received from - Recebido + Alındığı kişi Received via PrivateSend - Recebido via EnvioPrivado + Özel Gönder ile alındı Sent to - Enviado + Gönderildiği adres Payment to yourself - Pagamento a si mesmo + Kendinize ödeme Mined - Minerado + Madenden çıkarılan PrivateSend Denominate - PrivateSend Denominate + Özel Gönder Birimi PrivateSend Collateral Payment - PrivateSend Pagamento de Garantia + Özel Gönder Teminat Ödemesi PrivateSend Make Collateral Inputs - PrivateSend fazer insumos colaterais + Özel Gönder Teminat Girdisi Yap PrivateSend Create Denominations - PrivateSend Criar Denominações + Özel Gönder Birim Oluştur PrivateSend - EnvioPrivado + Özel Gönder watch-only - Monitorado + Sadece izlenen (n/a) - (n/a) + (mevcut değil) (no label) - (sem rótulo) + (etiket yok) Transaction status. Hover over this field to show number of confirmations. - Status da transação. Passe o mouse sobre este campo para mostrar o número de confirmações. + İşlem durumu. Doğrulama sayısını görüntülemek için fare imlecini bu alanın üzerinde tutunuz. Date and time that the transaction was received. - Data e hora que a transação foi recebida. + İşlemin alındığı tarih ve zaman. Type of transaction. - Tipo de transação. + İşlemin türü. Whether or not a watch-only address is involved in this transaction. - Mostrar ou não endereços monitorados na lista de transações. + Bu işleme sadece-izlenen bir adresin dahil edilip, edilmediği. User-defined intent/purpose of the transaction. - Intenção/Propósito definido pelo usuário para a transação + İşlemin kullanıcı tanımlı amacı. Amount removed from or added to balance. - Quantidade debitada ou creditada ao saldo. + Bakiyeden kaldırılan ya da bakiyeye eklenen tutar. TransactionView All - Todos + Hepsi Today - Hoje + Bugün This week - Essa semana + Bu hafta This month - Esse mês + Bu ay Last month - Último mês + Geçen ay This year - Esse ano + Bu yıl Range... - Intervalo... + Tarih Aralığı Most Common - Mais comum + En Sık Received with - Recebido com + Şununla alındı Sent to - Enviado para + Gönderildiği adres PrivateSend - EnvioPrivado + Özel Gönder PrivateSend Make Collateral Inputs - PrivateSend fazer insumos colaterais + Özel Gönder Teminat Girdisi Yap PrivateSend Create Denominations - PrivateSend Criar Denominações + Özel Gönder Birim Oluştur PrivateSend Denominate - PrivateSend Denominate + Özel Gönder Birimi PrivateSend Collateral Payment - PrivateSend Pagamento de Garantia + Özel Gönder Teminat Ödemesi To yourself - Para você mesmo + Kendinize Mined - Minerado + Madenden çıkarılan Other - Outro + Diğer Enter address or label to search - Procure um endereço ou etiqueta + Aranacak adres ya da etiket giriniz Min amount - Quantia mínima + En düşük tutar Abandon transaction - Abandonar transação + İşlemden vazgeç Copy address - Copiar endereço + Adres kopyala Copy label - Copiar rótulo + Etiket kopyala Copy amount - Copias quantia + Tutarı kopyala Copy transaction ID - Copiar ID da transação + İşlem ID'sini kopyala Copy raw transaction - Copiar o raw da transação + Ham işlemi kopyala Copy full transaction details - Copiar dados completos da transação + Tüm işlem ayrıntılarını kopyala Edit label - Editar rótulo + Etiketi düzenle Show transaction details - Mostrar detalhes da transação + İşlem ayrıntılarını göster Export Transaction History - Exportar histórico de transações + İşlem Tarihçesini Dışarı Aktar Comma separated file (*.csv) - Comma separated file (*.csv) + Virgülle ayrılmış değerler dosyası (*.csv) Confirmed - Confirmado + Doğrulandı Watch-only - Monitorado + Sadece izlenen Date - Data + Tarih Type - Tipo + Tür Label - Rótulo + Etiket Address - Endereço + Adres ID @@ -3755,1074 +3704,1230 @@ https://www.transifex.com/projects/p/dash/ Exporting Failed - Falha na exportação + Dışarı aktarmada hata There was an error trying to save the transaction history to %1. - Ocorreu um erro ao tentar salvar o histórico de transações em %1. + İşlem tarihçesinin %1 konumuna kaydedilmeye çalışıldığı sırada bir hata meydana geldi. Exporting Successful - Exportação feita com êxito + Dışarı Aktarma Başarılı The transaction history was successfully saved to %1. - O histórico de transação foi gravado com êxito em %1. + İşlem tarihçesi %1 konumuna başarıyla kaydedildi. Range: - Intervalo: + Tarih Aralığı: to - para + Alıcı UnitDisplayStatusBarControl Unit to show amounts in. Click to select another unit. - Unidade para mostrar. Clique para selecionar outra unidade. + Tutarı göstermek için birim. Başka bir birim seçmek için tıklayınız. WalletFrame No wallet has been loaded. - Nenhuma carteira carregada + Hiçbir cüzdan yüklenmedi. WalletModel Send Coins - Enviar moedas + Bitcoini Gönder InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH. - EnvioInstantâneo não suporta o envio de valores tão altos ainda. As transações são limitadas a %1 DASH. + Anında Gönder şu anda bu kadar yüksek tutarları göndermeyi desteklemiyor. İşlemler şimdilik %1 DASH ile sınırlı. Used way too many inputs (>%1) for this InstantSend transaction, fees could be huge. - Usado demais entradas (>%1) para esta transação do InstantSend, as taxas podem ser enormes. + Bu Anında Gönder işlemi için çok fazla girdi (>%1) kullanıldı, ücret çok yüksek olabilir. WalletView &Export - &Exportar + &Dışarı aktar Export the data in the current tab to a file - Exportar os dados da guia atual para um arquivo + Mevcut sekmedeki verileri bir dosyaya aktar Selected amount: - Quantidade selecionada: + Seçilen tutar: Backup Wallet - Backup da carteira + Cüzdanı Yedekle Wallet Data (*.dat) - Dados da carteira (*.dat) + Cüzdan Verileri (*.dat) Backup Failed - Falha no backup + Yedekleme Başarısız Oldu There was an error trying to save the wallet data to %1. - Ocorreu um erro ao tentar salvar os dados da carteira em %1. + Cüzdan verilerinin %1 konumuna kaydedilmesi sırasında bir hata meydana geldi. Backup Successful - Backup bem sucedido + Yedekleme Başarılı The wallet data was successfully saved to %1. - Os dados da carteira foram salvos com êxito em %1. + Cüzdan verileri %1 konumuna başarıyla kaydedildi. dash-core Bind to given address and always listen on it. Use [host]:port notation for IPv6 - Vincular ao endereço fornecido e sempre escutar nele. Use a notação [host]:port para IPv6 + Belirtilen adrese bağlan ve daima ondan dinle. IPv6 için [makine]:port yazımını kullanınız Error: Listening for incoming connections failed (listen returned error %s) - Erro: Aceitar conexões de entrada falhou (retornou erro %s) + Hata: İçeri gelen bağlantıların dinlenmesi başarısız oldu (dinleme %s hatasını verdi) Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message) - Executa um comando quando um alerta relevante é recebido ou vemos uma longa segregação (%s é substituída pela mensagem) + İlgili bir uyarı alındığında ya da gerçekten uzun bir çatallama gördüğümüzde komutu çalıştır (komuttaki %s mesaj ile değiştirilir) Execute command when a wallet transaction changes (%s in cmd is replaced by TxID) - Executa um comando quando uma transação da carteira mudar (%s no comando será substituído por TxID) + Bir cüzdan muamelesi değiştiğinde komutu çalıştır (komuttaki %s TxID ile değiştirilecektir) Execute command when the best block changes (%s in cmd is replaced by block hash) - Executa um comando quando o melhor bloco mudar (%s no comando será substituído pelo hash do bloco) + En iyi blok değiştiğinde komutu çalıştır (komut için %s parametresi blok hash değeri ile değiştirilecektir) Name to construct url for KeePass entry that stores the wallet passphrase - Nome para construir url para a entrada do KeePass que armazena a senha da carteira + Cüzdan parolanızı bulunduran KeePass girdisine url oluşturmak için ad Set maximum size of high-priority/low-fee transactions in bytes (default: %d) - Definir tamanho máximo de transações de alta prioridade/baixa taxa em bytes (padrão: %d) + Yüksek öncelikli/düşük ücretli muamelelerin azami boyutunu bayt olarak ayarla (varsayılan: %d) Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d) - Define o número de threads de verificação de script (%u a %d, 0 = automático, <0 = número de cores deixados livres, padrão: %d) + Betik kontrolü iş parçacıklarının sayısını belirler (%u ilâ %d, 0 = otomatik, <0 = bu sayıda çekirdeği kullanma, varsayılan: %d) This is a pre-release test build - use at your own risk - do not use for mining or merchant applications - Este é um build de teste pré-lançamento - use por sua conta e risco - não use para mineração ou comércio. + Bu yayın öncesi bir deneme sürümüdür - tüm riski siz üstlenmiş olursunuz - bitcoin oluşturmak ya da ticari uygulamalar için kullanmayınız Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues. - Atenção: A rede não parecem concordar plenamente! Alguns mineiros parecem estar enfrentando problemas. + Uyarı: şebeke tamamen mutabık değil gibi görünüyor! Bazı madenciler sorun yaşıyor gibi görünüyor. Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade. - Atenção: Nós não parecemos concordar plenamente com nossos nós! Você pode precisar atualizar ou outros nós podem precisar atualizar. + Uyarı: eşlerimizle tamamen mutabık değiliz gibi görünüyor! Güncelleme yapmanız gerekebilir ya da diğer düğümlerin güncelleme yapmaları gerekebilir. You must specify a masternodeprivkey in the configuration. Please see documentation for help. - Você deve especificar um masternodeprivkey na configuração. Por favor, consulte a documentação para obter ajuda. + Ayarlarda bir masternodeprivkey belirtmelisiniz. Lütfen yardım almak için dökümantasyona göz atın. Accept command line and JSON-RPC commands - Aceitar linha de comando e comandos JSON-RPC + Komut satırı ve JSON-RPC komutlarını kabul et Add a node to connect to and attempt to keep the connection open - Adicionar um nó com o qual se conectar e tentar manter a conexão ativa + Bağlanılacak düğüm ekle ve bağlantıyı zinde tutmaya çalış Allow DNS lookups for -addnode, -seednode and -connect - Permitir consultas DNS para -addnode, -seednode e -connect + -addnode, -seednode ve -connect için DNS aramalarına izin ver Already have that input. - Já tem essa entrada. + Bu girdi zaten var. Block creation options: - Opções de criação de bloco: + Blok oluşturma seçenekleri: Cannot downgrade wallet - Não é possível fazer downgrade da carteira + Cüzdan eski biçime geri alınamaz Cannot write default address - Não foi possível escrever no endereço padrão + Varsayılan adres yazılamadı Collateral not valid. - Colateral inválido. + Teminat geçerli değil. Connect to a node to retrieve peer addresses, and disconnect - Conectar a um nó para receber endereços de participantes, e desconectar. + Eş adresleri elde etmek için bir düğüme bağlan ve ardından bağlantıyı kes Connection options: - Opções de conexão: + Bağlantı seçenekleri: Corrupted block database detected - Detectada database de bloco corrompida + Bozuk blok veritabanı tespit edildi Debugging/Testing options: - Opções de depuração/teste: + Hata ayıklama/deneme seçenekleri: Do not load the wallet and disable wallet RPC calls - Não carrega a carteira e desabilita as chamadas RPC para a carteira + Cüzdanı yükleme ve cüzdan RPC çağrılarını devre dışı bırak Do you want to rebuild the block database now? - Você quer reconstruir o banco de dados de blocos agora? + Blok veritabanını şimdi yeniden inşa etmek istiyor musunuz? Done loading - Carregamento completo + Yükleme tamamlandı Entries are full. - Entradas estão cheias. + Girdiler dolu. Error initializing block database - Erro ao inicializar a database do bloco + Blok veritabanını başlatılırken bir hata meydana geldi Error initializing wallet database environment %s! - Erro ao inicializar ambiente de banco de dados de carteira %s! + %s cüzdan veritabanı ortamının başlatılmasında hata meydana geldi! Error loading block database - Erro ao carregar banco de dados de blocos + Blok veritabanının yüklenmesinde hata Error opening block database - Erro ao abrir banco de dados de blocos + Blok veritabanının açılışı sırasında hata Error reading from database, shutting down. - Erro ao ler o banco de dados. Finalizando. + Veritabanından okumada hata, kapatılıyor. Error - Erro + Hata Error: Disk space is low! - Erro: Pouco espaço em disco! + Hata: Disk alanı düşük! Failed to listen on any port. Use -listen=0 if you want this. - Falha ao escutar em qualquer porta. Use -listen=0 se você quiser isso. + Herhangi bir portun dinlenmesi başarısız oldu. Bunu istiyorsanız -listen=0 seçeneğini kullanınız. (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data) - (1 = manter metadados tx e.g. informação do dono da conta e requisição de pagamente, 2 = descartar metadados tx) + (1 = tx meta verilerini tut mesela hesap sahibi ve ödeme talebi bilgileri, 2 = tx meta verilerini at) -maxtxfee is set very high! Fees this large could be paid on a single transaction. - -maxtxfee é muito alto! Essa quantia poderia ser paga em uma única transação. + -maxtxfee çok yüksek bir değere ayarlanmış! Bu denli yüksek ücretler tek bir işlemde ödenebilir. A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s) - A variação da taxa (em %s/kB) que será usada quando não houver dados suficientes para se estimar a taxa (default: %s) + Ücret tahmini için yetersiz veri bulunduğunda kullanılacak ücret oranı (%s/kB olarak) (varsayılan: %s) Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d) - Aceita transações retransmitidas advindas de pares em lista branca, mesmo quando não estiver retransmitindo transações (padrão: %d) + İşlemler aktarılmadığında dahi beyaz listedeki eşlerden aktarılan işlemleri kabul et (varsayılan: %d) Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times - Permitir conexões JSON-RPC de uma fonte específica. Válido para um único ip (ex. 1.2.3.4), até uma rede/máscara (ex. 1.2.3.4/255.255.255.0) ou uma rede/CIDR (ex. 1.2.3.4/24). Esta opção pode ser usada múltiplas vezes + Belirtilen kaynaktan JSON-RPC bağlantılarını kabul et. Bir <ip> için geçerli olanlar şunlardır: IP adresi (mesela 1.2.3.4), bir ağ/ağ maskesi (örneğin 1.2.3.4/255.255.255.0) ya da bir ağ/CIDR (mesela 1.2.3.4/24). Bu seçenek birden fazla kez belirtilebilir Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6 - Vincular ao endereço fornecido e sempre escutar nele. Use a notação [host]:port para IPv6 + Belirtilen adrese bağlan ve ona bağlanan eşleri beyaz listeye al. IPv6 için [makine]:port imlasını kullanınız + + + Bind to given address to listen for JSON-RPC connections. Use [host]:port notation for IPv6. This option can be specified multiple times (default: bind to all interfaces) + JSON-RPC bağlantılarını dinlemek için verilen adrese bağla. IPv6 için [host]:port formatını kullanın. Bu seçenek birden fazla defa belirtilebilir (varsayılan: tüm arayüzlere bağla) Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality) - Criar novos arquivos com permissões padrão do sistema, em vez de umask 077 (apenas efetivo com funcionalidade de carteira desabilitada) + Yeni dosyaları umask 077 yerine varsayılan izinlerle oluştur (sadece devre dışı cüzdan işlevselliği ile etkilidir) Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup - Apaga todas as transações da carteira e somente recupera essas partes da blockchain usando o comando -rescan na inicialização + Tüm cüzdan işlemlerini sil ve başlangıçta -rescan ile sadece blok zincirinin parçası olanları geri getir Error loading %s: You can't enable HD on a already existing non-HD wallet - Erro ao carregar %s: você não pode ativar o HD em uma carteira não HD já existente + %s yüklenirken hata: Zaten var olan bir HD olmayan cüzdanda HD etkinleştiremezsiniz Found unconfirmed denominated outputs, will wait till they confirm to continue. - Encontrado saídas não confirmadas, esperará até que confirme para continuar. + Teyit edilmemiş birimlendirilmiş çıktılar bulundu, devam etmek için teyit edilene kadar beklenecek. How thorough the block verification of -checkblocks is (0-4, default: %u) - Quão completa a verificação de blocos do -checkblocks é (0-4, padrão: %u) + -checkblocks'un blok kontrolünün ne kadar kapsamlı olacağı (0 ilâ 4, varsayılan: %u) If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u) - Se paytxfee não estiver definida, incluir comissão suficiente para que as transações comecem a ter confirmações em média dentro de N blocos (padrão %u) + Eğer paytxfee ayarlanmadıysa kafi derecede ücret ekleyin ki işlemler teyite vasati n blok içinde başlasın (varsayılan: %u) 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: %s, testnet: %s) - Se este bloco está no blockchain, assume-se que ele e seus ancestrais são válidos e podem ignorar a verificação de scripts (0 para verificar todos, padrão: %s, testnet: %s) + Eğer bu blok zincirde yer alıyorsa onun ve atalarının geçerli olduğunu varsay ve potansiyel olarak onların betik doğrulamasını atla. (Tümünü doğrulamak için 0, varsayılan %s, testnet: %s) Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions) - Valor inválido para -maxtxfee=<valor>: '%s' (precisa ser pelo menos a taxa mínima de %s para prevenir que a transação nunca seja confirmada) + -maxtxfee=<tutar> için geçersiz tutar: '%s' (Sıkışmış işlemleri önlemek için en az %s değerinde en düşük aktarım ücretine eşit olmalıdır) Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u) - Mantém um índice completo de transações, usado pela chamada rpc getrawtransaction (padrão: %u) + İşlemlerin tamamının indeksini tut, getrawtransaction rpc çağrısı tarafından kullanılır (varsayılan: %u) Make sure to encrypt your wallet and delete all non-encrypted backups after you verified that wallet works! - Certifique-se de criptografar sua carteira e excluir todos os backups não criptografados depois de verificar se a carteira funciona! + Cüzdanın çalıştığından emin olduktan sonra mutlaka cüzdanınızı şifreleyip şifrelenmemiş tüm yedekleri silin! Maximum size of data in data carrier transactions we relay and mine (default: %u) - Tamanho máximo de dados em transações de dados de operadora (padrão %u) + Aktardığımız ve oluşturduğumuz veri taşıyıcı işlemlerindeki en yüksek veri boyutu (varsayılan: %u) Number of seconds to keep misbehaving peers from reconnecting (default: %u) - Número de segundos para impedir que peers mal comportados reconectem (padrão %u) + Aksaklık gösteren eşlerle terkar bağlantıyı engelleme süresi, saniye olarak (varsayılan: %u) Output debugging information (default: %u, supplying <category> is optional) - Informação de saída de debug (padrão: %u, definir <category> é opcional) + Hata ayıklama bilgisini dök (varsayılan: %u, <category> sağlanması seçime dayalıdır) Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s) - Use um proxy SOCKS5 separado para alcançar participantes da rede via serviços ocultos Tor (padrão: %s) + Eşlere gizli Tor servisleri ile ulaşmak için ayrı SOCKS5 vekil sunucusu kullan (varsayılan: %s) + + + User defined mnemonic for HD wallet (bip39). Only has effect during wallet creation/first start (default: randomly generated) + HD cüzdan (bip39) için kullanıcı tarafından belirlenmiş anımsatıcı. Sadece cüzdan oluşturma/ilk açılış sırasında etkilidir (varsayılan: rastgele oluşturulur) User defined seed for HD wallet (should be in hex). Only has effect during wallet creation/first start (default: randomly generated) - Semente definida pelo usuário para carteira HD (deve estar em hex). Só tem efeito durante a criação / início da carteira (padrão: gerado aleatoriamente) + HD cüzdan (hex olmalıdır) için kullanıcı tarafından belirlenmiş kaynak. Sadece cüzdan oluşturma/ilk açılış sırasında etkilidir (varsayılan: rastgele oluşturulur) Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway - Peers permitidos não podem ser banidos do DoS e suas transações sempre são transmitidas, até mesmo se eles já estão no pool de memória, útil, por exemplo, para um gateway + Beyaz listeye alınan eşler DoS yasaklamasına uğramazlar ve işlemleri zaten mempool'da olsalar da daima aktarılır, bu mesela bir geçit için kullanışlıdır You need to rebuild the database using -reindex-chainstate to change -txindex - Você precisa reconstruir o banco de dados usando -reindex-chainstate para alterar -txindex + -txindex'i değiştirmek için -reindex-chainstate kullanarak veritabanını baştan kurmalısınız (default: %s) - (padão: %s) + (varsayılan: %s) Accept public REST requests (default: %u) - Aceitar pedidos restantes públicas (padrão: %u) + Herkese açık REST taleplerini kabul et (varsayılan: %u) Always query for peer addresses via DNS lookup (default: %u) - Sempre pergunte pelo endereço de peer via pesquisa DNS (padrão: %u) + Eş adresleri sorgulaması için daima DNS aramasını kullan (varsayılan: %u) Can't mix: no compatible inputs found! - Não é possível misturar: não foram encontradas entradas compatíveis! + Karıştırılamıyor: hiç uyumlu girdi bulunamadı! Chain selection options: - Opções de seleção de cadeia: + Blok zinciri seçim ayarları: Connect through SOCKS5 proxy - Conecte através do proxy SOCKS5 + SOCKS5 vekil sunucusu vasıtasıyla bağlan Connect to KeePassHttp on port <port> (default: %u) - Conecte-se ao KeePassHttp na porta<port> (padrão: %u)  + Port <port> üstünde KeePassHttp'ye bağlan (varsayılan: %u) + + + Enable the client to act as a masternode (0-1, default: %u) + İstemcinin ana düğüm olarak davranmasını etkinleştir (0-1, varsayılan: %u) Entry exceeds maximum size. - Entrada excede o tamanho máximo. + Girdi maksimum boyutu aşıyor. + + + Error loading %s: You can't disable HD on a already existing HD wallet + %s yüklenirken hata: Zaten var olan bir HD cüzdanda HD'yi kapatamazsınız. Failed to load fulfilled requests cache from - Falha ao carregar o cache de solicitações preenchidas de + Şuradan tamamlanmış talep önbelleği yüklemesi başarısız: Failed to load governance cache from - Falha ao carregar o cache de governança de + Şuradan yönetim önbelleği yüklemesi başarısız: Failed to load masternode cache from - Falha ao carregar o cache masternode de + Şuradan ana düğüm önbelleği yüklemesi başarısız: Failed to load masternode payments cache from - Falha ao carregar o cache de pagamentos do masternode + Şuradan ana düğüm ödemeleri önbelleği yüklemesi başarısız: Found enough users, signing ( waiting %s ) - Encontrou usuários suficientes, assinando ( esperando %s ) + Yeterli kullanıcı bulundu, imzalanıyor ( %s bekleniyor ) Found enough users, signing ... - Encontrou usuários suficientes, assinando ... + Yeterli kullanıcı bulundu, imzalanıyor ... How many blocks to check at startup (default: %u, 0 = all) - Quantos blocos devem ser checados ao iniciar (padrão: %u, 0 = todos) + Başlangıçta kontrol edilecek blok sayısı (varsayılan: %u, 0 = hepsi) Importing... - Importando... + İçe aktarılıyor... Include IP addresses in debug output (default: %u) - Incluir endereço IP na saída de depuração (padrão: %u) + Hata ayıklama çıktısına IP adreslerini dahil et (varsayılan: %u) Incompatible mode. - Modo incompatível. + Uyumsuz mod. Incompatible version. - Versão incompatível. + Uyumsuz sürüm. Incorrect or no genesis block found. Wrong datadir for network? - Bloco gênese incorreto ou não encontrado. Datadir errado para a rede? + Yanlış ya da bulunamamış doğuş bloku. Şebeke için yanlış veri klasörü mü? Information - Informação + Bilgi Input is not valid. - A entrada não é válida. + Girdi geçerli değil. Insufficient funds. - Saldo insuficiente. + Yetersiz bakiye. Invalid -onion address: '%s' - Endereço -onion inválido: '%s' + Geçersiz -onion adresi: '%s' Invalid -proxy address: '%s' - Endereço - proxy inválido: '%s' + Geçersiz -proxy adresi: '%s' Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s) - Valor inválido para -paytxfee=<amount>: '%s' (precisa ser no mínimo %s) + -paytxfee=<tutar>:'%s' unsurunda geçersiz tutar (asgari %s olması lazımdır) Keypool ran out, please call keypoolrefill first - Erro na Keypool, favor executar keypoolrefill primeiro + Keypool tükendi, lütfen önce keypoolrefill'i çağırın Listen for JSON-RPC connections on <port> (default: %u or testnet: %u) - Escutar por conexões JSON-RPC na porta <port> (padrão: %u ou testnet: %u) + JSON-RPC bağlantılarını <port> üzerinde dinle (varsayılan: %u veya tesnet: %u) Listen for connections on <port> (default: %u or testnet: %u) - Aguardar por conexões na porta <port> (padrão: %u ou testnet: %u) + Bağlantılar için dinlenecek <port> (varsayılan: %u ya da testnet: %u) Loading banlist... - Carregando lista de banidos... + Yasaklama listesi yükleniyor... Loading fulfilled requests cache... - Carregando o cache de solicitações atendidas ... + Tamamlanan talepler önbelleği yükleniyor... Loading masternode cache... - Carregando o cache do masternode ... + Ana düğüm önbelleği yükleniyor... Loading masternode payment cache... - Carregando o cache de pagamento do masternode ... + Ana düğüm ödeme önbelleği yükleniyor... Lock is already in place. - Bloqueio já está no lugar. + Kilit zaten yerinde. + + + Lock masternodes from masternode configuration file (default: %u) + Ana düğüm ayar dosyasından ana düğümleri kilitle (varsayılan: %u) Maximum per-connection receive buffer, <n>*1000 bytes (default: %u) - Buffer máximo de recebimento por conexão, <n>*1000 bytes (padrão: %u) + Her bağlantı için en yüksek alım tamponu, <n>*1000 bayt (varsayılan: %u) Maximum per-connection send buffer, <n>*1000 bytes (default: %u) - Buffer máximo de envio por conexão, <n>*1000 bytes (padrão: %u) + Her bağlantı için çok gönderme tamponu, <n>*1000 bayt (varsayılan: %u) Mixing in progress... - Mixing em progresso... + Karışım devam ediyor... + + + Mnemonic passphrase is too long, must be at most 256 characters + Anımsatıcı parola çok uzun, en fazla 256 karakter olmalı Need to specify a port with -whitebind: '%s' - Necessário informar uma porta com -whitebind: '%s' + -whitebind: '%s' ile bir port belirtilmesi lazımdır No Masternodes detected. - Nenhum Masternode detectado. + Hiç Ana düğüm bulunamadı. No compatible Masternode found. - Nenhum Masternode compatível encontrado. + Hiç uyumlu Ana düğüm bulunamadı. Not in the Masternode list. - Não está na lista de Masternode. + Ana düğüm listesinde yok. Only connect to nodes in network <net> (ipv4, ipv6 or onion) - Somente conectar a clientes na rede <net> (ipv4, ipv6 ou onion) + Sadece <net> ağındaki düğümlere bağlan (ipv4, ipv6 veya onion) Port: %d - Porta: %d + Port: %d Prepend debug output with timestamp (default: %u) - Adiciona timestamp como prefixo no debug (padrão: %u) + Hata ayıklama verilerinin önüne zaman damgası ekle (varsayılan: %u) Rebuild chain state and block index from the blk*.dat files on disk - Reconstruir índice de cadeia de bloco a partir dos arquivos blk*.dat no disco + Zincir durumu ve blok indeksini diskteki blk*.dat dosyalarından yeniden derle Rebuild chain state from the currently indexed blocks - Reconstruir estado a partir dos blocos indexados + Zincir durumunu güncel olarak indekslenen bloklardan yeniden derle + + + Send trace/debug info to debug.log file (default: %u) + trace/debug bilgisini debug.log dosyasına gönder (varsayılan: %u) + + + Send transactions as zero-fee transactions if possible (default: %u) + Eğer mümkünse işlemleri sıfır maliyetli işlem olarak gönder (varsayılan: %u) Set key pool size to <n> (default: %u) - Defina o tamanho da chave para piscina<n> (padrão: %u) + Anahtar alan boyutunu <n> değerine ayarla (varsayılan: %u) Set the number of threads to service RPC calls (default: %d) - Defina o número de threads para chamadas do serviço RPC (padrão: %d) + Hizmet RCP aramaları iş parçacığı sayısını belirle (varsayılan: %d) Specify configuration file (default: %s) - Especificar arquivo de configuração (padrão: %s) + Yapılandırma dosyası belirtiniz (varsayılan: %s) Specify connection timeout in milliseconds (minimum: 1, default: %d) - Especificar tempo para desistência de conexões, em mili segundos (mínimo: 1, padrão: %d) + Bağlantı zaman aşım süresini milisaniye olarak belirt (en düşüki: 1, varsayılan: %d) + + + Specify masternode configuration file (default: %s) + Ana düğüm ayar dosyasını belirt (varsayılan: %s) Specify pid file (default: %s) - Especificar arquivo pid (padrão: %s) + Pid dosyası belirtiniz (varsayılan: %s) Spend unconfirmed change when sending transactions (default: %u) - Gastar troco não confirmado quando enviar transações (padrão: %u) + Gönderme işlemlerinde doğrulanmamış para üstünü harca (varsayılan: %u) + + + Submitted following entries to masternode: %u / %d + Ana düğüme şu girdiler gönderildi: %u / %d + + + Submitted to masternode, waiting for more entries ( %u / %d ) %s + Ana düğüme gönderildi, daha fazla girdi bekleniyor ( %u / %d ) %s Submitted to masternode, waiting in queue %s - Enviado para o masternode, esperando na fila %s + Ana düğüme gönderildi, kuyrukta bekleniyor %s Synchroning blockchain... - Sincronizando blockchain... + Blok zinciri eşleniyor... Synchronization failed - Sincronização falhou + Eşleme başarısız Synchronization finished - Sincronização finalizada + Eşleme bitti Synchronizing masternodes... - Sincronizando masternodes... + Ana düğümler eşleniyor... This is not a Masternode. - Este não é um Masternode. + Bu bir Ana Düğüm değil. Threshold for disconnecting misbehaving peers (default: %u) - Limite para desconectar peers mal comportados (padrão: %u) + Aksaklık gösteren eşlerle bağlantıyı kesme sınırı (varsayılan: %u) Too many %f denominations, removing. - Muitas %f denominações, removendo. + Çok fazla %f birimi, siliniyor. Tor control port password (default: empty) - Senha da porta de controle do Tor (padrão: vazio) + Tor kontrol portu parolası (varsayılan: boş) Tor control port to use if onion listening enabled (default: %s) - Porta de controle a ser usada se o monitoramento onion estiver habilitado (padrão: %s) + Eğer onion dinlemesi etkinse kullanılacak Tor kontrol portu (varsayılan: %s) Unable to start HTTP server. See debug log for details. - Não foi possível iniciar o servidor HTTP. Veja o log de debug para detalhes. + HTTP sunucusu başlatılamadı. Ayrıntılar için debug.log dosyasına bakınız. Unknown response. - Resposta desconhecida. + Bilinmeyen cevap. Unsupported argument -benchmark ignored, use -debug=bench. - Argument -benchmark não suportado e ignorado, use -debug=bench. + Desteklenmeyen -benchmark argümanı görmezden gelindi, -debug=bench kullanınız. Unsupported argument -debugnet ignored, use -debug=net. - Argument -debugnet não suportado e ignorado, use -debug=net. + Desteklenmeyen -debugnet argümanı görmezden gelindi, debug=net kullanınız. Unsupported argument -tor found, use -onion. - O argumento -tor não é suportado, use -onion. + Deskteklenmeyen -tor argümanı bulundu, -onion kullanınız. Upgrade wallet to latest format on startup - Atualizar carteira para o formato mais recente ao iniciar + Başlangıçta cüzdanı en yeni biçime güncelle + + + Use KeePass 2 integration using KeePassHttp plugin (default: %u) + KeePassHttp eklentisi kullanarak KeePass 2 entegrasyonu kullan (varsayılan: %u) Use UPnP to map the listening port (default: %u) - Usar UPnP para mapear a porta de entrada (padrão: %u) + Dinleme portunu haritalamak için UPnP kullan (varsayılan: %u) Use the test chain - Use a cadeia de teste + Test blok zincirini kullan User Agent comment (%s) contains unsafe characters. - Comentário User Agent (%s) contém caracteres inseguros. + Kullanıcı Aracı açıklaması (%s) güvensiz karakterler içermektedir. Will retry... - Será feita nova tentativa... + Tekrar denenecek... Invalid masternodeprivkey. Please see documenation. - masternodeprivkey inválida. Por favor, consulte a documentação. + Geçersiz anadüğümözelanahtarı. Lütfen dökümantasyona göz atın. Can't find random Masternode. - Não é possível encontrar o Masternode aleatório. + Rastgele Ana düğüm bulunamıyor. Can't mix while sync in progress. - Não é possível misturar enquanto a sincronização está em progresso. + Eşleme sürerken karıştırılamaz. Could not parse masternode.conf - Impossível avaliar masternode.conf + masternode.conf okunamıyor Invalid netmask specified in -whitelist: '%s' - Máscara de rede inválida detectada em -whitelist: '%s' + -whitelist: '%s' unsurunda geçersiz bir ağ maskesi belirtildi Invalid port detected in masternode.conf - Porta inválida detectada em masternode.conf + masternode.conf dosyasında geçersiz port tespit edildi Invalid script detected. - Detectado script inválido. + Geçersiz betik tespit edildi. KeePassHttp id for the established association - KeePassHttp id para a associação estabelecida + Birliktelik başlatmak için KeePassHttp kimliği + + + KeePassHttp key for AES encrypted communication with KeePass + KeePass ile AES şifreli iletişim sağlamak için KeePassHttp anahtarı Keep at most <n> unconnectable transactions in memory (default: %u) - Manter ao máximo <n> transações inconectáveis na memória (padrão: %u) + Hafızada en çok <n> bağlanılamaz işlem tut (varsayılan: %u) + + + Disable all Dash specific functionality (Masternodes, PrivateSend, InstantSend, Governance) (0-1, default: %u) + Tüm Dash spesifik işlevleri kapat (Ana Düğümler, Özel Gönder, Anında Gönder, Yönetim) (0-1, varsayılan: %u) + + + %s file contains all private keys from this wallet. Do not share it with anyone! + %s dosyası bu cüzdana ait tüm özel anahtarları tutuyor. Kimseyle paylaşmayın! Discover own IP addresses (default: 1 when listening and no -externalip or -proxy) - Descobrir o próprio IP (padrão: 1 enquanto aguardando conexões e sem -externalip ou -proxy) + Kendi IP adreslerini keşfet (varsayılan: dinlenildiğinde ve -externalip ya da -proxy yoksa 1) Do not keep transactions in the mempool longer than <n> hours (default: %u) - Não manter transações na mempool por mais que <n> horas (padrão: %u) + İşlemleri bellek alanında <n> saatten fazla tutma (varsayılan: %u) + + + Enable InstantSend, show confirmations for locked transactions (0-1, default: %u) + Anında Gönderi aç, kilitlenmiş işlemler için teyitleri gösterir (0-1, varsayılan: %u) + + + Enable multiple PrivateSend mixing sessions per block, experimental (0-1, default: %u) + Blok başına birden fazla Özel Gönder karışım işlemini aç, deneysel (0-1, varsayılan: %u) + + + Enable use of automated PrivateSend for funds stored in this wallet (0-1, default: %u) + Bu cüzdanda depolanan meblağ için otomatik Özel Gönder kullanımını aç (0-1, varsayılan: %u) + + + Execute command when a wallet InstantSend transaction is successfully locked (%s in cmd is replaced by TxID) + Bir cüzdan bir Anında Gönder işlemini başarılı şekilde kilitlediğinde bir komut çalıştır (cmd içinde %s TxID ile değiştirilir) + + + Extra transactions to keep in memory for compact block reconstructions (default: %u) + Kompakt blok yeniden inşası için hafıza tutmak için ekstra işlemler (varsayılan: %u) Failed to create backup, file already exists! This could happen if you restarted wallet in less than 60 seconds. You can continue if you are ok with this. - Falha em criar o backup, o arquivo já existe! Isso pode ter acontecido se você resetou sua carteira em menos de 60 segundos. Você pode continuar se está ok com isso. + Yedek oluşturulamadı, dosya zaten mevcut! Bu durum eğer 60 saniyeden kısa sürede cüzdanı yenidden başlattıysanız oluşabilir. Sizin için sorun yoksa devam edebilirsiniz. Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s) - Comissões (em %s/kB) menores serão consideradas como zero para relaying, mineração e criação de transação (padrão %s) + Bundan düşük ücretler (%s/kB olarak) aktarma, oluşturma ve işlem yaratma için sıfır değerinde ücret olarak kabul edilir (varsayılan: %s) Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s) - Comissões (em %s/kB) menores serão consideradas como zero para criação de transação (padrão %s) + Bundan düşük ücretler (%s/kB olarak) işlem oluşturulması için sıfır değerinde ücret olarak kabul edilir (varsayılan: %s) If <category> is not supplied or if <category> = 1, output all debugging information. - Se <category> não for suprida ou se <category> = 1, mostrar toda informação de depuração. + Eğer <category> belirtilmemişse ya da <category> = 1 ise, tüm hata ayıklama verilerini çıktı al. InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH. - EnvioInstantâneo não suporta o envio de valores tão altos ainda. As transações são limitadas a %1 DASH. + Özel Gönder şu anda bu kadar büyük meblağları desteklemiyor. Şu anda işlemler %1 DASH ile sınırlı. + + + InstantSend requires inputs with at least %d confirmations, you might need to wait a few minutes and try again. + Anında Gönder için girdilerin en az %d teyidi olması gerekiyor, birkaç dakika daha bekleyip tekrar denemeniz gerekebilir. + + + Maintain a full address index, used to query for the balance, txids and unspent outputs for addresses (default: %u) + Tam adres endeksi tut, bakiye, txid ve adresler için harcanmamış çıktı sorgusu yapmak için kullanılır (varsayılan: %u) + + + Maintain a full spent index, used to query the spending txid and input index for an outpoint (default: %u) + Tam harcama endeksi tut, harcama txidleri ve bir çıkış noktası için girdi endeksi sorgusu yapmak için kullanılır (varsayılan: %u) + + + Maintain a timestamp index for block hashes, used to query blocks hashes by a range of timestamps (default: %u) + Blok hashleri için bir zaman kodu endeksi tut, bir düzü zaman kodu ile hash bloğu sorgusu yapmak için kullanılır (varsayılan: %u) + + + Maintain at most <n> connections to peers (temporary service connections excluded) (default: %u) + Eşlerle en fazla <n> bağlantı sağla (geçici hizmet bağlantıları dahil değildir) (varsayılan: %u) + + + PrivateSend uses exact denominated amounts to send funds, you might simply need to anonymize some more coins. + Özel Gönder para göndermek için tam olarak birimlendirilmiş meblağlar kullanır, sadece biraz daha fazla parayı anonim hale getirmeniz gerekiyor. Prune configured below the minimum of %d MiB. Please use a higher number. - Prune configurado abaixo do mínimo de %d MiB. Por favor use um número mais alto. + Budama, en düşük değer olan %d MiB'den düşük olarak ayarlanmıştır. Lütfen daha yüksek bir sayı kullanınız. Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node) - Prune: A ultima sincronização da carteira foi além do dado comprimido. Você precisa reindexar (fazer o download de toda a blockchain novamente) + Budama: son cüzdan eşleşmesi budanmış verilerin ötesine gitmektedir. -reindex kullanmanız gerekmektedir (Budanmış düğüm ise tüm blok zincirini tekrar indirmeniz gerekir.) Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u) - Gerar credenciais aleatórias para cada conexão por proxy. Isto habilita o isolamento de stream do Tor (padrão: %u) + Her vekil bağlantısı için kimlik verilerini rastgele yap. Bu, Tor akış izolasyonunu etkinleştirir (varsayılan: %u) Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again. - Rescans não são possíveis no modo prune. Você precisa usar -reindex, que irá fazer o download de toda a blockchain novamente. + Tekrar taramalar budanmış kipte mümkün değildir. Tüm blok zincirini tekrar indirecek olan -reindex seçeneğini kullanmanız gerekecektir. Specify full path to directory for automatic wallet backups (must exist) - Especifique o caminho completo para o diretório de backups automáticos da carteira (deve existir) + Otomatik cüzdan yedekleri için tam yolu belirtin (yol şu an var olmalı) Support filtering of blocks and transaction with bloom filters (default: %u) - Suportar filtragem de blocos e transações com filtros bloom (padrão: %u) + Blokların ve işlemlerin bloom filtreleri ile süzülmesini destekle (varsayılan: %u) The block database contains a block which appears to be from the future. This may be due to your computer's date and time being set incorrectly. Only rebuild the block database if you are sure that your computer's date and time are correct - O banco de dados de blocos contém um bloco que parece ser do futuro. Isso pode ser devido à data e hora do seu computador estarem configuradas incorretamente. Apenas reconstrua o banco de dados de blocos se você estiver certo de que a data e hora de seu computador estão corretas. + Blok veritabanı gelecekten gibi görünen bir blok içermektedir. Bu, bilgisayarınızın saat ve tarihinin yanlış ayarlanmış olmasından kaynaklanabilir. Blok veritabanını sadece bilgisayarınızın tarih ve saatinin doğru olduğundan eminseniz yeniden derleyin. The transaction amount is too small to send after the fee has been deducted - O valor da transação é muito pequeno para ser enviado depois que a taxa foi deduzida + Bu işlem, tutar düşüldükten sonra göndermek için çok düşük Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments. - O tamanho total da string de versão da rede (%i) excede o tamanho máximo (%i). Reduza o numero ou tamanho de uacomments. + Ağ sürümü zincirinin toplam boyutu (%i) en yüksek boyutu geçmektedir (%i). Kullanıcı aracı açıklamasının sayısı veya boyutunu azaltınız. + + + Transaction index can't be disabled in full mode. Either start with -litemode command line switch or enable transaction index. + Tam modda işlem endeksi kapatılamaz. Ya komut satırından -litemode ile başlatın ya da işlem endeksini açın. Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d) - Tenta manter tráfego fora dos limites dentro do alvo especificado (em MiB por 24h), 0 = sem limite (padrão: %d) + Giden trafiği belirtilen hedefin altında tutmaya çalışır (24 saat başı MiB olarak), 0 = sınırsız (varsayılan: %d) Unable to locate enough PrivateSend denominated funds for this transaction. - Não é possível localizar fundos denominados do PrivateSend suficientes para essa transação. + Bu işlem için yeterli Özel Gönder ile birimlendirilmiş bakiye bulunamadı. Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported. - Argumento inválido -socks encontrado. Definir a versão do SOCKS não é mais possível, somente proxys SOCK5 são suportados. + Desteklenmeyen -socks argümanı bulundu. SOCKS sürümünün ayarlanması artık mümkün değildir, sadece SOCKS5 vekilleri desteklenmektedir. Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay. - Argumento não suportado -whitelistalwaysrelay foi ignorado, utilize -whitelistrelay e/ou -whitelistforcerelay. + Desteklenmeyen argüman -whitelistalwaysrelay görmezden gelindi, -whitelistrelay ve/veya -whitelistforcerelay kullanın. Use UPnP to map the listening port (default: 1 when listening and no -proxy) - Usar UPnP para mapear a porta de entrada (padrão: 1 quando listening e no -proxy) + Dinlenecek portu haritalamak için UPnP kullan (varsayılan: dinlenildiğinde ve -proxy olmadığında 1) WARNING! Failed to replenish keypool, please unlock your wallet to do so. - ATENÇÃO! Falha ao repor o keypool, desbloqueie sua carteira para fazer isso. + UYARI! Anahtar havuzu yenilenemedi, lütfen devam etmek için cüzdanınızın kilidini açın. + + + Wallet is locked, can't replenish keypool! Automatic backups and mixing are disabled, please unlock your wallet to replenish keypool. + Cüzdanınız kilitli, anahtar havuzu yenilenemiyor! Otomatik yedekleme ve karışım kapalı, anahtar havuzunu yenilemek için lütfen cüzdanınızın kilidini açın. Warning: Unknown block versions being mined! It's possible unknown rules are in effect - Alerta: Versões de blocos desconhecidas mineradas! É possível que regras desconhecidas estejam ativas + Uyarı: Bilinmeyen blok sürümü oluşturulmaya çalışılıyor. Bilinmeyen kuralların işlemesi mümkündür. + + + You are starting in lite mode, all Dash-specific functionality is disabled. + Lite modda başlatıyorsunuz, tüm Dash spesifik işlevler kapalı olacak. You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain - Você precisa reconstruir o banco de dados usando -reindex para sair do modo prune. Isso irá rebaixar todo o blockchain. + Budama olmayan kipe dönmek için veritabanını -reindex ile tekrar derlemeniz gerekir. Bu, tüm blok zincirini tekrar indirecektir (%d could be used only on mainnet) - (%d poderia ser usado somente na mainnet) + (%d sadece mainnet'te kullanılabilir) (default: %u) - (padrão: %u) + (varsayılan: %u) (must be %d for mainnet) - (precisa ser %d para mainnet) + (mainnet için %d olmalı) -maxmempool must be at least %d MB - -maxmempool deve ser pelo menos %d MB + -maxmempool en az %d MB olmalıdır <category> can be: - <category> pode ser: + <kategori> şunlar olabilir: Append comment to the user agent string - Adicionar comentário à string user agent + Kullanıcı aracı zincirine yorumu ekle Automatic backups disabled - Backup automáticos desabilitados. + Otomatik yedekleme kapalı Automatically create Tor hidden service (default: %d) - Criar automaticamente serviços ocultos do Tor (padrão: %d) + Otomatik olarak gizli Tor servisi oluştur (varsayılan: %d) ERROR! Failed to create automatic backup - ERRO! Falha ao criar o backup automático + HATA! Otomatik yedek oluşturulamadı Enable publish hash block in <address> - Abilitar a publicação da hash do block em <endereço> + Blok karma değerinin <adres>te yayınlanmasını etkinleştir + + + Enable publish hash of governance objects (like proposals) in <address> + Şuradaki yönetim nesnelerinin (teklifler gibi) hashlerini yayınlamayı etkinleştir: <address> + + + Enable publish hash of governance votes in <address> + Şuradaki yönetim hashlerini yayınlamayı etkinleştir: <address> Enable publish hash transaction (locked via InstantSend) in <address> - Ative a transação de hash de publicação (bloqueada via InstantSend) em<address> + Şurada hash işlemleri yayınlamayı etkinleştir (Anında Gönder ile kilitli): <address> Enable publish hash transaction in <address> - Abilitar a publicação da hash da transação em <endereço> + Karma değer işleminin <adres>te yayınlanmasını etkinleştir Enable publish raw block in <address> - Abilitar a publicação dos dados brutos do block em <endereço> + Ham blokun <adres>te yayınlanmasını etkinleştir Enable publish raw transaction (locked via InstantSend) in <address> - Ativar publicação de transação bruta (bloqueada via InstantSend) em<address> + Şurada ham işlemleri yayınlamayı etkinleştir (Anında Gönder ile kilitli): <address> Enable publish raw transaction in <address> - Abilitar a publicação dos dados brutos da transação em <endereço> + Ham işlemin <adres>te yayınlanmasını etkinleştir Enable transaction replacement in the memory pool (default: %u) - Habilita substituição de transação em memória (padrão: %u) + Bellek alanında işlem değiştirmeyi etkinleştir (varsayılan: %u) Error: A fatal internal error occurred, see debug.log for details - Erro: Um erro interno fatal ocorreu, veja debug.log para detalhes + Hata: Ölümcül dahili bir hata meydana geldi, ayrıntılar için debug.log dosyasına bakınız Failed to create backup %s! - Falhou ao criar backup %s! + Yedek %s oluşturulamadı! Failed to create backup, error: %s - Falha ao criar backup, erro: %s + Yedek oluşturulamadı, hata: %s Failed to delete backup, error: %s - Falha ao apagar backup, erro: %s + Yedek silinemedi, hata: %s + + + Failed to load InstantSend data cache from + Şuradan Anında Gönder verisi önbelleği yüklemesi başarısız: + + + Failed to load sporks cache from + Şuradan spork önbelleği yüklemesi başarısız: Failed to parse host:port string - Falha ao avaliar a string host:port + host:port metni okunamıyor Fee (in %s/kB) to add to transactions you send (default: %s) - Taxa (em %s/kB) a ser adicionada às transações que você mandar (padrão: %s) + Yolladığınız işlemlere eklenecek ücret (%s/kB olarak) (varsayılan: %s) Imports blocks from external blk000??.dat file on startup - Importar blocos a partir de arquivo externo blk000??.dat durante a inicialização + Başlangıçta harici blk000??.dat dosyasından blokları içe aktarır InstantSend options: - Opções de EnvioInstantâneo: + Anında Gönder seçenekleri: Invalid amount for -fallbackfee=<amount>: '%s' - Valor inválido para -fallbackfee=<amount>: '%s' + -fallbackfee=<tutar> için geçersiz tutar: '%s' + + + Invalid input count. + Girdi sayısı geçersiz. Keep the transaction memory pool below <n> megabytes (default: %u) - Mantenha a mempool de transações abaixo de <n> megabytes (padrão: %u) + İşlem bellek alanını <n> megabayttan düşük tut (varsayılan: %u) Last PrivateSend was too recent. - O último PrivateSend foi muito recente. + Son Özel Gönder çok yakında yapıldı. Last successful PrivateSend action was too recent. - A última ação PrivateSend com sucesso foi muito recente. + Son başarılı Özel Gönder eylemi çok yakında yapıldı. Line: %d - Linha: %d + Hat: %d + + + Loading InstantSend data cache... + Anında Gönder verisi önbelleği yükleniyor... Loading addresses... - Carregando endereços... + Adresler yükleniyor... Loading block index... - Carregando índice de blocos... + Blok indeksi yükleniyor... Loading governance cache... - Sincronizando cache de governança .... + Yönetim önbelleği yükleniyor... + + + Loading sporks cache... + Spork önbelleği yükleniyor... Loading wallet... (%3.2f %%) - Carregando carteira... (%3.2f %%) + Cüzdan yükleniyor... (%3.2f %%) Loading wallet... - Carregando carteira... + Cüzdan yükleniyor... Location of the auth cookie (default: data dir) - Localização do cookie de autenticação (padrão: diretório de dados) + auth çerezinin konumu (varsayılan: veri klasörü) Make the wallet broadcast transactions - Criar transações de transmissão de carteira + Cüzdanın işlemleri yayınlamasını sağla Masternode cache is empty, skipping payments and governance cache... - A cache masternode está vazia, pulando cache de pagamentos e governança... + Ana düğüm önbelleği boş, ödeme ve yönetim önbellekleri atlanıyor... Masternode options: - Opções do Masternode: + Ana düğüm seçenekleri: Masternode queue is full. - A fila do masternode está cheia. + Ana düğüm kuyruğu dolu. Masternode: - Masternode: + Ana düğüm: Minimum bytes per sigop in transactions we relay and mine (default: %u) - Mínimo de bytes por sigop nas transações que retransmitimos e mineramos (padrão: %u) + Yönlendirip kazacağımız işlemlerde sigop başına minimum bayt (varsayılan: %u) Missing input transaction information. - Falta informação da transação de entrada. + Girdi işlem bilgisi eksik. No errors detected. - Nenhum erro detectado. + Hata tespit edilmedi. No matching denominations found for mixing. - Nenhuma denominação correspondente encontrada para misturar. + Karışım için uyumlu birim bulunamadı. Node relay options: - Opções de relé nó : + Düğüm aktarma seçenekleri: Non-standard public key detected. - Nenhuma chave pública fora do padrão detectada. + Standart olmayan herkese açık anahtar tespit edildi. Not compatible with existing transactions. - Não é compatível com as transações existentes. + Var olan işlemlerle uyumlu değil. Not enough file descriptors available. - Não há descritores de arquivos disponíveis. + Kafi derecede dosya tanımlayıcıları mevcut değil. Not enough funds to anonymize. - Não há fundos suficientes para anonimo. + Anonim hale getirilecek yeterli bakiye yok. Number of automatic wallet backups (default: %u) - Número de backups automáticos da carteira (padrão: %u) + Otomatik cüzdan yedeği sayısı (varsayılan: %u) Options: - Opções: + Seçenekler: Password for JSON-RPC connections - Senha para conexões JSON-RPC + JSON-RPC bağlantıları için parola Print version and exit - Versão de impressão e sair + Sürümü yaz ve çık PrivateSend is idle. - PrivateSend está inativo. + Özel Gönder boşta. PrivateSend options: - Opções do PrivateSend: + Özel Gönder seçenekleri: PrivateSend request complete: - PrivateSend solicitação completa: + Özel Gönder talebi tamamlandı: PrivateSend request incomplete: - PrivateSend solicitação incompleta: + Özel Gönder talebi tamamlanmadı: Prune cannot be configured with a negative value. - O modo prune não pode ser configurado com um valor negativo. + Budama negatif bir değerle yapılandırılamaz. Prune mode is incompatible with -txindex. - O modo prune é incompatível com -txindex. + Budama kipi -txindex ile uyumsuzdur. Pruning blockstore... - Prunando os blocos existentes... + Blockstore budanıyor... Rescan the block chain for missing wallet transactions on startup - Analise novamente o blockchain para ver se há transações da carteira em falta na inicialização + Başlangıçta blok zincirini eksik cüzdan işlemleri için tekrar tara The wallet will avoid paying less than the minimum relay fee. - A carteira irá evitar pagar menos que a taxa mínima de retransmissão. + Cüzdan en az aktarma ücretinden daha az ödeme yapmaktan sakınacaktır. This is the minimum transaction fee you pay on every transaction. - Esta é a taxa mínima que você paga em todas as transação. + Bu her işlemde ödeceğiniz en düşük işlem ücretidir. This is the transaction fee you will pay if you send a transaction. - Esta é a taxa que você irá pagar se enviar uma transação. + Eğer bir gönderme işlemi yaparsanız bu ödeyeceğiniz işlem ücretidir. Transaction amounts must not be negative - As quantidades nas transações não podem ser negativas. + İşlem tutarı negatif olmamalıdır Transaction has too long of a mempool chain - A transação demorou muito na memória + İşlem çok uzun bir mempool zincirine sahip Transaction must have at least one recipient - A transação deve ter ao menos um destinatário + İşlemin en az bir alıcısı olması gerekir + + + Transaction too large + İşlem çok büyük Trying to connect... - Tentando se conectar... + Bağlanmaya çalışılıyor... Unable to bind to %s on this computer. %s is probably already running. - Impossível vincular a %s neste computador. O %s provavelmente já está rodando. + Bu bilgisayarda %s unsuruna bağlanılamadı. %s muhtemelen hâlihazırda çalışmaktadır. Upgrading UTXO database - Atualizando banco de dados UTXO + UTXO veritabanı yükseltiliyor Use devnet chain with provided name - Use a cadeia devnet com o nome fornecido + Verilen ad ile devnet zinciri kullan Wallet debugging/testing options: - Opções de depuração/teste da Carteira + Cüzdan hata ayıklama/test etme seçenekleri: + + + Wallet is not initialized + Cüzdan başlatılmadı Wallet needed to be rewritten: restart %s to complete - A Carteira precisa ser reescrita: reinicie o %s para completar + Cüzdanın tekrar yazılması gerekiyordu: işlemi tamamlamak için %s programını yeniden başlatınız Warning: unknown new rules activated (versionbit %i) - Alerta: regras novas desconhecidas ativadas (versionbit %i) + Uyarı: bilinmeyen yeni kurallar etkinleştirilmiştir (versionbit %i) Wasn't able to create wallet backup folder %s! - Não foi possível criar a pasta para backup da carteira %s! + Cüzdan yedeği klasörü %s oluşturulamadı! Whether to operate in a blocks only mode (default: %u) - Se é para operar em modo somente blocos (padrão: %u) + Sadece blok kipinde çalışılıp çalışılmayacağı (varsayılan: %u) + + + You can not start a masternode in lite mode. + Bir ana düğümü lite modda başlatamazsınız. ZeroMQ notification options: - ZeroMQ notificação opções + ZeroMQ bildirim seçenekleri: no mixing available. - mixing não disponível. + karışım mevcut değil. see debug.log for details. - Consulte debug.log para obter detalhes. + ayrıntlar için debug.log dosyasına bakın RPC server options: - Opções do servidor RPC: + RPC sunucu seçenekleri: Dash Core @@ -4830,387 +4935,395 @@ https://www.transifex.com/projects/p/dash/ The %s developers - Desenvolvedores do %s + %s geliştiricileri + + + Accept connections from outside (default: 1 if no -proxy or -connect/-noconnect) + Dışarıdan gelen bağlantıları kabul et (varsayılan: -proxy veya -connect/-noconnect yoksa 1) Cannot obtain a lock on data directory %s. %s is probably already running. - Não foi possível obter exclusividade de escrita no endereço %s. O %s provavelmente já está sendo executado. + %s veri dizininde kilit elde edilemedi. %s muhtemelen hâlihazırda çalışmaktadır. + + + Connect only to the specified node(s); -noconnect or -connect=0 alone to disable automatic connections + Sadece belirtilen düğümler bağlan; otomatik bağlantıları devredışı bırakmak için -noconnect veya -connect=0 yeterli Distributed under the MIT software license, see the accompanying file %s or %s - Distribuído sob a licença de software MIT, veja o arquivo %s ou %s + MIT yazılım lisansı altında dağıtılmıştır, beraberindeki %s ya da %s dosyasına bakınız. Error reading %s! All keys read correctly, but transaction data or address book entries might be missing or incorrect. - Erro ao ler arquivo %s! Todas as chaves privadas foram lidas corretamente, mas os dados de transação ou o livro de endereços podem estar faltando ou incorretos. + %s dosyasının okunması sırasında bir hata meydana geldi! Tüm anahtarlar doğru bir şekilde okundu, ancak işlem verileri ya da adres defteri ögeleri hatalı veya eksik olabilir. Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d) - Força a retransmissão de transações de pares da lista branca, mesmo quando violam a política local de retransmissão (default: %d) + Yerel aktarma politikasını ihlal etseler bile beyaz listedeki eşlerden gelen işlemlerin aktarılmasını zorla (varsayılan: %d) Incorrect or no devnet genesis block found. Wrong datadir for devnet specified? - Bloco gênese incorreto ou não encontrado. Datadir errado para a devnet rede? + Geçersiz veya sıfır devnet genesis bloğu bulundu. Devnet için belirtilen datadir yanlış mı? Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds) - Ajuste da média máxima permitida do time offset para um par. A perspectiva local de horário pode ser influenciada por pares à frente ou atrás neste montante. (padrão: %u segundos) + İzin verilen edilen en yüksek medyan eş zamanı değişiklik sınırının ayarlaması. Zamanın yerel perspektifi bu miktar kadar ileri ya da geri eşler tarafından etkilenebilir. (Varsayılan %u saniye) Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s) - Total máximo de taxas (em %s) para usar em uma única transação de carteira ou transação crua; especificar um valor muito baixo pode abortar grandes transações (padrão: %s) + Tek bir cüzdan işleminde ya da ham işlemde kullanılacak en yüksek toplam ücret (%s olarak); bunu çok düşük olarak ayarlamak büyük işlemleri iptal edebilir (varsayılan: %s) Override spork address. Only useful for regtest and devnet. Using this on mainnet or testnet will ban you. - Substituir endereço de spork. Útil apenas para regtest e devnet. Usando isto na mainnet ou testnet irá banir você. + Spork adresini geçersiz kıl. Sadece regtest ve devnet içindir. Bunu mainnet veya testnet'te kullanmak engellenmenize yol açar. Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly. - Por favor verifique se a data e o horário de seu computador estão corretos. Se o relógio de seu computador estiver incorreto, %s não funcionará corretamente. + Lütfen bilgisayarınızın saat ve tarihinin doğru olduğunu kontrol ediniz! Saatinizde gecikme varsa %s doğru şekilde çalışamaz. Please contribute if you find %s useful. Visit %s for further information about the software. - Por favor contribua se você entender que %s é útil. Visite %s para mais informações sobre o software. + %s programını faydalı buluyorsanız lütfen katkıda bulununuz. Yazılım hakkında daha fazla bilgi için %s adresini ziyaret ediniz. + + + 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) + Sürekli olarak para karışımı yaparak Özel Göndere likidite sağla (%u-%u, varsayılan: %u, 1=çok sık, yüksek ücret, %u=düşük sıklıkta, düşük ücret) Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect/-noconnect) - Consulta por endereços de pares via pesquisa de DNS, se baixa nos endereços (default: 1 unless -connect/-noconnect) + Eğer adresler az ise DNS lookup ile eş adresi talebi (varsayılan: 1 eğer -connect/-noconnect değilse) Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. Warning: Reverting this setting requires re-downloading the entire blockchain. (default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB) - Reduz o requerimente de espaço habiitando a poda (apagando) de blocos antigos. Isso permite chamar o comando pruneblockchain via RPC para apagar blocos específicos, e habiita o poda automático de blocos antigos se o tamanho em MiB for atingido. Esse modo é incompatível com -txindex e -rescan. Aviso: Reverter essa configuração requer baixar novamente o blockchain inteiro. (padrão: 0 = desabilitado, 1 = permite a poda manual via RPC, >%u = poda os blocos para ficar abaixo do especificado, em MiB) + Eski blokları budamayı (silme) etkinleştirerek depolama gereksinimlerini azaltın. Bu belirli blokları silmek için pruneblockchain uzak yordam çağrısına (RPC) izin verir. Eğer bloklar hedef mebibyte boyutuna ulaşırsa eski blokların otomatik olarak budanmasını sağlar. Bu kip, -txindex ve -rescan ile uyumsuzdur. Uyarı: Bu ayarı geri almak, blok zincirinin tamamını yeniden yüklemeyi gerektirir. (varsayılan: 0 = blok budaması devre dışı, 1 = RPC üzerinden manuel budamaya izin verir, >%u = mebibyte olarak belirtilen hedef boyutun altında kalması için blok dosyalarını otomatik olarak budar) Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) - Especificar a taxa miníma (em %s/KiB) para transações para ser incluída na criação de blocos. (padrão: %s) - - - Show N confirmations for a successfully locked transaction (%u-%u, default: %u) - Mostrar N confirmações para uma transação bloqueada com sucesso (%u-%u, padrão: %u) + Blok oluşturmaya dahil olan işlemler için en düşük ücret oranını (%s/kB olarak) ayarla. (varsayılan: %s) This is the transaction fee you may pay when fee estimates are not available. - Esta é a taxa que você deve pagar quando a taxa estimada não está disponível. + İşlem ücret tahminleri mevcut olmadığında ödeyebileceğiniz işlem ücreti budur. This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit %s and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard. - Esse produto inclui um software desenvolvido pelo OpenSSL Project para uso na OpenSSL Toolkit %s e software criptográfico escrito por Eric Young e software UPnP escrito por Thomas Bernard. + Bu ürün OpenSSL Projesi tarafından geliştirilen OpenSSL araç takımınında kullanılmak üzere yazılan yazılımları %s Eric Young tarafından yazılmış şifreleme yazılımını ve Thomas Bernard tarafından yazılmış UPnP yazılımını içerir. Unable to locate enough PrivateSend non-denominated funds for this transaction. - Não é possível localizar fundos não-expressos do PrivateSend suficientes para essa transação. + Bu işlem için yeterli Özel Gönder ile birimlendirilmemiş bakiye bulunamadı. Use N separate masternodes for each denominated input to mix funds (%u-%u, default: %u) - Use N masternodes separados para cada entrada denominada para misturar fundos (%u-%u, padrão: %u) + Her birimlendirilmiş girdi tutarı için N ayrı ana düğüm kullan (%u-%u, varsayılan: %u) Use hierarchical deterministic key generation (HD) after BIP39/BIP44. Only has effect during wallet creation/first start - Use a geração hierárquica de chaves determinísticas (HD) após o BIP39 / BIP44. Só tem efeito durante a criação / início da carteira + BIP39/BIP44'ten sonra hiyerarşik deterministik anahtar oluşturma (HD) kullan. Sadece cüzdan oluşturma/ilk açılış sırasında etkilidir User defined mnemonic passphrase for HD wallet (BIP39). Only has effect during wallet creation/first start (default: empty string) - Senha mnemônica definida pelo usuário para carteira HD (BIP39). Só tem efeito durante a criação da carteira / primeira inicialização (padrão: string vazia) + HD cüzdan (BIP39) için kullanıcı tarafından belirlenmiş anımsatıcı parola. Sadece cüzdan oluşturma/ilk açılış sırasında etkilidir (varsayılan: boş metin) Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times - Nome de usuário e hash da senha para conexões JSON-RPC. O campo <userpw> vem com o formato: <USERNAME>:<SALT>$<HASH>. Um script python canônico é incluído em share/rpcuser. O cliente pode conectar normalmente usando o rpcuser=<USERNAME>/rpcpassword=<PASSWORD>. Esta opção pode ser especificado multiplas vezes + JSON-RPC bağlantıları için kullanıcı adı ve karmalanmış parola. <userpw> alanı şu biçimdedir: <KULLANICI ADI>:<SALT>$<HASH>. Kanonik bir Python betiği share/rpcuser klasöründe bulunabilir. Ardından istemci normal şekilde rpcuser=<KULLANICI ADI>/rpcpassword=<PAROLA> argüman çiftini kullanarak bağlanabilir. Bu seçenek birden çok kez belirtilebilir. Wallet will not create transactions that violate mempool chain limits (default: %u) - A carteira não irá criar transações que violem o limite da mempool chain (padrão: %u) + Cüzdan, zincir bellek alanı limitlerini ihlal eden işlem oluşturmayacak. (varsayılan: %u) Warning: At least %d of %d masternodes are running on a newer software version. Please check latest releases, you might need to update too. - Aviso: Pelo menos %d de %d masternodes estão sendo executados em uma versão de software mais recente. Por favor, verifique os últimos lançamentos, talvez você precise atualizar também. + Uyarı: En az %d / %d ana düğüm yeni bir yazılım sürümüyle çalışıyor. Lütfen son sürümleri kontrol edin, sizin de güncellemeye ihtiyacınız olabilir. Warning: Every masternode (out of %d known ones) is running on a newer software version. Please check latest releases, it's very likely that you missed a major/critical update. - Aviso: Cada masternode (fora de %d conhecidos) está sendo executado em uma versão de software mais recente. Por favor, verifique os últimos lançamentos, é muito provável que você tenha perdido uma atualização importante / crítica. + Uyarı her ana düğüm (%d bilinen arasında) daha yeni bir yazılım sürümüyle çalışıyor. Lütfen son sürümleri kontrol edin, büyük/kritik bir güncellemeyi atlamış olmanız yüksek ihtimal dahilinde. Warning: Wallet file corrupt, data salvaged! Original %s saved as %s in %s; if your balance or transactions are incorrect you should restore from a backup. - Atenção: Arquivo da carteira corrompido, dados recuperados! Original %s salvo como %s em %s; se seu saldo ou transações estiverem incorretos, você deve restaurar o backup. + Uyarı: wallet.dat bozuk, veriler geri kazanıldı! Özgün %s, %s olarak %s klasörüne kaydedildi; bakiyeniz ya da işlemleriniz yanlışsa bir yedeklemeden tekrar yüklemeniz gerekir. Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times. - Inclui na lista branca pares conectando do dado endereço IP (ex: 1.2.3.4) ou rede em notação CIDR (ex: 1.2.3.0/24). Pode ser especificado várias vezes. + Beyaz listeye eklenen eşler verilen IP adresinden (ör. 1.2.3.4) veya CIDR ağından (ör. 1.2.3.0/24) bağlanabilir. Değerler birden çok kez kullanılabilir. %s corrupt, salvage failed - %s corrompido, recuperação falhou + %s bozuk, geri kazanım başarısız oldu %s is not a valid backup folder! - %s não é uma pasta de backup válida! + %s geçerli bir yedek klasörü değil! %s is set very high! - %s está muito alto! + %s çok yüksek ayarlanmış! (press q to shutdown and continue later) - (pressione q para desligar e continuar depois) + (kapatıp daha sonra devam etmek için q tuşuna basın) -devnet can only be specified once - -devnet só pode ser especificado uma vez + -devnet sadece bir kere belirtilebilir -port must be specified when -devnet and -listen are specified - -rpcport deve ser especificado quando -devnet e -server são especificados + -devnet ve -listen belirtildiğinde -port da belirtilmelidir -rpcport must be specified when -devnet and -server are specified - -rpcport deve ser especificado quando -devnet e -server são especificados + -devnet ve -server belirtildiğinde -rpcport da belirtilmelidir Allow RFC1918 addresses to be relayed and connected to (default: %u) - Permitir que os endereços RFC1918 sejam retransmitidos e conectados a (padrão: %u) + RFC1918 adreslerine iletim ve bağlantıya izin ver (varsayılan %u) Attempt to recover private keys from a corrupt wallet on startup - Tenta recuperar as chaves privadas de uma carteira corrompida ao inicializar + Başlangıçta bozuk bir cüzdandan özel anahtarları geri kazanmayı dene Cannot resolve -%s address: '%s' - Não foi possível encontrar o endereço de -%s: '%s' + Çözümlenemedi - %s adres: '%s' Change index out of range - Índice de mudança fora do intervalo + Aralık dışında değişiklik indeksi Copyright (C) - Copyright + Copyright (C) Error loading %s - Erro ao carregar %s + %s unsurunun yüklenmesinde hata oluştu Error loading %s: Wallet corrupted - Erro ao carregar %s Carteira corrompida + %s unsurunun yüklenmesinde hata oluştu: bozuk cüzdan Error loading %s: Wallet requires newer version of %s - Erro ao carregar %s A carteira requer a versão mais nova do %s + %s unsurunun yüklenmesinde hata oluştu: cüzdan %s programının yeni bir sürümüne ihtiyaç duyuyor Error upgrading chainstate database - Erro ao atualizar banco de dados do chainstate + Zincirdurumu veritabanı yükseltme hatası Failed to find mixing queue to join - Não foi possível encontrar a fila de mistura para participar + Katılacak karışım kuyruğu bulunamadı Failed to start a new mixing queue - Falha ao iniciar uma nova fila de mistura + Yeni bir karışım kuyruğu başlatılamadı Initialization sanity check failed. %s is shutting down. - O teste de integridade de inicialização falhou. O %s está sendo desligado. + Başlatma sınaması başarısız oldu. %s kapatılıyor. Invalid amount for -%s=<amount>: '%s' - Quantidade inválida para -%s=<amount>: '%s' + -%s=<tutar> için geçersiz tutar: '%s' Invalid spork address specified with -sporkaddr - Endereço de spork inválido especificado com -sporkaddr + -sporkaddr ile yanlış spork adresi belirtildi Keep N DASH anonymized (%u-%u, default: %u) - Mantenha o N DASH anônimo (%u-%u, padrão: %u) + N DASH'i anonim tut (%u-%u, varsayılan: %u) Print this help message and exit - Mostra essa mensagem de ajuda e sai + Bu yardım mesajını yaz ve çık Receive and display P2P network alerts (default: %u) - Receba e exiba alertas de rede P2P (default: %u) + P2P ağ uyarılarını al ve göster (varsayılan: %u) Reducing -maxconnections from %d to %d, because of system limitations. - Reduzindo -maxconnections de %d para %d, devido à limitações do sistema. + Sistem sınırlamaları sebebiyle -maxconnections %d değerinden %d değerine düşürülmüştür. Relay and mine data carrier transactions (default: %u) - Transações de dados de operadora (padrão: %u) + Veri taşıyıcı işlemleri oluştur ve aktar (varsayılan: %u) Relay non-P2SH multisig (default: %u) - Retransmitir P2SH não multisig (padrão: %u) + P2SH olmayan çoklu imzaları aktar (varsayılan: %u) Rescanning... - Escaneando... + Yeniden tarama... Run in the background as a daemon and accept commands - Executar em segundo plano como um daemon e aceitar comandos + Arka planda daemon (servis) olarak çalış ve komutları kabul et Send trace/debug info to console instead of debug.log file - Envia informação de trace/debug para o console e não para o arquivo debug.log + İzleme/hata ayıklama verilerini debug.log dosyası yerine konsola gönder Session not complete! - Sessão não completa! + Oturum tamamlanmadı! Session timed out. - A sessão expirou. + Oturum zaman aşımına uğradı. Set database cache size in megabytes (%d to %d, default: %d) - Define o tamanho da cache em megabytes (%d a %d, padrão: %d) + Veritabanı önbellek boyutunu megabayt olarak belirt (%d ilâ %d, varsayılan: %d) Set maximum block size in bytes (default: %d) - Definir tamanho máximo do bloco em bytes (padrão: %d) + Azami blok boyutunu bayt olarak ayarla (varsayılan: %d) Set the masternode private key - Definir a chave privada do masternode + Ana düğüm özel anahtarubu belirle Show all debugging options (usage: --help -help-debug) - Exibe todas as opçoes de debug (uso: --help -help-debug) + Tüm hata ayıklama seçeneklerini göster (kullanımı: --help -help-debug) Shrink debug.log file on client startup (default: 1 when no -debug) - Compactar arquivo debug.log ao executar o cliente (padrão: 1 sem -debug) + İstemci başlatıldığında debug.log dosyasını küçült (varsayılan: -debug bulunmadığında 1) Signing transaction failed - A transação assinada falhou + Muamelenin imzalanması başarısız oldu Specify data directory - Especificar diretório de dados + Veri dizinini belirt Specify wallet file (within data directory) - Especifique o arquivo da carteira (dentro do diretório de dados) + Cüzdan dosyası belirtiniz (veri klasörünün içinde) Specify your own public address - Especificar seu próprio endereço público + Kendi genel adresinizi tanımlayın Starting network threads... - Iniciando threads de rede... + Ağ iş parçacıkları başlatılıyor... Synchronization pending... - Sincronização pendente + Eşleme bekliyor... Synchronizing governance objects... - Sincronizando objetos de governança .... + Yönetim nesneleri eşleniyor... Synchronizing masternode payments... - Sincronizando pagamentos do masternode... + Ana düğüm ödemeleri eşleniyor... The source code is available from %s. - O código fonte está disponível pelo %s. + Kaynak kod şuradan elde edilebilir: %s. The transaction amount is too small to pay the fee - O valor da transação é muito pequeno para pagar a taxa + İşlemdeki bitcoin tutarı ücreti ödemek için çok düşük This is experimental software. - Este é um software experimental. + Bu, deneysel bir yazılımdır. Transaction amount too small - Quantia da transação muito pequena + Muamele meblağı çok düşük Transaction created successfully. - Transação criada com sucesso. + İşlem başarıyla oluşturuldu. Transaction fees are too high. - Taxa de transação muito alta. + İşlem ücretleri çok yüksek. Transaction not valid. - Transação inválida. + İşlem geçerli değil. Transaction too large for fee policy - Transação muito grande para a política de tarifas + Ücret politikası için işlem çok büyük Unable to bind to %s on this computer (bind returned error %s) - Impossível abrir %s neste computador (bind retornou o erro %s) + Bu bilgisayarda %s ögesine bağlanılamadı (bağlanma %s hatasını verdi) Unable to sign spork message, wrong key? - Impossível assinar mensagem spork, chave incorreta? + Spork mesajı imzalanamadı, anahtar mı yanlış? Unknown network specified in -onlynet: '%s' - Rede desconhecida fornecida em -onlynet: '%s' + -onlynet için bilinmeyen bir şebeke belirtildi: '%s' Unknown state: id = %u - Estado desconhecido: id = %u + Bilinmeyen durum: id = %u Username for JSON-RPC connections - Nome de usuario para conexão JSON-RPC + JSON-RPC bağlantıları için kullanıcı ismi Verifying blocks... - Verificando blocos... + Bloklar kontrol ediliyor... Verifying wallet... - Verificando carteira... + Cüzdan kontrol ediliyor... Very low number of keys left: %d - Número muito baixo de chaves restantes: %d + Çok az sayıda anahtar kaldı: %d Wallet %s resides outside data directory %s - Carteira %s está fora do diretório de dados %s + %s cüzdan %s veri klasörünün dışında bulunuyor Wallet is locked. - Carteira está bloqueada. + Cüzdan kilitli. Wallet options: - Opções da carteira: + Cüzdan seçenekleri: Wallet window title - Título da janela da carteira + Cüzdan penceresi başlığı Warning - Atenção + Uyarı Your entries added successfully. - Suas entradas foram adicionadas com êxito. + Girdileriniz başarıyla eklendi. Your transaction was accepted into the pool! - Sua transação foi aceita na pool! + İşleminiz havuza kabul edildi! Zapping all transactions from wallet... - Fazendo o Zapping de todas as transações da wallet... + Cüzdandaki tüm muameleler kaldırılıyor... \ No newline at end of file diff --git a/src/qt/locale/dash_vi.ts b/src/qt/locale/dash_vi.ts index 1639a053e256..e950edc8c892 100644 --- a/src/qt/locale/dash_vi.ts +++ b/src/qt/locale/dash_vi.ts @@ -793,10 +793,6 @@ Please switch to "List mode" to use this function. Hãy chuyển về "Chế độ danh sách" để sử dụng tính năng này. - - Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again. - Đầu vào không ẩn danh được chọn. <b> PrivateSend sẽ bị tắt.</b><br><br>Nếu bạn vẫn muốn sử dụng PrivateSend, hãy bỏ chọn tất cả các đầu vào không ẩn danh trước và đánh dấu vào hộp PrivateSend lần nữa. - (%1 locked) (%1 được khoá) @@ -834,73 +830,6 @@ không áp dụng - - DarksendConfig - - Configure PrivateSend - Cấu hình PrivateSend - - - Basic Privacy - Mức Riêng tư Cơ bản - - - High Privacy - Mức Riêng tư Cao - - - Maximum Privacy - Mức Riêng tư Tối đa - - - Please select a privacy level. - Hãy chọn mức độ riêng tư. - - - Use 2 separate masternodes to mix funds up to 1000 DASH - Sử dụng 2 masternode khác nhau để trộn số tiền lên đến 1000 DASH - - - Use 8 separate masternodes to mix funds up to 1000 DASH - Sử dụng 8 masternode khác nhau để trộn số tiền lên đến 1000 DASH - - - Use 16 separate masternodes - Sử dụng 16 masternode khác nhau - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - Tuỳ chọn này là nhanh nhất và sẽ mất chi phí khoảng ~0.025 DASH để ẩn danh 1000 DASH - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - Tuỳ chọn này là tương đối nhanh và sẽ mất chi phí khoảng ~0.05 DASH để ẩn danh 1000 DASH - - - 0.1 DASH per 1000 DASH you anonymize. - 0.1 DASH cho mỗi 1000 DASH bạn muốn ẩn danh. - - - This is the slowest and most secure option. Using maximum anonymity will cost - Đây là tuỳ chọn chậm nhất và an toàn nhất. Sử dụng mức vô danh cao nhất sẽ tốn kém - - - PrivateSend Configuration - Cấu hình của PrivateSend - - - PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend đã được thiết lập thành công về mức cơ bản (%1 và 2 vòng). Bạn có thể thay đổi nó bất cứ thời gian nào bằng cách mở chức năng cấu hình của Dash. - - - PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend đã được thiết lập thành công ở mức cao (%1 và 8 vòng). Bạn có thể thay đổi nó bất cứ lúc nào bằng cách mở chức năng cấu hình Dash. - - - PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - PrivateSend đã được thiết lập thành công ở mức tối đa (%1 và 16 vòng). Bạn có thể thay đổi nó bất cứ thời gian nào bằng cách mở chức năng cấu hình của Dash. - - EditAddressDialog @@ -1037,11 +966,7 @@ PrivateSend information Thông tin PrivateSend - - <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more info see <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> - <h3>Cơ bản về PrivateSend</h3> PrivateSend cho bạn sự thực sự riêng tư về tài chính bằng việc che dấu những nguồn cung. Tất cả Dash trong ví của bạn bao gồm những "nguồn" khác nhau mà bạn nghĩ đó là các coin riêng biệt và rời rạc.<br> PrivateSend sử dụng một tiến trình độc đáo để trộn các đầu vào của bạn với đầu vào của những người khác, mà không làm cho các coin rời khỏi ví của bạn. Bạn vẫn giữ quyền kiểm soát tiền của bạn bất cứ lúc nào..<hr> <b>Quá trình PrivateSend làm việc như sau: </b> <ol type="1"> <li>PrivateSend bắt đầu bằng việc chia các giao dịch đầu vào của bạn thành những mệnh giá chuẩn. Những mệnh giá đó là 0.01 DASH, 0.1 DASH, 1 DASH, và 10 DASH -- cũng giống như các mệnh giá trên tiền giấy mà bạn sử dụng hàng ngày.<li> <li>Ví của bạn sau đó gửi yêu cầu đến những nút phần mềm được cấu hình đặc biệt trên mạng gọi là các "masternode". Những masternode được báo rằng bạn quan tâm đến việc xáo trộn một mệnh giá nào đó. Không có thông tin định danh nào được gửi đến cho các masternode, và như vậy họ không bao giờ biết bạn "là ai".</li> <li>Khi hai người khác nhau gửi những thông điệp tương tự, có nghĩa là họ muốn xáo trộn cùng loại mệnh giá, một phiên xáo trộn bắt đầu.Masternode xáo trộn các đầu vào và hướng dẫn tất cả ví của tất cả ba người dùng để trả đầu vào đã được chuyển đổi trở lại cho chính họ. Ví của bạn sẽ trả mệnh giá đó trực tiếp cho nó, nhưng với một địa chỉ khác (được gọi là địa chỉ trả tiền lẻ).</li> </li>Để thực sự che khuất nguồn tiền của bạn, ví của bạn phải lặp lại quy trình đó một số lần với mỗi mệnh giá nhất định. Mỗi lần tiến trình hoàn tất, nó được gọi là một "vòng". Mỗi vòng của PrivateSend làm nên độ khó bậc số mũ để xác định nguồn tiền của bạn đến từ đâu.</li> <li>Quá trình xáo trộn này xảy ra trong chế độ nền mà không xen vào những việc khác của bạn. Khi bạn muốn làm một giao dịch, nguồn tiền của bạn đã được ẩn danh hoá rồi. Do đó bạn không cần phải đợi thêm gì nữa.</li> </ol> <hr> <b>QUAN TRỌNG:</b> Ví của bạn chỉ có chứa 1000 "địa chỉ tiền trả lại". Mỗi lần một sự kiện xáo trộn xảy ra, có đến 9 địa chỉ sẽ được sử dụng. Điều đó có nghĩa với ví mới với 1000 địa chỉ thì dùng cho 100 lần trộn. Khi 900 địa chỉ đã được sử dụng, ví của bạn phải tạo thêm các địa chỉ mới. Nó chỉ có thể làm việc đó, tuy nhiên, nếu bạn có chế độ tự động backup được bật<br> Kết quả là, những người dùng mà chế độ backup bị tắt sẽ có chế độ PrivateSend cũng bị tắt.<hr> Để biết thêm thông tin hãy xem <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> - - + Intro @@ -1829,10 +1754,6 @@ https://www.transifex.com/projects/p/dash/ We are about to create a new automatic backup for you, however <span style='color:red;'> you should always make sure you have backups saved in some safe place</span>! Chúng tôi sẽ tạo một bản tự động backup cho bạn, tuy nhiên <span style='color:red;'>bạn nên luôn chắc chắn rằng bạn đã lưu backup ở nơi nào đó an toàn</span>! - - Note: You can turn this message off in options. - Chú ý: Bạn đã tắt thông báo này trong phần tuỳ chọn. - WARNING! Something went wrong on automatic backup CHÚ Ý: Có gì đó có vấn đề khi đang tự động backup @@ -5012,10 +4933,6 @@ https://www.transifex.com/projects/p/dash/ Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) - - Show N confirmations for a successfully locked transaction (%u-%u, default: %u) - Hiển thị N xác thực cho một giao dịch được khoá thành công (%u-%u, ngầm định: %u) - This is the transaction fee you may pay when fee estimates are not available. This is the transaction fee you may pay when fee estimates are not available. diff --git a/src/qt/locale/dash_zh_CN.ts b/src/qt/locale/dash_zh_CN.ts index a1626bb619c1..89ab56421aa6 100644 --- a/src/qt/locale/dash_zh_CN.ts +++ b/src/qt/locale/dash_zh_CN.ts @@ -794,7 +794,7 @@ 请切换到“列表模式”来使用此功能。 - Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again. + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. 已选择非匿名输入。<b>匿名发送将会被禁用。</b><br><br>如果你仍然想使用匿名发送功能,请先取消所选的非匿名输入,然后再勾选匿名发送。 @@ -834,73 +834,6 @@ 不可用 - - DarksendConfig - - Configure PrivateSend - 配置匿名发送 - - - Basic Privacy - 基本隐私 - - - High Privacy - 高度隐私 - - - Maximum Privacy - 最大隐私 - - - Please select a privacy level. - 请选择隐私等级。 - - - Use 2 separate masternodes to mix funds up to 1000 DASH - 使用2个独立的主节点来混合最多1000个达世币 - - - Use 8 separate masternodes to mix funds up to 1000 DASH - 使用8个独立的主节点来混合最多1000个达世币 - - - Use 16 separate masternodes - 使用16个独立主节点 - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - 此选项为最快速度,需要支付约0.025达世币/1000达世币的匿名处理费用。 - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - 此选项为一般速度,需要支付约0.05达世币/1000达世币的匿名处理费用。 - - - 0.1 DASH per 1000 DASH you anonymize. - 0.1达世币/1000达世币的匿名处理费用。 - - - This is the slowest and most secure option. Using maximum anonymity will cost - 此选项提供最慢速度却最安全的匿名服务。耗费最多的匿名处理费 - - - PrivateSend Configuration - 匿名发送设置 - - - PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - 匿名发送已成功设置为基本 (%1 和2次循环)。您可以随时打开达世币配置窗口来进行修改。 - - - PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - 匿名发送已成功设置为高(%1到8次循环)。您可以随时打开达世币配置窗口来进行修改。 - - - PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - 匿名发送已成功设置为最高(%1到16次循环)。您可以随时打开达世币配置窗口来进行修改。 - - EditAddressDialog @@ -1038,8 +971,8 @@ 匿名发送信息 - <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more info see <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> - <h3>匿名发送基础知识</h3>匿名发送通过隐藏您的资金来源为您提供真正的财务隐私。您的钱包中所有的达世币都由不同的“输入”组成,您可以将其视为分开的离散硬币。<br>匿名发送使用创新的方法将您的输入与其他两个人的输入相结合,而过程中不会让您的达世币离开您的钱包。每时每刻,您仍然控制着您的钱。<hr><b>匿名发送的运作原理如下:</b><ol type="1"><li>匿名发送首先将您的交易分柝成多个标准面额的交易。这些标准面额分别为0.01 DASH,0.1 DASH,1 DASH和10 DASH --有点像您每天使用的纸币。</li><li>您的钱包然后发送请求到网络上有专门配置的软件节点,称为“主节点”。这些主节点会收到您希望混合一些资金的通知。没有可识别的信息发送到主节点,所以他们永远不会知道你是“谁”。<li>当另外两个人发送类似的消息时,表示希望混合相同的面额的话,混合会话就会开始。相关的主节点会混合这些输入,并指示所有三个用户的钱包将已经转换了输入的交易支付给自己。您的钱包直接支付给自己,但是付给不同的位址(称之为找零地址)。</li><li>为了完全掩盖您的资金来源,您的钱包必须以每个面额来重复此过程数次。每次这个过程完成后,都称之为一个“循环”。每个循环的匿名发送都会令确定您的资金来源的工作倍加困难。</li><li>这种混合过程发生在后台,而不需要您进行任何操作。当您想进行交易时,您的资金将已被匿名处理。不需再花额外的时间等待。</li></ol><hr>重要:<b>您的钱包只能拥有1000个“找零地址”。每次混合事件发生时,最多会使用9个找零地址。这意味着这1000个地址可以容许100次的混合事件。当其的中900个已经被使用后,您的钱包必须创建更多的地址。如果您启用了自动备份,则只能够这样做。<br>因此,禁用备份的用户也将禁用匿名发送。<hr>如欲了解更多信息请参阅<a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. + <h3>匿名发送基础知识</h3>匿名发送通过隐藏您的资金来源为您提供真正的财务隐私。您的钱包中所有的达世币都由不同的“输入”组成,您可以将其视为分开的离散硬币。<br>匿名发送使用创新的方法将您的输入与其他两个人的输入相结合,而过程中不会让您的达世币离开您的钱包。每时每刻,您仍然控制着您的钱。<hr><b>匿名发送的运作原理如下:</b><ol type="1"><li>匿名发送首先将您的交易分柝成多个标准面额的交易。这些标准面额分别为0.01 DASH,0.1 DASH,1 DASH和10 DASH --有点像您每天使用的纸币。</li><li>您的钱包然后发送请求到网络上有专门配置的软件节点,称为“主节点”。这些主节点会收到您希望混合一些资金的通知。没有可识别的信息发送到主节点,所以他们永远不会知道你是“谁”。<li>当另外两个人发送类似的消息时,表示希望混合相同的面额的话,混合会话就会开始。相关的主节点会混合这些输入,并指示所有三个用户的钱包将已经转换了输入的交易支付给自己。您的钱包直接支付给自己,但是付给不同的位址(称之为找零地址)。</li><li>为了完全掩盖您的资金来源,您的钱包必须以每个面额来重复此过程数次。每次这个过程完成后,都称之为一个“循环”。每个循环的匿名发送都会令确定您的资金来源的工作倍加困难。</li><li>这种混合过程发生在后台,而不需要您进行任何操作。当您想进行交易时,您的资金将已被匿名处理。不需再花额外的时间等待。</li></ol><hr>重要:<b>您的钱包只能拥有1000个“找零地址”。每次混合事件发生时,最多会使用9个找零地址。这意味着这1000个地址可以容许100次的混合事件。当其的中900个已经被使用后,您的钱包必须创建更多的地址。如果您启用了自动备份,则只能够这样做。<br>因此,禁用备份的用户也将禁用匿名发送。<hr>如欲了解更多信息请参阅<a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">匿名发送文档</a>。 @@ -1131,6 +1064,10 @@ S&tart alias 启动别名(&T) + + Start &all + 启动全部(&A) + Start &MISSING 启动遗漏(&M) @@ -1825,10 +1762,6 @@ https://www.transifex.com/projects/p/dash/ We are about to create a new automatic backup for you, however <span style='color:red;'> you should always make sure you have backups saved in some safe place</span>! 我们即将为您创建一个新的自动备份,但<span style='color:red;'>您应该始终确保您有另外的一些备份保存在安全的位置</span>! - - Note: You can turn this message off in options. - 注意:您在选项中关闭了这项信息。 - WARNING! Something went wrong on automatic backup 警告!自动备份出现问题 @@ -2299,6 +2232,10 @@ https://www.transifex.com/projects/p/dash/ Datadir 数据目录 + + Open the %1 debug log file from the current data directory. This can take a few seconds for large log files. + 打开当前目录中的%1调试日志文件。日志文件大的话可能要等上几秒钟。 + Decrease font size 缩小文字 @@ -4372,6 +4309,10 @@ https://www.transifex.com/projects/p/dash/ Threshold for disconnecting misbehaving peers (default: %u) 断开无效节点的临界值(默认:%u) + + Too many %f denominations, removing. + 太多%f面额化资金,正在移除。 + Tor control port password (default: empty) Tor 控制端口密码 (默认值:空白) @@ -5000,10 +4941,6 @@ https://www.transifex.com/projects/p/dash/ Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) 设定生成区块时,所要包含交易每千字节的最低手续费(单位是%s)。(默认:%s) - - Show N confirmations for a successfully locked transaction (%u-%u, default: %u) - 显示N个确认为一个成功锁定的交易(%u-%u, 默认: %u) - This is the transaction fee you may pay when fee estimates are not available. 这是在费用估计不可用时你可能会支付的交易费。 @@ -5124,6 +5061,10 @@ https://www.transifex.com/projects/p/dash/ Failed to start a new mixing queue 无法开始一个新的混币队列 + + Initialization sanity check failed. %s is shutting down. + 初始化完整性检查失败。%s即将关闭。 + Invalid amount for -%s=<amount>: '%s' 无效的金额 -%s=<amount>: '%s' diff --git a/src/qt/locale/dash_zh_TW.ts b/src/qt/locale/dash_zh_TW.ts index 9dc7a4f003a1..8402b903e5f2 100644 --- a/src/qt/locale/dash_zh_TW.ts +++ b/src/qt/locale/dash_zh_TW.ts @@ -794,8 +794,8 @@ 請切換到“列表模式”來使用此功能。 - Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-nonymized inputs first and then check PrivateSend checkbox again. - 選擇了非匿名的輸入。 <b> 匿名發送將會被禁用。</ b><br> <br>如果你仍然想用匿名發送,請先取消選取所有非匿名的輸入,然後再勾選匿名發送的核取方塊 + Non-anonymized input selected. <b>PrivateSend will be disabled.</b><br><br>If you still want to use PrivateSend, please deselect all non-anonymized inputs first and then check the PrivateSend checkbox again. + 選擇了非匿名的輸入。 <b>匿名發送將會被禁用。</b><br><br>如果你仍然想用匿名發送,請先取消選取所有非匿名的輸入,然後再勾選匿名發送的核取方塊。 (%1 locked) @@ -834,73 +834,6 @@ 不適用 - - DarksendConfig - - Configure PrivateSend - 配置匿名發送 - - - Basic Privacy - 基本隱私 - - - High Privacy - 高度隱私 - - - Maximum Privacy - 最大隱私 - - - Please select a privacy level. - 請選擇穩私級別。 - - - Use 2 separate masternodes to mix funds up to 1000 DASH - 使用2個獨立的主節點來混合最高達1000達世幣的資金 - - - Use 8 separate masternodes to mix funds up to 1000 DASH - 使用8個獨立的主節點來混合最高達1000達世幣的資金 - - - Use 16 separate masternodes - 使用16個獨立的主節點 - - - This option is the quickest and will cost about ~0.025 DASH to anonymize 1000 DASH - 這個是最快的選項,將耗費約~0.025達世幣作為匿名處理1000達世幣的費用 - - - This option is moderately fast and will cost about 0.05 DASH to anonymize 1000 DASH - 這個是一般的選項,將耗費約0.05達世幣作為匿名處理1000達世幣的費用 - - - 0.1 DASH per 1000 DASH you anonymize. - 每1000達世幣耗費0.1達世幣作為匿名處理費用 - - - This is the slowest and most secure option. Using maximum anonymity will cost - 這是最慢的和最安全的選頁。耗費最多的匿名處理費 - - - PrivateSend Configuration - 匿名發送配置 - - - PrivateSend was successfully set to basic (%1 and 2 rounds). You can change this at any time by opening Dash's configuration screen. - 匿名發送已成功設置為一般(%1 到2次循環)。你可以隨時打開達世幣配置窗口來修改。 - - - PrivateSend was successfully set to high (%1 and 8 rounds). You can change this at any time by opening Dash's configuration screen. - 匿名發送已成功設置為高(%1 到8次循環)。你可以隨時打開達世幣配置窗口來修改。 - - - PrivateSend was successfully set to maximum (%1 and 16 rounds). You can change this at any time by opening Dash's configuration screen. - 匿名發送已成功設置為最高(%1 到16次循環)。你可以隨時打開達世幣配置窗口來修改。 - - EditAddressDialog @@ -1038,8 +971,8 @@ 匿名發送資訊 - <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more info see <a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> - <h3>匿名發送基礎知識</h3> 匿名發送通過隱藏您的資金來源為您提供真正的財務隱私。您的錢包中所有的達世幣都由不同的“輸入”組成,您可以將其視為分開的離散硬幣。<br> 匿名發送使用創新的方法將您的輸入與其他兩個人的輸入相結合,而過程中不會讓您的達世幣離開您的錢包。每時每刻,您仍然控制著您的錢。<hr> <b>匿名發送的運作原理如下:</b><ol type="1"> <li>匿名發送首先將您的交易分柝成多個標準面額的交易。這些標準面額分別為0.01 DASH, 0.1 DASH, 1 DASH 和10 DASH --有點像您每天使用的紙幣。</li> <li>您的錢包然後發送請求到網絡上有專門配置的軟件節點,稱為“主節點”。這些主節點會收到您希望混合一些資金的通知。沒有可識別的信息發送到主節點,所以他們永遠不會知道你是"誰"。</li> <li>當另外兩個人發送類似的消息時,表示希望混合相同的面額的話,混合會話就會開始。相關的主節點會混合這些輸入,並指示所有三個用戶的錢包將已經轉換了輸入的交易支付給自己。你的錢包直接支付給自己,但是付給不同的位址 (稱之為找零位址)。</li> <li>為了完全掩蓋您的資金來源,您的錢包必須以每個面額來重複此過程數次。每次這個過程完成後,都稱之為一個 "循環"。每個循環的匿名發送都會令確定您的資金來源的工作倍加困難。</li> <li>這種混合過程發生在後台,而不需要您進行任何操作。當您想進行交易時,您的資金將已被匿名處理。不需再花額外的時間等待。</li> </ol> <hr><b>重要:</b>您的錢包只能擁有1000個"找零位址。" 每次混合事件發生時,最多會使用9個找零位址。這意味著這1000個位址可以容許100次的混合事件。當其的中900個已經被使用後,您的錢包必須創建更多的位址。如果您啟用了自動備份,則只能夠這樣做。<br>因此,禁用備份的用戶也將禁用匿名發送。<hr>如欲了解更多信息請參閱<a href="https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend">https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend</a> + <h3>PrivateSend Basics</h3> PrivateSend gives you true financial privacy by obscuring the origins of your funds. All the Dash in your wallet is comprised of different "inputs" which you can think of as separate, discrete coins.<br> PrivateSend uses an innovative process to mix your inputs with the inputs of two other people, without having your coins ever leave your wallet. You retain control of your money at all times.<hr> <b>The PrivateSend process works like this:</b><ol type="1"> <li>PrivateSend begins by breaking your transaction inputs down into standard denominations. These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.</li> <li>Your wallet then sends requests to specially configured software nodes on the network, called "masternodes." These masternodes are informed then that you are interested in mixing a certain denomination. No identifiable information is sent to the masternodes, so they never know "who" you are.</li> <li>When two other people send similar messages, indicating that they wish to mix the same denomination, a mixing session begins. The masternode mixes up the inputs and instructs all three users' wallets to pay the now-transformed input back to themselves. Your wallet pays that denomination directly to itself, but in a different address (called a change address).</li> <li>In order to fully obscure your funds, your wallet must repeat this process a number of times with each denomination. Each time the process is completed, it's called a "round." Each round of PrivateSend makes it exponentially more difficult to determine where your funds originated.</li> <li>This mixing process happens in the background without any intervention on your part. When you wish to make a transaction, your funds will already be anonymized. No additional waiting is required.</li> </ol> <hr><b>IMPORTANT:</b> Your wallet only contains 1000 of these "change addresses." Every time a mixing event happens, up to 9 of your addresses are used up. This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. It can only do this, however, if you have automatic backups enabled.<br> Consequently, users who have backups disabled will also have PrivateSend disabled. <hr>For more information, see the <a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">PrivateSend documentation</a>. + <h3>匿名發送基礎知識</h3> 匿名發送通過隱藏您的資金來源為您提供真正的財務隱私。您的錢包中所有的達世幣都由不同的“輸入”組成,您可以將其視為分開的離散硬幣。<br> 匿名發送使用創新的方法將您的輸入與其他兩個人的輸入相結合,而過程中不會讓您的達世幣離開您的錢包。每時每刻,您仍然控制著您的錢。<hr> <b>匿名發送的運作原理如下:</b><ol type="1"> <li>匿名發送首先將您的交易分柝成多個標準面額的交易。這些標準面額分別為0.01 DASH, 0.1 DASH, 1 DASH 和10 DASH --有點像您每天使用的紙幣。</li> <li>您的錢包然後發送請求到網絡上有專門配置的軟件節點,稱為“主節點”。這些主節點會收到您希望混合一些資金的通知。沒有可識別的信息發送到主節點,所以他們永遠不會知道你是"誰"。</li> <li>當另外兩個人發送類似的消息時,表示希望混合相同的面額的話,混合會話就會開始。相關的主節點會混合這些輸入,並指示所有三個用戶的錢包將已經轉換了輸入的交易支付給自己。你的錢包直接支付給自己,但是付給不同的位址 (稱之為找零位址)。</li> <li>為了完全掩蓋您的資金來源,您的錢包必須以每個面額來重複此過程數次。每次這個過程完成後,都稱之為一個 "循環"。每個循環的匿名發送都會令確定您的資金來源的工作倍加困難。</li> <li>這種混合過程發生在後台,而不需要您進行任何操作。當您想進行交易時,您的資金將已被匿名處理。不需再花額外的時間等待。</li> </ol> <hr><b>重要:</b>您的錢包只能擁有1000個"找零位址。" 每次混合事件發生時,最多會使用9個找零位址。這意味著這1000個位址可以容許100次的混合事件。當其的中900個已經被使用後,您的錢包必須創建更多的位址。如果您啟用了自動備份,則只能夠這樣做。<br>因此,禁用備份的用戶也將禁用匿名發送。<hr>如欲了解更多信息請參閱<a href="https://docs.dash.org/en/latest/wallets/dashcore/privatesend-instantsend.html">匿名發送文檔</a>。 @@ -1831,7 +1764,7 @@ https://www.transifex.com/projects/p/dash/ Note: You can turn this message off in options. - 注意 : 您在選項中關閉了這項訊息。 + 注意 : 您可以在選項中關閉這項訊息。 WARNING! Something went wrong on automatic backup @@ -4616,6 +4549,10 @@ https://www.transifex.com/projects/p/dash/ Warning: Unknown block versions being mined! It's possible unknown rules are in effect 警告 : 正在開採未知版本的區塊。未知的規則可能正在生效 + + You are starting in lite mode, all Dash-specific functionality is disabled. + 您正在使用精簡模式,故此所有與達世幣相關的特別功能已被禁用。 + You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain 您需要使用-reindex 來重建數據庫以回到未修剪模式。 這將重新下載整個區塊鏈 @@ -4700,6 +4637,14 @@ https://www.transifex.com/projects/p/dash/ Failed to delete backup, error: %s 無法刪除備份,錯誤: %s + + Failed to load InstantSend data cache from + 無法載入即時到帳數據緩存 + + + Failed to load sporks cache from + 無法載入勺叉緩存 + Failed to parse host:port string 無法解析主機 : 端口串 @@ -4736,6 +4681,10 @@ https://www.transifex.com/projects/p/dash/ Line: %d 行: %d + + Loading InstantSend data cache... + 正在載入即時到帳數據緩存... + Loading addresses... 正在載入位址資料... @@ -4748,6 +4697,10 @@ https://www.transifex.com/projects/p/dash/ Loading governance cache... 正在載入治理緩存... + + Loading sporks cache... + 正在載入勺叉緩存... + Loading wallet... (%3.2f %%) 正在載入錢包資料... (%3.2f %%) @@ -4888,6 +4841,10 @@ https://www.transifex.com/projects/p/dash/ Transaction must have at least one recipient 交易必須至少要有一個收款人 + + Transaction too large + 交易太大了 + Trying to connect... 嘗試連接... @@ -5012,10 +4969,6 @@ https://www.transifex.com/projects/p/dash/ Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s) 設定製造區塊時,所要包含交易每千位元組的最低手續費(單位是 %s)。(預設值: %s) - - Show N confirmations for a successfully locked transaction (%u-%u, default: %u) - 如果交易成功鎖定的話,則顯示N個確認信息 (%u-%u, 預設值: %u) - This is the transaction fee you may pay when fee estimates are not available. 這是當預估手續費還沒計算出來時,付款交易預設會付的手續費。 diff --git a/src/qt/masternodelist.cpp b/src/qt/masternodelist.cpp index 6805c320b0f8..fba28e4210c2 100644 --- a/src/qt/masternodelist.cpp +++ b/src/qt/masternodelist.cpp @@ -4,18 +4,22 @@ #include "activemasternode.h" #include "clientmodel.h" #include "clientversion.h" -#include "init.h" #include "guiutil.h" +#include "init.h" #include "masternode-sync.h" #include "masternodeconfig.h" #include "masternodeman.h" +#include "netbase.h" #include "qrdialog.h" #include "sync.h" #include "wallet/wallet.h" #include "walletmodel.h" -#include +#include + #include +#include +#include int GetOffsetFromUtc() { @@ -28,7 +32,7 @@ int GetOffsetFromUtc() #endif } -MasternodeList::MasternodeList(const PlatformStyle *platformStyle, QWidget *parent) : +MasternodeList::MasternodeList(const PlatformStyle* platformStyle, QWidget* parent) : QWidget(parent), ui(new Ui::MasternodeList), clientModel(0), @@ -45,6 +49,13 @@ MasternodeList::MasternodeList(const PlatformStyle *platformStyle, QWidget *pare int columnActiveWidth = 130; int columnLastSeenWidth = 130; + int columnPoSeScoreWidth = 80; + int columnRegisteredWidth = 80; + int columnLastPaidWidth = 80; + int columnNextPaymentWidth = 100; + int columnPayeeWidth = 130; + int columnOperatorRewardWidth = 130; + ui->tableWidgetMyMasternodes->setColumnWidth(0, columnAliasWidth); ui->tableWidgetMyMasternodes->setColumnWidth(1, columnAddressWidth); ui->tableWidgetMyMasternodes->setColumnWidth(2, columnProtocolWidth); @@ -58,23 +69,52 @@ MasternodeList::MasternodeList(const PlatformStyle *platformStyle, QWidget *pare ui->tableWidgetMasternodes->setColumnWidth(3, columnActiveWidth); ui->tableWidgetMasternodes->setColumnWidth(4, columnLastSeenWidth); + ui->tableWidgetMasternodesDIP3->setColumnWidth(0, columnAddressWidth); + ui->tableWidgetMasternodesDIP3->setColumnWidth(1, columnStatusWidth); + ui->tableWidgetMasternodesDIP3->setColumnWidth(2, columnPoSeScoreWidth); + ui->tableWidgetMasternodesDIP3->setColumnWidth(3, columnRegisteredWidth); + ui->tableWidgetMasternodesDIP3->setColumnWidth(4, columnLastPaidWidth); + ui->tableWidgetMasternodesDIP3->setColumnWidth(5, columnNextPaymentWidth); + ui->tableWidgetMasternodesDIP3->setColumnWidth(6, columnPayeeWidth); + ui->tableWidgetMasternodesDIP3->setColumnWidth(7, columnOperatorRewardWidth); + + // dummy column for proTxHash + // TODO use a proper table model for the MN list + ui->tableWidgetMasternodesDIP3->insertColumn(8); + ui->tableWidgetMasternodesDIP3->setColumnHidden(8, true); + ui->tableWidgetMyMasternodes->setContextMenuPolicy(Qt::CustomContextMenu); + ui->tableWidgetMasternodesDIP3->setContextMenuPolicy(Qt::CustomContextMenu); - QAction *startAliasAction = new QAction(tr("Start alias"), this); + QAction* startAliasAction = new QAction(tr("Start alias"), this); contextMenu = new QMenu(); contextMenu->addAction(startAliasAction); connect(ui->tableWidgetMyMasternodes, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&))); connect(ui->tableWidgetMyMasternodes, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(on_QRButton_clicked())); connect(startAliasAction, SIGNAL(triggered()), this, SLOT(on_startButton_clicked())); + QAction* copyProTxHashAction = new QAction(tr("Copy ProTx Hash"), this); + QAction* copyCollateralOutpointAction = new QAction(tr("Copy Collateral Outpoint"), this); + contextMenuDIP3 = new QMenu(); + contextMenuDIP3->addAction(copyProTxHashAction); + contextMenuDIP3->addAction(copyCollateralOutpointAction); + connect(ui->tableWidgetMasternodesDIP3, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenuDIP3(const QPoint&))); + connect(ui->tableWidgetMasternodesDIP3, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(extraInfoDIP3_clicked())); + connect(copyProTxHashAction, SIGNAL(triggered()), this, SLOT(copyProTxHash_clicked())); + connect(copyCollateralOutpointAction, SIGNAL(triggered()), this, SLOT(copyCollateralOutpoint_clicked())); + timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(updateNodeList())); connect(timer, SIGNAL(timeout()), this, SLOT(updateMyNodeList())); + connect(timer, SIGNAL(timeout()), this, SLOT(updateDIP3List())); timer->start(1000); fFilterUpdated = false; + fFilterUpdatedDIP3 = false; nTimeFilterUpdated = GetTime(); + nTimeFilterUpdatedDIP3 = GetTime(); updateNodeList(); + updateDIP3List(); } MasternodeList::~MasternodeList() @@ -82,24 +122,30 @@ MasternodeList::~MasternodeList() delete ui; } -void MasternodeList::setClientModel(ClientModel *model) +void MasternodeList::setClientModel(ClientModel* model) { this->clientModel = model; - if(model) { + if (model) { // try to update list when masternode count changes connect(clientModel, SIGNAL(strMasternodesChanged(QString)), this, SLOT(updateNodeList())); } } -void MasternodeList::setWalletModel(WalletModel *model) +void MasternodeList::setWalletModel(WalletModel* model) { this->walletModel = model; } -void MasternodeList::showContextMenu(const QPoint &point) +void MasternodeList::showContextMenu(const QPoint& point) { - QTableWidgetItem *item = ui->tableWidgetMyMasternodes->itemAt(point); - if(item) contextMenu->exec(QCursor::pos()); + QTableWidgetItem* item = ui->tableWidgetMyMasternodes->itemAt(point); + if (item) contextMenu->exec(QCursor::pos()); +} + +void MasternodeList::showContextMenuDIP3(const QPoint& point) +{ + QTableWidgetItem* item = ui->tableWidgetMasternodesDIP3->itemAt(point); + if (item) contextMenuDIP3->exec(QCursor::pos()); } void MasternodeList::StartAlias(std::string strAlias) @@ -108,7 +154,7 @@ void MasternodeList::StartAlias(std::string strAlias) strStatusHtml += "
Alias: " + strAlias; for (const auto& mne : masternodeConfig.getEntries()) { - if(mne.getAlias() == strAlias) { + if (mne.getAlias() == strAlias) { std::string strError; CMasternodeBroadcast mnb; @@ -120,7 +166,7 @@ void MasternodeList::StartAlias(std::string strAlias) fSuccess = false; } - if(fSuccess) { + if (fSuccess) { strStatusHtml += "
Successfully started masternode."; mnodeman.NotifyMasternodeUpdates(*g_connman); } else { @@ -149,13 +195,13 @@ void MasternodeList::StartAll(std::string strCommand) CMasternodeBroadcast mnb; int32_t nOutputIndex = 0; - if(!ParseInt32(mne.getOutputIndex(), &nOutputIndex)) { + if (!ParseInt32(mne.getOutputIndex(), &nOutputIndex)) { continue; } COutPoint outpoint = COutPoint(uint256S(mne.getTxHash()), nOutputIndex); - if(strCommand == "start-missing" && mnodeman.Has(outpoint)) continue; + if (strCommand == "start-missing" && mnodeman.Has(outpoint)) continue; bool fSuccess = CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb); @@ -165,7 +211,7 @@ void MasternodeList::StartAll(std::string strCommand) fSuccess = false; } - if(fSuccess) { + if (fSuccess) { nCountSuccessful++; mnodeman.NotifyMasternodeUpdates(*g_connman); } else { @@ -192,15 +238,15 @@ void MasternodeList::updateMyMasternodeInfo(QString strAlias, QString strAddr, c bool fOldRowFound = false; int nNewRow = 0; - for(int i = 0; i < ui->tableWidgetMyMasternodes->rowCount(); i++) { - if(ui->tableWidgetMyMasternodes->item(i, 0)->text() == strAlias) { + for (int i = 0; i < ui->tableWidgetMyMasternodes->rowCount(); i++) { + if (ui->tableWidgetMyMasternodes->item(i, 0)->text() == strAlias) { fOldRowFound = true; nNewRow = i; break; } } - if(nNewRow == 0 && !fOldRowFound) { + if (nNewRow == 0 && !fOldRowFound) { nNewRow = ui->tableWidgetMyMasternodes->rowCount(); ui->tableWidgetMyMasternodes->insertRow(nNewRow); } @@ -208,14 +254,14 @@ void MasternodeList::updateMyMasternodeInfo(QString strAlias, QString strAddr, c masternode_info_t infoMn; bool fFound = mnodeman.GetMasternodeInfo(outpoint, infoMn); - QTableWidgetItem *aliasItem = new QTableWidgetItem(strAlias); - QTableWidgetItem *addrItem = new QTableWidgetItem(fFound ? QString::fromStdString(infoMn.addr.ToString()) : strAddr); - 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(DateTimeStrFormat("%Y-%m-%d %H:%M", - fFound ? infoMn.nTimeLastPing + GetOffsetFromUtc() : 0))); - QTableWidgetItem *pubkeyItem = new QTableWidgetItem(QString::fromStdString(fFound ? CBitcoinAddress(infoMn.pubKeyCollateralAddress.GetID()).ToString() : "")); + QTableWidgetItem* aliasItem = new QTableWidgetItem(strAlias); + QTableWidgetItem* addrItem = new QTableWidgetItem(fFound ? QString::fromStdString(infoMn.addr.ToString()) : strAddr); + 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(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); @@ -228,10 +274,16 @@ void MasternodeList::updateMyMasternodeInfo(QString strAlias, QString strAddr, c void MasternodeList::updateMyNodeList(bool fForce) { - TRY_LOCK(cs_mymnlist, fLockAcquired); - if(!fLockAcquired) { + if (ShutdownRequested()) { return; } + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + return; + } + + TRY_LOCK(cs_mymnlist, fLockAcquired); + if (!fLockAcquired) return; + static int64_t nTimeMyListUpdated = 0; // automatically update my masternode list only once in MY_MASTERNODELIST_UPDATE_SECONDS seconds, @@ -239,7 +291,7 @@ void MasternodeList::updateMyNodeList(bool fForce) int64_t nSecondsTillUpdate = nTimeMyListUpdated + MY_MASTERNODELIST_UPDATE_SECONDS - GetTime(); ui->secondsLabel->setText(QString::number(nSecondsTillUpdate)); - if(nSecondsTillUpdate > 0 && !fForce) return; + if (nSecondsTillUpdate > 0 && !fForce) return; nTimeMyListUpdated = GetTime(); // Find selected row @@ -250,7 +302,7 @@ void MasternodeList::updateMyNodeList(bool fForce) ui->tableWidgetMyMasternodes->setSortingEnabled(false); for (const auto& mne : masternodeConfig.getEntries()) { int32_t nOutputIndex = 0; - if(!ParseInt32(mne.getOutputIndex(), &nOutputIndex)) { + if (!ParseInt32(mne.getOutputIndex(), &nOutputIndex)) { continue; } @@ -265,21 +317,35 @@ void MasternodeList::updateMyNodeList(bool fForce) void MasternodeList::updateNodeList() { - TRY_LOCK(cs_mnlist, fLockAcquired); - if(!fLockAcquired) { + if (ShutdownRequested()) { return; } + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + // we misuse the fact that updateNodeList is called regularely here and remove both tabs + if (ui->tabWidget->indexOf(ui->tabDIP3Masternodes) != 0) { + // remove "My Masternode" and "All Masternodes" tabs + ui->tabWidget->removeTab(0); + ui->tabWidget->removeTab(0); + } + return; + } + + TRY_LOCK(cs_mnlist, fLockAcquired); + if (!fLockAcquired) return; + static int64_t nTimeListUpdated = GetTime(); // to prevent high cpu usage update only once in MASTERNODELIST_UPDATE_SECONDS seconds // or MASTERNODELIST_FILTER_COOLDOWN_SECONDS seconds after filter was last changed int64_t nSecondsToWait = fFilterUpdated - ? nTimeFilterUpdated - GetTime() + MASTERNODELIST_FILTER_COOLDOWN_SECONDS - : nTimeListUpdated - GetTime() + MASTERNODELIST_UPDATE_SECONDS; + ? nTimeFilterUpdated - GetTime() + MASTERNODELIST_FILTER_COOLDOWN_SECONDS + : nTimeListUpdated - GetTime() + MASTERNODELIST_UPDATE_SECONDS; - if(fFilterUpdated) ui->countLabel->setText(QString::fromStdString(strprintf("Please wait... %d", nSecondsToWait))); - if(nSecondsToWait > 0) return; + if (fFilterUpdated) { + ui->countLabel->setText(QString::fromStdString(strprintf("Please wait... %d", nSecondsToWait))); + } + if (nSecondsToWait > 0) return; nTimeListUpdated = GetTime(); fFilterUpdated = false; @@ -289,29 +355,34 @@ void MasternodeList::updateNodeList() ui->tableWidgetMasternodes->setSortingEnabled(false); ui->tableWidgetMasternodes->clearContents(); ui->tableWidgetMasternodes->setRowCount(0); - std::map mapMasternodes = mnodeman.GetFullMasternodeMap(); + + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + ui->countLabel->setText(QString::number(0)); + return; + } + int offsetFromUtc = GetOffsetFromUtc(); - for (const auto& mnpair : mapMasternodes) - { + std::map mapMasternodes = mnodeman.GetFullMasternodeMap(); + + for (const auto& mnpair : mapMasternodes) { CMasternode mn = mnpair.second; // populate list // Address, Protocol, Status, Active Seconds, Last Seen, Pub Key - QTableWidgetItem *addressItem = new QTableWidgetItem(QString::fromStdString(mn.addr.ToString())); - QTableWidgetItem *protocolItem = new QTableWidgetItem(QString::number(mn.nProtocolVersion)); - 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())); - - if (strCurrentFilter != "") - { - strToFilter = addressItem->text() + " " + - protocolItem->text() + " " + - statusItem->text() + " " + - activeSecondsItem->text() + " " + - lastSeenItem->text() + " " + - pubkeyItem->text(); + QTableWidgetItem* addressItem = new QTableWidgetItem(QString::fromStdString(mn.addr.ToString())); + QTableWidgetItem* protocolItem = new QTableWidgetItem(QString::number(mn.nProtocolVersion)); + 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.keyIDCollateralAddress).ToString())); + + if (strCurrentFilter != "") { + strToFilter = addressItem->text() + " " + + protocolItem->text() + " " + + statusItem->text() + " " + + activeSecondsItem->text() + " " + + lastSeenItem->text() + " " + + pubkeyItem->text(); if (!strToFilter.contains(strCurrentFilter)) continue; } @@ -328,7 +399,118 @@ void MasternodeList::updateNodeList() ui->tableWidgetMasternodes->setSortingEnabled(true); } -void MasternodeList::on_filterLineEdit_textChanged(const QString &strFilterIn) +void MasternodeList::updateDIP3List() +{ + if (ShutdownRequested()) { + return; + } + + if (deterministicMNManager->IsDeterministicMNsSporkActive()) { + ui->dip3NoteLabel->setVisible(false); + } + + TRY_LOCK(cs_dip3list, fLockAcquired); + if (!fLockAcquired) return; + + static int64_t nTimeListUpdated = GetTime(); + + // to prevent high cpu usage update only once in MASTERNODELIST_UPDATE_SECONDS seconds + // or MASTERNODELIST_FILTER_COOLDOWN_SECONDS seconds after filter was last changed + int64_t nSecondsToWait = fFilterUpdatedDIP3 + ? nTimeFilterUpdatedDIP3 - GetTime() + MASTERNODELIST_FILTER_COOLDOWN_SECONDS + : nTimeListUpdated - GetTime() + MASTERNODELIST_UPDATE_SECONDS; + + if (fFilterUpdatedDIP3) { + ui->countLabel->setText(QString::fromStdString(strprintf("Please wait... %d", nSecondsToWait))); + } + if (nSecondsToWait > 0) return; + + nTimeListUpdated = GetTime(); + fFilterUpdatedDIP3 = false; + + QString strToFilter; + ui->countLabelDIP3->setText("Updating..."); + ui->tableWidgetMasternodesDIP3->setSortingEnabled(false); + ui->tableWidgetMasternodesDIP3->clearContents(); + ui->tableWidgetMasternodesDIP3->setRowCount(0); + + auto mnList = deterministicMNManager->GetListAtChainTip(); + auto projectedPayees = mnList.GetProjectedMNPayees(mnList.GetValidMNsCount()); + std::map nextPayments; + for (size_t i = 0; i < projectedPayees.size(); i++) { + const auto& dmn = projectedPayees[i]; + nextPayments.emplace(dmn->proTxHash, mnList.GetHeight() + (int)i + 1); + } + + mnList.ForEachMN(false, [&](const CDeterministicMNCPtr& dmn) { + // populate list + // Address, Protocol, Status, Active Seconds, Last Seen, Pub Key + QTableWidgetItem* addressItem = new QTableWidgetItem(QString::fromStdString(dmn->pdmnState->addr.ToString())); + QTableWidgetItem* statusItem = new QTableWidgetItem(mnList.IsMNValid(dmn) ? tr("ENABLED") : (mnList.IsMNPoSeBanned(dmn) ? tr("POSE_BANNED") : tr("UNKNOWN"))); + QTableWidgetItem* PoSeScoreItem = new QTableWidgetItem(QString::number(dmn->pdmnState->nPoSePenalty)); + QTableWidgetItem* registeredItem = new QTableWidgetItem(QString::number(dmn->pdmnState->nRegisteredHeight)); + QTableWidgetItem* lastPaidItem = new QTableWidgetItem(QString::number(dmn->pdmnState->nLastPaidHeight)); + QTableWidgetItem* nextPaymentItem = new QTableWidgetItem(nextPayments.count(dmn->proTxHash) ? QString::number(nextPayments[dmn->proTxHash]) : tr("UNKNOWN")); + + CTxDestination payeeDest; + QString payeeStr; + if (ExtractDestination(dmn->pdmnState->scriptPayout, payeeDest)) { + payeeStr = QString::fromStdString(CBitcoinAddress(payeeDest).ToString()); + } else { + payeeStr = tr("UNKNOWN"); + } + QTableWidgetItem* payeeItem = new QTableWidgetItem(payeeStr); + + QString operatorRewardStr; + if (dmn->nOperatorReward) { + operatorRewardStr += QString::number(dmn->nOperatorReward / 100.0, 'f', 2) + "%"; + + if (dmn->pdmnState->scriptOperatorPayout != CScript()) { + CTxDestination operatorDest; + if (ExtractDestination(dmn->pdmnState->scriptOperatorPayout, operatorDest)) { + operatorRewardStr += tr(" to %1").arg(QString::fromStdString(CBitcoinAddress(operatorDest).ToString())); + } else { + operatorRewardStr += tr(" to UNKNOWN"); + } + } else { + operatorRewardStr += tr(" but not claimed"); + } + } else { + operatorRewardStr = tr("NONE"); + } + QTableWidgetItem* operatorRewardItem = new QTableWidgetItem(operatorRewardStr); + QTableWidgetItem* proTxHashItem = new QTableWidgetItem(QString::fromStdString(dmn->proTxHash.ToString())); + + if (strCurrentFilterDIP3 != "") { + strToFilter = addressItem->text() + " " + + statusItem->text() + " " + + PoSeScoreItem->text() + " " + + registeredItem->text() + " " + + lastPaidItem->text() + " " + + nextPaymentItem->text() + " " + + payeeItem->text() + " " + + operatorRewardItem->text() + " " + + proTxHashItem->text(); + if (!strToFilter.contains(strCurrentFilterDIP3)) return; + } + + ui->tableWidgetMasternodesDIP3->insertRow(0); + ui->tableWidgetMasternodesDIP3->setItem(0, 0, addressItem); + ui->tableWidgetMasternodesDIP3->setItem(0, 1, statusItem); + ui->tableWidgetMasternodesDIP3->setItem(0, 2, PoSeScoreItem); + ui->tableWidgetMasternodesDIP3->setItem(0, 3, registeredItem); + ui->tableWidgetMasternodesDIP3->setItem(0, 4, lastPaidItem); + ui->tableWidgetMasternodesDIP3->setItem(0, 5, nextPaymentItem); + ui->tableWidgetMasternodesDIP3->setItem(0, 6, payeeItem); + ui->tableWidgetMasternodesDIP3->setItem(0, 7, operatorRewardItem); + ui->tableWidgetMasternodesDIP3->setItem(0, 8, proTxHashItem); + }); + + ui->countLabelDIP3->setText(QString::number(ui->tableWidgetMasternodesDIP3->rowCount())); + ui->tableWidgetMasternodesDIP3->setSortingEnabled(true); +} + +void MasternodeList::on_filterLineEdit_textChanged(const QString& strFilterIn) { strCurrentFilter = strFilterIn; nTimeFilterUpdated = GetTime(); @@ -336,6 +518,14 @@ void MasternodeList::on_filterLineEdit_textChanged(const QString &strFilterIn) ui->countLabel->setText(QString::fromStdString(strprintf("Please wait... %d", MASTERNODELIST_FILTER_COOLDOWN_SECONDS))); } +void MasternodeList::on_filterLineEditDIP3_textChanged(const QString& strFilterIn) +{ + strCurrentFilterDIP3 = strFilterIn; + nTimeFilterUpdatedDIP3 = GetTime(); + fFilterUpdatedDIP3 = true; + ui->countLabelDIP3->setText(QString::fromStdString(strprintf("Please wait... %d", MASTERNODELIST_FILTER_COOLDOWN_SECONDS))); +} + void MasternodeList::on_startButton_clicked() { std::string strAlias; @@ -345,7 +535,7 @@ void MasternodeList::on_startButton_clicked() QItemSelectionModel* selectionModel = ui->tableWidgetMyMasternodes->selectionModel(); QModelIndexList selected = selectionModel->selectedRows(); - if(selected.count() == 0) return; + if (selected.count() == 0) return; QModelIndex index = selected.at(0); int nSelectedRow = index.row(); @@ -358,14 +548,14 @@ void MasternodeList::on_startButton_clicked() QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); - if(retval != QMessageBox::Yes) return; + if (retval != QMessageBox::Yes) return; WalletModel::EncryptionStatus encStatus = walletModel->getEncryptionStatus(); - if(encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForMixingOnly) { + if (encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForMixingOnly) { WalletModel::UnlockContext ctx(walletModel->requestUnlock()); - if(!ctx.isValid()) return; // Unlock wallet was cancelled + if (!ctx.isValid()) return; // Unlock wallet was cancelled StartAlias(strAlias); return; @@ -382,14 +572,14 @@ void MasternodeList::on_startAllButton_clicked() QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); - if(retval != QMessageBox::Yes) return; + if (retval != QMessageBox::Yes) return; WalletModel::EncryptionStatus encStatus = walletModel->getEncryptionStatus(); - if(encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForMixingOnly) { + if (encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForMixingOnly) { WalletModel::UnlockContext ctx(walletModel->requestUnlock()); - if(!ctx.isValid()) return; // Unlock wallet was cancelled + if (!ctx.isValid()) return; // Unlock wallet was cancelled StartAll(); return; @@ -400,8 +590,7 @@ void MasternodeList::on_startAllButton_clicked() void MasternodeList::on_startMissingButton_clicked() { - - if(!masternodeSync.IsMasternodeListSynced()) { + if (!masternodeSync.IsMasternodeListSynced()) { QMessageBox::critical(this, tr("Command is not available right now"), tr("You can't use this command until masternode list is synced")); return; @@ -414,14 +603,14 @@ void MasternodeList::on_startMissingButton_clicked() QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); - if(retval != QMessageBox::Yes) return; + if (retval != QMessageBox::Yes) return; WalletModel::EncryptionStatus encStatus = walletModel->getEncryptionStatus(); - if(encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForMixingOnly) { + if (encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForMixingOnly) { WalletModel::UnlockContext ctx(walletModel->requestUnlock()); - if(!ctx.isValid()) return; // Unlock wallet was cancelled + if (!ctx.isValid()) return; // Unlock wallet was cancelled StartAll("start-missing"); return; @@ -432,7 +621,7 @@ void MasternodeList::on_startMissingButton_clicked() void MasternodeList::on_tableWidgetMyMasternodes_itemSelectionChanged() { - if(ui->tableWidgetMyMasternodes->selectedItems().count() > 0) { + if (ui->tableWidgetMyMasternodes->selectedItems().count() > 0) { ui->startButton->setEnabled(true); } } @@ -451,7 +640,7 @@ void MasternodeList::on_QRButton_clicked() QItemSelectionModel* selectionModel = ui->tableWidgetMyMasternodes->selectionModel(); QModelIndexList selected = selectionModel->selectedRows(); - if(selected.count() == 0) return; + if (selected.count() == 0) return; QModelIndex index = selected.at(0); int nSelectedRow = index.row(); @@ -461,10 +650,9 @@ void MasternodeList::on_QRButton_clicked() ShowQRCode(strAlias); } -void MasternodeList::ShowQRCode(std::string strAlias) { - - if(!walletModel || !walletModel->getOptionsModel()) - return; +void MasternodeList::ShowQRCode(std::string strAlias) +{ + if (!walletModel || !walletModel->getOptionsModel()) return; // Get private key for this alias std::string strMNPrivKey = ""; @@ -474,10 +662,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(); @@ -509,9 +694,69 @@ void MasternodeList::ShowQRCode(std::string strAlias) { } // Open QR dialog - QRDialog *dialog = new QRDialog(this); + QRDialog* dialog = new QRDialog(this); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->setModel(walletModel->getOptionsModel()); dialog->setInfo(strWindowtitle, QString::fromStdString(strMNPrivKey), strHTML, strQRCodeTitle); dialog->show(); } + +CDeterministicMNCPtr MasternodeList::GetSelectedDIP3MN() +{ + std::string strProTxHash; + { + LOCK(cs_dip3list); + + QItemSelectionModel* selectionModel = ui->tableWidgetMasternodesDIP3->selectionModel(); + QModelIndexList selected = selectionModel->selectedRows(); + + if (selected.count() == 0) return nullptr; + + QModelIndex index = selected.at(0); + int nSelectedRow = index.row(); + strProTxHash = ui->tableWidgetMasternodesDIP3->item(nSelectedRow, 8)->text().toStdString(); + } + + uint256 proTxHash; + proTxHash.SetHex(strProTxHash); + + auto mnList = deterministicMNManager->GetListAtChainTip(); + return mnList.GetMN(proTxHash); +} + +void MasternodeList::extraInfoDIP3_clicked() +{ + auto dmn = GetSelectedDIP3MN(); + if (!dmn) { + return; + } + + UniValue json(UniValue::VOBJ); + dmn->ToJson(json); + + // Title of popup window + QString strWindowtitle = tr("Additional information for DIP3 Masternode %1").arg(QString::fromStdString(dmn->proTxHash.ToString())); + QString strText = QString::fromStdString(json.write(2)); + + QMessageBox::information(this, strWindowtitle, strText); +} + +void MasternodeList::copyProTxHash_clicked() +{ + auto dmn = GetSelectedDIP3MN(); + if (!dmn) { + return; + } + + QApplication::clipboard()->setText(QString::fromStdString(dmn->proTxHash.ToString())); +} + +void MasternodeList::copyCollateralOutpoint_clicked() +{ + auto dmn = GetSelectedDIP3MN(); + if (!dmn) { + return; + } + + QApplication::clipboard()->setText(QString::fromStdString(dmn->collateralOutpoint.ToStringShort())); +} diff --git a/src/qt/masternodelist.h b/src/qt/masternodelist.h index 8a6ef5445e53..5532c94e661c 100644 --- a/src/qt/masternodelist.h +++ b/src/qt/masternodelist.h @@ -1,21 +1,24 @@ #ifndef MASTERNODELIST_H #define MASTERNODELIST_H -#include "primitives/transaction.h" #include "platformstyle.h" +#include "primitives/transaction.h" #include "sync.h" #include "util.h" +#include + #include #include #include -#define MY_MASTERNODELIST_UPDATE_SECONDS 60 -#define MASTERNODELIST_UPDATE_SECONDS 15 -#define MASTERNODELIST_FILTER_COOLDOWN_SECONDS 3 +#define MY_MASTERNODELIST_UPDATE_SECONDS 60 +#define MASTERNODELIST_UPDATE_SECONDS 15 +#define MASTERNODELIST_FILTER_COOLDOWN_SECONDS 3 -namespace Ui { - class MasternodeList; +namespace Ui +{ +class MasternodeList; } class ClientModel; @@ -31,33 +34,38 @@ class MasternodeList : public QWidget Q_OBJECT public: - explicit MasternodeList(const PlatformStyle *platformStyle, QWidget *parent = 0); + explicit MasternodeList(const PlatformStyle* platformStyle, QWidget* parent = 0); ~MasternodeList(); - void setClientModel(ClientModel *clientModel); - void setWalletModel(WalletModel *walletModel); + void setClientModel(ClientModel* clientModel); + void setWalletModel(WalletModel* walletModel); void ShowQRCode(std::string strAlias); void StartAlias(std::string strAlias); void StartAll(std::string strCommand = "start-all"); + CDeterministicMNCPtr GetSelectedDIP3MN(); private: - QMenu *contextMenu; + QMenu* contextMenu; + QMenu* contextMenuDIP3; int64_t nTimeFilterUpdated; + int64_t nTimeFilterUpdatedDIP3; bool fFilterUpdated; + bool fFilterUpdatedDIP3; public Q_SLOTS: void updateMyMasternodeInfo(QString strAlias, QString strAddr, const COutPoint& outpoint); void updateMyNodeList(bool fForce = false); void updateNodeList(); + void updateDIP3List(); Q_SIGNALS: void doubleClicked(const QModelIndex&); private: - QTimer *timer; - Ui::MasternodeList *ui; - ClientModel *clientModel; - WalletModel *walletModel; + QTimer* timer; + Ui::MasternodeList* ui; + ClientModel* clientModel; + WalletModel* walletModel; // Protects tableWidgetMasternodes CCriticalSection cs_mnlist; @@ -65,16 +73,26 @@ public Q_SLOTS: // Protects tableWidgetMyMasternodes CCriticalSection cs_mymnlist; + // Protects tableWidgetMasternodesDIP3 + CCriticalSection cs_dip3list; + QString strCurrentFilter; + QString strCurrentFilterDIP3; private Q_SLOTS: - void showContextMenu(const QPoint &); - void on_filterLineEdit_textChanged(const QString &strFilterIn); + void showContextMenu(const QPoint&); + void showContextMenuDIP3(const QPoint&); + void on_filterLineEdit_textChanged(const QString& strFilterIn); + void on_filterLineEditDIP3_textChanged(const QString& strFilterIn); void on_QRButton_clicked(); void on_startButton_clicked(); void on_startAllButton_clicked(); void on_startMissingButton_clicked(); void on_tableWidgetMyMasternodes_itemSelectionChanged(); void on_UpdateButton_clicked(); + + void extraInfoDIP3_clicked(); + void copyProTxHash_clicked(); + void copyCollateralOutpoint_clicked(); }; #endif // MASTERNODELIST_H diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 668a5afdd84e..0e702b9488a9 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -211,6 +211,7 @@ void OptionsDialog::setMapper() mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures); mapper->addMapping(ui->showMasternodesTab, OptionsModel::ShowMasternodesTab); mapper->addMapping(ui->showAdvancedPSUI, OptionsModel::ShowAdvancedPSUI); + mapper->addMapping(ui->showPrivateSendPopups, OptionsModel::ShowPrivateSendPopups); mapper->addMapping(ui->lowKeysWarning, OptionsModel::LowKeysWarning); mapper->addMapping(ui->privateSendMultiSession, OptionsModel::PrivateSendMultiSession); mapper->addMapping(ui->spendZeroConfChange, OptionsModel::SpendZeroConfChange); diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 7e8d9cdb652b..27b68fe2c02c 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -101,6 +101,9 @@ void OptionsModel::Init(bool resetSettings) settings.setValue("fShowAdvancedPSUI", false); fShowAdvancedPSUI = settings.value("fShowAdvancedPSUI", false).toBool(); + if (!settings.contains("fShowPrivateSendPopups")) + settings.setValue("fShowPrivateSendPopups", true); + if (!settings.contains("fLowKeysWarning")) settings.setValue("fLowKeysWarning", true); #endif // ENABLE_WALLET @@ -272,6 +275,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const return settings.value("fShowMasternodesTab"); case ShowAdvancedPSUI: return fShowAdvancedPSUI; + case ShowPrivateSendPopups: + return settings.value("fShowPrivateSendPopups"); case LowKeysWarning: return settings.value("fLowKeysWarning"); case PrivateSendRounds: @@ -422,6 +427,9 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in settings.setValue("fShowAdvancedPSUI", fShowAdvancedPSUI); Q_EMIT advancedPSUIChanged(fShowAdvancedPSUI); break; + case ShowPrivateSendPopups: + settings.setValue("fShowPrivateSendPopups", value); + break; case LowKeysWarning: settings.setValue("fLowKeysWarning", value); break; diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 8bc051ae1d17..3dd4ff15167d 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -49,6 +49,7 @@ class OptionsModel : public QAbstractListModel SpendZeroConfChange, // bool ShowMasternodesTab, // bool ShowAdvancedPSUI, // bool + ShowPrivateSendPopups, // bool LowKeysWarning, // bool PrivateSendRounds, // int PrivateSendAmount, // int diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 806ff34decd8..3f62b36e443e 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -19,7 +19,6 @@ #include "walletmodel.h" #include "instantx.h" -#include "darksendconfig.h" #include "masternode-sync.h" #include "privatesend-client.h" @@ -558,14 +557,14 @@ void OverviewPage::privateSendStatus() ui->labelPrivateSendEnabled->setToolTip(strWarning); } - // check darksend status and unlock if needed + // check privatesend status and unlock if needed if(nBestHeight != privateSendClient.nCachedNumBlocks) { // Balance and number of transactions might have changed privateSendClient.nCachedNumBlocks = nBestHeight; updatePrivateSendProgress(); } - QString strStatus = QString(privateSendClient.GetStatus().c_str()); + QString strStatus = QString(privateSendClient.GetStatuses().c_str()); QString s = tr("Last PrivateSend message:\n") + strStatus; @@ -574,13 +573,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(){ @@ -643,18 +636,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/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 0aedece4def6..c89d7171a362 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -256,10 +256,8 @@ void SendCoinsDialog::on_sendButton_clicked() QString strFunds = tr("using") + " " + tr("anonymous funds") + ""; QString strFee = ""; - recipients[0].inputType = ONLY_DENOMINATED; if(ui->checkUsePrivateSend->isChecked()) { - recipients[0].inputType = ONLY_DENOMINATED; strFunds = tr("using") + " " + tr("anonymous funds") + ""; QString strNearestAmount( BitcoinUnits::formatWithUnit( @@ -268,18 +266,18 @@ void SendCoinsDialog::on_sendButton_clicked() "(privatesend requires this amount to be rounded up to the nearest %1)." ).arg(strNearestAmount)); } else { - recipients[0].inputType = ALL_COINS; strFunds = tr("using") + " " + tr("any available funds (not anonymous)") + ""; } if(ui->checkUseInstantSend->isChecked()) { - recipients[0].fUseInstantSend = true; strFunds += " "; strFunds += tr("and InstantSend"); - } else { - recipients[0].fUseInstantSend = false; } + for (SendCoinsRecipient& rcp : recipients) { + rcp.inputType = ui->checkUsePrivateSend->isChecked() ? ONLY_DENOMINATED : ALL_COINS; + rcp.fUseInstantSend = ui->checkUseInstantSend->isChecked(); + } fNewRecipientAllowed = false; // request unlock only if was locked or unlocked for mixing: diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index 407f0f4544e9..e67b63ec8e9a 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -14,6 +14,9 @@ #include "univalue.h" #include "util.h" +#include "evo/deterministicmns.h" +#include "llmq/quorums_init.h" + #include #include @@ -42,13 +45,18 @@ 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); + + llmq::InitLLMQSystem(*evoDb); + pcoinsTip = new CCoinsViewCache(pcoinsdbview); InitBlockIndex(chainparams); { diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index 76ca8e00e97f..e4eff3e3fc14 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -23,6 +23,7 @@ TransactionFilterProxy::TransactionFilterProxy(QObject *parent) : addrPrefix(), typeFilter(COMMON_TYPES), watchOnlyFilter(WatchOnlyFilter_All), + instantsendFilter(InstantSendFilter_All), minAmount(0), limitRows(-1), showInactive(true) @@ -36,6 +37,7 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & int type = index.data(TransactionTableModel::TypeRole).toInt(); QDateTime datetime = index.data(TransactionTableModel::DateRole).toDateTime(); bool involvesWatchAddress = index.data(TransactionTableModel::WatchonlyRole).toBool(); + bool lockedByInstantSend = index.data(TransactionTableModel::InstantSendRole).toBool(); QString address = index.data(TransactionTableModel::AddressRole).toString(); QString label = index.data(TransactionTableModel::LabelRole).toString(); qint64 amount = llabs(index.data(TransactionTableModel::AmountRole).toLongLong()); @@ -49,6 +51,10 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & return false; if (!involvesWatchAddress && watchOnlyFilter == WatchOnlyFilter_Yes) return false; + if (lockedByInstantSend && instantsendFilter == InstantSendFilter_No) + return false; + if (!lockedByInstantSend && instantsendFilter == InstantSendFilter_Yes) + return false; if(datetime < dateFrom || datetime > dateTo) return false; if (!address.contains(addrPrefix, Qt::CaseInsensitive) && !label.contains(addrPrefix, Qt::CaseInsensitive)) @@ -90,6 +96,12 @@ void TransactionFilterProxy::setWatchOnlyFilter(WatchOnlyFilter filter) invalidateFilter(); } +void TransactionFilterProxy::setInstantSendFilter(InstantSendFilter filter) +{ + this->instantsendFilter = filter; + invalidateFilter(); +} + void TransactionFilterProxy::setLimit(int limit) { this->limitRows = limit; diff --git a/src/qt/transactionfilterproxy.h b/src/qt/transactionfilterproxy.h index fb3215cd0ff8..ed25b29c99c8 100644 --- a/src/qt/transactionfilterproxy.h +++ b/src/qt/transactionfilterproxy.h @@ -36,6 +36,13 @@ class TransactionFilterProxy : public QSortFilterProxyModel WatchOnlyFilter_No }; + enum InstantSendFilter + { + InstantSendFilter_All, + InstantSendFilter_Yes, + InstantSendFilter_No + }; + void setDateRange(const QDateTime &from, const QDateTime &to); void setAddressPrefix(const QString &addrPrefix); /** @@ -44,6 +51,7 @@ class TransactionFilterProxy : public QSortFilterProxyModel void setTypeFilter(quint32 modes); void setMinAmount(const CAmount& minimum); void setWatchOnlyFilter(WatchOnlyFilter filter); + void setInstantSendFilter(InstantSendFilter filter); /** Set maximum number of rows returned, -1 if unlimited. */ void setLimit(int limit); @@ -62,6 +70,7 @@ class TransactionFilterProxy : public QSortFilterProxyModel QString addrPrefix; quint32 typeFilter; WatchOnlyFilter watchOnlyFilter; + InstantSendFilter instantsendFilter; CAmount minAmount; int limitRows; bool showInactive; diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 21ee35593c6f..db9ae26b78aa 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -146,14 +146,23 @@ 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; + continue; // Keep looking, could be a part of PrivateSendCreateDenominations + } else if (CPrivateSend::IsDenominatedAmount(txout.nValue)) { + sub.type = TransactionRecord::PrivateSendCreateDenominations; + break; // Done, it's definitely a tx creating mixing denoms, no need to look any further + } + } } } @@ -171,7 +180,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); @@ -291,6 +314,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 673b01736836..95027928a7ff 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..aa792adab8f0 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -32,6 +32,7 @@ static int column_alignments[] = { Qt::AlignLeft|Qt::AlignVCenter, /* status */ Qt::AlignLeft|Qt::AlignVCenter, /* watchonly */ + Qt::AlignLeft|Qt::AlignVCenter, /* instantsend */ Qt::AlignLeft|Qt::AlignVCenter, /* date */ Qt::AlignLeft|Qt::AlignVCenter, /* type */ Qt::AlignLeft|Qt::AlignVCenter, /* address */ @@ -243,7 +244,7 @@ TransactionTableModel::TransactionTableModel(const PlatformStyle *_platformStyle fProcessingQueuedTransactions(false), platformStyle(_platformStyle) { - columns << QString() << QString() << tr("Date") << tr("Type") << tr("Address / Label") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); + columns << QString() << QString() << QString() << tr("Date") << tr("Type") << tr("Address / Label") << BitcoinUnits::getAmountColumnTitle(walletModel->getOptionsModel()->getDisplayUnit()); priv->refreshWallet(); connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); @@ -410,6 +411,7 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx case TransactionRecord::RecvWithAddress: case TransactionRecord::RecvFromOther: return QIcon(":/icons/" + theme + "/tx_input"); + case TransactionRecord::PrivateSend: case TransactionRecord::SendToAddress: case TransactionRecord::SendToOther: return QIcon(":/icons/" + theme + "/tx_output"); @@ -533,6 +535,15 @@ QVariant TransactionTableModel::txWatchonlyDecoration(const TransactionRecord *w return QVariant(); } +QVariant TransactionTableModel::txInstantSendDecoration(const TransactionRecord *wtx) const +{ + if (wtx->status.lockedByInstantSend) { + QString theme = GUIUtil::getThemeName(); + return QIcon(":/icons/" + theme + "/verify"); + } + return QVariant(); +} + QString TransactionTableModel::formatTooltip(const TransactionRecord *rec) const { QString tooltip = formatTxStatus(rec) + QString("\n") + formatTxType(rec); @@ -559,6 +570,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return txStatusDecoration(rec); case Watchonly: return txWatchonlyDecoration(rec); + case InstantSend: + return txInstantSendDecoration(rec); case ToAddress: return txAddressDecoration(rec); } @@ -592,6 +605,8 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return formatTxType(rec); case Watchonly: return (rec->involvesWatchAddress ? 1 : 0); + case InstantSend: + return (rec->status.lockedByInstantSend ? 1 : 0); case ToAddress: return formatTxToAddress(rec, true); case Amount: @@ -608,6 +623,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) { @@ -630,6 +649,10 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return rec->involvesWatchAddress; case WatchonlyDecorationRole: return txWatchonlyDecoration(rec); + case InstantSendRole: + return rec->status.lockedByInstantSend; + case InstantSendDecorationRole: + return txInstantSendDecoration(rec); case LongDescriptionRole: return priv->describe(rec, walletModel->getOptionsModel()->getDisplayUnit()); case AddressRole: @@ -705,6 +728,8 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat return tr("Type of transaction."); case Watchonly: return tr("Whether or not a watch-only address is involved in this transaction."); + case InstantSend: + return tr("Whether or not this transaction was locked by InstantSend."); case ToAddress: return tr("User-defined intent/purpose of the transaction."); case Amount: diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 6932646e1e50..9b97e1e26e14 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -30,10 +30,11 @@ class TransactionTableModel : public QAbstractTableModel enum ColumnIndex { Status = 0, Watchonly = 1, - Date = 2, - Type = 3, - ToAddress = 4, - Amount = 5 + InstantSend = 2, + Date = 3, + Type = 4, + ToAddress = 5, + Amount = 6 }; /** Roles to get specific information from a transaction row. @@ -48,6 +49,10 @@ class TransactionTableModel : public QAbstractTableModel WatchonlyRole, /** Watch-only icon */ WatchonlyDecorationRole, + /** InstantSend boolean */ + InstantSendRole, + /** InstantSend icon */ + InstantSendDecorationRole, /** Long description (HTML format) */ LongDescriptionRole, /** Address of transaction */ @@ -102,6 +107,7 @@ class TransactionTableModel : public QAbstractTableModel QString formatTooltip(const TransactionRecord *rec) const; QVariant txStatusDecoration(const TransactionRecord *wtx) const; QVariant txWatchonlyDecoration(const TransactionRecord *wtx) const; + QVariant txInstantSendDecoration(const TransactionRecord *wtx) const; QVariant txAddressDecoration(const TransactionRecord *wtx) const; public Q_SLOTS: diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index b64f217c8ae3..ce8d2f36c3c3 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -51,10 +51,10 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa hlayout->setContentsMargins(0,0,0,0); if (platformStyle->getUseExtraSpacing()) { hlayout->setSpacing(0); - hlayout->addSpacing(6); + hlayout->addSpacing(STATUS_COLUMN_WIDTH - 1); } else { hlayout->setSpacing(1); - hlayout->addSpacing(5); + hlayout->addSpacing(STATUS_COLUMN_WIDTH - 2); } QString theme = GUIUtil::getThemeName(); watchOnlyWidget = new QComboBox(this); @@ -64,6 +64,13 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa watchOnlyWidget->addItem(QIcon(":/icons/" + theme + "/eye_minus"), "", TransactionFilterProxy::WatchOnlyFilter_No); hlayout->addWidget(watchOnlyWidget); + instantsendWidget = new QComboBox(this); + instantsendWidget->setFixedWidth(24); + instantsendWidget->addItem(tr("All"), TransactionFilterProxy::InstantSendFilter_All); + instantsendWidget->addItem(tr("Locked by InstantSend"), TransactionFilterProxy::InstantSendFilter_Yes); + instantsendWidget->addItem(tr("Not locked by InstantSend"), TransactionFilterProxy::InstantSendFilter_No); + hlayout->addWidget(instantsendWidget); + dateWidget = new QComboBox(this); if (platformStyle->getUseExtraSpacing()) { dateWidget->setFixedWidth(120); @@ -181,6 +188,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); connect(watchOnlyWidget, SIGNAL(activated(int)), this, SLOT(chooseWatchonly(int))); + connect(instantsendWidget, SIGNAL(activated(int)), this, SLOT(chooseInstantSend(int))); connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString))); connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString))); @@ -224,6 +232,7 @@ void TransactionView::setModel(WalletModel *_model) transactionView->setColumnWidth(TransactionTableModel::Status, STATUS_COLUMN_WIDTH); transactionView->setColumnWidth(TransactionTableModel::Watchonly, WATCHONLY_COLUMN_WIDTH); + transactionView->setColumnWidth(TransactionTableModel::InstantSend, INSTANTSEND_COLUMN_WIDTH); transactionView->setColumnWidth(TransactionTableModel::Date, DATE_COLUMN_WIDTH); transactionView->setColumnWidth(TransactionTableModel::Type, TYPE_COLUMN_WIDTH); transactionView->setColumnWidth(TransactionTableModel::Amount, AMOUNT_MINIMUM_COLUMN_WIDTH); @@ -339,6 +348,14 @@ void TransactionView::chooseWatchonly(int idx) (TransactionFilterProxy::WatchOnlyFilter)watchOnlyWidget->itemData(idx).toInt()); } +void TransactionView::chooseInstantSend(int idx) +{ + if(!transactionProxyModel) + return; + transactionProxyModel->setInstantSendFilter( + (TransactionFilterProxy::InstantSendFilter)instantsendWidget->itemData(idx).toInt()); +} + void TransactionView::changedPrefix(const QString &prefix) { if(!transactionProxyModel) @@ -647,6 +664,6 @@ bool TransactionView::eventFilter(QObject *obj, QEvent *event) // show/hide column Watch-only void TransactionView::updateWatchOnlyColumn(bool fHaveWatchOnly) { - watchOnlyWidget->setVisible(true); + watchOnlyWidget->setVisible(fHaveWatchOnly); transactionView->setColumnHidden(TransactionTableModel::Watchonly, !fHaveWatchOnly); } diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 995b4170e296..7b62e8f31eae 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -53,6 +53,7 @@ class TransactionView : public QWidget enum ColumnWidths { STATUS_COLUMN_WIDTH = 30, WATCHONLY_COLUMN_WIDTH = 23, + INSTANTSEND_COLUMN_WIDTH = 23, DATE_COLUMN_WIDTH = 120, TYPE_COLUMN_WIDTH = 240, AMOUNT_MINIMUM_COLUMN_WIDTH = 120, @@ -66,6 +67,7 @@ class TransactionView : public QWidget QComboBox *dateWidget; QComboBox *typeWidget; QComboBox *watchOnlyWidget; + QComboBox *instantsendWidget; QLineEdit *addressWidget; QLineEdit *amountWidget; @@ -113,6 +115,7 @@ public Q_SLOTS: void chooseDate(int idx); void chooseType(int idx); void chooseWatchonly(int idx); + void chooseInstantSend(int idx); void changedPrefix(const QString &prefix); void changedAmount(const QString &amount); void exportClicked(); diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 4cb568ee03e9..c807f233b639 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -145,7 +145,7 @@ You retain control of your money at all times.
\ The PrivateSend process works like this:\
    \
  1. PrivateSend begins by breaking your transaction inputs down into standard denominations. \ -These denominations are 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day.
  2. \ +These denominations are 0.001 DASH, 0.01 DASH, 0.1 DASH, 1 DASH and 10 DASH -- sort of like the paper money you use every day. \
  3. Your wallet then sends requests to specially configured software nodes on the network, called \"masternodes.\" \ These masternodes are informed then that you are interested in mixing a certain denomination. \ No identifiable information is sent to the masternodes, so they never know \"who\" you are.
  4. \ @@ -161,7 +161,7 @@ your funds will already be anonymized. No additional waiting is required. \ This means those 1000 addresses last for about 100 mixing events. When 900 of them are used, your wallet must create more addresses. \ It can only do this, however, if you have automatic backups enabled.
    \ Consequently, users who have backups disabled will also have PrivateSend disabled.
    \ -For more info see https://dashpay.atlassian.net/wiki/display/DOC/PrivateSend \ +For more information, see the PrivateSend documentation. \ ")); ui->aboutMessage->setWordWrap(true); ui->helpMessage->setVisible(false); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index d5e5550b599d..e754e5070524 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -295,59 +295,65 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact return AmountExceedsBalance; } + if(recipients[0].fUseInstantSend && total > sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)*COIN) { + Q_EMIT message(tr("Send Coins"), tr("InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH.").arg(sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)), + CClientUIInterface::MSG_ERROR); + return TransactionCreationFailed; + } + + CAmount nFeeRequired = 0; + CAmount nValueOut = 0; + size_t nVinSize = 0; + bool fCreated; + std::string strFailReason; { LOCK2(cs_main, wallet->cs_wallet); transaction.newPossibleKeyChange(wallet); - CAmount nFeeRequired = 0; int nChangePosRet = -1; - std::string strFailReason; - CWalletTx *newTx = transaction.getTransaction(); + CWalletTx* newTx = transaction.getTransaction(); CReserveKey *keyChange = transaction.getPossibleKeyChange(); - if(recipients[0].fUseInstantSend && total > sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)*COIN){ - Q_EMIT message(tr("Send Coins"), tr("InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH.").arg(sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)), - CClientUIInterface::MSG_ERROR); - return TransactionCreationFailed; - } - - bool fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl, true, recipients[0].inputType, recipients[0].fUseInstantSend); + fCreated = wallet->CreateTransaction(vecSend, *newTx, *keyChange, nFeeRequired, nChangePosRet, strFailReason, coinControl, true, recipients[0].inputType, recipients[0].fUseInstantSend); transaction.setTransactionFee(nFeeRequired); if (fSubtractFeeFromAmount && fCreated) - transaction.reassignAmounts(nChangePosRet); + transaction.reassignAmounts(); - if(recipients[0].fUseInstantSend) { - if(newTx->tx->GetValueOut() > sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)*COIN) { - Q_EMIT message(tr("Send Coins"), tr("InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH.").arg(sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)), - CClientUIInterface::MSG_ERROR); - return TransactionCreationFailed; - } - if(newTx->tx->vin.size() > CTxLockRequest::WARN_MANY_INPUTS) { - Q_EMIT message(tr("Send Coins"), tr("Used way too many inputs (>%1) for this InstantSend transaction, fees could be huge.").arg(CTxLockRequest::WARN_MANY_INPUTS), - CClientUIInterface::MSG_WARNING); - } - } + nValueOut = newTx->tx->GetValueOut(); + nVinSize = newTx->tx->vin.size(); + } - if(!fCreated) - { - if(!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance) - { - return SendCoinsReturn(AmountWithFeeExceedsBalance); - } - Q_EMIT message(tr("Send Coins"), QString::fromStdString(strFailReason), + if(recipients[0].fUseInstantSend) { + if(nValueOut > sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)*COIN) { + Q_EMIT message(tr("Send Coins"), tr("InstantSend doesn't support sending values that high yet. Transactions are currently limited to %1 DASH.").arg(sporkManager.GetSporkValue(SPORK_5_INSTANTSEND_MAX_VALUE)), CClientUIInterface::MSG_ERROR); return TransactionCreationFailed; } + if(nVinSize > CTxLockRequest::WARN_MANY_INPUTS) { + Q_EMIT message(tr("Send Coins"), tr("Used way too many inputs (>%1) for this InstantSend transaction, fees could be huge.").arg(CTxLockRequest::WARN_MANY_INPUTS), + CClientUIInterface::MSG_WARNING); + } + } - // reject absurdly high fee. (This can never happen because the - // wallet caps the fee at maxTxFee. This merely serves as a - // belt-and-suspenders check) - if (nFeeRequired > maxTxFee) - return AbsurdFee; + if(!fCreated) + { + if(!fSubtractFeeFromAmount && (total + nFeeRequired) > nBalance) + { + return SendCoinsReturn(AmountWithFeeExceedsBalance); + } + Q_EMIT message(tr("Send Coins"), QString::fromStdString(strFailReason), + CClientUIInterface::MSG_ERROR); + return TransactionCreationFailed; } + // reject absurdly high fee. (This can never happen because the + // wallet caps the fee at maxTxFee. This merely serves as a + // belt-and-suspenders check) + if (nFeeRequired > maxTxFee) + return AbsurdFee; + return SendCoinsReturn(OK); } @@ -754,7 +760,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/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp index 63a0d3c98651..8077156d9e3e 100644 --- a/src/qt/walletmodeltransaction.cpp +++ b/src/qt/walletmodeltransaction.cpp @@ -46,9 +46,9 @@ void WalletModelTransaction::setTransactionFee(const CAmount& newFee) fee = newFee; } -void WalletModelTransaction::reassignAmounts(int nChangePosRet) +void WalletModelTransaction::reassignAmounts() { - int i = 0; + // For each recipient look for a matching CTxOut in walletTransaction and reassign amounts for (QList::iterator it = recipients.begin(); it != recipients.end(); ++it) { SendCoinsRecipient& rcp = (*it); @@ -61,19 +61,26 @@ void WalletModelTransaction::reassignAmounts(int nChangePosRet) { const payments::Output& out = details.outputs(j); if (out.amount() <= 0) continue; - if (i == nChangePosRet) - i++; - subtotal += walletTransaction->tx->vout[i].nValue; - i++; + const unsigned char* scriptStr = (const unsigned char*)out.script().data(); + CScript scriptPubKey(scriptStr, scriptStr+out.script().size()); + for (const auto& txout : walletTransaction->tx->vout) { + if (txout.scriptPubKey == scriptPubKey) { + subtotal += txout.nValue; + break; + } + } } rcp.amount = subtotal; } else // normal recipient (no payment request) { - if (i == nChangePosRet) - i++; - rcp.amount = walletTransaction->tx->vout[i].nValue; - i++; + for (const auto& txout : walletTransaction->tx->vout) { + CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); + if (txout.scriptPubKey == scriptPubKey) { + rcp.amount = txout.nValue; + break; + } + } } } } diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h index 64922efada5e..e3eabcc2abcc 100644 --- a/src/qt/walletmodeltransaction.h +++ b/src/qt/walletmodeltransaction.h @@ -35,7 +35,7 @@ class WalletModelTransaction void newPossibleKeyChange(CWallet *wallet); CReserveKey *getPossibleKeyChange(); - void reassignAmounts(int nChangePosRet); // needed for the subtract-fee-from-amount feature + void reassignAmounts(); // needed for the subtract-fee-from-amount feature private: QList recipients; diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 3fc088165fa8..16239a947643 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -16,6 +16,7 @@ #include "receivecoinsdialog.h" #include "sendcoinsdialog.h" #include "signverifymessagedialog.h" +#include "transactionrecord.h" #include "transactiontablemodel.h" #include "transactionview.h" #include "walletmodel.h" @@ -194,10 +195,19 @@ void WalletView::processNewTransaction(const QModelIndex& parent, int start, int if (!ttm || ttm->processingQueuedTransactions()) return; + QModelIndex index = ttm->index(start, 0, parent); + QSettings settings; + if (!settings.value("fShowPrivateSendPopups").toBool()) { + QVariant nType = ttm->data(index, TransactionTableModel::TypeRole); + if (nType == TransactionRecord::PrivateSendDenominate || + nType == TransactionRecord::PrivateSendCollateralPayment || + nType == TransactionRecord::PrivateSendMakeCollaterals || + nType == TransactionRecord::PrivateSendCreateDenominations) return; + } + QString date = ttm->index(start, TransactionTableModel::Date, parent).data().toString(); qint64 amount = ttm->index(start, TransactionTableModel::Amount, parent).data(Qt::EditRole).toULongLong(); QString type = ttm->index(start, TransactionTableModel::Type, parent).data().toString(); - QModelIndex index = ttm->index(start, 0, parent); QString address = ttm->data(index, TransactionTableModel::AddressRole).toString(); QString label = ttm->data(index, TransactionTableModel::LabelRole).toString(); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 481402b176f5..d72ed10f8fbe 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -23,6 +23,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)); @@ -819,13 +830,16 @@ UniValue getblock(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( - "getblock \"blockhash\" ( verbose )\n" - "\nIf verbose is false, returns a string that is serialized, hex-encoded data for block 'hash'.\n" - "If verbose is true, returns an Object with information about block .\n" + "getblock \"blockhash\" ( verbosity ) \n" + "\nIf verbosity is 0, returns a string that is serialized, hex-encoded data for block 'hash'.\n" + "If verbosity is 1, returns an Object with information about block .\n" + "If verbosity is 2, returns an Object with information about block and information about each transaction. \n" "\nArguments:\n" "1. \"blockhash\" (string, required) The block hash\n" - "2. verbose (boolean, optional, default=true) true for a json object, false for the hex encoded data\n" - "\nResult (for verbose = true):\n" + "2. verbosity (numeric, optional, default=1) 0 for hex-encoded data, 1 for a json object, and 2 for json object with transaction data\n" + "\nResult (for verbosity = 0):\n" + "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" + "\nResult (for verbose = 1):\n" "{\n" " \"hash\" : \"hash\", (string) the block hash (same as provided)\n" " \"confirmations\" : n, (numeric) The number of confirmations, or -1 if the block is not on the main chain\n" @@ -847,8 +861,14 @@ UniValue getblock(const JSONRPCRequest& request) " \"previousblockhash\" : \"hash\", (string) The hash of the previous block\n" " \"nextblockhash\" : \"hash\" (string) The hash of the next block\n" "}\n" - "\nResult (for verbose=false):\n" - "\"data\" (string) A string that is serialized, hex-encoded data for block 'hash'.\n" + "\nResult (for verbosity = 2):\n" + "{\n" + " ..., Same output as verbosity = 1.\n" + " \"tx\" : [ (array of Objects) The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result.\n" + " ,...\n" + " ],\n" + " ,... Same output as verbosity = 1.\n" + "}\n" "\nExamples:\n" + HelpExampleCli("getblock", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"") + HelpExampleRpc("getblock", "\"00000000000fd08c2fb661d2fcb0d49abb3a91e5f27082ce64feed3b4dede2e2\"") @@ -859,9 +879,13 @@ UniValue getblock(const JSONRPCRequest& request) std::string strHash = request.params[0].get_str(); uint256 hash(uint256S(strHash)); - bool fVerbose = true; - if (request.params.size() > 1) - fVerbose = request.params[1].get_bool(); + int verbosity = 1; + if (request.params.size() > 1) { + if(request.params[1].isNum()) + verbosity = request.params[1].get_int(); + else + verbosity = request.params[1].get_bool() ? 1 : 0; + } if (mapBlockIndex.count(hash) == 0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); @@ -875,7 +899,7 @@ UniValue getblock(const JSONRPCRequest& request) if(!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); - if (!fVerbose) + if (verbosity <= 0) { CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); ssBlock << block; @@ -883,7 +907,7 @@ UniValue getblock(const JSONRPCRequest& request) return strHex; } - return blockToJSON(block, pblockindex); + return blockToJSON(block, pblockindex, verbosity >= 2); } struct CCoinsStats @@ -1194,6 +1218,16 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse if (THRESHOLD_STARTED == thresholdState) { rv.push_back(Pair("bit", consensusParams.vDeployments[id].bit)); + + int nBlockCount = VersionBitsCountBlocksInWindow(chainActive.Tip(), consensusParams, id); + int64_t nPeriod = consensusParams.vDeployments[id].nWindowSize ? consensusParams.vDeployments[id].nWindowSize : consensusParams.nMinerConfirmationWindow; + int64_t nThreshold = consensusParams.vDeployments[id].nThreshold ? consensusParams.vDeployments[id].nThreshold : consensusParams.nRuleChangeActivationThreshold; + int64_t nWindowStart = chainActive.Height() - (chainActive.Height() % nPeriod); + rv.push_back(Pair("period", nPeriod)); + rv.push_back(Pair("threshold", nThreshold)); + rv.push_back(Pair("windowStart", nWindowStart)); + rv.push_back(Pair("windowBlocks", nBlockCount)); + rv.push_back(Pair("windowProgress", std::min(1.0, (double)nBlockCount / nThreshold))); } rv.push_back(Pair("startTime", consensusParams.vDeployments[id].nStartTime)); rv.push_back(Pair("timeout", consensusParams.vDeployments[id].nTimeout)); @@ -1241,6 +1275,11 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) " \"xxxx\" : { (string) name of the softfork\n" " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n" " \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n" + " \"period\": xx, (numeric) the window size/period for this softfork (only for \"started\" status)\n" + " \"threshold\": xx, (numeric) the threshold for this softfork (only for \"started\" status)\n" + " \"windowStart\": xx, (numeric) the starting block height of the current window (only for \"started\" status)\n" + " \"windowBlocks\": xx, (numeric) the number of blocks in the current window that had the version bit set for this softfork (only for \"started\" status)\n" + " \"windowProgress\": xx, (numeric) the progress (between 0 and 1) for activation of this softfork (only for \"started\" status)\n" " \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n" " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n" " \"since\": xx (numeric) height of the first block to which the status applies\n" @@ -1274,6 +1313,7 @@ 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, "dip0003", consensusParams, Consensus::DEPLOYMENT_DIP0003); BIP9SoftForkDescPushBack(bip9_softforks, "bip147", consensusParams, Consensus::DEPLOYMENT_BIP147); obj.push_back(Pair("softforks", softforks)); obj.push_back(Pair("bip9_softforks", bip9_softforks)); @@ -1586,7 +1626,7 @@ static const CRPCCommand commands[] = { "blockchain", "getblockchaininfo", &getblockchaininfo, true, {} }, { "blockchain", "getbestblockhash", &getbestblockhash, true, {} }, { "blockchain", "getblockcount", &getblockcount, true, {} }, - { "blockchain", "getblock", &getblock, true, {"blockhash","verbose"} }, + { "blockchain", "getblock", &getblock, true, {"blockhash","verbosity|verbose"} }, { "blockchain", "getblockhashes", &getblockhashes, true, {"high","low"} }, { "blockchain", "getblockhash", &getblockhash, true, {"height"} }, { "blockchain", "getblockheader", &getblockheader, true, {"blockhash","verbose"} }, diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index c5b07d6d0da6..643beb54e7c9 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -97,7 +97,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "listunspent", 1, "maxconf" }, { "listunspent", 2, "addresses" }, { "listunspent", 3, "include_unsafe" }, - { "getblock", 1, "verbose" }, + { "getblock", 1, "verbosity" }, { "getblockheader", 1, "verbose" }, { "getblockheaders", 1, "count" }, { "getblockheaders", 2, "verbose" }, @@ -139,6 +139,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 830e9f7cdcd6..441f7453d6d8 100644 --- a/src/rpc/governance.cpp +++ b/src/rpc/governance.cpp @@ -26,839 +26,1064 @@ 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 dash.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; + UniValue objResult(UniValue::VOBJ); - if (request.params.size() != 5) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Correct usage is 'gobject prepare