diff options
346 files changed, 5411 insertions, 4360 deletions
diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000000..84cd596bbd --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,59 @@ +version: '{branch}.{build}' +skip_tags: true +image: Visual Studio 2017 +configuration: Release +platform: x64 +environment: + APPVEYOR_SAVE_CACHE_ON_ERROR: true + CLCACHE_SERVER: 1 + PACKAGES: boost-filesystem boost-signals2 boost-interprocess boost-test libevent openssl zeromq berkeleydb secp256k1 leveldb + PYTHONIOENCODING: utf-8 +cache: +- C:\tools\vcpkg\installed +- C:\Users\appveyor\clcache +- build_msvc\cache +init: +- cmd: set PATH=C:\Python36-x64;C:\Python36-x64\Scripts;%PATH% +install: +- cmd: pip install git+https://github.com/frerich/clcache.git +# Disable zmq test for now since python zmq library on Windows would cause Access violation sometimes. +# - cmd: pip install zmq +- ps: $packages = $env:PACKAGES -Split ' ' +- ps: for ($i=0; $i -lt $packages.length; $i++) { + $env:ALL_PACKAGES += $packages[$i] + ":" + $env:PLATFORM + "-windows-static " + } +- cmd: git -C C:\Tools\vcpkg pull # This is a temporary fix, can be removed after appveyor update its image to include Microsoft/vcpkg#4046 +- cmd: vcpkg install %ALL_PACKAGES% +- cmd: vcpkg upgrade --no-dry-run +- cmd: del /s /q C:\Tools\vcpkg\installed\%PLATFORM%-windows-static\debug # Remove unused debug library +before_build: +- cmd: if not exist build_msvc\cache\ (del build_msvc\cache & mkdir build_msvc\cache) +- cmd: if not exist build_msvc\%PLATFORM%\%CONFIGURATION%\ (mkdir build_msvc\%PLATFORM%\%CONFIGURATION%) +- cmd: if exist build_msvc\cache\*.iobj (move build_msvc\cache\* build_msvc\%PLATFORM%\%CONFIGURATION%\) +- cmd: clcache -M 2147483648 +- cmd: python build_msvc\msvc-autogen.py +- ps: $files = (Get-ChildItem -Recurse | where {$_.extension -eq ".vcxproj"}).FullName +- ps: for ($i = 0; $i -lt $files.length; $i++) { + (Get-Content $files[$i]).Replace("</RuntimeLibrary>", "</RuntimeLibrary><DebugInformationFormat>None</DebugInformationFormat>").Replace("NDEBUG;", "") | Set-Content $files[$i] + } +- ps: Start-Process clcache-server +build_script: +- cmd: msbuild /p:TrackFileAccess=false /p:CLToolExe=clcache.exe build_msvc\bitcoin.sln /m /v:q /nowarn:C4244;C4267;C4715 /nologo +after_build: +- cmd: move build_msvc\%PLATFORM%\%CONFIGURATION%\*.iobj build_msvc\cache\ +- cmd: move build_msvc\%PLATFORM%\%CONFIGURATION%\*.ipdb build_msvc\cache\ +- cmd: del C:\Users\appveyor\clcache\stats.txt +before_test: +- ps: ${conf_ini} = (Get-Content([IO.Path]::Combine(${env:APPVEYOR_BUILD_FOLDER}, "test", "config.ini.in"))) +- ps: ${conf_ini} = $conf_ini.Replace("@abs_top_srcdir@", ${env:APPVEYOR_BUILD_FOLDER}).Replace("@abs_top_builddir@", ${env:APPVEYOR_BUILD_FOLDER}).Replace("@EXEEXT@", ".exe") +- ps: ${conf_ini} = $conf_ini.Replace("@ENABLE_WALLET_TRUE@", "").Replace("@BUILD_BITCOIN_UTILS_TRUE@", "").Replace("@BUILD_BITCOIND_TRUE@", "").Replace("@ENABLE_ZMQ_TRUE@", "") +- ps: ${utf8} = New-Object System.Text.UTF8Encoding ${false} +- ps: '[IO.File]::WriteAllLines([IO.Path]::Combine(${env:APPVEYOR_BUILD_FOLDER}, "test", "config.ini"), $conf_ini, ${utf8})' +- ps: move "build_msvc\${env:PLATFORM}\${env:CONFIGURATION}\*.exe" src +test_script: +- cmd: src\test_bitcoin.exe +- ps: src\bench_bitcoin.exe -evals=1 -scaling=0 +- ps: python test\util\bitcoin-util-test.py +- cmd: python test\util\rpcauth-test.py +- cmd: python test\functional\test_runner.py --force --quiet --combinedlogslen=4000 --exclude "wallet_multiwallet,wallet_multiwallet.py --usecli" +deploy: off diff --git a/.travis.yml b/.travis.yml index aafdc2e89d..8819d38914 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,19 +5,19 @@ language: minimal cache: ccache: true directories: - - depends/built - - depends/sdk-sources - - $HOME/.ccache + - depends/built + - depends/sdk-sources + - $HOME/.ccache stages: - lint - test env: global: - MAKEJOBS=-j3 - - RUN_TESTS=false + - RUN_UNIT_TESTS=true + - RUN_FUNCTIONAL_TESTS=true - RUN_BENCH=false # Set to true for any one job that has debug enabled, to quickly check bench is not crashing or hitting assertions - DOCKER_NAME_TAG=ubuntu:18.04 - - LC_ALL=C.UTF-8 - BOOST_TEST_RANDOM=1$TRAVIS_BUILD_ID - CCACHE_SIZE=100M - CCACHE_TEMPDIR=/tmp/.ccache-temp @@ -28,54 +28,43 @@ env: - WINEDEBUG=fixme-all - DOCKER_PACKAGES="build-essential libtool autotools-dev automake pkg-config bsdmainutils curl git ca-certificates ccache" before_install: - - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") - - BEGIN_FOLD () { echo ""; CURRENT_FOLD_NAME=$1; echo "travis_fold:start:${CURRENT_FOLD_NAME}"; } - - END_FOLD () { RET=$?; echo "travis_fold:end:${CURRENT_FOLD_NAME}"; return $RET; } + - set -o errexit; source .travis/test_03_before_install.sh install: - - travis_retry docker pull $DOCKER_NAME_TAG - - env | grep -E '^(CCACHE_|WINEDEBUG|LC_ALL|BOOST_TEST_RANDOM|CONFIG_SHELL)' | tee /tmp/env - - if [[ $HOST = *-mingw32 ]]; then DOCKER_ADMIN="--cap-add SYS_ADMIN"; fi - - DOCKER_ID=$(docker run $DOCKER_ADMIN -idt --mount type=bind,src=$TRAVIS_BUILD_DIR,dst=$TRAVIS_BUILD_DIR --mount type=bind,src=$CCACHE_DIR,dst=$CCACHE_DIR -w $TRAVIS_BUILD_DIR --env-file /tmp/env $DOCKER_NAME_TAG) - - DOCKER_EXEC () { docker exec $DOCKER_ID bash -c "cd $PWD && $*"; } - - if [ -n "$DPKG_ADD_ARCH" ]; then DOCKER_EXEC dpkg --add-architecture "$DPKG_ADD_ARCH" ; fi - - travis_retry DOCKER_EXEC apt-get update - - travis_retry DOCKER_EXEC apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES $DOCKER_PACKAGES + - set -o errexit; source .travis/test_04_install.sh before_script: - - DOCKER_EXEC echo \> \$HOME/.bitcoin # Make sure default datadir does not exist and is never read by creating a dummy file - - 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 - - if [[ $HOST = *-mingw32 ]]; then DOCKER_EXEC update-alternatives --set $HOST-g++ \$\(which $HOST-g++-posix\); fi - - if [ -z "$NO_DEPENDS" ]; then DOCKER_EXEC CONFIG_SHELL= make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS; fi + - set -o errexit; source .travis/test_05_before_script.sh script: - - export TRAVIS_COMMIT_LOG=`git log --format=fuller -1` - - 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" - - if [ -z "$NO_DEPENDS" ]; then DOCKER_EXEC ccache --max-size=$CCACHE_SIZE; fi - - BEGIN_FOLD autogen; test -n "$CONFIG_SHELL" && DOCKER_EXEC "$CONFIG_SHELL" -c "./autogen.sh" || DOCKER_EXEC ./autogen.sh; END_FOLD - - mkdir build && cd build - - BEGIN_FOLD configure; DOCKER_EXEC ../configure --cache-file=config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false); END_FOLD - - BEGIN_FOLD distdir; DOCKER_EXEC make distdir VERSION=$HOST; END_FOLD - - cd bitcoin-$HOST - - BEGIN_FOLD configure; DOCKER_EXEC ./configure --cache-file=../config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false); END_FOLD - - BEGIN_FOLD build; DOCKER_EXEC make $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && DOCKER_EXEC make $GOAL V=1 ; false ); END_FOLD - - if [ "$RUN_TESTS" = "true" ]; then BEGIN_FOLD unit-tests; DOCKER_EXEC LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib make $MAKEJOBS check VERBOSE=1; END_FOLD; fi - - if [ "$RUN_BENCH" = "true" ]; then BEGIN_FOLD bench; DOCKER_EXEC LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib $OUTDIR/bin/bench_bitcoin -scaling=0.001 ; END_FOLD; fi - - if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then extended="--extended --exclude feature_pruning,feature_dbcrash"; fi - - if [ "$RUN_TESTS" = "true" ]; then BEGIN_FOLD functional-tests; DOCKER_EXEC test/functional/test_runner.py --combinedlogslen=4000 --coverage --quiet --failfast ${extended}; END_FOLD; fi + - set -o errexit; source .travis/test_06_script.sh after_script: - - echo $TRAVIS_COMMIT_RANGE - - echo $TRAVIS_COMMIT_LOG + - echo $TRAVIS_COMMIT_RANGE + - echo $TRAVIS_COMMIT_LOG jobs: include: +# lint stage + - stage: lint + env: + sudo: false + cache: false + language: python + python: '3.6' + install: + - set -o errexit; source .travis/lint_04_install.sh + before_script: + - set -o errexit; source .travis/lint_05_before_script.sh + script: + - set -o errexit; source .travis/lint_06_script.sh # ARM - stage: test env: >- HOST=arm-linux-gnueabihf PACKAGES="g++-arm-linux-gnueabihf" DEP_OPTS="NO_QT=1" + RUN_UNIT_TESTS=false + RUN_FUNCTIONAL_TESTS=false GOAL="install" - BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" + # -Wno-psabi is to disable ABI warnings: "note: parameter passing for argument of type ... changed in GCC 7.1" + # This could be removed once the ABI change warning does not show up by default + BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports CXXFLAGS=-Wno-psabi" # Win32 - stage: test env: >- @@ -83,7 +72,6 @@ jobs: DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="python3 nsis g++-mingw-w64-i686 wine-binfmt wine32" - RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports" # Win64 @@ -92,7 +80,6 @@ jobs: HOST=x86_64-w64-mingw32 DEP_OPTS="NO_QT=1" PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine-binfmt wine64" - RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports" # 32-bit + dash @@ -101,7 +88,6 @@ jobs: HOST=i686-pc-linux-gnu PACKAGES="g++-multilib 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++" CONFIG_SHELL="/bin/dash" @@ -111,26 +97,32 @@ jobs: HOST=x86_64-unknown-linux-gnu PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools protobuf-compiler libdbus-1-dev libharfbuzz-dev libprotobuf-dev" DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1" - RUN_TESTS=true - RUN_BENCH=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports --enable-debug CXXFLAGS=\"-g0 -O2\"" -# x86_64 Linux (Qt5 & system libs) +# x86_64 Linux (no depends, only system libs) - stage: test env: >- HOST=x86_64-unknown-linux-gnu PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libssl1.0-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libprotobuf-dev protobuf-compiler libqrencode-dev" NO_DEPENDS=1 - RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --enable-glibc-back-compat --enable-reduce-exports --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER" +# x86_64 Linux (sanitizers) + - stage: test + env: >- + HOST=x86_64-unknown-linux-gnu + PACKAGES="clang python3-zmq qtbase5-dev qttools5-dev-tools libssl1.0-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev libprotobuf-dev protobuf-compiler libqrencode-dev" + NO_DEPENDS=1 + RUN_BENCH=true + RUN_FUNCTIONAL_TESTS=false # Disabled for now, can be combined with the other x86_64 linux NO_DEPENDS job when functional tests pass the sanitizers + GOAL="install" + BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --enable-glibc-back-compat --enable-reduce-exports --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER --with-sanitizers=undefined CC=clang CXX=clang++" # x86_64 Linux, No wallet - stage: test env: >- HOST=x86_64-unknown-linux-gnu PACKAGES="python3" DEP_OPTS="NO_WALLET=1" - RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" # Cross-Mac @@ -139,28 +131,7 @@ jobs: HOST=x86_64-apple-darwin14 PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git" OSX_SDK=10.11 + RUN_UNIT_TESTS=false + RUN_FUNCTIONAL_TESTS=false GOAL="all deploy" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports --enable-werror" - - stage: lint - env: - sudo: false - cache: false - language: python - python: '3.6' - install: - - travis_retry pip install flake8 - before_script: - - git fetch --unshallow - script: - - if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then test/lint/commit-script-check.sh $TRAVIS_COMMIT_RANGE; fi - - test/lint/git-subtree-check.sh src/crypto/ctaes - - test/lint/git-subtree-check.sh src/secp256k1 - - test/lint/git-subtree-check.sh src/univalue - - test/lint/git-subtree-check.sh src/leveldb - - test/lint/check-doc.py - - test/lint/check-rpc-mappings.py . - - test/lint/lint-all.sh - - if [ "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_EVENT_TYPE" = "cron" ]; then - while read LINE; do travis_retry gpg --keyserver hkp://subset.pool.sks-keyservers.net --recv-keys $LINE; done < contrib/verify-commits/trusted-keys && - travis_wait 50 contrib/verify-commits/verify-commits.py; - fi diff --git a/.travis/README.md b/.travis/README.md new file mode 100644 index 0000000000..21d1b9cc03 --- /dev/null +++ b/.travis/README.md @@ -0,0 +1,8 @@ +## travis build scripts + +The `.travis` directory contains scripts for each build step in each build stage. +Currently the travis build defines two stages `lint` and `test`. Each stage has +it's own [lifecycle](https://docs.travis-ci.com/user/customizing-the-build/#the-build-lifecycle). +Every script in here is named and numbered according to which stage and lifecycle +step it belongs to. + diff --git a/.travis/lint_04_install.sh b/.travis/lint_04_install.sh new file mode 100755 index 0000000000..3a6ff73d79 --- /dev/null +++ b/.travis/lint_04_install.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +export LC_ALL=C + +travis_retry pip install codespell +travis_retry pip install flake8 diff --git a/.travis/lint_05_before_script.sh b/.travis/lint_05_before_script.sh new file mode 100755 index 0000000000..28bcbb47f7 --- /dev/null +++ b/.travis/lint_05_before_script.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +export LC_ALL=C + +git fetch --unshallow diff --git a/.travis/lint_06_script.sh b/.travis/lint_06_script.sh new file mode 100755 index 0000000000..6191d82571 --- /dev/null +++ b/.travis/lint_06_script.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +export LC_ALL=C + +if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then + test/lint/commit-script-check.sh $TRAVIS_COMMIT_RANGE +fi + +test/lint/git-subtree-check.sh src/crypto/ctaes +test/lint/git-subtree-check.sh src/secp256k1 +test/lint/git-subtree-check.sh src/univalue +test/lint/git-subtree-check.sh src/leveldb +test/lint/check-doc.py +test/lint/check-rpc-mappings.py . +test/lint/lint-all.sh + +if [ "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_EVENT_TYPE" = "cron" ]; then + while read -r LINE; do travis_retry gpg --keyserver hkp://subset.pool.sks-keyservers.net --recv-keys $LINE; done < contrib/verify-commits/trusted-keys && + travis_wait 50 contrib/verify-commits/verify-commits.py; +fi diff --git a/.travis/test_03_before_install.sh b/.travis/test_03_before_install.sh new file mode 100755 index 0000000000..d091a67ca9 --- /dev/null +++ b/.travis/test_03_before_install.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +export LC_ALL=C.UTF-8 + +PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") +export PATH + +BEGIN_FOLD () { + echo "" + CURRENT_FOLD_NAME=$1 + echo "travis_fold:start:${CURRENT_FOLD_NAME}" +} + +END_FOLD () { + RET=$? + echo "travis_fold:end:${CURRENT_FOLD_NAME}" + if [ $RET != 0 ]; then + echo "${CURRENT_FOLD_NAME} failed with status code ${RET}" + fi +} + diff --git a/.travis/test_04_install.sh b/.travis/test_04_install.sh new file mode 100755 index 0000000000..ef595287b7 --- /dev/null +++ b/.travis/test_04_install.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +export LC_ALL=C.UTF-8 + +travis_retry docker pull "$DOCKER_NAME_TAG" +env | grep -E '^(CCACHE_|WINEDEBUG|LC_ALL|BOOST_TEST_RANDOM|CONFIG_SHELL)' | tee /tmp/env +if [[ $HOST = *-mingw32 ]]; then + DOCKER_ADMIN="--cap-add SYS_ADMIN" +fi +DOCKER_ID=$(docker run $DOCKER_ADMIN -idt --mount type=bind,src=$TRAVIS_BUILD_DIR,dst=$TRAVIS_BUILD_DIR --mount type=bind,src=$CCACHE_DIR,dst=$CCACHE_DIR -w $TRAVIS_BUILD_DIR --env-file /tmp/env $DOCKER_NAME_TAG) + +DOCKER_EXEC () { + docker exec $DOCKER_ID bash -c "cd $PWD && $*" +} + +if [ -n "$DPKG_ADD_ARCH" ]; then + DOCKER_EXEC dpkg --add-architecture "$DPKG_ADD_ARCH" +fi + +travis_retry DOCKER_EXEC apt-get update +travis_retry DOCKER_EXEC apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES $DOCKER_PACKAGES + diff --git a/.travis/test_05_before_script.sh b/.travis/test_05_before_script.sh new file mode 100755 index 0000000000..70caee979c --- /dev/null +++ b/.travis/test_05_before_script.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +export LC_ALL=C.UTF-8 + +DOCKER_EXEC echo \> \$HOME/.bitcoin # Make sure default datadir does not exist and is never read by creating a dummy file + +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 +if [[ $HOST = *-mingw32 ]]; then + DOCKER_EXEC update-alternatives --set $HOST-g++ \$\(which $HOST-g++-posix\) +fi +if [ -z "$NO_DEPENDS" ]; then + DOCKER_EXEC CONFIG_SHELL= make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS +fi + diff --git a/.travis/test_06_script.sh b/.travis/test_06_script.sh new file mode 100755 index 0000000000..59cc110db5 --- /dev/null +++ b/.travis/test_06_script.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +export LC_ALL=C.UTF-8 + +TRAVIS_COMMIT_LOG=$(git log --format=fuller -1) +export TRAVIS_COMMIT_LOG + +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" +if [ -z "$NO_DEPENDS" ]; then + DOCKER_EXEC ccache --max-size=$CCACHE_SIZE +fi + +BEGIN_FOLD autogen +if [ -n "$CONFIG_SHELL" ]; then + DOCKER_EXEC "$CONFIG_SHELL" -c "./autogen.sh" +else + DOCKER_EXEC ./autogen.sh +fi +END_FOLD + +mkdir build +cd build || (echo "could not enter build directory"; exit 1) + +BEGIN_FOLD configure +DOCKER_EXEC ../configure --cache-file=config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) +END_FOLD + +BEGIN_FOLD distdir +DOCKER_EXEC make distdir VERSION=$HOST +END_FOLD + +cd "bitcoin-$HOST" || (echo "could not enter distdir bitcoin-$HOST"; exit 1) + +BEGIN_FOLD configure +DOCKER_EXEC ./configure --cache-file=../config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) +END_FOLD + +BEGIN_FOLD build +DOCKER_EXEC make $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && DOCKER_EXEC make $GOAL V=1 ; false ) +END_FOLD + +if [ "$RUN_UNIT_TESTS" = "true" ]; then + BEGIN_FOLD unit-tests + DOCKER_EXEC LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib make $MAKEJOBS check VERBOSE=1 + END_FOLD +fi + +if [ "$RUN_BENCH" = "true" ]; then + BEGIN_FOLD bench + DOCKER_EXEC LD_LIBRARY_PATH=$TRAVIS_BUILD_DIR/depends/$HOST/lib $OUTDIR/bin/bench_bitcoin -scaling=0.001 + END_FOLD +fi + +if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then + extended="--extended --exclude feature_pruning" +fi + +if [ "$RUN_FUNCTIONAL_TESTS" = "true" ]; then + BEGIN_FOLD functional-tests + DOCKER_EXEC test/functional/test_runner.py --combinedlogslen=4000 --coverage --quiet --failfast ${extended} + END_FOLD +fi diff --git a/Makefile.am b/Makefile.am index 10dda65b21..eec9f72215 100644 --- a/Makefile.am +++ b/Makefile.am @@ -168,7 +168,7 @@ $(BITCOIN_CLI_BIN): FORCE $(MAKE) -C src $(@F) if USE_LCOV -LCOV_FILTER_PATTERN=-p "/usr/include/" -p "src/leveldb/" -p "src/bench/" -p "src/univalue" -p "src/crypto/ctaes" -p "src/secp256k1" +LCOV_FILTER_PATTERN=-p "/usr/include/" -p "/usr/lib/" -p "src/leveldb/" -p "src/bench/" -p "src/univalue" -p "src/crypto/ctaes" -p "src/secp256k1" baseline.info: $(LCOV) -c -i -d $(abs_builddir)/src -o $@ diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index c6a5a91ba5..0000000000 --- a/appveyor.yml +++ /dev/null @@ -1,37 +0,0 @@ -version: '{branch}.{build}' -skip_tags: true -image: Visual Studio 2017 -configuration: Release -platform: x64 -environment: - APPVEYOR_SAVE_CACHE_ON_ERROR: true -cache: C:\tools\vcpkg\installed\ -before_build: -- ps: >- - $packages = @( - "boost-filesystem", - "boost-signals2", - "boost-interprocess", - "boost-test", - "libevent", - "openssl", - "zeromq", - "berkeleydb", - "secp256k1", - "leveldb" - ) - - for ($i=0; $i -lt $packages.length; $i++) { - $all_packages += $packages[$i] + ":" + $env:PLATFORM + "-windows-static " - } - - git -C C:\Tools\vcpkg pull # This is a temporary fix, can be removed after appveyor update its image to include Microsoft/vcpkg#4046 - - Invoke-Expression -Command "vcpkg install $all_packages" -build: - project: build_msvc\bitcoin.sln - parallel: true - verbosity: minimal -test_script: -- cmd: build_msvc\%PLATFORM%\Release\test_bitcoin.exe -deploy: off diff --git a/build_msvc/.gitignore b/build_msvc/.gitignore index fbcd1077b3..d5aa22c05e 100644 --- a/build_msvc/.gitignore +++ b/build_msvc/.gitignore @@ -8,3 +8,4 @@ packages/* */Release */x64 *.vcxproj.user +*.vcxproj diff --git a/build_msvc/README.md b/build_msvc/README.md index 6de464e92f..5fb08df8d7 100644 --- a/build_msvc/README.md +++ b/build_msvc/README.md @@ -47,4 +47,10 @@ The instructions below use vcpkg to install the dependencies. leveldb:x64-windows-static ``` +- Use Python to generate *.vcxproj from Makefile + +``` + PS >python msvc-autogen.py +``` + - Build in Visual Studio.
\ No newline at end of file diff --git a/build_msvc/libbitcoin_cli/libbitcoin_cli.vcxproj b/build_msvc/libbitcoin_cli/libbitcoin_cli.vcxproj.in index 623d24da10..b7265054fb 100644 --- a/build_msvc/libbitcoin_cli/libbitcoin_cli.vcxproj +++ b/build_msvc/libbitcoin_cli/libbitcoin_cli.vcxproj.in @@ -20,7 +20,7 @@ </ProjectConfiguration> </ItemGroup> <ItemGroup> - <ClCompile Include="..\..\src\rpc\client.cpp" /> +@SOURCE_FILES@ </ItemGroup> <ItemGroup> <None Include="packages.config" /> diff --git a/build_msvc/libbitcoin_common/libbitcoin_common.vcxproj b/build_msvc/libbitcoin_common/libbitcoin_common.vcxproj.in index e6d95ed5f3..42145c15ad 100644 --- a/build_msvc/libbitcoin_common/libbitcoin_common.vcxproj +++ b/build_msvc/libbitcoin_common/libbitcoin_common.vcxproj.in @@ -165,43 +165,7 @@ </Link> </ItemDefinitionGroup> <ItemGroup> - <ClInclude Include="..\..\src\base58.h" /> - <ClInclude Include="..\..\src\bech32.h" /> - <ClInclude Include="..\..\src\chainparams.h" /> - <ClInclude Include="..\..\src\coins.h" /> - <ClInclude Include="..\..\src\compressor.h" /> - <ClInclude Include="..\..\src\key.h" /> - <ClInclude Include="..\..\src\keystore.h" /> - <ClInclude Include="..\..\src\netaddress.h" /> - <ClInclude Include="..\..\src\netbase.h" /> - <ClInclude Include="..\..\src\policy\feerate.h" /> - <ClInclude Include="..\..\src\protocol.h" /> - <ClInclude Include="..\..\src\scheduler.h" /> - <ClInclude Include="..\..\src\script\sign.h" /> - <ClInclude Include="..\..\src\script\standard.h" /> - <ClInclude Include="..\..\src\warnings.h" /> - </ItemGroup> - <ItemGroup> - <ClCompile Include="..\..\src\base58.cpp" /> - <ClCompile Include="..\..\src\bech32.cpp" /> - <ClCompile Include="..\..\src\chainparams.cpp" /> - <ClCompile Include="..\..\src\coins.cpp" /> - <ClCompile Include="..\..\src\compressor.cpp" /> - <ClCompile Include="..\..\src\core_read.cpp" /> - <ClCompile Include="..\..\src\core_write.cpp" /> - <ClCompile Include="..\..\src\key.cpp" /> - <ClCompile Include="..\..\src\key_io.cpp" /> - <ClCompile Include="..\..\src\keystore.cpp" /> - <ClCompile Include="..\..\src\netaddress.cpp" /> - <ClCompile Include="..\..\src\netbase.cpp" /> - <ClCompile Include="..\..\src\policy\feerate.cpp" /> - <ClCompile Include="..\..\src\protocol.cpp" /> - <ClCompile Include="..\..\src\scheduler.cpp" /> - <ClCompile Include="..\..\src\script\descriptor.cpp" /> - <ClCompile Include="..\..\src\script\sign.cpp" /> - <ClCompile Include="..\..\src\script\ismine.cpp" /> - <ClCompile Include="..\..\src\script\standard.cpp" /> - <ClCompile Include="..\..\src\warnings.cpp" /> +@SOURCE_FILES@ </ItemGroup> <ItemGroup> <None Include="packages.config" /> diff --git a/build_msvc/libbitcoin_crypto/libbitcoin_crypto.vcxproj b/build_msvc/libbitcoin_crypto/libbitcoin_crypto.vcxproj.in index 50519d045f..a05125723a 100644 --- a/build_msvc/libbitcoin_crypto/libbitcoin_crypto.vcxproj +++ b/build_msvc/libbitcoin_crypto/libbitcoin_crypto.vcxproj.in @@ -151,25 +151,7 @@ </Link> </ItemDefinitionGroup> <ItemGroup> - <ClInclude Include="..\..\src\crypto\aes.h" /> - <ClInclude Include="..\..\src\crypto\chacha20.h" /> - <ClInclude Include="..\..\src\crypto\common.h" /> - <ClInclude Include="..\..\src\crypto\hmac_sha256.h" /> - <ClInclude Include="..\..\src\crypto\hmac_sha512.h" /> - <ClInclude Include="..\..\src\crypto\ripemd160.h" /> - <ClInclude Include="..\..\src\crypto\sha1.h" /> - <ClInclude Include="..\..\src\crypto\sha256.h" /> - <ClInclude Include="..\..\src\crypto\sha512.h" /> - </ItemGroup> - <ItemGroup> - <ClCompile Include="..\..\src\crypto\aes.cpp" /> - <ClCompile Include="..\..\src\crypto\chacha20.cpp" /> - <ClCompile Include="..\..\src\crypto\hmac_sha256.cpp" /> - <ClCompile Include="..\..\src\crypto\hmac_sha512.cpp" /> - <ClCompile Include="..\..\src\crypto\ripemd160.cpp" /> - <ClCompile Include="..\..\src\crypto\sha1.cpp" /> - <ClCompile Include="..\..\src\crypto\sha256.cpp" /> - <ClCompile Include="..\..\src\crypto\sha512.cpp" /> +@SOURCE_FILES@ </ItemGroup> <Import Label="configTarget" Project="..\common.vcxproj" /> </Project> diff --git a/build_msvc/libbitcoin_server/libbitcoin_server.vcxproj b/build_msvc/libbitcoin_server/libbitcoin_server.vcxproj.in index 9d4ac1a7f7..0a165d0b75 100644 --- a/build_msvc/libbitcoin_server/libbitcoin_server.vcxproj +++ b/build_msvc/libbitcoin_server/libbitcoin_server.vcxproj.in @@ -154,53 +154,13 @@ </Link> </ItemDefinitionGroup> <ItemGroup> - <ClCompile Include="..\..\src\addrdb.cpp" /> - <ClCompile Include="..\..\src\addrman.cpp" /> - <ClCompile Include="..\..\src\blockencodings.cpp" /> - <ClCompile Include="..\..\src\bloom.cpp" /> - <ClCompile Include="..\..\src\chain.cpp" /> - <ClCompile Include="..\..\src\checkpoints.cpp" /> - <ClCompile Include="..\..\src\consensus\tx_verify.cpp" /> - <ClCompile Include="..\..\src\dbwrapper.cpp" /> - <ClCompile Include="..\..\src\httprpc.cpp" /> - <ClCompile Include="..\..\src\httpserver.cpp" /> - <ClCompile Include="..\..\src\index\base.cpp" /> - <ClCompile Include="..\..\src\init.cpp" /> - <ClCompile Include="..\..\src\merkleblock.cpp" /> - <ClCompile Include="..\..\src\miner.cpp" /> - <ClCompile Include="..\..\src\net.cpp" /> - <ClCompile Include="..\..\src\net_processing.cpp" /> - <ClCompile Include="..\..\src\noui.cpp" /> - <ClCompile Include="..\..\src\outputtype.cpp" /> - <ClCompile Include="..\..\src\policy\fees.cpp" /> - <ClCompile Include="..\..\src\policy\policy.cpp" /> - <ClCompile Include="..\..\src\policy\rbf.cpp" /> - <ClCompile Include="..\..\src\pow.cpp" /> - <ClCompile Include="..\..\src\rest.cpp" /> - <ClCompile Include="..\..\src\shutdown.cpp" /> - <ClCompile Include="..\..\src\rpc\blockchain.cpp" /> - <ClCompile Include="..\..\src\rpc\mining.cpp" /> - <ClCompile Include="..\..\src\rpc\misc.cpp" /> <ClCompile Include="..\..\src\rpc\net.cpp"> <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)\netrpc.obj</ObjectFileName> <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)\netrpc.obj</ObjectFileName> <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(IntDir)\netrpc.obj</ObjectFileName> <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(IntDir)\netrpc.obj</ObjectFileName> </ClCompile> - <ClCompile Include="..\..\src\rpc\rawtransaction.cpp" /> - <ClCompile Include="..\..\src\rpc\server.cpp" /> - <ClCompile Include="..\..\src\rpc\util.cpp" /> - <ClCompile Include="..\..\src\script\ismine.cpp" /> - <ClCompile Include="..\..\src\script\sigcache.cpp" /> - <ClCompile Include="..\..\src\timedata.cpp" /> - <ClCompile Include="..\..\src\torcontrol.cpp" /> - <ClCompile Include="..\..\src\txdb.cpp" /> - <ClCompile Include="..\..\src\txmempool.cpp" /> - <ClCompile Include="..\..\src\ui_interface.cpp" /> - <ClCompile Include="..\..\src\validation.cpp" /> - <ClCompile Include="..\..\src\validationinterface.cpp" /> - <ClCompile Include="..\..\src\versionbits.cpp" /> - <ClCompile Include="..\..\src\index\txindex.cpp" /> +@SOURCE_FILES@ </ItemGroup> <ItemGroup> <None Include="packages.config" /> diff --git a/build_msvc/libbitcoin_util/libbitcoin_util.vcxproj b/build_msvc/libbitcoin_util/libbitcoin_util.vcxproj.in index 9e960682e5..dc17c98e98 100644 --- a/build_msvc/libbitcoin_util/libbitcoin_util.vcxproj +++ b/build_msvc/libbitcoin_util/libbitcoin_util.vcxproj.in @@ -174,23 +174,7 @@ </ProjectReference> </ItemDefinitionGroup> <ItemGroup> - <ClCompile Include="..\..\src\chainparamsbase.cpp" /> - <ClCompile Include="..\..\src\clientversion.cpp" /> - <ClCompile Include="..\..\src\compat\glibcxx_sanity.cpp" /> - <ClCompile Include="..\..\src\compat\glibc_sanity.cpp" /> - <ClCompile Include="..\..\src\compat\strnlen.cpp" /> - <ClCompile Include="..\..\src\fs.cpp" /> - <ClCompile Include="..\..\src\logging.cpp" /> - <ClCompile Include="..\..\src\random.cpp" /> - <ClCompile Include="..\..\src\rpc\protocol.cpp" /> - <ClCompile Include="..\..\src\support\cleanse.cpp" /> - <ClCompile Include="..\..\src\support\lockedpool.cpp" /> - <ClCompile Include="..\..\src\sync.cpp" /> - <ClCompile Include="..\..\src\threadinterrupt.cpp" /> - <ClCompile Include="..\..\src\util.cpp" /> - <ClCompile Include="..\..\src\utilmoneystr.cpp" /> - <ClCompile Include="..\..\src\utilstrencodings.cpp" /> - <ClCompile Include="..\..\src\utiltime.cpp" /> +@SOURCE_FILES@ </ItemGroup> <Import Label="configTarget" Project="..\common.vcxproj" /> </Project> diff --git a/build_msvc/libbitcoin_wallet/libbitcoin_wallet.vcxproj b/build_msvc/libbitcoin_wallet/libbitcoin_wallet.vcxproj.in index 64f3e16289..1bb7be6f7f 100644 --- a/build_msvc/libbitcoin_wallet/libbitcoin_wallet.vcxproj +++ b/build_msvc/libbitcoin_wallet/libbitcoin_wallet.vcxproj.in @@ -20,18 +20,7 @@ </ProjectConfiguration> </ItemGroup> <ItemGroup> - <ClCompile Include="..\..\src\wallet\coinselection.cpp" /> - <ClCompile Include="..\..\src\wallet\coincontrol.cpp" /> - <ClCompile Include="..\..\src\wallet\crypter.cpp" /> - <ClCompile Include="..\..\src\wallet\db.cpp" /> - <ClCompile Include="..\..\src\wallet\feebumper.cpp" /> - <ClCompile Include="..\..\src\wallet\fees.cpp" /> - <ClCompile Include="..\..\src\wallet\init.cpp" /> - <ClCompile Include="..\..\src\wallet\rpcdump.cpp" /> - <ClCompile Include="..\..\src\wallet\rpcwallet.cpp" /> - <ClCompile Include="..\..\src\wallet\wallet.cpp" /> - <ClCompile Include="..\..\src\wallet\walletdb.cpp" /> - <ClCompile Include="..\..\src\wallet\walletutil.cpp" /> +@SOURCE_FILES@ </ItemGroup> <ItemGroup> <None Include="packages.config" /> diff --git a/build_msvc/libbitcoin_zmq/libbitcoin_zmq.vcxproj b/build_msvc/libbitcoin_zmq/libbitcoin_zmq.vcxproj.in index d7fbdd94b7..e396c1ad0c 100644 --- a/build_msvc/libbitcoin_zmq/libbitcoin_zmq.vcxproj +++ b/build_msvc/libbitcoin_zmq/libbitcoin_zmq.vcxproj.in @@ -20,10 +20,7 @@ </ProjectConfiguration> </ItemGroup> <ItemGroup> - <ClCompile Include="..\..\src\zmq\zmqabstractnotifier.cpp" /> - <ClCompile Include="..\..\src\zmq\zmqnotificationinterface.cpp" /> - <ClCompile Include="..\..\src\zmq\zmqpublishnotifier.cpp" /> - <ClCompile Include="..\..\src\zmq\zmqrpc.cpp" /> +@SOURCE_FILES@ </ItemGroup> <ItemGroup> <None Include="packages.config" /> diff --git a/build_msvc/libbitcoinconsensus/libbitcoinconsensus.vcxproj b/build_msvc/libbitcoinconsensus/libbitcoinconsensus.vcxproj index e6f4885e5e..0be7e7e430 100644 --- a/build_msvc/libbitcoinconsensus/libbitcoinconsensus.vcxproj +++ b/build_msvc/libbitcoinconsensus/libbitcoinconsensus.vcxproj @@ -127,7 +127,7 @@ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <SDLCheck>false</SDLCheck> <AdditionalIncludeDirectories>..\..\src;</AdditionalIncludeDirectories> - <ExceptionHandling>false</ExceptionHandling> + <ExceptionHandling>Sync</ExceptionHandling> <SuppressStartupBanner>false</SuppressStartupBanner> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> </ClCompile> @@ -144,7 +144,7 @@ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <SDLCheck>false</SDLCheck> <AdditionalIncludeDirectories>..\..\src;</AdditionalIncludeDirectories> - <ExceptionHandling>false</ExceptionHandling> + <ExceptionHandling>Sync</ExceptionHandling> <SuppressStartupBanner>false</SuppressStartupBanner> <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> </ClCompile> @@ -163,7 +163,7 @@ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <SDLCheck>false</SDLCheck> <AdditionalIncludeDirectories>..\..\src;</AdditionalIncludeDirectories> - <ExceptionHandling>false</ExceptionHandling> + <ExceptionHandling>Sync</ExceptionHandling> <SuppressStartupBanner>false</SuppressStartupBanner> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> @@ -184,7 +184,7 @@ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <SDLCheck>false</SDLCheck> <AdditionalIncludeDirectories>..\..\src;</AdditionalIncludeDirectories> - <ExceptionHandling>false</ExceptionHandling> + <ExceptionHandling>Sync</ExceptionHandling> <SuppressStartupBanner>false</SuppressStartupBanner> <RuntimeLibrary>MultiThreaded</RuntimeLibrary> </ClCompile> diff --git a/build_msvc/msvc-autogen.py b/build_msvc/msvc-autogen.py new file mode 100644 index 0000000000..8888487e75 --- /dev/null +++ b/build_msvc/msvc-autogen.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +import os +import re + +SOURCE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'src')) + +libs = [ + 'libbitcoin_cli', + 'libbitcoin_common', + 'libbitcoin_crypto', + 'libbitcoin_server', + 'libbitcoin_util', + 'libbitcoin_wallet', + 'libbitcoin_zmq', +] + +ignore_list = [ + 'rpc/net.cpp', + 'interfaces/handler.cpp', + 'interfaces/node.cpp', + 'interfaces/wallet.cpp', +] + +lib_sources = {} + + +def parse_makefile(makefile): + with open(makefile, 'r', encoding='utf-8') as file: + current_lib = '' + for line in file.read().splitlines(): + if current_lib: + source = line.split()[0] + if source.endswith('.cpp') and not source.startswith('$') and source not in ignore_list: + lib_sources[current_lib].append(source.replace('/', '\\')) + if not line.endswith('\\'): + current_lib = '' + continue + for lib in libs: + _lib = lib.replace('-', '_') + if re.search(_lib + '.*_SOURCES \\= \\\\', line): + current_lib = lib + lib_sources[current_lib] = [] + break + + +def main(): + for makefile_name in os.listdir(SOURCE_DIR): + if 'Makefile' in makefile_name: + parse_makefile(os.path.join(SOURCE_DIR, makefile_name)) + for key, value in lib_sources.items(): + vcxproj_filename = os.path.abspath(os.path.join(os.path.dirname(__file__), key, key + '.vcxproj')) + content = '' + for source_filename in value: + content += ' <ClCompile Include="..\\..\\src\\' + source_filename + '" />\n' + with open(vcxproj_filename + '.in', 'r', encoding='utf-8') as vcxproj_in_file: + with open(vcxproj_filename, 'w', encoding='utf-8') as vcxproj_file: + vcxproj_file.write(vcxproj_in_file.read().replace( + '@SOURCE_FILES@\n', content)) + + +if __name__ == '__main__': + main() diff --git a/configure.ac b/configure.ac index 4275796b50..72bd785e2e 100644 --- a/configure.ac +++ b/configure.ac @@ -130,6 +130,12 @@ AC_ARG_ENABLE(gui-tests, [use_gui_tests=$enableval], [use_gui_tests=$use_tests]) +AC_ARG_WITH([rapidcheck], + [AS_HELP_STRING([--with-rapidcheck], + [enable RapidCheck property based tests (default is yes if librapidcheck is found)])], + [use_rapidcheck=$withval], + [use_rapidcheck=auto]) + AC_ARG_ENABLE(bench, AS_HELP_STRING([--disable-bench],[do not compile benchmarks (default is to compile)]), [use_bench=$enableval], @@ -400,6 +406,18 @@ AC_ARG_WITH([utils], [build_bitcoin_utils=$withval], [build_bitcoin_utils=yes]) +AC_ARG_ENABLE([util-cli], + [AS_HELP_STRING([--enable-util-cli], + [build bitcoin-cli])], + [build_bitcoin_cli=$enableval], + [build_bitcoin_cli=$build_bitcoin_utils]) + +AC_ARG_ENABLE([util-tx], + [AS_HELP_STRING([--enable-util-tx], + [build bitcoin-tx])], + [build_bitcoin_tx=$enableval], + [build_bitcoin_tx=$build_bitcoin_utils]) + AC_ARG_WITH([libs], [AS_HELP_STRING([--with-libs], [build libraries (default=yes)])], @@ -727,6 +745,10 @@ fi AC_CHECK_HEADERS([endian.h sys/endian.h byteswap.h stdio.h stdlib.h unistd.h strings.h sys/types.h sys/stat.h sys/select.h sys/prctl.h]) +AC_CHECK_DECLS([getifaddrs, freeifaddrs],,, + [#include <sys/types.h> + #include <ifaddrs.h>] +) AC_CHECK_DECLS([strnlen]) # Check for daemon(3), unrelated to --with-daemon (although used by it) @@ -876,7 +898,7 @@ BITCOIN_QT_INIT dnl sets $bitcoin_enable_qt, $bitcoin_enable_qt_test, $bitcoin_enable_qt_dbus BITCOIN_QT_CONFIGURE([$use_pkgconfig]) -if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononono; then +if test x$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnononononono; then use_boost=no else use_boost=yes @@ -1064,7 +1086,7 @@ if test x$use_pkgconfig = xyes; then if test x$use_qr != xno; then BITCOIN_QT_CHECK([PKG_CHECK_MODULES([QR], [libqrencode], [have_qrencode=yes], [have_qrencode=no])]) fi - if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests != xnononono; then + if test x$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests != xnonononono; then PKG_CHECK_MODULES([EVENT], [libevent],, [AC_MSG_ERROR(libevent not found.)]) if test x$TARGET_OS != xwindows; then PKG_CHECK_MODULES([EVENT_PTHREADS], [libevent_pthreads],, [AC_MSG_ERROR(libevent_pthreads not found.)]) @@ -1089,7 +1111,7 @@ else AC_CHECK_HEADER([openssl/ssl.h],, AC_MSG_ERROR(libssl headers missing),) AC_CHECK_LIB([ssl], [main],SSL_LIBS=-lssl, AC_MSG_ERROR(libssl missing)) - if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests != xnononono; then + if test x$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests != xnonononono; then AC_CHECK_HEADER([event2/event.h],, AC_MSG_ERROR(libevent headers missing),) AC_CHECK_LIB([event],[main],EVENT_LIBS=-levent,AC_MSG_ERROR(libevent missing)) if test x$TARGET_OS != xwindows; then @@ -1134,11 +1156,27 @@ AC_CHECK_DECLS([EVP_MD_CTX_new],,,[AC_INCLUDES_DEFAULT ]) CXXFLAGS="${save_CXXFLAGS}" +dnl RapidCheck Property Based Testing + +enable_property_tests=no +if test "x$use_rapidcheck" = xauto; then + AC_CHECK_HEADERS([rapidcheck.h], [enable_property_tests=yes]) +elif test "x$use_rapidcheck" != xno; then + enable_property_tests=yes +fi + +RAPIDCHECK_LIBS= +if test "x$enable_property_tests" = xyes; then + RAPIDCHECK_LIBS=-lrapidcheck +fi +AC_SUBST(RAPIDCHECK_LIBS) +AM_CONDITIONAL([ENABLE_PROPERTY_TESTS], [test x$enable_property_tests = xyes]) + dnl univalue check need_bundled_univalue=yes -if test x$build_bitcoin_utils$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnonononono; then +if test x$build_bitcoin_cli$build_bitcoin_tx$build_bitcoind$bitcoin_enable_qt$use_tests$use_bench = xnononononono; then need_bundled_univalue=no else @@ -1188,9 +1226,13 @@ AC_MSG_CHECKING([whether to build bitcoind]) AM_CONDITIONAL([BUILD_BITCOIND], [test x$build_bitcoind = xyes]) AC_MSG_RESULT($build_bitcoind) -AC_MSG_CHECKING([whether to build utils (bitcoin-cli bitcoin-tx)]) -AM_CONDITIONAL([BUILD_BITCOIN_UTILS], [test x$build_bitcoin_utils = xyes]) -AC_MSG_RESULT($build_bitcoin_utils) +AC_MSG_CHECKING([whether to build bitcoin-cli]) +AM_CONDITIONAL([BUILD_BITCOIN_CLI], [test x$build_bitcoin_cli = xyes]) +AC_MSG_RESULT($build_bitcoin_cli) + +AC_MSG_CHECKING([whether to build bitcoin-tx]) +AM_CONDITIONAL([BUILD_BITCOIN_TX], [test x$build_bitcoin_tx = xyes]) +AC_MSG_RESULT($build_bitcoin_tx) AC_MSG_CHECKING([whether to build libraries]) AM_CONDITIONAL([BUILD_BITCOIN_LIBS], [test x$build_bitcoin_libs = xyes]) @@ -1316,7 +1358,7 @@ else AC_MSG_RESULT([no]) fi -if test x$build_bitcoin_utils$build_bitcoin_libs$build_bitcoind$bitcoin_enable_qt$use_bench$use_tests = xnononononono; then +if test x$build_bitcoin_cli$build_bitcoin_tx$build_bitcoin_libs$build_bitcoind$bitcoin_enable_qt$use_bench$use_tests = xnonononononono; then AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --with-gui --enable-bench or --enable-tests]) fi diff --git a/contrib/debian/copyright b/contrib/debian/copyright index 21cca7d9ac..e5b9cbaa40 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -96,7 +96,7 @@ Comment: Site: https://bitcointalk.org/?topic=1756.0 Files: src/qt/res/icons/proxy.png src/qt/res/src/proxy.svg Copyright: Cristian Mircea Messel -Licese: public-domain +License: public-domain License: Expat diff --git a/contrib/devtools/optimize-pngs.py b/contrib/devtools/optimize-pngs.py index 8b1f41f7e1..e9481dbbcf 100755 --- a/contrib/devtools/optimize-pngs.py +++ b/contrib/devtools/optimize-pngs.py @@ -27,7 +27,7 @@ def content_hash(filename): pngcrush = 'pngcrush' git = 'git' folders = ["src/qt/res/movies", "src/qt/res/icons", "share/pixmaps"] -basePath = subprocess.check_output([git, 'rev-parse', '--show-toplevel'], universal_newlines=True).rstrip('\n') +basePath = subprocess.check_output([git, 'rev-parse', '--show-toplevel'], universal_newlines=True, encoding='utf8').rstrip('\n') totalSaveBytes = 0 noHashChange = True @@ -50,7 +50,7 @@ for folder in folders: sys.exit(0) #verify - if "Not a PNG file" in subprocess.check_output([pngcrush, "-n", "-v", file_path], stderr=subprocess.STDOUT, universal_newlines=True): + if "Not a PNG file" in subprocess.check_output([pngcrush, "-n", "-v", file_path], stderr=subprocess.STDOUT, universal_newlines=True, encoding='utf8'): print("PNG file "+file+" is corrupted after crushing, check out pngcursh version") sys.exit(1) diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py index 6808e77da7..c6158c9422 100755 --- a/contrib/devtools/symbol-check.py +++ b/contrib/devtools/symbol-check.py @@ -36,17 +36,18 @@ import os # (glibc) GLIBC_2_11 # MAX_VERSIONS = { -'GCC': (4,4,0), -'CXXABI': (1,3,3), -'GLIBCXX': (3,4,13), -'GLIBC': (2,11) +'GCC': (4,4,0), +'CXXABI': (1,3,3), +'GLIBCXX': (3,4,13), +'GLIBC': (2,11), +'LIBATOMIC': (1,0) } # See here for a description of _IO_stdin_used: # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=634261#109 # Ignore symbols that are exported as part of every executable IGNORE_EXPORTS = { -'_edata', '_end', '_init', '__bss_start', '_fini', '_IO_stdin_used', 'stdin', 'stdout', 'stderr' +'_edata', '_end', '__end__', '_init', '__bss_start', '__bss_start__', '_bss_end__', '__bss_end__', '_fini', '_IO_stdin_used', 'stdin', 'stdout', 'stderr' } READELF_CMD = os.getenv('READELF', '/usr/bin/readelf') CPPFILT_CMD = os.getenv('CPPFILT', '/usr/bin/c++filt') @@ -59,8 +60,12 @@ ALLOWED_LIBRARIES = { 'libanl.so.1', # DNS resolve 'libm.so.6', # math library 'librt.so.1', # real-time (clock) +'libatomic.so.1', 'ld-linux-x86-64.so.2', # 64-bit dynamic linker 'ld-linux.so.2', # 32-bit dynamic linker +'ld-linux-aarch64.so.1', # 64-bit ARM dynamic linker +'ld-linux-armhf.so.3', # 32-bit ARM dynamic linker +'ld-linux-riscv64-lp64d.so.1', # 64-bit RISC-V dynamic linker # bitcoin-qt only 'libX11-xcb.so.1', # part of X11 'libX11.so.6', # part of X11 @@ -69,7 +74,13 @@ ALLOWED_LIBRARIES = { 'libfreetype.so.6', # font parsing 'libdl.so.2' # programming interface to dynamic linker } - +ARCH_MIN_GLIBC_VER = { +'80386': (2,1), +'X86-64': (2,2,5), +'ARM': (2,4), +'AArch64':(2,17), +'RISC-V': (2,27) +} class CPPFilt(object): ''' Demangle C++ symbol names. @@ -94,23 +105,25 @@ 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, universal_newlines=True) + p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', '-h', 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.splitlines(): line = line.split() + if 'Machine:' in line: + arch = line[-1] 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)) + syms.append((sym, version, arch)) return syms -def check_version(max_versions, version): +def check_version(max_versions, version, arch): if '_' in version: (lib, _, ver) = version.rpartition('_') else: @@ -119,7 +132,7 @@ def check_version(max_versions, version): ver = tuple([int(x) for x in ver.split('.')]) if not lib in max_versions: return False - return ver <= max_versions[lib] + return ver <= max_versions[lib] or lib == 'GLIBC' and ver <= ARCH_MIN_GLIBC_VER[arch] def read_libraries(filename): p = subprocess.Popen([READELF_CMD, '-d', '-W', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) @@ -142,16 +155,17 @@ if __name__ == '__main__': retval = 0 for filename in sys.argv[1:]: # Check imported symbols - for sym,version in read_symbols(filename, True): - if version and not check_version(MAX_VERSIONS, version): + for sym,version,arch in read_symbols(filename, True): + if version and not check_version(MAX_VERSIONS, version, arch): 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))) - retval = 1 + if arch != 'RISC-V': + for sym,version,arch in read_symbols(filename, False): + if sym in IGNORE_EXPORTS: + continue + 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: diff --git a/contrib/gitian-build.py b/contrib/gitian-build.py index 0e53c3dfd5..2e8c99247d 100755 --- a/contrib/gitian-build.py +++ b/contrib/gitian-build.py @@ -170,7 +170,7 @@ def main(): args.sign_prog = 'true' if args.detach_sign else 'gpg --detach-sign' - # Set enviroment variable USE_LXC or USE_DOCKER, let gitian-builder know that we use lxc or docker + # Set environment variable USE_LXC or USE_DOCKER, let gitian-builder know that we use lxc or docker if args.docker: os.environ['USE_DOCKER'] = '1' elif not args.kvm: @@ -209,7 +209,7 @@ def main(): subprocess.check_call(['git', 'fetch', args.url, 'refs/pull/'+args.version+'/merge']) os.chdir('../gitian-builder/inputs/bitcoin') 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).strip() + 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']) diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index c7933cff20..e97072c80a 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -173,18 +173,7 @@ script: | 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}" make ${MAKEOPTS} make ${MAKEOPTS} -C src check-security - - #TODO: This is a quick hack that disables symbol checking for arm. - # Instead, we should investigate why these are popping up. - # For aarch64, we'll need to bump up the min GLIBC version, as the abi - # support wasn't introduced until 2.17. - case $i in - aarch64-*) : ;; - arm-*) : ;; - riscv64-*) : ;; - *) make ${MAKEOPTS} -C src check-symbols ;; - esac - + make ${MAKEOPTS} -C src check-symbols make install DESTDIR=${INSTALLPATH} cd installed find . -name "lib*.la" -delete @@ -192,6 +181,7 @@ script: | rm -rf ${DISTNAME}/lib/pkgconfig find ${DISTNAME}/bin -type f -executable -exec ../contrib/devtools/split-debug.sh {} {} {}.dbg \; find ${DISTNAME}/lib -type f -exec ../contrib/devtools/split-debug.sh {} {} {}.dbg \; + cp ../doc/README.md ${DISTNAME}/ find ${DISTNAME} -not -name "*.dbg" | 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 find ${DISTNAME} -name "*.dbg" | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}-debug.tar.gz cd ../../ diff --git a/contrib/testgen/README.md b/contrib/testgen/README.md index 83624f443a..580ed541cf 100644 --- a/contrib/testgen/README.md +++ b/contrib/testgen/README.md @@ -4,5 +4,5 @@ Utilities to generate test vectors for the data-driven Bitcoin tests. 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
\ No newline at end of file + PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py valid 50 > ../../src/test/data/key_io_keys_valid.json + PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py invalid 50 > ../../src/test/data/key_io_keys_invalid.json diff --git a/contrib/testgen/gen_base58_test_vectors.py b/contrib/testgen/gen_base58_test_vectors.py deleted file mode 100755 index c76de5ce75..0000000000 --- a/contrib/testgen/gen_base58_test_vectors.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2012-2018 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -''' -Generate valid and invalid base58 address and private key test vectors. - -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 -''' -# 2012 Wladimir J. van der Laan -# Released under MIT License -import os -from itertools import islice -from base58 import b58encode_chk, b58decode_chk, b58chars -import random -from binascii import b2a_hex - -# key types -PUBKEY_ADDRESS = 0 -SCRIPT_ADDRESS = 5 -PUBKEY_ADDRESS_TEST = 111 -SCRIPT_ADDRESS_TEST = 196 -PRIVKEY = 128 -PRIVKEY_TEST = 239 - -metadata_keys = ['isPrivkey', 'isTestnet', 'addrType', 'isCompressed'] -# templates for valid sequences -templates = [ - # prefix, payload_size, suffix, metadata - # None = N/A - ((PUBKEY_ADDRESS,), 20, (), (False, False, 'pubkey', None)), - ((SCRIPT_ADDRESS,), 20, (), (False, False, 'script', None)), - ((PUBKEY_ADDRESS_TEST,), 20, (), (False, True, 'pubkey', None)), - ((SCRIPT_ADDRESS_TEST,), 20, (), (False, True, 'script', None)), - ((PRIVKEY,), 32, (), (True, False, None, False)), - ((PRIVKEY,), 32, (1,), (True, False, None, True)), - ((PRIVKEY_TEST,), 32, (), (True, True, None, False)), - ((PRIVKEY_TEST,), 32, (1,), (True, True, None, True)) -] - -def is_valid(v): - '''Check vector v for validity''' - result = b58decode_chk(v) - if result is None: - return False - for template in templates: - 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 - return False - -def gen_valid_vectors(): - '''Generate valid test vectors''' - while True: - for template in templates: - 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 = {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 = bytearray(template[0]) - - if randomize_payload_size: - payload = os.urandom(max(int(random.expovariate(0.5)), 50)) - else: - payload = os.urandom(template[1]) - - if corrupt_suffix: - suffix = os.urandom(len(template[2])) - else: - suffix = bytearray(template[2]) - - return b58encode_chk(prefix + payload + suffix) - -def randbool(p = 0.5): - '''Return True with P(p)''' - return random.random() < p - -def gen_invalid_vectors(): - '''Generate invalid test vectors''' - # start with some manual edge-cases - yield "", - yield "x", - while True: - # kinds of invalid vectors: - # invalid prefix - # invalid payload length - # invalid (randomized) suffix (add random data) - # corrupt checksum - for template in templates: - val = gen_invalid_vector(template, randbool(0.2), randbool(0.2), randbool(0.2)) - if random.randint(0,10)<1: # line corruption - if randbool(): # add random character to end - val += random.choice(b58chars) - else: # replace random character in the middle - n = random.randint(0, len(val)) - val = val[0:n] + random.choice(b58chars) + val[n+1:] - if not is_valid(val): - yield val, - -if __name__ == '__main__': - import sys - import json - iters = {'valid':gen_valid_vectors, 'invalid':gen_invalid_vectors} - try: - uiter = iters[sys.argv[1]] - except IndexError: - uiter = gen_valid_vectors - try: - count = int(sys.argv[2]) - except IndexError: - count = 0 - - data = list(islice(uiter(), count)) - json.dump(data, sys.stdout, sort_keys=True, indent=4) - sys.stdout.write('\n') - diff --git a/contrib/testgen/gen_key_io_test_vectors.py b/contrib/testgen/gen_key_io_test_vectors.py new file mode 100755 index 0000000000..a00acb1f41 --- /dev/null +++ b/contrib/testgen/gen_key_io_test_vectors.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python3 +# Copyright (c) 2012-2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +''' +Generate valid and invalid base58 address and private key test vectors. + +Usage: + PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py valid 50 > ../../src/test/data/key_io_valid.json + PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py invalid 50 > ../../src/test/data/key_io_invalid.json +''' +# 2012 Wladimir J. van der Laan +# Released under MIT License +import os +from itertools import islice +from base58 import b58encode_chk, b58decode_chk, b58chars +import random +from binascii import b2a_hex +from segwit_addr import bech32_encode, decode, convertbits, CHARSET + +# key types +PUBKEY_ADDRESS = 0 +SCRIPT_ADDRESS = 5 +PUBKEY_ADDRESS_TEST = 111 +SCRIPT_ADDRESS_TEST = 196 +PUBKEY_ADDRESS_REGTEST = 111 +SCRIPT_ADDRESS_REGTEST = 196 +PRIVKEY = 128 +PRIVKEY_TEST = 239 +PRIVKEY_REGTEST = 239 + +# script +OP_0 = 0x00 +OP_1 = 0x51 +OP_2 = 0x52 +OP_16 = 0x60 +OP_DUP = 0x76 +OP_EQUAL = 0x87 +OP_EQUALVERIFY = 0x88 +OP_HASH160 = 0xa9 +OP_CHECKSIG = 0xac +pubkey_prefix = (OP_DUP, OP_HASH160, 20) +pubkey_suffix = (OP_EQUALVERIFY, OP_CHECKSIG) +script_prefix = (OP_HASH160, 20) +script_suffix = (OP_EQUAL,) +p2wpkh_prefix = (OP_0, 20) +p2wsh_prefix = (OP_0, 32) + +metadata_keys = ['isPrivkey', 'chain', 'isCompressed', 'tryCaseFlip'] +# templates for valid sequences +templates = [ + # prefix, payload_size, suffix, metadata, output_prefix, output_suffix + # None = N/A + ((PUBKEY_ADDRESS,), 20, (), (False, 'main', None, None), pubkey_prefix, pubkey_suffix), + ((SCRIPT_ADDRESS,), 20, (), (False, 'main', None, None), script_prefix, script_suffix), + ((PUBKEY_ADDRESS_TEST,), 20, (), (False, 'test', None, None), pubkey_prefix, pubkey_suffix), + ((SCRIPT_ADDRESS_TEST,), 20, (), (False, 'test', None, None), script_prefix, script_suffix), + ((PUBKEY_ADDRESS_REGTEST,), 20, (), (False, 'regtest', None, None), pubkey_prefix, pubkey_suffix), + ((SCRIPT_ADDRESS_REGTEST,), 20, (), (False, 'regtest', None, None), script_prefix, script_suffix), + ((PRIVKEY,), 32, (), (True, 'main', False, None), (), ()), + ((PRIVKEY,), 32, (1,), (True, 'main', True, None), (), ()), + ((PRIVKEY_TEST,), 32, (), (True, 'test', False, None), (), ()), + ((PRIVKEY_TEST,), 32, (1,), (True, 'test', True, None), (), ()), + ((PRIVKEY_REGTEST,), 32, (), (True, 'regtest', False, None), (), ()), + ((PRIVKEY_REGTEST,), 32, (1,), (True, 'regtest', True, None), (), ()) +] +# templates for valid bech32 sequences +bech32_templates = [ + # hrp, version, witprog_size, metadata, output_prefix + ('bc', 0, 20, (False, 'main', None, True), p2wpkh_prefix), + ('bc', 0, 32, (False, 'main', None, True), p2wsh_prefix), + ('bc', 1, 2, (False, 'main', None, True), (OP_1, 2)), + ('tb', 0, 20, (False, 'test', None, True), p2wpkh_prefix), + ('tb', 0, 32, (False, 'test', None, True), p2wsh_prefix), + ('tb', 2, 16, (False, 'test', None, True), (OP_2, 16)), + ('bcrt', 0, 20, (False, 'regtest', None, True), p2wpkh_prefix), + ('bcrt', 0, 32, (False, 'regtest', None, True), p2wsh_prefix), + ('bcrt', 16, 40, (False, 'regtest', None, True), (OP_16, 40)) +] +# templates for invalid bech32 sequences +bech32_ng_templates = [ + # hrp, version, witprog_size, invalid_bech32, invalid_checksum, invalid_char + ('tc', 0, 20, False, False, False), + ('tb', 17, 32, False, False, False), + ('bcrt', 3, 1, False, False, False), + ('bc', 15, 41, False, False, False), + ('tb', 0, 16, False, False, False), + ('bcrt', 0, 32, True, False, False), + ('bc', 0, 16, True, False, False), + ('tb', 0, 32, False, True, False), + ('bcrt', 0, 20, False, False, True) +] + +def is_valid(v): + '''Check vector v for validity''' + if len(set(v) - set(b58chars)) > 0: + return is_valid_bech32(v) + result = b58decode_chk(v) + if result is None: + return is_valid_bech32(v) + for template in templates: + 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 + return is_valid_bech32(v) + +def is_valid_bech32(v): + '''Check vector v for bech32 validity''' + for hrp in ['bc', 'tb', 'bcrt']: + if decode(hrp, v) != (None, None): + return True + return False + +def gen_valid_base58_vector(template): + '''Generate valid base58 vector''' + prefix = bytearray(template[0]) + payload = bytearray(os.urandom(template[1])) + suffix = bytearray(template[2]) + dst_prefix = bytearray(template[4]) + dst_suffix = bytearray(template[5]) + rv = b58encode_chk(prefix + payload + suffix) + return rv, dst_prefix + payload + dst_suffix + +def gen_valid_bech32_vector(template): + '''Generate valid bech32 vector''' + hrp = template[0] + witver = template[1] + witprog = bytearray(os.urandom(template[2])) + dst_prefix = bytearray(template[4]) + rv = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5)) + return rv, dst_prefix + witprog + +def gen_valid_vectors(): + '''Generate valid test vectors''' + glist = [gen_valid_base58_vector, gen_valid_bech32_vector] + tlist = [templates, bech32_templates] + while True: + for template, valid_vector_generator in [(t, g) for g, l in zip(glist, tlist) for t in l]: + rv, payload = valid_vector_generator(template) + assert is_valid(rv) + 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_base58_vector(template): + '''Generate possibly invalid vector''' + # kinds of invalid vectors: + # invalid prefix + # invalid payload length + # invalid (randomized) suffix (add random data) + # corrupt checksum + corrupt_prefix = randbool(0.2) + randomize_payload_size = randbool(0.2) + corrupt_suffix = randbool(0.2) + + if corrupt_prefix: + prefix = os.urandom(1) + else: + prefix = bytearray(template[0]) + + if randomize_payload_size: + payload = os.urandom(max(int(random.expovariate(0.5)), 50)) + else: + payload = os.urandom(template[1]) + + if corrupt_suffix: + suffix = os.urandom(len(template[2])) + else: + suffix = bytearray(template[2]) + + val = b58encode_chk(prefix + payload + suffix) + if random.randint(0,10)<1: # line corruption + if randbool(): # add random character to end + val += random.choice(b58chars) + else: # replace random character in the middle + n = random.randint(0, len(val)) + val = val[0:n] + random.choice(b58chars) + val[n+1:] + + return val + +def gen_invalid_bech32_vector(template): + '''Generate possibly invalid bech32 vector''' + no_data = randbool(0.1) + to_upper = randbool(0.1) + hrp = template[0] + witver = template[1] + witprog = bytearray(os.urandom(template[2])) + + if no_data: + rv = bech32_encode(hrp, []) + else: + data = [witver] + convertbits(witprog, 8, 5) + if template[3] and not no_data: + if template[2] % 5 in {2, 4}: + data[-1] |= 1 + else: + data.append(0) + rv = bech32_encode(hrp, data) + + if template[4]: + i = len(rv) - random.randrange(1, 7) + rv = rv[:i] + random.choice(CHARSET.replace(rv[i], '')) + rv[i + 1:] + if template[5]: + i = len(hrp) + 1 + random.randrange(0, len(rv) - len(hrp) - 4) + rv = rv[:i] + rv[i:i + 4].upper() + rv[i + 4:] + + if to_upper: + rv = rv.swapcase() + + return rv + +def randbool(p = 0.5): + '''Return True with P(p)''' + return random.random() < p + +def gen_invalid_vectors(): + '''Generate invalid test vectors''' + # start with some manual edge-cases + yield "", + yield "x", + glist = [gen_invalid_base58_vector, gen_invalid_bech32_vector] + tlist = [templates, bech32_ng_templates] + while True: + for template, invalid_vector_generator in [(t, g) for g, l in zip(glist, tlist) for t in l]: + val = invalid_vector_generator(template) + if not is_valid(val): + yield val, + +if __name__ == '__main__': + import sys + import json + iters = {'valid':gen_valid_vectors, 'invalid':gen_invalid_vectors} + try: + uiter = iters[sys.argv[1]] + except IndexError: + uiter = gen_valid_vectors + try: + count = int(sys.argv[2]) + except IndexError: + count = 0 + + data = list(islice(uiter(), count)) + json.dump(data, sys.stdout, sort_keys=True, indent=4) + sys.stdout.write('\n') + diff --git a/contrib/verify-commits/verify-commits.py b/contrib/verify-commits/verify-commits.py index a9e4977715..544f4dc48d 100755 --- a/contrib/verify-commits/verify-commits.py +++ b/contrib/verify-commits/verify-commits.py @@ -91,7 +91,7 @@ def main(): no_sha1 = True prev_commit = "" initial_commit = current_commit - branch = subprocess.check_output([GIT, 'show', '-s', '--format=%H', initial_commit], universal_newlines=True).splitlines()[0] + branch = subprocess.check_output([GIT, 'show', '-s', '--format=%H', initial_commit], universal_newlines=True, encoding='utf8').splitlines()[0] # Iterate through commits while True: @@ -112,7 +112,7 @@ def main(): if prev_commit != "": print("No parent of {} was signed with a trusted key!".format(prev_commit), file=sys.stderr) print("Parents are:", file=sys.stderr) - parents = subprocess.check_output([GIT, 'show', '-s', '--format=format:%P', prev_commit], universal_newlines=True).splitlines()[0].split(' ') + parents = subprocess.check_output([GIT, 'show', '-s', '--format=format:%P', prev_commit], universal_newlines=True, encoding='utf8').splitlines()[0].split(' ') for parent in parents: subprocess.call([GIT, 'show', '-s', parent], stdout=sys.stderr) else: @@ -122,25 +122,25 @@ def main(): # Check the Tree-SHA512 if (verify_tree or prev_commit == "") and current_commit not in incorrect_sha512_allowed: tree_hash = tree_sha512sum(current_commit) - if ("Tree-SHA512: {}".format(tree_hash)) not in subprocess.check_output([GIT, 'show', '-s', '--format=format:%B', current_commit], universal_newlines=True).splitlines(): + if ("Tree-SHA512: {}".format(tree_hash)) not in subprocess.check_output([GIT, 'show', '-s', '--format=format:%B', current_commit], universal_newlines=True, encoding='utf8').splitlines(): print("Tree-SHA512 did not match for commit " + current_commit, file=sys.stderr) sys.exit(1) # Merge commits should only have two parents - parents = subprocess.check_output([GIT, 'show', '-s', '--format=format:%P', current_commit], universal_newlines=True).splitlines()[0].split(' ') + parents = subprocess.check_output([GIT, 'show', '-s', '--format=format:%P', current_commit], universal_newlines=True, encoding='utf8').splitlines()[0].split(' ') if len(parents) > 2: print("Commit {} is an octopus merge".format(current_commit), file=sys.stderr) sys.exit(1) # Check that the merge commit is clean - commit_time = int(subprocess.check_output([GIT, 'show', '-s', '--format=format:%ct', current_commit], universal_newlines=True).splitlines()[0]) + commit_time = int(subprocess.check_output([GIT, 'show', '-s', '--format=format:%ct', current_commit], universal_newlines=True, encoding='utf8').splitlines()[0]) check_merge = commit_time > time.time() - args.clean_merge * 24 * 60 * 60 # Only check commits in clean_merge days allow_unclean = current_commit in unclean_merge_allowed if len(parents) == 2 and check_merge and not allow_unclean: - current_tree = subprocess.check_output([GIT, 'show', '--format=%T', current_commit], universal_newlines=True).splitlines()[0] + current_tree = subprocess.check_output([GIT, 'show', '--format=%T', current_commit], universal_newlines=True, encoding='utf8').splitlines()[0] subprocess.call([GIT, 'checkout', '--force', '--quiet', parents[0]]) subprocess.call([GIT, 'merge', '--no-ff', '--quiet', parents[1]], stdout=subprocess.DEVNULL) - recreated_tree = subprocess.check_output([GIT, 'show', '--format=format:%T', 'HEAD'], universal_newlines=True).splitlines()[0] + recreated_tree = subprocess.check_output([GIT, 'show', '--format=format:%T', 'HEAD'], universal_newlines=True, encoding='utf8').splitlines()[0] if current_tree != recreated_tree: print("Merge commit {} is not clean".format(current_commit), file=sys.stderr) subprocess.call([GIT, 'diff', current_commit]) diff --git a/depends/Makefile b/depends/Makefile index 3686aaf1f8..50cc77ddeb 100644 --- a/depends/Makefile +++ b/depends/Makefile @@ -5,6 +5,7 @@ WORK_PATH = $(BASEDIR)/work BASE_CACHE ?= $(BASEDIR)/built SDK_PATH ?= $(BASEDIR)/SDKs NO_QT ?= +RAPIDCHECK ?= NO_WALLET ?= NO_UPNP ?= FALLBACK_DOWNLOAD_PATH ?= https://bitcoincore.org/depends-sources @@ -14,7 +15,7 @@ HOST ?= $(BUILD) PATCHES_PATH = $(BASEDIR)/patches BASEDIR = $(CURDIR) HASH_LENGTH:=11 -DOWNLOAD_CONNECT_TIMEOUT:=10 +DOWNLOAD_CONNECT_TIMEOUT:=30 DOWNLOAD_RETRIES:=3 HOST_ID_SALT ?= salt BUILD_ID_SALT ?= salt @@ -93,6 +94,8 @@ qt_packages_$(NO_QT) = $(qt_packages) $(qt_$(host_os)_packages) $(qt_$(host_arch wallet_packages_$(NO_WALLET) = $(wallet_packages) upnp_packages_$(NO_UPNP) = $(upnp_packages) +rapidcheck_packages_$(RAPIDCHECK) = $(rapidcheck_packages) + packages += $($(host_arch)_$(host_os)_packages) $($(host_os)_packages) $(qt_packages_) $(wallet_packages_) $(upnp_packages_) native_packages += $($(host_arch)_$(host_os)_native_packages) $($(host_os)_native_packages) @@ -100,6 +103,10 @@ ifneq ($(qt_packages_),) native_packages += $(qt_native_packages) endif +ifeq ($(rapidcheck_packages_),) +packages += $(rapidcheck_packages) +endif + all_packages = $(packages) $(native_packages) meta_depends = Makefile funcs.mk builders/default.mk hosts/default.mk hosts/$(host_os).mk builders/$(build_os).mk diff --git a/depends/README.md b/depends/README.md index fd343f7010..73bfc8b726 100644 --- a/depends/README.md +++ b/depends/README.md @@ -63,6 +63,7 @@ The following can be set when running make: make FOO=bar NO_WALLET: Don't download/build/cache libs needed to enable the wallet NO_UPNP: Don't download/build/cache packages needed for enabling upnp DEBUG: disable some optimizations and enable more runtime checking + RAPIDCHECK: build rapidcheck (experimental) HOST_ID_SALT: Optional salt to use when generating host package ids BUILD_ID_SALT: Optional salt to use when generating build package ids diff --git a/depends/config.site.in b/depends/config.site.in index 8444dc26f2..b7a5e795c8 100644 --- a/depends/config.site.in +++ b/depends/config.site.in @@ -61,9 +61,12 @@ fi CPPFLAGS="-I$depends_prefix/include/ $CPPFLAGS" LDFLAGS="-L$depends_prefix/lib $LDFLAGS" -CC="@CC@" -CXX="@CXX@" -OBJC="${CC}" +if test -n "@CC@" -a -z "${CC}"; then + CC="@CC@" +fi +if test -n "@CXX@" -a -z "${CXX}"; then + CXX="@CXX@" +fi PYTHONPATH=$depends_prefix/native/lib/python/dist-packages:$PYTHONPATH if test -n "@AR@"; then diff --git a/depends/packages/libICE.mk b/depends/packages/libICE.mk deleted file mode 100644 index fc60323b1c..0000000000 --- a/depends/packages/libICE.mk +++ /dev/null @@ -1,23 +0,0 @@ -package=libICE -$(package)_version=1.0.9 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=8f7032f2c1c64352b5423f6b48a8ebdc339cc63064af34d66a6c9aa79759e202 -$(package)_dependencies=xtrans xproto - -define $(package)_set_vars - $(package)_config_opts=--disable-static --disable-docs --disable-specs --without-xsltproc - $(package)_config_opts_linux=--with-pic -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/libSM.mk b/depends/packages/libSM.mk deleted file mode 100644 index 0f9307ca76..0000000000 --- a/depends/packages/libSM.mk +++ /dev/null @@ -1,23 +0,0 @@ -package=libSM -$(package)_version=1.2.2 -$(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=0baca8c9f5d934450a70896c4ad38d06475521255ca63b717a6510fdb6e287bd -$(package)_dependencies=xtrans xproto libICE - -define $(package)_set_vars - $(package)_config_opts=--without-libuuid --without-xsltproc --disable-docs --disable-static - $(package)_config_opts_linux=--with-pic -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/packages.mk b/depends/packages/packages.mk index 5fe6f98da2..38329d16d7 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -5,6 +5,8 @@ qt_packages = qrencode protobuf zlib qt_linux_packages:=qt expat dbus libxcb xcb_proto libXau xproto freetype fontconfig libX11 xextproto libXext xtrans +rapidcheck_packages = rapidcheck + qt_darwin_packages=qt qt_mingw32_packages=qt diff --git a/depends/packages/rapidcheck.mk b/depends/packages/rapidcheck.mk new file mode 100644 index 0000000000..19cf1cae2e --- /dev/null +++ b/depends/packages/rapidcheck.mk @@ -0,0 +1,18 @@ +package=rapidcheck +$(package)_version=10fc0cb +$(package)_download_path=https://github.com/MarcoFalke/rapidcheck/archive +$(package)_file_name=$(package)-$($(package)_version).tar.gz +$(package)_sha256_hash=9640926223c00af45bce4c7df8b756b5458a89b2ba74cfe3e404467f13ce26df + +define $(package)_config_cmds + cmake -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=true . +endef + +define $(package)_build_cmds + $(MAKE) && \ + mkdir -p $($(package)_staging_dir)$(host_prefix)/include && \ + cp -a include/* $($(package)_staging_dir)$(host_prefix)/include/ && \ + cp -a extras/boost_test/include/rapidcheck/* $($(package)_staging_dir)$(host_prefix)/include/rapidcheck/ && \ + mkdir -p $($(package)_staging_dir)$(host_prefix)/lib && \ + cp -a librapidcheck.a $($(package)_staging_dir)$(host_prefix)/lib/ +endef diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk index d5fd1f39ab..c9068e83a5 100644 --- a/depends/packages/zeromq.mk +++ b/depends/packages/zeromq.mk @@ -1,8 +1,8 @@ package=zeromq -$(package)_version=4.2.3 +$(package)_version=4.2.5 $(package)_download_path=https://github.com/zeromq/libzmq/releases/download/v$($(package)_version)/ $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=8f1e2b2aade4dbfde98d82366d61baef2f62e812530160d2e6d0a5bb24e40bc0 +$(package)_sha256_hash=cc9090ba35713d59bb2f7d7965f877036c49c5558ea0c290b0dcc6f2a17e489f $(package)_patches=0001-fix-build-with-older-mingw64.patch 0002-disable-pthread_set_name_np.patch define $(package)_set_vars diff --git a/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch b/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch index a6c508fb8a..b911ac5672 100644 --- a/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch +++ b/depends/patches/zeromq/0001-fix-build-with-older-mingw64.patch @@ -1,6 +1,6 @@ -From 1a159c128c69a42d90819375c06a39994f3fbfc1 Mon Sep 17 00:00:00 2001 -From: Cory Fields <cory-nospam-@coryfields.com> -Date: Tue, 28 Nov 2017 20:33:25 -0500 +From f6866b0f166ad168618aae64c7fbee8775d3eb23 Mon Sep 17 00:00:00 2001 +From: mruddy <6440430+mruddy@users.noreply.github.com> +Date: Sat, 30 Jun 2018 09:44:58 -0400 Subject: [PATCH] fix build with older mingw64 --- @@ -8,10 +8,10 @@ Subject: [PATCH] fix build with older mingw64 1 file changed, 7 insertions(+) diff --git a/src/windows.hpp b/src/windows.hpp -index 99e889d..e69038e 100644 +index 6c3839fd..2c32ec79 100644 --- a/src/windows.hpp +++ b/src/windows.hpp -@@ -55,6 +55,13 @@ +@@ -58,6 +58,13 @@ #include <winsock2.h> #include <windows.h> #include <mswsock.h> @@ -23,8 +23,8 @@ index 99e889d..e69038e 100644 +#include <ws2ipdef.h> +#endif #include <iphlpapi.h> - + #if !defined __MINGW32__ --- -2.7.4 +-- +2.17.1 diff --git a/depends/patches/zeromq/0002-disable-pthread_set_name_np.patch b/depends/patches/zeromq/0002-disable-pthread_set_name_np.patch index d220b54f3e..022e311977 100644 --- a/depends/patches/zeromq/0002-disable-pthread_set_name_np.patch +++ b/depends/patches/zeromq/0002-disable-pthread_set_name_np.patch @@ -1,6 +1,6 @@ -From 6e6b47d5ab381c3df3b30bb0b0a6cf210dfb1eba Mon Sep 17 00:00:00 2001 -From: Cory Fields <cory-nospam-@coryfields.com> -Date: Mon, 5 Mar 2018 14:22:05 -0500 +From c9bbdd6581d07acfe8971e4bcebe278a3676cf03 Mon Sep 17 00:00:00 2001 +From: mruddy <6440430+mruddy@users.noreply.github.com> +Date: Sat, 30 Jun 2018 09:57:18 -0400 Subject: [PATCH] disable pthread_set_name_np pthread_set_name_np adds a Glibc requirement on >= 2.12. @@ -9,21 +9,21 @@ pthread_set_name_np adds a Glibc requirement on >= 2.12. 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/thread.cpp b/src/thread.cpp -index 4fc59c3e..c3fdfd46 100644 +index a1086b0c..9943f354 100644 --- a/src/thread.cpp +++ b/src/thread.cpp -@@ -220,7 +220,7 @@ void zmq::thread_t::setThreadName(const char *name_) +@@ -307,7 +307,7 @@ void zmq::thread_t::setThreadName (const char *name_) */ if (!name_) return; - +#if 0 #if defined(ZMQ_HAVE_PTHREAD_SETNAME_1) - int rc = pthread_setname_np(name_); - if(rc) return; -@@ -233,6 +233,8 @@ void zmq::thread_t::setThreadName(const char *name_) + int rc = pthread_setname_np (name_); + if (rc) +@@ -323,6 +323,8 @@ void zmq::thread_t::setThreadName (const char *name_) #elif defined(ZMQ_HAVE_PTHREAD_SET_NAME) - pthread_set_name_np(descriptor, name_); + pthread_set_name_np (descriptor, name_); #endif +#endif + return; @@ -31,5 +31,5 @@ index 4fc59c3e..c3fdfd46 100644 #endif -- -2.11.1 +2.17.1 diff --git a/doc/build-osx.md b/doc/build-osx.md index 448918e8cb..1fa01d0d6e 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -64,6 +64,17 @@ Build Bitcoin Core make deploy +Disable-wallet mode +-------------------- +When the intention is to run only a P2P node without a wallet, Bitcoin Core may be compiled in +disable-wallet mode with: + + ./configure --disable-wallet + +In this case there is no dependency on Berkeley DB 4.8. + +Mining is also possible in disable-wallet mode using the `getblocktemplate` RPC call. + Running ------- diff --git a/doc/build-unix.md b/doc/build-unix.md index a01ff59fa6..3f8848abf2 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -47,7 +47,7 @@ Optional dependencies: 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) + libzmq3 | ZMQ notification | Optional, allows generating ZMQ notifications (requires ZMQ version >= 4.0.0) For the versions used, see [dependencies.md](dependencies.md) @@ -93,7 +93,7 @@ Optional (see --with-miniupnpc and --enable-upnp-default): sudo apt-get install libminiupnpc-dev -ZMQ dependencies (provides ZMQ API 4.x): +ZMQ dependencies (provides ZMQ API): sudo apt-get install libzmq3-dev @@ -234,8 +234,7 @@ disable-wallet mode with: In this case there is no dependency on Berkeley DB 4.8. -Mining is also possible in disable-wallet mode, but only using the `getblocktemplate` RPC -call not `getwork`. +Mining is also possible in disable-wallet mode using the `getblocktemplate` RPC call. Additional Configure Flags -------------------------- @@ -279,9 +278,9 @@ To build executables for ARM: cd depends make HOST=arm-linux-gnueabihf NO_QT=1 cd .. + ./autogen.sh ./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. - diff --git a/doc/dependencies.md b/doc/dependencies.md index 3fc7aecba8..d777aaf66f 100644 --- a/doc/dependencies.md +++ b/doc/dependencies.md @@ -26,5 +26,5 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct | Qt | [5.9.6](https://download.qt.io/official_releases/qt/) | 5.x | No | | | | XCB | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L87) (Linux only) | | xkbcommon | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L86) (Linux only) | -| ZeroMQ | [4.2.3](https://github.com/zeromq/libzmq/releases) | | No | | | +| ZeroMQ | [4.2.5](https://github.com/zeromq/libzmq/releases) | 4.0.0 | No | | | | zlib | [1.2.11](https://zlib.net/) | | | | No | diff --git a/doc/descriptors.md b/doc/descriptors.md new file mode 100644 index 0000000000..c23ac06e8f --- /dev/null +++ b/doc/descriptors.md @@ -0,0 +1,124 @@ +# Support for Output Descriptors in Bitcoin Core + +Since Bitcoin Core v0.17, there is support for Output Descriptors in the +`scantxoutset` RPC call. This is a simple language which can be used to +describe collections of output scripts. + +This document describes the language. For the specifics on usage for scanning +the UTXO set, see the `scantxoutset` RPC help. + +## Features + +Output descriptors currently support: +- Pay-to-pubkey scripts (P2PK), through the `pk` function. +- Pay-to-pubkey-hash scripts (P2PKH), through the `pkh` function. +- Pay-to-witness-pubkey-hash scripts (P2WPKH), through the `wpkh` function. +- Pay-to-script-hash scripts (P2SH), through the `sh` function. +- Pay-to-witness-script-hash scripts (P2WSH), through the `wsh` function. +- Multisig scripts, through the `multi` function. +- Any type of supported address through the `addr` function. +- Raw hex scripts through the `raw` function. +- Public keys (compressed and uncompressed) in hex notation, or BIP32 extended pubkeys with derivation paths. + +## Examples + +- `pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)` represents a P2PK output. +- `pkh(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)` represents a P2PKH output. +- `wpkh(02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9)` represents a P2WPKH output. +- `sh(wpkh(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))` represents a P2SH-P2WPKH output. +- `combo(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798)` represents a P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH output. +- `sh(wsh(pkh(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13)))` represents a (overly complicated) P2SH-P2WSH-P2PKH output. +- `multi(1,022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)` represents a bare *1-of-2* multisig. +- `sh(multi(2,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))` represents a P2SH *2-of-2* multisig. +- `wsh(multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a))` represents a P2WSH *2-of-3* multisig. +- `sh(wsh(multi(1,03f28773c2d975288bc7d1d205c3748651b075fbc6610e58cddeeddf8f19405aa8,03499fdf9e895e719cfd64e67f07d38e3226aa7b63678949e6e49b241a60e823e4,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e)))` represents a P2SH-P2WSH *1-of-3* multisig. +- `pk(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8)` refers to a single P2PK output, using the public key part from the specified xpub. +- `pkh(xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw/1'/2)` refers to a single P2PKH output, using child key *1'/2* of the specified xpub. +- `wsh(multi(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/1/0/*))` refers to a chain of *1-of-2* P2WSH multisig outputs, using public keys taken from two HD chains with corresponding derivation paths. + +## Reference + +Descriptors consist of several types of expressions. The top level expression is always a `SCRIPT`. + +`SCRIPT` expressions: +- `sh(SCRIPT)` (top level only): P2SH embed the argument. +- `wsh(SCRIPT)` (not inside another 'wsh'): P2WSH embed the argument. +- `pk(KEY)` (anywhere): P2PK output for the given public key. +- `pkh(KEY)` (anywhere): P2PKH output for the given public key (use `addr` if you only know the pubkey hash). +- `wpkh(KEY)` (not inside `wsh`): P2WPKH output for the given compressed pubkey. +- `combo(KEY)` (top level only): an alias for the collection of `pk(KEY)` and `pkh(KEY)`. If the key is compressed, it also includes `wpkh(KEY)` and `sh(wpkh(KEY))`. +- `multi(k,KEY_1,KEY_2,...,KEY_n)` (anywhere): k-of-n multisig script. +- `addr(ADDR)` (top level only): the script which ADDR expands to. +- `raw(HEX)` (top level only): the script whose hex encoding is HEX. + +`KEY` expressions: +- Hex encoded public keys (66 characters starting with `02` or `03`, or 130 characters starting with `04`). + - Inside `wpkh` and `wsh`, only compressed public keys are permitted. +- [WIF](https://en.bitcoin.it/wiki/Wallet_import_format) encoded private keys may be specified instead of the corresponding public key, with the same meaning. +-`xpub` encoded extended public key or `xprv` encoded private key (as defined in [BIP 32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)). + - Followed by zero or more `/NUM` unhardened and `/NUM'` hardened BIP32 derivation steps. + - Optionally followed by a single `/*` or `/*'` final step to denote all (direct) unhardened or hardened children. + - The usage of hardened derivation steps requires providing the private key. + - Instead of a `'`, the suffix `h` can be used to denote hardened derivation. + +`ADDR` expressions are any type of supported address: +- P2PKH addresses (base58, of the form `1...`). Note that P2PKH addresses in descriptors cannot be used for P2PK outputs (use the `pk` function instead). +- P2SH addresses (base58, of the form `3...`, defined in [BIP 13](https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki)). +- Segwit addresses (bech32, of the form `bc1...`, defined in [BIP 173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki)). + +## Explanation + +### Single-key scripts + +Many single-key constructions are used in practice, generally including +P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH. Many more combinations are +imaginable, though they may not be optimal: P2SH-P2PK, P2SH-P2PKH, +P2WSH-P2PK, P2WSH-P2PKH, P2SH-P2WSH-P2PK, P2SH-P2WSH-P2PKH. + +To describe these, we model these as functions. The functions `pk` +(P2PK), `pkh` (P2PKH) and `wpkh` (P2WPKH) take as input a public key in +hexadecimal notation (which will be extended later), and return the +corresponding *scriptPubKey*. The functions `sh` (P2SH) and `wsh` (P2WSH) +take as input a script, and return the script describing P2SH and P2WSH +outputs with the input as embedded script. The names of the functions do +not contain "p2" for brevity. + +### Multisig + +Several pieces of software use multi-signature (multisig) scripts based +on Bitcoin's OP_CHECKMULTISIG opcode. To support these, we introduce the +`multi(k,key_1,key_2,...,key_n)` function. It represents a *k-of-n* +multisig policy, where any *k* out of the *n* provided public keys must +sign. + +### BIP32 derived keys and chains + +Most modern wallet software and hardware uses keys that are derived using +BIP32 ("HD keys"). We support these directly by permitting strings +consisting of an extended public key (commonly referred to as an *xpub*) +plus derivation path anywhere a public key is expected. The derivation +path consists of a sequence of 0 or more integers (in the range +*0..2<sup>31</sup>-1*) each optionally followed by `'` or `h`, and +separated by `/` characters. The string may optionally end with the +literal `/*` or `/*'` (or `/*h`) to refer to all unhardened or hardened +child keys instead. + +Whenever a public key is described using a hardened derivation step, the +script cannot be computed without access to the corresponding private +key. + +### Including private keys + +Often it is useful to communicate a description of scripts along with the +necessary private keys. For this reason, anywhere a public key or xpub is +supported, a private key in WIF format or xprv may be provided instead. +This is useful when private keys are necessary for hardened derivation +steps, or for dumping wallet descriptors including private key material. + +### Compatibility with old wallets + +In order to easily represent the sets of scripts currently supported by +existing Bitcoin Core wallets, a convenience function `combo` is +provided, which takes as input a public key, and constructs the P2PK, +P2PKH, P2WPKH, and P2SH-P2WPH scripts for that key. In case the key is +uncompressed, it only constructs P2PK and P2PKH. diff --git a/doc/developer-notes.md b/doc/developer-notes.md index da8384c537..1d103d481b 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -439,6 +439,11 @@ General C++ - *Rationale*: This avoids memory and resource leaks, and ensures exception safety +- Use `MakeUnique()` to construct objects owned by `unique_ptr`s + + - *Rationale*: `MakeUnique` is concise and ensures exception safety in complex expressions. + `MakeUnique` is a temporary project local implementation of `std::make_unique` (C++14). + C++ data structures -------------------- @@ -512,7 +517,7 @@ Strings and formatting - *Rationale*: These functions do overflow checking, and avoid pesky locale issues. - Avoid using locale dependent functions if possible. You can use the provided - [`lint-locale-dependence.sh`](/contrib/devtools/lint-locale-dependence.sh) + [`lint-locale-dependence.sh`](/test/lint/lint-locale-dependence.sh) to check for accidental use of locale dependent functions. - *Rationale*: Unnecessary locale dependence can cause bugs that are very tricky to isolate and fix. diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am index 08ff4d6ac1..9b36319e64 100644 --- a/doc/man/Makefile.am +++ b/doc/man/Makefile.am @@ -8,6 +8,10 @@ if ENABLE_QT dist_man1_MANS+=bitcoin-qt.1 endif -if BUILD_BITCOIN_UTILS - dist_man1_MANS+=bitcoin-cli.1 bitcoin-tx.1 +if BUILD_BITCOIN_CLI + dist_man1_MANS+=bitcoin-cli.1 +endif + +if BUILD_BITCOIN_TX + dist_man1_MANS+=bitcoin-tx.1 endif diff --git a/doc/release-notes-13152.md b/doc/release-notes-13152.md new file mode 100644 index 0000000000..72526f5355 --- /dev/null +++ b/doc/release-notes-13152.md @@ -0,0 +1,4 @@ +New RPC methods +------------ + +- `getnodeaddresses` returns peer addresses known to this node. It may be used to connect to nodes over TCP without using the DNS seeds.
\ No newline at end of file diff --git a/doc/release-notes-14023.md b/doc/release-notes-14023.md new file mode 100644 index 0000000000..18ea6f26d0 --- /dev/null +++ b/doc/release-notes-14023.md @@ -0,0 +1,8 @@ +Account API removed +------------------- + +The 'account' API was deprecated in v0.17 and has been fully removed in v0.18. +The 'label' API was introduced in v0.17 as a replacement for accounts. + +See the release notes from v0.17 for a full description of the changes from the +'account' API to the 'label' API. diff --git a/doc/release-notes-14282.md b/doc/release-notes-14282.md new file mode 100644 index 0000000000..e6d8e0b70c --- /dev/null +++ b/doc/release-notes-14282.md @@ -0,0 +1,6 @@ +Low-level RPC changes +---------------------- + +`-usehd` was removed in version 0.16. From that version onwards, all new +wallets created are hierarchical deterministic wallets. Version 0.18 makes +specifying `-usehd` invalid config. diff --git a/doc/release-notes.md b/doc/release-notes.md index d3ba067657..2044a50098 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -59,6 +59,16 @@ support versions of macOS older than 10.10. Notable changes =============== +Command line option changes +--------------------------- + +The `-enablebip61` command line option (introduced in Bitcoin Core 0.17.0) is +used to toggle sending of BIP 61 reject messages. Reject messages have no use +case on the P2P network and are only logged for debugging by most network +nodes. The option will now by default be off for improved privacy and security +as well as reduced upload usage. The option can explicitly be turned on for +local-network debugging purposes. + Example item ------------ diff --git a/doc/release-notes/release-notes-0.16.3.md b/doc/release-notes/release-notes-0.16.3.md new file mode 100644 index 0000000000..2e52d309c2 --- /dev/null +++ b/doc/release-notes/release-notes-0.16.3.md @@ -0,0 +1,88 @@ +Bitcoin Core version 0.16.3 is now available from: + + <https://bitcoincore.org/bin/bitcoin-core-0.16.3/> + +This is a new minor version release, with various bugfixes. + +Please report bugs using the issue tracker at GitHub: + + <https://github.com/bitcoin/bitcoin/issues> + +To receive security and update notifications, please subscribe to: + + <https://bitcoincore.org/en/list/announcements/join/> + +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/Bitcoin-Qt` (on Mac) +or `bitcoind`/`bitcoin-qt` (on Linux). + +The first time you run version 0.15.0 or newer, your chainstate database will be converted to a +new format, which will take anywhere from a few minutes to half an hour, +depending on the speed of your machine. + +Note that the block database format also changed in version 0.8.0 and there is no +automatic upgrade code from before version 0.8 to version 0.15.0 or higher. Upgrading +directly from 0.7.x and earlier without re-downloading the blockchain is not supported. +However, as usual, old wallet versions are still supported. + +Downgrading warning +------------------- + +Wallets created in 0.16 and later are not compatible with versions prior to 0.16 +and will not work if you try to use newly created wallets in older versions. Existing +wallets that were created with older versions are not affected by this. + +Compatibility +============== + +Bitcoin Core is extensively tested on multiple operating systems using +the Linux kernel, macOS 10.8+, and Windows Vista and later. Windows XP is not supported. + +Bitcoin Core should also work on most other Unix-like systems but is not +frequently tested on them. + +Notable changes +=============== + +Denial-of-Service vulnerability +------------------------------- + +A denial-of-service vulnerability (CVE-2018-17144) exploitable by miners has +been discovered in Bitcoin Core versions 0.14.0 up to 0.16.2. It is recommended +to upgrade any of the vulnerable versions to 0.16.3 as soon as possible. + +0.16.3 change log +------------------ + +### Consensus +- #14249 `696b936` Fix crash bug with duplicate inputs within a transaction (TheBlueMatt, sdaftuar) + +### RPC and other APIs +- #13547 `212ef1f` Make `signrawtransaction*` give an error when amount is needed but missing (ajtowns) + +### Miscellaneous +- #13655 `1cdbea7` bitcoinconsensus: invalid flags error should be set to `bitcoinconsensus_err` (afk11) + +### Documentation +- #13844 `11b9dbb` correct the help output for -prune (hebasto) + +Credits +======= + +Thanks to everyone who directly contributed to this release: + +- Anthony Towns +- Hennadii Stepanov +- Matt Corallo +- Suhas Daftuar +- Thomas Kerin +- Wladimir J. van der Laan + +And to those that reported security issues: + +- (anonymous reporter) + diff --git a/doc/tor.md b/doc/tor.md index 2d0676c89a..dc0b88618a 100644 --- a/doc/tor.md +++ b/doc/tor.md @@ -93,7 +93,7 @@ API, to create and destroy 'ephemeral' hidden services programmatically. Bitcoin Core has been updated to make use of this. This means that if Tor is running (and proper authentication has been configured), -Bitcoin Core automatically creates a hidden service to listen on. This will positively +Bitcoin Core automatically creates a hidden service to listen on. This will positively affect the number of available .onion nodes. This new feature is enabled by default if Bitcoin Core is listening (`-listen`), and @@ -102,8 +102,9 @@ and, if not disabled, configured using the `-torcontrol` and `-torpassword` sett To show verbose debugging information, pass `-debug=tor`. Connecting to Tor's control socket API requires one of two authentication methods to be -configured. For cookie authentication the user running bitcoind must have write access -to the `CookieAuthFile` specified in Tor configuration. In some cases, this is +configured. It also requires the control socket to be enabled, e.g. put `ControlPort 9051` +in `torrc` config file. For cookie authentication the user running bitcoind must have read +access to the `CookieAuthFile` specified in Tor configuration. In some cases this is preconfigured and the creation of a hidden service is automatic. If permission problems are seen with `-debug=tor` they can be resolved by adding both the user running Tor and the user running bitcoind to the same group and setting permissions appropriately. On diff --git a/doc/zmq.md b/doc/zmq.md index 5d67df9d22..a1a506f2e7 100644 --- a/doc/zmq.md +++ b/doc/zmq.md @@ -33,8 +33,10 @@ buffering or reassembly. ## Prerequisites -The ZeroMQ feature in Bitcoin Core requires ZeroMQ API version 4.x or -newer. Typically, it is packaged by distributions as something like +The ZeroMQ feature in Bitcoin Core requires the ZeroMQ API >= 4.0.0 +[libzmq](https://github.com/zeromq/libzmq/releases). +For version information, see [dependencies.md](dependencies.md). +Typically, it is packaged by distributions as something like *libzmq3-dev*. The C++ wrapper for ZeroMQ is *not* needed. In order to run the example Python client scripts in contrib/ one must diff --git a/src/Makefile.am b/src/Makefile.am index d1693fa85c..7a2e9fa5e8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -83,8 +83,11 @@ if BUILD_BITCOIND bin_PROGRAMS += bitcoind endif -if BUILD_BITCOIN_UTILS - bin_PROGRAMS += bitcoin-cli bitcoin-tx +if BUILD_BITCOIN_CLI + bin_PROGRAMS += bitcoin-cli +endif +if BUILD_BITCOIN_TX + bin_PROGRAMS += bitcoin-tx endif .PHONY: FORCE check-symbols check-security @@ -96,6 +99,7 @@ BITCOIN_CORE_H = \ bech32.h \ bloom.h \ blockencodings.h \ + blockfilter.h \ chain.h \ chainparams.h \ chainparamsbase.h \ @@ -186,6 +190,7 @@ BITCOIN_CORE_H = \ validation.h \ validationinterface.h \ versionbits.h \ + versionbitsinfo.h \ walletinitinterface.h \ wallet/coincontrol.h \ wallet/crypter.h \ @@ -219,6 +224,7 @@ libbitcoin_server_a_SOURCES = \ addrman.cpp \ bloom.cpp \ blockencodings.cpp \ + blockfilter.cpp \ chain.cpp \ checkpoints.cpp \ consensus/tx_verify.cpp \ @@ -226,6 +232,8 @@ libbitcoin_server_a_SOURCES = \ httpserver.cpp \ index/base.cpp \ index/txindex.cpp \ + interfaces/handler.cpp \ + interfaces/node.cpp \ init.cpp \ dbwrapper.cpp \ merkleblock.cpp \ @@ -258,6 +266,10 @@ libbitcoin_server_a_SOURCES = \ versionbits.cpp \ $(BITCOIN_CORE_H) +if !ENABLE_WALLET +libbitcoin_server_a_SOURCES += dummywallet.cpp +endif + if ENABLE_ZMQ libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS) libbitcoin_zmq_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -392,6 +404,7 @@ libbitcoin_common_a_SOURCES = \ script/ismine.cpp \ script/sign.cpp \ script/standard.cpp \ + versionbitsinfo.cpp \ warnings.cpp \ $(BITCOIN_CORE_H) @@ -408,8 +421,6 @@ libbitcoin_util_a_SOURCES = \ compat/glibcxx_sanity.cpp \ compat/strnlen.cpp \ fs.cpp \ - interfaces/handler.cpp \ - interfaces/node.cpp \ logging.cpp \ random.cpp \ rpc/protocol.cpp \ @@ -461,7 +472,7 @@ bitcoind_LDADD = \ $(LIBMEMENV) \ $(LIBSECP256K1) -bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) +bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) # bitcoin-cli binary # bitcoin_cli_SOURCES = bitcoin-cli.cpp @@ -479,7 +490,7 @@ bitcoin_cli_LDADD = \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) -bitcoin_cli_LDADD += $(BOOST_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS) +bitcoin_cli_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS) # # bitcoin-tx binary # diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index f5293585a0..544092520f 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -22,6 +22,7 @@ bench_bench_bitcoin_SOURCES = \ bench/rollingbloom.cpp \ bench/crypto_hash.cpp \ bench/ccoins_caching.cpp \ + bench/gcs_filter.cpp \ bench/merkle_root.cpp \ bench/mempool_eviction.cpp \ bench/verify_script.cpp \ @@ -55,7 +56,7 @@ if ENABLE_WALLET bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp endif -bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) +bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 6f401636f5..8a537ed4f6 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -8,7 +8,9 @@ TEST_SRCDIR = test TEST_BINARY=test/test_bitcoin$(EXEEXT) JSON_TEST_FILES = \ + test/data/script_tests.json \ test/data/base58_encode_decode.json \ + test/data/blockfilters.json \ test/data/key_io_valid.json \ test/data/key_io_invalid.json \ test/data/script_tests.json \ @@ -39,6 +41,7 @@ BITCOIN_TESTS =\ test/bip32_tests.cpp \ test/blockchain_tests.cpp \ test/blockencodings_tests.cpp \ + test/blockfilter_tests.cpp \ test/bloom_tests.cpp \ test/bswap_tests.cpp \ test/checkqueue_tests.cpp \ @@ -81,6 +84,7 @@ BITCOIN_TESTS =\ test/sigopcount_tests.cpp \ test/skiplist_tests.cpp \ test/streams_tests.cpp \ + test/sync_tests.cpp \ test/timedata_tests.cpp \ test/torcontrol_tests.cpp \ test/transaction_tests.cpp \ @@ -92,9 +96,17 @@ BITCOIN_TESTS =\ test/validation_block_tests.cpp \ test/versionbits_tests.cpp +if ENABLE_PROPERTY_TESTS +BITCOIN_TESTS += \ + test/key_properties.cpp + +BITCOIN_TEST_SUITE += \ + test/gen/crypto_gen.cpp \ + test/gen/crypto_gen.h +endif + if ENABLE_WALLET BITCOIN_TESTS += \ - wallet/test/accounting_tests.cpp \ wallet/test/psbt_wallet_tests.cpp \ wallet/test/wallet_tests.cpp \ wallet/test/wallet_crypto_tests.cpp \ @@ -116,7 +128,7 @@ test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_C $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS) test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) -test_test_bitcoin_LDADD += $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) +test_test_bitcoin_LDADD += $(LIBBITCOIN_CONSENSUS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(RAPIDCHECK_LIBS) test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static if ENABLE_ZMQ @@ -162,8 +174,10 @@ bitcoin_test_clean : FORCE rm -f $(CLEAN_BITCOIN_TEST) $(test_test_bitcoin_OBJECTS) $(TEST_BINARY) check-local: $(BITCOIN_TESTS:.cpp=.cpp.test) +if BUILD_BITCOIN_TX @echo "Running test/util/bitcoin-util-test.py..." $(PYTHON) $(top_builddir)/test/util/bitcoin-util-test.py +endif @echo "Running test/util/rpcauth-test.py..." $(PYTHON) $(top_builddir)/test/util/rpcauth-test.py $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check diff --git a/src/bech32.cpp b/src/bech32.cpp index c55f22b9b7..d6b29391a9 100644 --- a/src/bech32.cpp +++ b/src/bech32.cpp @@ -62,7 +62,7 @@ uint32_t PolyMod(const data& v) // v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value // for `c`. uint32_t c = 1; - for (auto v_i : v) { + for (const auto v_i : v) { // We want to update `c` to correspond to a polynomial with one extra term. If the initial // value of `c` consists of the coefficients of c(x) = f(x) mod g(x), we modify it to // correspond to c'(x) = (f(x) * x + v_i) mod g(x), where v_i is the next input to @@ -149,7 +149,7 @@ std::string Encode(const std::string& hrp, const data& values) { data combined = Cat(values, checksum); std::string ret = hrp + '1'; ret.reserve(ret.size() + combined.size()); - for (auto c : combined) { + for (const auto c : combined) { ret += CHARSET[c]; } return ret; diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp index 603b858e54..4fa516cb81 100644 --- a/src/bench/bench_bitcoin.cpp +++ b/src/bench/bench_bitcoin.cpp @@ -13,6 +13,8 @@ #include <memory> +const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr; + static const int64_t DEFAULT_BENCH_EVALUATIONS = 5; static const char* DEFAULT_BENCH_FILTER = ".*"; static const char* DEFAULT_BENCH_SCALING = "1.0"; @@ -80,7 +82,7 @@ int main(int argc, char** argv) return EXIT_FAILURE; } - std::unique_ptr<benchmark::Printer> printer(new benchmark::ConsolePrinter()); + std::unique_ptr<benchmark::Printer> printer = MakeUnique<benchmark::ConsolePrinter>(); std::string printer_arg = gArgs.GetArg("-printer", DEFAULT_BENCH_PRINTER); if ("plot" == printer_arg) { printer.reset(new benchmark::PlotlyPrinter( diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp index 0a6f5d85ea..27c23d6834 100644 --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -66,7 +66,7 @@ static void add_coin(const CAmount& nValue, int nInput, std::vector<OutputGroup> CMutableTransaction tx; tx.vout.resize(nInput + 1); tx.vout[nInput].nValue = nValue; - std::unique_ptr<CWalletTx> wtx(new CWalletTx(&testWallet, MakeTransactionRef(std::move(tx)))); + std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx))); set.emplace_back(COutput(wtx.get(), nInput, 0, true, true, true).GetInputCoin(), 0, true, 0, 0); wtxn.emplace_back(std::move(wtx)); } diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp index bab22f5984..5b0cf27e04 100644 --- a/src/bench/crypto_hash.cpp +++ b/src/bench/crypto_hash.cpp @@ -80,18 +80,16 @@ static void SipHash_32b(benchmark::State& state) static void FastRandom_32bit(benchmark::State& state) { FastRandomContext rng(true); - uint32_t x = 0; while (state.KeepRunning()) { - x += rng.rand32(); + rng.rand32(); } } static void FastRandom_1bit(benchmark::State& state) { FastRandomContext rng(true); - uint32_t x = 0; while (state.KeepRunning()) { - x += rng.randbool(); + rng.randbool(); } } diff --git a/src/bench/gcs_filter.cpp b/src/bench/gcs_filter.cpp new file mode 100644 index 0000000000..6f4e384e3b --- /dev/null +++ b/src/bench/gcs_filter.cpp @@ -0,0 +1,43 @@ +// Copyright (c) 2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <bench/bench.h> +#include <blockfilter.h> + +static void ConstructGCSFilter(benchmark::State& state) +{ + GCSFilter::ElementSet elements; + for (int i = 0; i < 10000; ++i) { + GCSFilter::Element element(32); + element[0] = static_cast<unsigned char>(i); + element[1] = static_cast<unsigned char>(i >> 8); + elements.insert(std::move(element)); + } + + uint64_t siphash_k0 = 0; + while (state.KeepRunning()) { + GCSFilter filter(siphash_k0, 0, 20, 1 << 20, elements); + + siphash_k0++; + } +} + +static void MatchGCSFilter(benchmark::State& state) +{ + GCSFilter::ElementSet elements; + for (int i = 0; i < 10000; ++i) { + GCSFilter::Element element(32); + element[0] = static_cast<unsigned char>(i); + element[1] = static_cast<unsigned char>(i >> 8); + elements.insert(std::move(element)); + } + GCSFilter filter(0, 0, 20, 1 << 20, elements); + + while (state.KeepRunning()) { + filter.Match(GCSFilter::Element()); + } +} + +BENCHMARK(ConstructGCSFilter, 1000); +BENCHMARK(MatchGCSFilter, 50 * 1000); diff --git a/src/bench/mempool_eviction.cpp b/src/bench/mempool_eviction.cpp index 0ec7c158cc..3908a7d231 100644 --- a/src/bench/mempool_eviction.cpp +++ b/src/bench/mempool_eviction.cpp @@ -16,7 +16,7 @@ static void AddTx(const CTransactionRef& tx, const CAmount& nFee, CTxMemPool& po bool spendsCoinbase = false; unsigned int sigOpCost = 4; LockPoints lp; - pool.addUnchecked(tx->GetHash(), CTxMemPoolEntry( + pool.addUnchecked(CTxMemPoolEntry( tx, nFee, nTime, nHeight, spendsCoinbase, sigOpCost, lp)); } diff --git a/src/bench/rollingbloom.cpp b/src/bench/rollingbloom.cpp index 43e7635047..0a99ea3184 100644 --- a/src/bench/rollingbloom.cpp +++ b/src/bench/rollingbloom.cpp @@ -12,7 +12,6 @@ static void RollingBloom(benchmark::State& state) CRollingBloomFilter filter(120000, 0.000001); std::vector<unsigned char> data(32); uint32_t count = 0; - uint64_t match = 0; while (state.KeepRunning()) { count++; data[0] = count; @@ -25,7 +24,7 @@ static void RollingBloom(benchmark::State& state) data[1] = count >> 16; data[2] = count >> 8; data[3] = count; - match += filter.contains(data); + filter.contains(data); } } diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index db713f58d2..09507fd249 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -24,6 +24,8 @@ #include <univalue.h> +const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr; + static const char DEFAULT_RPCCONNECT[] = "127.0.0.1"; static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900; static const bool DEFAULT_NAMED=false; @@ -33,6 +35,7 @@ static void SetupCliArgs() { const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN); const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET); + const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST); gArgs.AddArg("-?", "This help message", false, OptionsCategory::OPTIONS); gArgs.AddArg("-version", "Print version and exit", false, OptionsCategory::OPTIONS); @@ -43,9 +46,9 @@ static void SetupCliArgs() gArgs.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), false, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcclienttimeout=<n>", strprintf("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), false, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcconnect=<ip>", strprintf("Send commands to node running on <ip> (default: %s)", DEFAULT_RPCCONNECT), false, OptionsCategory::OPTIONS); - gArgs.AddArg("-rpccookiefile=<loc>", _("Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", false, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", false, OptionsCategory::OPTIONS); - gArgs.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u or testnet: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), false, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", false, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcwait", "Wait for RPC server to start", false, OptionsCategory::OPTIONS); gArgs.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind)", false, OptionsCategory::OPTIONS); @@ -136,11 +139,6 @@ static int AppInitRPC(int argc, char* argv[]) fprintf(stderr, "Error: %s\n", e.what()); return EXIT_FAILURE; } - if (gArgs.GetBoolArg("-rpcssl", false)) - { - fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n"); - return EXIT_FAILURE; - } return CONTINUE_EXECUTION; } diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 5fef4724c9..9ee3e8eee7 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -31,6 +31,8 @@ static bool fCreateBlank; static std::map<std::string,UniValue> registers; static const int CONTINUE_EXECUTION=-1; +const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr; + static void SetupBitcoinTxArgs() { gArgs.AddArg("-?", "This help message", false, OptionsCategory::OPTIONS); @@ -383,7 +385,7 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s CScript scriptPubKey = GetScriptForMultisig(required, pubkeys); if (bSegWit) { - for (CPubKey& pubkey : pubkeys) { + for (const CPubKey& pubkey : pubkeys) { if (!pubkey.IsCompressed()) { throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs"); } diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 06f8622426..bf04d95b50 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -23,6 +23,8 @@ #include <stdio.h> +const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr; + /* Introduction text for doxygen: */ /*! \mainpage Developer documentation diff --git a/src/blockencodings.cpp b/src/blockencodings.cpp index b17ff3507d..4c57965bec 100644 --- a/src/blockencodings.cpp +++ b/src/blockencodings.cpp @@ -162,7 +162,7 @@ ReadStatus PartiallyDownloadedBlock::InitData(const CBlockHeaderAndShortTxIDs& c break; } - LogPrint(BCLog::CMPCTBLOCK, "Initialized PartiallyDownloadedBlock for block %s using a cmpctblock of size %lu\n", cmpctblock.header.GetHash().ToString(), GetSerializeSize(cmpctblock, SER_NETWORK, PROTOCOL_VERSION)); + LogPrint(BCLog::CMPCTBLOCK, "Initialized PartiallyDownloadedBlock for block %s using a cmpctblock of size %lu\n", cmpctblock.header.GetHash().ToString(), GetSerializeSize(cmpctblock, PROTOCOL_VERSION)); return READ_STATUS_OK; } diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp new file mode 100644 index 0000000000..91623fe70a --- /dev/null +++ b/src/blockfilter.cpp @@ -0,0 +1,261 @@ +// Copyright (c) 2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <blockfilter.h> +#include <hash.h> +#include <primitives/transaction.h> +#include <script/script.h> +#include <streams.h> + +/// SerType used to serialize parameters in GCS filter encoding. +static constexpr int GCS_SER_TYPE = SER_NETWORK; + +/// Protocol version used to serialize parameters in GCS filter encoding. +static constexpr int GCS_SER_VERSION = 0; + +template <typename OStream> +static void GolombRiceEncode(BitStreamWriter<OStream>& bitwriter, uint8_t P, uint64_t x) +{ + // Write quotient as unary-encoded: q 1's followed by one 0. + uint64_t q = x >> P; + while (q > 0) { + int nbits = q <= 64 ? static_cast<int>(q) : 64; + bitwriter.Write(~0ULL, nbits); + q -= nbits; + } + bitwriter.Write(0, 1); + + // Write the remainder in P bits. Since the remainder is just the bottom + // P bits of x, there is no need to mask first. + bitwriter.Write(x, P); +} + +template <typename IStream> +static uint64_t GolombRiceDecode(BitStreamReader<IStream>& bitreader, uint8_t P) +{ + // Read unary-encoded quotient: q 1's followed by one 0. + uint64_t q = 0; + while (bitreader.Read(1) == 1) { + ++q; + } + + uint64_t r = bitreader.Read(P); + + return (q << P) + r; +} + +// Map a value x that is uniformly distributed in the range [0, 2^64) to a +// value uniformly distributed in [0, n) by returning the upper 64 bits of +// x * n. +// +// See: https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ +static uint64_t MapIntoRange(uint64_t x, uint64_t n) +{ +#ifdef __SIZEOF_INT128__ + return (static_cast<unsigned __int128>(x) * static_cast<unsigned __int128>(n)) >> 64; +#else + // To perform the calculation on 64-bit numbers without losing the + // result to overflow, split the numbers into the most significant and + // least significant 32 bits and perform multiplication piece-wise. + // + // See: https://stackoverflow.com/a/26855440 + uint64_t x_hi = x >> 32; + uint64_t x_lo = x & 0xFFFFFFFF; + uint64_t n_hi = n >> 32; + uint64_t n_lo = n & 0xFFFFFFFF; + + uint64_t ac = x_hi * n_hi; + uint64_t ad = x_hi * n_lo; + uint64_t bc = x_lo * n_hi; + uint64_t bd = x_lo * n_lo; + + uint64_t mid34 = (bd >> 32) + (bc & 0xFFFFFFFF) + (ad & 0xFFFFFFFF); + uint64_t upper64 = ac + (bc >> 32) + (ad >> 32) + (mid34 >> 32); + return upper64; +#endif +} + +uint64_t GCSFilter::HashToRange(const Element& element) const +{ + uint64_t hash = CSipHasher(m_siphash_k0, m_siphash_k1) + .Write(element.data(), element.size()) + .Finalize(); + return MapIntoRange(hash, m_F); +} + +std::vector<uint64_t> GCSFilter::BuildHashedSet(const ElementSet& elements) const +{ + std::vector<uint64_t> hashed_elements; + hashed_elements.reserve(elements.size()); + for (const Element& element : elements) { + hashed_elements.push_back(HashToRange(element)); + } + std::sort(hashed_elements.begin(), hashed_elements.end()); + return hashed_elements; +} + +GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M) + : m_siphash_k0(siphash_k0), m_siphash_k1(siphash_k1), m_P(P), m_M(M), m_N(0), m_F(0) +{} + +GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M, + std::vector<unsigned char> encoded_filter) + : GCSFilter(siphash_k0, siphash_k1, P, M) +{ + m_encoded = std::move(encoded_filter); + + VectorReader stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0); + + uint64_t N = ReadCompactSize(stream); + m_N = static_cast<uint32_t>(N); + if (m_N != N) { + throw std::ios_base::failure("N must be <2^32"); + } + m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_M); + + // Verify that the encoded filter contains exactly N elements. If it has too much or too little + // data, a std::ios_base::failure exception will be raised. + BitStreamReader<VectorReader> bitreader(stream); + for (uint64_t i = 0; i < m_N; ++i) { + GolombRiceDecode(bitreader, m_P); + } + if (!stream.empty()) { + throw std::ios_base::failure("encoded_filter contains excess data"); + } +} + +GCSFilter::GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M, + const ElementSet& elements) + : GCSFilter(siphash_k0, siphash_k1, P, M) +{ + size_t N = elements.size(); + m_N = static_cast<uint32_t>(N); + if (m_N != N) { + throw std::invalid_argument("N must be <2^32"); + } + m_F = static_cast<uint64_t>(m_N) * static_cast<uint64_t>(m_M); + + CVectorWriter stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0); + + WriteCompactSize(stream, m_N); + + if (elements.empty()) { + return; + } + + BitStreamWriter<CVectorWriter> bitwriter(stream); + + uint64_t last_value = 0; + for (uint64_t value : BuildHashedSet(elements)) { + uint64_t delta = value - last_value; + GolombRiceEncode(bitwriter, m_P, delta); + last_value = value; + } + + bitwriter.Flush(); +} + +bool GCSFilter::MatchInternal(const uint64_t* element_hashes, size_t size) const +{ + VectorReader stream(GCS_SER_TYPE, GCS_SER_VERSION, m_encoded, 0); + + // Seek forward by size of N + uint64_t N = ReadCompactSize(stream); + assert(N == m_N); + + BitStreamReader<VectorReader> bitreader(stream); + + uint64_t value = 0; + size_t hashes_index = 0; + for (uint32_t i = 0; i < m_N; ++i) { + uint64_t delta = GolombRiceDecode(bitreader, m_P); + value += delta; + + while (true) { + if (hashes_index == size) { + return false; + } else if (element_hashes[hashes_index] == value) { + return true; + } else if (element_hashes[hashes_index] > value) { + break; + } + + hashes_index++; + } + } + + return false; +} + +bool GCSFilter::Match(const Element& element) const +{ + uint64_t query = HashToRange(element); + return MatchInternal(&query, 1); +} + +bool GCSFilter::MatchAny(const ElementSet& elements) const +{ + const std::vector<uint64_t> queries = BuildHashedSet(elements); + return MatchInternal(queries.data(), queries.size()); +} + +static GCSFilter::ElementSet BasicFilterElements(const CBlock& block, + const CBlockUndo& block_undo) +{ + GCSFilter::ElementSet elements; + + for (const CTransactionRef& tx : block.vtx) { + for (const CTxOut& txout : tx->vout) { + const CScript& script = txout.scriptPubKey; + if (script.empty() || script[0] == OP_RETURN) continue; + elements.emplace(script.begin(), script.end()); + } + } + + for (const CTxUndo& tx_undo : block_undo.vtxundo) { + for (const Coin& prevout : tx_undo.vprevout) { + const CScript& script = prevout.out.scriptPubKey; + if (script.empty()) continue; + elements.emplace(script.begin(), script.end()); + } + } + + return elements; +} + +BlockFilter::BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo) + : m_filter_type(filter_type), m_block_hash(block.GetHash()) +{ + switch (m_filter_type) { + case BlockFilterType::BASIC: + m_filter = GCSFilter(m_block_hash.GetUint64(0), m_block_hash.GetUint64(1), + BASIC_FILTER_P, BASIC_FILTER_M, + BasicFilterElements(block, block_undo)); + break; + + default: + throw std::invalid_argument("unknown filter_type"); + } +} + +uint256 BlockFilter::GetHash() const +{ + const std::vector<unsigned char>& data = GetEncodedFilter(); + + uint256 result; + CHash256().Write(data.data(), data.size()).Finalize(result.begin()); + return result; +} + +uint256 BlockFilter::ComputeHeader(const uint256& prev_header) const +{ + const uint256& filter_hash = GetHash(); + + uint256 result; + CHash256() + .Write(filter_hash.begin(), filter_hash.size()) + .Write(prev_header.begin(), prev_header.size()) + .Finalize(result.begin()); + return result; +} diff --git a/src/blockfilter.h b/src/blockfilter.h new file mode 100644 index 0000000000..46833ac0be --- /dev/null +++ b/src/blockfilter.h @@ -0,0 +1,145 @@ +// Copyright (c) 2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_BLOCKFILTER_H +#define BITCOIN_BLOCKFILTER_H + +#include <set> +#include <stdint.h> +#include <vector> + +#include <primitives/block.h> +#include <serialize.h> +#include <uint256.h> +#include <undo.h> + +/** + * This implements a Golomb-coded set as defined in BIP 158. It is a + * compact, probabilistic data structure for testing set membership. + */ +class GCSFilter +{ +public: + typedef std::vector<unsigned char> Element; + typedef std::set<Element> ElementSet; + +private: + uint64_t m_siphash_k0; + uint64_t m_siphash_k1; + uint8_t m_P; //!< Golomb-Rice coding parameter + uint32_t m_M; //!< Inverse false positive rate + uint32_t m_N; //!< Number of elements in the filter + uint64_t m_F; //!< Range of element hashes, F = N * M + std::vector<unsigned char> m_encoded; + + /** Hash a data element to an integer in the range [0, N * M). */ + uint64_t HashToRange(const Element& element) const; + + std::vector<uint64_t> BuildHashedSet(const ElementSet& elements) const; + + /** Helper method used to implement Match and MatchAny */ + bool MatchInternal(const uint64_t* sorted_element_hashes, size_t size) const; + +public: + + /** Constructs an empty filter. */ + GCSFilter(uint64_t siphash_k0 = 0, uint64_t siphash_k1 = 0, uint8_t P = 0, uint32_t M = 0); + + /** Reconstructs an already-created filter from an encoding. */ + GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M, + std::vector<unsigned char> encoded_filter); + + /** Builds a new filter from the params and set of elements. */ + GCSFilter(uint64_t siphash_k0, uint64_t siphash_k1, uint8_t P, uint32_t M, + const ElementSet& elements); + + uint8_t GetP() const { return m_P; } + uint32_t GetN() const { return m_N; } + uint32_t GetM() const { return m_M; } + const std::vector<unsigned char>& GetEncoded() const { return m_encoded; } + + /** + * Checks if the element may be in the set. False positives are possible + * with probability 1/M. + */ + bool Match(const Element& element) const; + + /** + * Checks if any of the given elements may be in the set. False positives + * are possible with probability 1/M per element checked. This is more + * efficient that checking Match on multiple elements separately. + */ + bool MatchAny(const ElementSet& elements) const; +}; + +constexpr uint8_t BASIC_FILTER_P = 19; +constexpr uint32_t BASIC_FILTER_M = 784931; + +enum BlockFilterType : uint8_t +{ + BASIC = 0, +}; + +/** + * Complete block filter struct as defined in BIP 157. Serialization matches + * payload of "cfilter" messages. + */ +class BlockFilter +{ +private: + BlockFilterType m_filter_type; + uint256 m_block_hash; + GCSFilter m_filter; + +public: + + // Construct a new BlockFilter of the specified type from a block. + BlockFilter(BlockFilterType filter_type, const CBlock& block, const CBlockUndo& block_undo); + + BlockFilterType GetFilterType() const { return m_filter_type; } + + const GCSFilter& GetFilter() const { return m_filter; } + + const std::vector<unsigned char>& GetEncodedFilter() const + { + return m_filter.GetEncoded(); + } + + // Compute the filter hash. + uint256 GetHash() const; + + // Compute the filter header given the previous one. + uint256 ComputeHeader(const uint256& prev_header) const; + + template <typename Stream> + void Serialize(Stream& s) const { + s << m_block_hash + << static_cast<uint8_t>(m_filter_type) + << m_filter.GetEncoded(); + } + + template <typename Stream> + void Unserialize(Stream& s) { + std::vector<unsigned char> encoded_filter; + uint8_t filter_type; + + s >> m_block_hash + >> filter_type + >> encoded_filter; + + m_filter_type = static_cast<BlockFilterType>(filter_type); + + switch (m_filter_type) { + case BlockFilterType::BASIC: + m_filter = GCSFilter(m_block_hash.GetUint64(0), m_block_hash.GetUint64(1), + BASIC_FILTER_P, BASIC_FILTER_M, std::move(encoded_filter)); + break; + + default: + throw std::ios_base::failure("unknown filter_type"); + } + } +}; + +#endif // BITCOIN_BLOCKFILTER_H diff --git a/src/bloom.cpp b/src/bloom.cpp index 94aae742f1..7732cee275 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -153,11 +153,11 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) insert(COutPoint(hash, i)); else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY) { - txnouttype type; std::vector<std::vector<unsigned char> > vSolutions; - if (Solver(txout.scriptPubKey, type, vSolutions) && - (type == TX_PUBKEY || type == TX_MULTISIG)) + txnouttype type = Solver(txout.scriptPubKey, vSolutions); + if (type == TX_PUBKEY || type == TX_MULTISIG) { insert(COutPoint(hash, i)); + } } break; } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 93bb3e7647..0574e2395e 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -4,15 +4,18 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <chainparams.h> -#include <consensus/merkle.h> +#include <chainparamsseeds.h> +#include <consensus/merkle.h> #include <tinyformat.h> #include <util.h> #include <utilstrencodings.h> +#include <versionbitsinfo.h> #include <assert.h> -#include <chainparamsseeds.h> +#include <boost/algorithm/string/classification.hpp> +#include <boost/algorithm/string/split.hpp> static CBlock CreateGenesisBlock(const char* pszTimestamp, const CScript& genesisOutputScript, uint32_t nTime, uint32_t nNonce, uint32_t nBits, int32_t nVersion, const CAmount& genesisReward) { @@ -53,23 +56,9 @@ static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward); } -void CChainParams::UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout) -{ - consensus.vDeployments[d].nStartTime = nStartTime; - consensus.vDeployments[d].nTimeout = nTimeout; -} - /** * Main network */ -/** - * What makes a good checkpoint block? - * + Is surrounded by blocks with reasonable timestamps - * (no blocks before with a timestamp after, none after with - * timestamp before) - * + Contains no strange transactions - */ - class CMainParams : public CChainParams { public: CMainParams() { @@ -279,7 +268,7 @@ public: */ class CRegTestParams : public CChainParams { public: - CRegTestParams() { + explicit CRegTestParams(const ArgsManager& args) { strNetworkID = "regtest"; consensus.nSubsidyHalvingInterval = 150; consensus.BIP16Exception = uint256(); @@ -317,6 +306,8 @@ public: nDefaultPort = 18444; nPruneAfterHeight = 1000; + UpdateVersionBitsParametersFromArgs(args); + genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN); consensus.hashGenesisBlock = genesis.GetHash(); assert(consensus.hashGenesisBlock == uint256S("0x0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")); @@ -352,23 +343,65 @@ public: /* enable fallback fee on regtest */ m_fallback_fee_enabled = true; } + + /** + * Allows modifying the Version Bits regtest parameters. + */ + void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout) + { + consensus.vDeployments[d].nStartTime = nStartTime; + consensus.vDeployments[d].nTimeout = nTimeout; + } + void UpdateVersionBitsParametersFromArgs(const ArgsManager& args); }; -static std::unique_ptr<CChainParams> globalChainParams; +void CRegTestParams::UpdateVersionBitsParametersFromArgs(const ArgsManager& args) +{ + if (!args.IsArgSet("-vbparams")) return; + + for (const std::string& strDeployment : args.GetArgs("-vbparams")) { + std::vector<std::string> vDeploymentParams; + boost::split(vDeploymentParams, strDeployment, boost::is_any_of(":")); + if (vDeploymentParams.size() != 3) { + throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end"); + } + int64_t nStartTime, nTimeout; + if (!ParseInt64(vDeploymentParams[1], &nStartTime)) { + throw std::runtime_error(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1])); + } + if (!ParseInt64(vDeploymentParams[2], &nTimeout)) { + throw std::runtime_error(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2])); + } + bool found = false; + for (int j=0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) { + if (vDeploymentParams[0] == VersionBitsDeploymentInfo[j].name) { + UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout); + found = true; + LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld\n", vDeploymentParams[0], nStartTime, nTimeout); + break; + } + } + if (!found) { + throw std::runtime_error(strprintf("Invalid deployment (%s)", vDeploymentParams[0])); + } + } +} + +static std::unique_ptr<const CChainParams> globalChainParams; const CChainParams &Params() { assert(globalChainParams); return *globalChainParams; } -std::unique_ptr<CChainParams> CreateChainParams(const std::string& chain) +std::unique_ptr<const CChainParams> CreateChainParams(const std::string& chain) { if (chain == CBaseChainParams::MAIN) return std::unique_ptr<CChainParams>(new CMainParams()); else if (chain == CBaseChainParams::TESTNET) return std::unique_ptr<CChainParams>(new CTestNetParams()); else if (chain == CBaseChainParams::REGTEST) - return std::unique_ptr<CChainParams>(new CRegTestParams()); + return std::unique_ptr<CChainParams>(new CRegTestParams(gArgs)); throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain)); } @@ -377,8 +410,3 @@ void SelectParams(const std::string& network) SelectBaseParams(network); globalChainParams = CreateChainParams(network); } - -void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout) -{ - globalChainParams->UpdateVersionBitsParameters(d, nStartTime, nTimeout); -} diff --git a/src/chainparams.h b/src/chainparams.h index 722e52ff40..19818b40af 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -80,7 +80,6 @@ public: const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; } const CCheckpointData& Checkpoints() const { return checkpointData; } const ChainTxData& TxData() const { return chainTxData; } - void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout); protected: CChainParams() {} @@ -107,7 +106,7 @@ protected: * @returns a CChainParams* of the chosen chain. * @throws a std::runtime_error if the chain is not supported. */ -std::unique_ptr<CChainParams> CreateChainParams(const std::string& chain); +std::unique_ptr<const CChainParams> CreateChainParams(const std::string& chain); /** * Return the currently selected parameters. This won't change after app @@ -121,9 +120,4 @@ const CChainParams &Params(); */ void SelectParams(const std::string& chain); -/** - * Allows modifying the Version Bits regtest parameters. - */ -void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout); - #endif // BITCOIN_CHAINPARAMS_H diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index e9e8ce03b4..870640e77d 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -20,6 +20,7 @@ void SetupChainParamsBaseOptions() gArgs.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. " "This is intended for regression testing tools and app development.", true, OptionsCategory::CHAINPARAMS); gArgs.AddArg("-testnet", "Use the test chain", false, OptionsCategory::CHAINPARAMS); + gArgs.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", true, OptionsCategory::CHAINPARAMS); } static std::unique_ptr<CBaseChainParams> globalChainBaseParams; diff --git a/src/coins.cpp b/src/coins.cpp index da1036acfb..f125b483bb 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -244,7 +244,7 @@ bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const return true; } -static const size_t MIN_TRANSACTION_OUTPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxOut(), SER_NETWORK, PROTOCOL_VERSION); +static const size_t MIN_TRANSACTION_OUTPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxOut(), PROTOCOL_VERSION); static const size_t MAX_OUTPUTS_PER_BLOCK = MAX_BLOCK_WEIGHT / MIN_TRANSACTION_OUTPUT_WEIGHT; const Coin& AccessByTxid(const CCoinsViewCache& view, const uint256& txid) diff --git a/src/coins.h b/src/coins.h index 41a422f485..3867a37b39 100644 --- a/src/coins.h +++ b/src/coins.h @@ -285,8 +285,8 @@ public: * Note that lightweight clients may not know anything besides the hash of previous transactions, * so may not be able to calculate this. * - * @param[in] tx transaction for which we are checking input total - * @return Sum of value of all inputs (scriptSigs) + * @param[in] tx transaction for which we are checking input total + * @return Sum of value of all inputs (scriptSigs) */ CAmount GetValueIn(const CTransaction& tx) const; diff --git a/src/compat.h b/src/compat.h index 19f9813fd4..d228611160 100644 --- a/src/compat.h +++ b/src/compat.h @@ -14,10 +14,10 @@ // GCC 4.8 is missing some C++11 type_traits, // https://www.gnu.org/software/gcc/gcc-5/changes.html -#if defined(__GNUC__) && __GNUC__ < 5 -#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivial +#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5 +#define IS_TRIVIALLY_CONSTRUCTIBLE std::has_trivial_default_constructor #else -#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivially_constructible +#define IS_TRIVIALLY_CONSTRUCTIBLE std::is_trivially_default_constructible #endif #ifdef WIN32 diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index be73d0a2f9..0628ec1d47 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -164,7 +164,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe if (tx.vout.empty()) return state.DoS(10, false, REJECT_INVALID, "bad-txns-vout-empty"); // Size limits (this doesn't take the witness into account, as that hasn't been checked for malleability) - if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT) + if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT) return state.DoS(100, false, REJECT_INVALID, "bad-txns-oversize"); // Check for negative or overflow output values diff --git a/src/consensus/validation.h b/src/consensus/validation.h index 008eda69b2..f2e2c3585a 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -95,16 +95,16 @@ public: // weight = (stripped_size * 3) + total_size. static inline int64_t GetTransactionWeight(const CTransaction& tx) { - return ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + return ::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(tx, PROTOCOL_VERSION); } static inline int64_t GetBlockWeight(const CBlock& block) { - return ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION); + return ::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, PROTOCOL_VERSION); } static inline int64_t GetTransactionInputWeight(const CTxIn& txin) { // scriptWitness size is added here because witnesses and txins are split up in segwit serialization. - return ::GetSerializeSize(txin, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(txin, SER_NETWORK, PROTOCOL_VERSION) + ::GetSerializeSize(txin.scriptWitness.stack, SER_NETWORK, PROTOCOL_VERSION); + return ::GetSerializeSize(txin, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(txin, PROTOCOL_VERSION) + ::GetSerializeSize(txin.scriptWitness.stack, PROTOCOL_VERSION); } #endif // BITCOIN_CONSENSUS_VALIDATION_H diff --git a/src/core_write.cpp b/src/core_write.cpp index 6331a3d880..b86490716f 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -141,8 +141,7 @@ void ScriptToUniv(const CScript& script, UniValue& out, bool include_address) out.pushKV("hex", HexStr(script.begin(), script.end())); std::vector<std::vector<unsigned char>> solns; - txnouttype type; - Solver(script, type, solns); + txnouttype type = Solver(script, solns); out.pushKV("type", GetTxnOutputType(type)); CTxDestination address; @@ -182,7 +181,7 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, entry.pushKV("txid", tx.GetHash().GetHex()); entry.pushKV("hash", tx.GetWitnessHash().GetHex()); entry.pushKV("version", tx.nVersion); - entry.pushKV("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)); + entry.pushKV("size", (int)::GetSerializeSize(tx, PROTOCOL_VERSION)); entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR); entry.pushKV("weight", GetTransactionWeight(tx)); entry.pushKV("locktime", (int64_t)tx.nLockTime); diff --git a/src/cuckoocache.h b/src/cuckoocache.h index 15f6873961..4d0b094fa2 100644 --- a/src/cuckoocache.h +++ b/src/cuckoocache.h @@ -397,7 +397,7 @@ public: std::array<uint32_t, 8> locs = compute_hashes(e); // Make sure we have not already inserted this element // If we have, make sure that it does not get deleted - for (uint32_t loc : locs) + for (const uint32_t loc : locs) if (table[loc] == e) { please_keep(loc); epoch_flags[loc] = last_epoch; @@ -405,7 +405,7 @@ public: } for (uint8_t depth = 0; depth < depth_limit; ++depth) { // First try to insert to an empty slot, if one exists - for (uint32_t loc : locs) { + for (const uint32_t loc : locs) { if (!collection_flags.bit_is_set(loc)) continue; table[loc] = std::move(e); @@ -467,7 +467,7 @@ public: inline bool contains(const Element& e, const bool erase) const { std::array<uint32_t, 8> locs = compute_hashes(e); - for (uint32_t loc : locs) + for (const uint32_t loc : locs) if (table[loc] == e) { if (erase) allow_erase(loc); diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index f5fb715800..58d8cc2c9d 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -78,7 +78,7 @@ static void SetMaxOpenFiles(leveldb::Options *options) { // do not interfere with select() loops. On 64-bit Unix hosts this value is // also OK, because up to that amount LevelDB will use an mmap // implementation that does not use extra file descriptors (the fds are - // closed after being mmaped). + // closed after being mmap'ed). // // Increasing the value beyond the default is dangerous because LevelDB will // fall back to a non-mmap implementation when the file count is too large. diff --git a/src/dummywallet.cpp b/src/dummywallet.cpp new file mode 100644 index 0000000000..3714187a96 --- /dev/null +++ b/src/dummywallet.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <stdio.h> +#include <util.h> +#include <walletinitinterface.h> + +class CWallet; + +class DummyWalletInit : public WalletInitInterface { +public: + + bool HasWalletSupport() const override {return false;} + void AddWalletOptions() const override; + bool ParameterInteraction() const override {return true;} + void RegisterRPC(CRPCTable &) const override {} + bool Verify() const override {return true;} + bool Open() const override {LogPrintf("No wallet support compiled in!\n"); return true;} + void Start(CScheduler& scheduler) const override {} + void Flush() const override {} + void Stop() const override {} + void Close() const override {} +}; + +void DummyWalletInit::AddWalletOptions() const +{ + std::vector<std::string> opts = {"-addresstype", "-changetype", "-disablewallet", "-discardfee=<amt>", "-fallbackfee=<amt>", + "-keypool=<n>", "-mintxfee=<amt>", "-paytxfee=<amt>", "-rescan", "-salvagewallet", "-spendzeroconfchange", "-txconfirmtarget=<n>", + "-upgradewallet", "-wallet=<path>", "-walletbroadcast", "-walletdir=<dir>", "-walletnotify=<cmd>", "-walletrbf", "-zapwallettxes=<mode>", + "-dblogsize=<n>", "-flushwallet", "-privdb", "-walletrejectlongchains"}; + gArgs.AddHiddenArgs(opts); +} + +const WalletInitInterface& g_wallet_init_interface = DummyWalletInit(); + +std::vector<std::shared_ptr<CWallet>> GetWallets() +{ + throw std::logic_error("Wallet function called in non-wallet build."); +} + +namespace interfaces { + +class Wallet; + +std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) +{ + throw std::logic_error("Wallet function called in non-wallet build."); +} + +} // namespace interfaces diff --git a/src/fs.cpp b/src/fs.cpp index 570ed3e2ee..df79b5e3df 100644 --- a/src/fs.cpp +++ b/src/fs.cpp @@ -1,15 +1,116 @@ #include <fs.h> +#ifndef WIN32 +#include <fcntl.h> +#else +#include <codecvt> +#include <windows.h> +#endif + namespace fsbridge { FILE *fopen(const fs::path& p, const char *mode) { +#ifndef WIN32 return ::fopen(p.string().c_str(), mode); +#else + std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t> utf8_cvt; + return ::_wfopen(p.wstring().c_str(), utf8_cvt.from_bytes(mode).c_str()); +#endif +} + +#ifndef WIN32 + +static std::string GetErrorReason() { + return std::strerror(errno); +} + +FileLock::FileLock(const fs::path& file) +{ + fd = open(file.string().c_str(), O_RDWR); + if (fd == -1) { + reason = GetErrorReason(); + } } -FILE *freopen(const fs::path& p, const char *mode, FILE *stream) +FileLock::~FileLock() { - return ::freopen(p.string().c_str(), mode, stream); + if (fd != -1) { + close(fd); + } +} + +bool FileLock::TryLock() +{ + if (fd == -1) { + return false; + } + struct flock lock; + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + if (fcntl(fd, F_SETLK, &lock) == -1) { + reason = GetErrorReason(); + return false; + } + return true; +} +#else + +static std::string GetErrorReason() { + wchar_t* err; + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<WCHAR*>(&err), 0, nullptr); + std::wstring err_str(err); + LocalFree(err); + return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(err_str); +} + +FileLock::FileLock(const fs::path& file) +{ + hFile = CreateFileW(file.wstring().c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile == INVALID_HANDLE_VALUE) { + reason = GetErrorReason(); + } +} + +FileLock::~FileLock() +{ + if (hFile != INVALID_HANDLE_VALUE) { + CloseHandle(hFile); + } +} + +bool FileLock::TryLock() +{ + if (hFile == INVALID_HANDLE_VALUE) { + return false; + } + _OVERLAPPED overlapped = {0}; + if (!LockFileEx(hFile, LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY, 0, 0, 0, &overlapped)) { + reason = GetErrorReason(); + return false; + } + return true; +} +#endif + +std::string get_filesystem_error_message(const fs::filesystem_error& e) +{ +#ifndef WIN32 + return e.what(); +#else + // Convert from Multi Byte to utf-16 + std::string mb_string(e.what()); + int size = MultiByteToWideChar(CP_ACP, 0, mb_string.c_str(), mb_string.size(), nullptr, 0); + + std::wstring utf16_string(size, L'\0'); + MultiByteToWideChar(CP_ACP, 0, mb_string.c_str(), mb_string.size(), &*utf16_string.begin(), size); + // Convert from utf-16 to utf-8 + return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>().to_bytes(utf16_string); +#endif } } // fsbridge @@ -10,7 +10,6 @@ #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> -#include <boost/filesystem/detail/utf8_codecvt_facet.hpp> /** Filesystem operations and types */ namespace fs = boost::filesystem; @@ -18,7 +17,28 @@ namespace fs = boost::filesystem; /** Bridge operations to C stdio */ namespace fsbridge { FILE *fopen(const fs::path& p, const char *mode); - FILE *freopen(const fs::path& p, const char *mode, FILE *stream); + + class FileLock + { + public: + FileLock() = delete; + FileLock(const FileLock&) = delete; + FileLock(FileLock&&) = delete; + explicit FileLock(const fs::path& file); + ~FileLock(); + bool TryLock(); + std::string GetReason() { return reason; } + + private: + std::string reason; +#ifndef WIN32 + int fd = -1; +#else + void* hFile = (void*)-1; // INVALID_HANDLE_VALUE +#endif + }; + + std::string get_filesystem_error_message(const fs::filesystem_error& e); }; #endif // BITCOIN_FS_H diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 38f6e79643..4064251c71 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -14,6 +14,7 @@ #include <util.h> #include <utilstrencodings.h> #include <ui_interface.h> +#include <walletinitinterface.h> #include <crypto/hmac_sha256.h> #include <stdio.h> @@ -30,7 +31,7 @@ static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\""; class HTTPRPCTimer : public RPCTimerBase { public: - HTTPRPCTimer(struct event_base* eventBase, std::function<void(void)>& func, int64_t millis) : + HTTPRPCTimer(struct event_base* eventBase, std::function<void()>& func, int64_t millis) : ev(eventBase, false, func) { struct timeval tv; @@ -52,7 +53,7 @@ public: { return "HTTP"; } - RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis) override + RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis) override { return new HTTPRPCTimer(base, func, millis); } @@ -240,10 +241,9 @@ bool StartHTTPRPC() return false; RegisterHTTPHandler("/", true, HTTPReq_JSONRPC); -#ifdef ENABLE_WALLET - // ifdef can be removed once we switch to better endpoint support and API versioning - RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC); -#endif + if (g_wallet_init_interface.HasWalletSupport()) { + RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC); + } struct event_base* eventBase = EventBase(); assert(eventBase); httpRPCTimerInterface = MakeUnique<HTTPRPCTimerInterface>(eventBase); @@ -260,9 +260,9 @@ void StopHTTPRPC() { LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n"); UnregisterHTTPHandler("/", true); -#ifdef ENABLE_WALLET - UnregisterHTTPHandler("/wallet/", false); -#endif + if (g_wallet_init_interface.HasWalletSupport()) { + UnregisterHTTPHandler("/wallet/", false); + } if (httpRPCTimerInterface) { RPCUnsetTimerInterface(httpRPCTimerInterface.get()); httpRPCTimerInterface.reset(); diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 0fdc3e5ff3..c29f7a4375 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -69,7 +69,7 @@ class WorkQueue { private: /** Mutex protects entire object */ - std::mutex cs; + Mutex cs; std::condition_variable cond; std::deque<std::unique_ptr<WorkItem>> queue; bool running; @@ -88,7 +88,7 @@ public: /** Enqueue a work item */ bool Enqueue(WorkItem* item) { - std::unique_lock<std::mutex> lock(cs); + LOCK(cs); if (queue.size() >= maxDepth) { return false; } @@ -102,7 +102,7 @@ public: while (true) { std::unique_ptr<WorkItem> i; { - std::unique_lock<std::mutex> lock(cs); + WAIT_LOCK(cs, lock); while (running && queue.empty()) cond.wait(lock); if (!running) @@ -116,7 +116,7 @@ public: /** Interrupt and exit loops */ void Interrupt() { - std::unique_lock<std::mutex> lock(cs); + LOCK(cs); running = false; cond.notify_all(); } @@ -352,13 +352,6 @@ bool InitHTTPServer() if (!InitHTTPAllowList()) return false; - if (gArgs.GetBoolArg("-rpcssl", false)) { - uiInterface.ThreadSafeMessageBox( - "SSL mode for RPC (-rpcssl) is no longer supported.", - "", CClientUIInterface::MSG_ERROR); - return false; - } - // Redirect libevent's logging to our own log event_set_log_callback(&libevent_log_cb); // Update libevent's log handling. Returns false if our version of @@ -505,7 +498,7 @@ static void httpevent_callback_fn(evutil_socket_t, short, void* data) delete self; } -HTTPEvent::HTTPEvent(struct event_base* base, bool _deleteWhenTriggered, const std::function<void(void)>& _handler): +HTTPEvent::HTTPEvent(struct event_base* base, bool _deleteWhenTriggered, const std::function<void()>& _handler): deleteWhenTriggered(_deleteWhenTriggered), handler(_handler) { ev = event_new(base, -1, 0, httpevent_callback_fn, this); diff --git a/src/httpserver.h b/src/httpserver.h index 67c6a88314..63f96734f8 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -134,7 +134,7 @@ public: * deleteWhenTriggered deletes this event object after the event is triggered (and the handler called) * handler is the handler to call when the event is triggered. */ - HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const std::function<void(void)>& handler); + HTTPEvent(struct event_base* base, bool deleteWhenTriggered, const std::function<void()>& handler); ~HTTPEvent(); /** Trigger the event. If tv is 0, trigger it immediately. Otherwise trigger it after @@ -143,7 +143,7 @@ public: void trigger(struct timeval* tv); bool deleteWhenTriggered; - std::function<void(void)> handler; + std::function<void()> handler; private: struct event* ev; }; diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp index c85030e18e..f606c8993c 100644 --- a/src/index/txindex.cpp +++ b/src/index/txindex.cpp @@ -250,7 +250,7 @@ bool TxIndex::WriteBlock(const CBlock& block, const CBlockIndex* pindex) vPos.reserve(block.vtx.size()); for (const auto& tx : block.vtx) { vPos.emplace_back(tx->GetHash(), pos); - pos.nTxOffset += ::GetSerializeSize(*tx, SER_DISK, CLIENT_VERSION); + pos.nTxOffset += ::GetSerializeSize(*tx, CLIENT_VERSION); } return m_db->WriteTxs(vPos); } diff --git a/src/init.cpp b/src/init.cpp index 2131a6adc0..f9efaf7dc1 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -51,13 +51,13 @@ #ifndef WIN32 #include <signal.h> +#include <sys/stat.h> #endif #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/split.hpp> #include <boost/bind.hpp> -#include <boost/interprocess/sync/file_lock.hpp> #include <boost/thread.hpp> #include <openssl/crypto.h> @@ -74,33 +74,6 @@ static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false; std::unique_ptr<CConnman> g_connman; std::unique_ptr<PeerLogicValidation> peerLogic; -#if !(ENABLE_WALLET) -class DummyWalletInit : public WalletInitInterface { -public: - - void AddWalletOptions() const override; - bool ParameterInteraction() const override {return true;} - void RegisterRPC(CRPCTable &) const override {} - bool Verify() const override {return true;} - bool Open() const override {LogPrintf("No wallet support compiled in!\n"); return true;} - void Start(CScheduler& scheduler) const override {} - void Flush() const override {} - void Stop() const override {} - void Close() const override {} -}; - -void DummyWalletInit::AddWalletOptions() const -{ - std::vector<std::string> opts = {"-addresstype", "-changetype", "-disablewallet", "-discardfee=<amt>", "-fallbackfee=<amt>", - "-keypool=<n>", "-mintxfee=<amt>", "-paytxfee=<amt>", "-rescan", "-salvagewallet", "-spendzeroconfchange", "-txconfirmtarget=<n>", - "-upgradewallet", "-wallet=<path>", "-walletbroadcast", "-walletdir=<dir>", "-walletnotify=<cmd>", "-walletrbf", "-zapwallettxes=<mode>", - "-dblogsize=<n>", "-flushwallet", "-privdb", "-walletrejectlongchains"}; - gArgs.AddHiddenArgs(opts); -} - -const WalletInitInterface& g_wallet_init_interface = DummyWalletInit(); -#endif - #ifdef WIN32 // Win32 LevelDB doesn't use filedescriptors, and the ones used for // accessing block files don't count towards the fd_set size limit @@ -343,12 +316,14 @@ void SetupServerArgs() { const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN); const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET); + const auto regtestBaseParams = CreateBaseChainParams(CBaseChainParams::REGTEST); const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN); const auto testnetChainParams = CreateChainParams(CBaseChainParams::TESTNET); + const auto regtestChainParams = CreateChainParams(CBaseChainParams::REGTEST); // Hidden Options - std::vector<std::string> hidden_args = {"-rpcssl", "-benchmark", "-h", "-help", "-socks", "-tor", "-debugnet", "-whitelistalwaysrelay", - "-prematurewitness", "-walletprematurewitness", "-promiscuousmempoolflags", "-blockminsize", "-dbcrashratio", "-forcecompactdb", "-usehd", + std::vector<std::string> hidden_args = {"-h", "-help", + "-dbcrashratio", "-forcecompactdb", // GUI args. These will be overwritten by SetupUIArgs for the GUI "-allowselfsignedrootcertificates", "-choosedatadir", "-lang=<lang>", "-min", "-resetguisettings", "-rootcertificates=<file>", "-splash", "-uiplatform"}; @@ -366,7 +341,7 @@ void SetupServerArgs() gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS); gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), true, OptionsCategory::OPTIONS); gArgs.AddArg("-dbcache=<n>", strprintf("Set database cache size in megabytes (%d to %d, default: %d)", nMinDbCache, nMaxDbCache, nDefaultDbCache), false, OptionsCategory::OPTIONS); - gArgs.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (0 to disable; default: %s)", DEFAULT_DEBUGLOGFILE), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (-nodebuglogfile to disable; default: %s)", DEFAULT_DEBUGLOGFILE), false, OptionsCategory::OPTIONS); gArgs.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER), true, OptionsCategory::OPTIONS); gArgs.AddArg("-includeconf=<file>", "Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)", false, OptionsCategory::OPTIONS); gArgs.AddArg("-loadblock=<file>", "Imports blocks from external blk000??.dat file on startup", false, OptionsCategory::OPTIONS); @@ -386,7 +361,7 @@ void SetupServerArgs() "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)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), false, OptionsCategory::OPTIONS); gArgs.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk", false, OptionsCategory::OPTIONS); - gArgs.AddArg("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks. When in pruning mode or if blocks on disk might be corrupted, use full -reindex instead.", false, OptionsCategory::OPTIONS); #ifndef WIN32 gArgs.AddArg("-sysperms", "Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)", false, OptionsCategory::OPTIONS); #else @@ -398,7 +373,7 @@ void SetupServerArgs() gArgs.AddArg("-banscore=<n>", strprintf("Threshold for disconnecting misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), false, OptionsCategory::CONNECTION); gArgs.AddArg("-bantime=<n>", strprintf("Number of seconds to keep misbehaving peers from reconnecting (default: %u)", DEFAULT_MISBEHAVING_BANTIME), false, OptionsCategory::CONNECTION); gArgs.AddArg("-bind=<addr>", "Bind to given address and always listen on it. Use [host]:port notation for IPv6", false, OptionsCategory::CONNECTION); - gArgs.AddArg("-connect=<ip>", "Connect only to the specified node; -connect=0 disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-connect=<ip>", "Connect only to the specified node; -noconnect disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", false, OptionsCategory::CONNECTION); gArgs.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", false, OptionsCategory::CONNECTION); gArgs.AddArg("-dns", strprintf("Allow DNS lookups for -addnode, -seednode and -connect (default: %u)", DEFAULT_NAME_LOOKUP), false, OptionsCategory::CONNECTION); gArgs.AddArg("-dnsseed", "Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)", false, OptionsCategory::CONNECTION); @@ -412,12 +387,12 @@ void SetupServerArgs() gArgs.AddArg("-maxsendbuffer=<n>", strprintf("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXSENDBUFFER), false, OptionsCategory::CONNECTION); gArgs.AddArg("-maxtimeadjustment", strprintf("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)", DEFAULT_MAX_TIME_ADJUSTMENT), false, OptionsCategory::CONNECTION); gArgs.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), false, OptionsCategory::CONNECTION); - gArgs.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: -proxy)", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor hidden services, set -noonion to disable (default: -proxy)", false, OptionsCategory::CONNECTION); gArgs.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (ipv4, ipv6 or onion). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks.", false, OptionsCategory::CONNECTION); gArgs.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), false, OptionsCategory::CONNECTION); gArgs.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), false, OptionsCategory::CONNECTION); - gArgs.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u or testnet: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort()), false, OptionsCategory::CONNECTION); - gArgs.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u, testnet: %u, regtest: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", false, OptionsCategory::CONNECTION); gArgs.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), false, OptionsCategory::CONNECTION); gArgs.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect. This option can be specified multiple times to connect to multiple nodes.", false, OptionsCategory::CONNECTION); gArgs.AddArg("-timeout=<n>", strprintf("Specify connection timeout in milliseconds (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), false, OptionsCategory::CONNECTION); @@ -451,9 +426,16 @@ void SetupServerArgs() #endif gArgs.AddArg("-checkblocks=<n>", strprintf("How many blocks to check at startup (default: %u, 0 = all)", DEFAULT_CHECKBLOCKS), true, OptionsCategory::DEBUG_TEST); - gArgs.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is (0-4, default: %u)", DEFAULT_CHECKLEVEL), true, OptionsCategory::DEBUG_TEST); - gArgs.AddArg("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. (default: %u)", defaultChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST); - gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", defaultChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is: " + "level 0 reads the blocks from disk, " + "level 1 verifies block validity, " + "level 2 verifies undo data, " + "level 3 checks disconnection of tip blocks, " + "and level 4 tries to reconnect the blocks, " + "each level includes the checks of the previous levels " + "(0-4, default: %u)", DEFAULT_CHECKLEVEL), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used", true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages", true, OptionsCategory::DEBUG_TEST); @@ -463,12 +445,11 @@ void SetupServerArgs() gArgs.AddArg("-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT), true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT), true, OptionsCategory::DEBUG_TEST); - gArgs.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", true, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-addrmantest", "Allows to test address relay on localhost", true, OptionsCategory::DEBUG_TEST); - gArgs.AddArg("-debug=<category>", strprintf("Output debugging information (default: %u, supplying <category> is optional)", 0) + ". " + + gArgs.AddArg("-debug=<category>", "Output debugging information (default: -nodebug, supplying <category> is optional). " "If <category> is not supplied or if <category> = 1, output all debugging information. <category> can be: " + ListLogCategories() + ".", false, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-debugexclude=<category>", strprintf("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories."), false, OptionsCategory::DEBUG_TEST); - gArgs.AddArg("-help-debug", "Show all debugging options (usage: --help -help-debug)", false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-help-debug", "Print help message with debugging options and exit", false, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), false, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), false, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), true, OptionsCategory::DEBUG_TEST); @@ -478,7 +459,7 @@ void SetupServerArgs() gArgs.AddArg("-maxtxfee=<amt>", strprintf("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)", CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), false, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), true, OptionsCategory::DEBUG_TEST); - gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -daemon. To disable logging to file, set debuglogfile=0)", false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -daemon. To disable logging to file, set -nodebuglogfile)", false, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-shrinkdebugfile", "Shrink debug.log file on client startup (default: 1 when no -debug)", false, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-uacomment=<cmt>", "Append comment to the user agent string", false, OptionsCategory::DEBUG_TEST); @@ -507,7 +488,7 @@ void SetupServerArgs() gArgs.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)", false, OptionsCategory::RPC); gArgs.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", false, OptionsCategory::RPC); gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", false, OptionsCategory::RPC); - gArgs.AddArg("-rpcport=<port>", strprintf("Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()), false, OptionsCategory::RPC); + gArgs.AddArg("-rpcport=<port>", strprintf("Listen for JSON-RPC connections on <port> (default: %u, testnet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), false, OptionsCategory::RPC); gArgs.AddArg("-rpcserialversion", strprintf("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)", DEFAULT_RPC_SERIALIZE_VERSION), false, OptionsCategory::RPC); gArgs.AddArg("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), true, OptionsCategory::RPC); gArgs.AddArg("-rpcthreads=<n>", strprintf("Set the number of threads to service RPC calls (default: %d)", DEFAULT_HTTP_THREADS), false, OptionsCategory::RPC); @@ -561,17 +542,17 @@ static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex } static bool fHaveGenesis = false; -static CWaitableCriticalSection cs_GenesisWait; -static CConditionVariable condvar_GenesisWait; +static Mutex g_genesis_wait_mutex; +static std::condition_variable g_genesis_wait_cv; static void BlockNotifyGenesisWait(bool, const CBlockIndex *pBlockIndex) { if (pBlockIndex != nullptr) { { - WaitableLock lock_GenesisWait(cs_GenesisWait); + LOCK(g_genesis_wait_mutex); fHaveGenesis = true; } - condvar_GenesisWait.notify_all(); + g_genesis_wait_cv.notify_all(); } } @@ -709,7 +690,7 @@ static void ThreadImport(std::vector<fs::path> vImportFiles) * Ensure that Bitcoin is running in a usable environment with all * necessary library support. */ -static bool InitSanityCheck(void) +static bool InitSanityCheck() { if(!ECC_InitSanityCheck()) { InitError("Elliptic curve cryptography sanity check failure. Aborting."); @@ -979,25 +960,6 @@ bool AppInitParameterInteraction() } } - // Check for -debugnet - if (gArgs.GetBoolArg("-debugnet", false)) - InitWarning(_("Unsupported argument -debugnet ignored, use -debug=net.")); - // Check for -socks - as this is a privacy risk to continue, exit here - if (gArgs.IsArgSet("-socks")) - return InitError(_("Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported.")); - // Check for -tor - as this is a privacy risk to continue, exit here - if (gArgs.GetBoolArg("-tor", false)) - return InitError(_("Unsupported argument -tor found, use -onion.")); - - if (gArgs.GetBoolArg("-benchmark", false)) - InitWarning(_("Unsupported argument -benchmark ignored, use -debug=bench.")); - - if (gArgs.GetBoolArg("-whitelistalwaysrelay", false)) - InitWarning(_("Unsupported argument -whitelistalwaysrelay ignored, use -whitelistrelay and/or -whitelistforcerelay.")); - - if (gArgs.IsArgSet("-blockminsize")) - InitWarning("Unsupported argument -blockminsize ignored."); - // Checkmempool and checkblockindex default to true in regtest mode int ratio = std::min<int>(std::max<int>(gArgs.GetArg("-checkmempool", chainparams.DefaultConsistencyChecks() ? 1 : 0), 0), 1000000); if (ratio != 0) { @@ -1138,39 +1100,6 @@ bool AppInitParameterInteraction() fEnableReplacement = (std::find(vstrReplacementModes.begin(), vstrReplacementModes.end(), "fee") != vstrReplacementModes.end()); } - if (gArgs.IsArgSet("-vbparams")) { - // Allow overriding version bits parameters for testing - if (!chainparams.MineBlocksOnDemand()) { - return InitError("Version bits parameters may only be overridden on regtest."); - } - for (const std::string& strDeployment : gArgs.GetArgs("-vbparams")) { - std::vector<std::string> vDeploymentParams; - boost::split(vDeploymentParams, strDeployment, boost::is_any_of(":")); - if (vDeploymentParams.size() != 3) { - return InitError("Version bits parameters malformed, expecting deployment:start:end"); - } - int64_t nStartTime, nTimeout; - if (!ParseInt64(vDeploymentParams[1], &nStartTime)) { - return InitError(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1])); - } - if (!ParseInt64(vDeploymentParams[2], &nTimeout)) { - return InitError(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2])); - } - bool found = false; - for (int j=0; j<(int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) - { - if (vDeploymentParams[0].compare(VersionBitsDeploymentInfo[j].name) == 0) { - UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout); - found = true; - LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld\n", vDeploymentParams[0], nStartTime, nTimeout); - break; - } - } - if (!found) { - return InitError(strprintf("Invalid deployment (%s)", vDeploymentParams[0])); - } - } - } return true; } @@ -1243,7 +1172,19 @@ bool AppInitMain() LogPrintf("Startup time: %s\n", FormatISO8601DateTime(GetTime())); LogPrintf("Default data directory %s\n", GetDefaultDataDir().string()); LogPrintf("Using data directory %s\n", GetDataDir().string()); - LogPrintf("Using config file %s\n", GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string()); + + // Only log conf file usage message if conf file actually exists. + fs::path config_file_path = GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); + if (fs::exists(config_file_path)) { + LogPrintf("Config file: %s\n", config_file_path.string()); + } else if (gArgs.IsArgSet("-conf")) { + // Warn if no conf file exists at path provided by user + InitWarning(strprintf(_("The specified config file %s does not exist\n"), config_file_path.string())); + } else { + // Not categorizing as "Warning" because it's the default behavior + LogPrintf("Config file: %s (not found, skipping)\n", config_file_path.string()); + } + LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD); // Warn about relative -datadir path. @@ -1661,12 +1602,12 @@ bool AppInitMain() // Wait for genesis block to be processed { - WaitableLock lock(cs_GenesisWait); + WAIT_LOCK(g_genesis_wait_mutex, lock); // We previously could hang here if StartShutdown() is called prior to // ThreadImport getting started, so instead we just wait on a timer to // check ShutdownRequested() regularly. while (!fHaveGenesis && !ShutdownRequested()) { - condvar_GenesisWait.wait_for(lock, std::chrono::milliseconds(500)); + g_genesis_wait_cv.wait_for(lock, std::chrono::milliseconds(500)); } uiInterface.NotifyBlockTip_disconnect(BlockNotifyGenesisWait); } diff --git a/src/init.h b/src/init.h index 0c85d3c9dc..c58ba5cfd3 100644 --- a/src/init.h +++ b/src/init.h @@ -13,9 +13,6 @@ class CScheduler; class CWallet; -class WalletInitInterface; -extern const WalletInitInterface& g_wallet_init_interface; - namespace boost { class thread_group; diff --git a/src/interfaces/handler.cpp b/src/interfaces/handler.cpp index 80f461f4d3..ddf9b342d8 100644 --- a/src/interfaces/handler.cpp +++ b/src/interfaces/handler.cpp @@ -15,7 +15,7 @@ namespace { class HandlerImpl : public Handler { public: - HandlerImpl(boost::signals2::connection connection) : m_connection(std::move(connection)) {} + explicit HandlerImpl(boost::signals2::connection connection) : m_connection(std::move(connection)) {} void disconnect() override { m_connection.disconnect(); } diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index 1da58fe487..d95b41657c 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -32,19 +32,18 @@ #if defined(HAVE_CONFIG_H) #include <config/bitcoin-config.h> #endif -#ifdef ENABLE_WALLET -#include <wallet/fees.h> -#include <wallet/wallet.h> -#define CHECK_WALLET(x) x -#else -#define CHECK_WALLET(x) throw std::logic_error("Wallet function called in non-wallet build.") -#endif #include <atomic> #include <boost/thread/thread.hpp> #include <univalue.h> +class CWallet; +std::vector<std::shared_ptr<CWallet>> GetWallets(); + namespace interfaces { + +class Wallet; + namespace { class NodeImpl : public Node @@ -221,15 +220,11 @@ class NodeImpl : public Node } std::vector<std::unique_ptr<Wallet>> getWallets() override { -#ifdef ENABLE_WALLET std::vector<std::unique_ptr<Wallet>> wallets; for (const std::shared_ptr<CWallet>& wallet : GetWallets()) { wallets.emplace_back(MakeWallet(wallet)); } return wallets; -#else - throw std::logic_error("Node::getWallets() called in non-wallet build."); -#endif } std::unique_ptr<Handler> handleInitMessage(InitMessageFn fn) override { @@ -249,8 +244,7 @@ class NodeImpl : public Node } std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) override { - CHECK_WALLET( - return MakeHandler(::uiInterface.LoadWallet_connect([fn](std::shared_ptr<CWallet> wallet) { fn(MakeWallet(wallet)); }))); + return MakeHandler(::uiInterface.LoadWallet_connect([fn](std::shared_ptr<CWallet> wallet) { fn(MakeWallet(wallet)); })); } std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override { diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp index 55a6f771e5..566ae37509 100644 --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -31,7 +31,7 @@ namespace { class PendingWalletTxImpl : public PendingWalletTx { public: - PendingWalletTxImpl(CWallet& wallet) : m_wallet(wallet), m_key(&wallet) {} + explicit PendingWalletTxImpl(CWallet& wallet) : m_wallet(wallet), m_key(&wallet) {} const CTransaction& get() override { return *m_tx; } @@ -39,12 +39,11 @@ public: bool commit(WalletValueMap value_map, WalletOrderForm order_form, - std::string from_account, std::string& reject_reason) override { LOCK2(cs_main, m_wallet.cs_wallet); CValidationState state; - if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), std::move(from_account), m_key, g_connman.get(), state)) { + if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_key, g_connman.get(), state)) { reject_reason = state.GetRejectReason(); return false; } @@ -57,7 +56,7 @@ public: }; //! Construct wallet tx struct. -WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx) +static WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { WalletTx result; result.tx = wtx.tx; @@ -85,7 +84,7 @@ WalletTx MakeWalletTx(CWallet& wallet, const CWalletTx& wtx) } //! Construct wallet tx status struct. -WalletTxStatus MakeWalletTxStatus(const CWalletTx& wtx) +static WalletTxStatus MakeWalletTxStatus(const CWalletTx& wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { WalletTxStatus result; auto mi = ::mapBlockIndex.find(wtx.hashBlock); @@ -104,7 +103,7 @@ WalletTxStatus MakeWalletTxStatus(const CWalletTx& wtx) } //! Construct wallet TxOut struct. -WalletTxOut MakeWalletTxOut(CWallet& wallet, const CWalletTx& wtx, int n, int depth) +static WalletTxOut MakeWalletTxOut(CWallet& wallet, const CWalletTx& wtx, int n, int depth) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { WalletTxOut result; result.txout = wtx.tx->vout[n]; @@ -117,7 +116,7 @@ WalletTxOut MakeWalletTxOut(CWallet& wallet, const CWalletTx& wtx, int n, int de class WalletImpl : public Wallet { public: - WalletImpl(const std::shared_ptr<CWallet>& wallet) : m_shared_wallet(wallet), m_wallet(*wallet.get()) {} + explicit WalletImpl(const std::shared_ptr<CWallet>& wallet) : m_shared_wallet(wallet), m_wallet(*wallet.get()) {} bool encryptWallet(const SecureString& wallet_passphrase) override { diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index ae54d42442..7aa91f37e1 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -289,7 +289,6 @@ public: //! Send pending transaction and commit to wallet. virtual bool commit(WalletValueMap value_map, WalletOrderForm order_form, - std::string from_account, std::string& reject_reason) = 0; }; @@ -367,8 +366,8 @@ struct WalletTxOut bool is_spent = false; }; -//! Return implementation of Wallet interface. This function will be undefined -//! in builds where ENABLE_WALLET is false. +//! Return implementation of Wallet interface. This function is defined in +//! dummywallet.cpp and throws if the wallet component is not compiled. std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet); } // namespace interfaces diff --git a/src/key.cpp b/src/key.cpp index df452cd330..80d6589a3c 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -89,7 +89,7 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou * will be set to the number of bytes used in the buffer. * key32 must point to a 32-byte raw private key. */ -static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, int compressed) { +static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, bool compressed) { assert(*privkeylen >= CKey::PRIVATE_KEY_SIZE); secp256k1_pubkey pubkey; size_t pubkeylen = 0; @@ -170,7 +170,7 @@ CPrivKey CKey::GetPrivKey() const { size_t privkeylen; privkey.resize(PRIVATE_KEY_SIZE); privkeylen = PRIVATE_KEY_SIZE; - ret = ec_privkey_export_der(secp256k1_context_sign, privkey.data(), &privkeylen, begin(), fCompressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); + ret = ec_privkey_export_der(secp256k1_context_sign, privkey.data(), &privkeylen, begin(), fCompressed); assert(ret); privkey.resize(privkeylen); return privkey; @@ -181,12 +181,12 @@ struct CExtKey { }; /** Initialize the elliptic curve support. May not be called twice without calling ECC_Stop first. */ -void ECC_Start(void); +void ECC_Start(); /** Deinitialize the elliptic curve support. No-op if ECC_Start wasn't called first. */ -void ECC_Stop(void); +void ECC_Stop(); /** Check that required EC support is available at runtime. */ -bool ECC_InitSanityCheck(void); +bool ECC_InitSanityCheck(); #endif // BITCOIN_KEY_H diff --git a/src/key_io.cpp b/src/key_io.cpp index 5c5e2dc031..c6a541cdb1 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -24,7 +24,7 @@ private: const CChainParams& m_params; public: - DestinationEncoder(const CChainParams& params) : m_params(params) {} + explicit DestinationEncoder(const CChainParams& params) : m_params(params) {} std::string operator()(const CKeyID& id) const { diff --git a/src/logging.cpp b/src/logging.cpp index 6557dddffb..0ae4f8121e 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -219,13 +219,13 @@ void BCLog::Logger::LogPrintStr(const std::string &str) // reopen the log file, if requested if (m_reopen_file) { m_reopen_file = false; - m_fileout = fsbridge::freopen(m_file_path, "a", m_fileout); - if (!m_fileout) { - return; + FILE* new_fileout = fsbridge::fopen(m_file_path, "a"); + if (new_fileout) { + setbuf(new_fileout, nullptr); // unbuffered + fclose(m_fileout); + m_fileout = new_fileout; } - setbuf(m_fileout, nullptr); // unbuffered } - FileWriteStr(strTimestamped, m_fileout); } } diff --git a/src/logging.h b/src/logging.h index 6400b131c2..0c8e7f5291 100644 --- a/src/logging.h +++ b/src/logging.h @@ -125,42 +125,31 @@ std::vector<CLogCategoryActive> ListActiveLogCategories(); /** Return true if str parses as a log category and set the flag */ bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str); -/** Get format string from VA_ARGS for error reporting */ -template<typename... Args> std::string FormatStringFromLogArgs(const char *fmt, const Args&... args) { return fmt; } - -static inline void MarkUsed() {} -template<typename T, typename... Args> static inline void MarkUsed(const T& t, const Args&... args) -{ - (void)t; - MarkUsed(args...); -} - // Be conservative when using LogPrintf/error or other things which // unconditionally log to debug.log! It should not be the case that an inbound // peer can fill up a user's disk with debug.log entries. -#ifdef USE_COVERAGE -#define LogPrintf(...) do { MarkUsed(__VA_ARGS__); } while(0) -#define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0) -#else -#define LogPrintf(...) do { \ - if (g_logger->Enabled()) { \ - std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \ - try { \ - _log_msg_ = tfm::format(__VA_ARGS__); \ - } catch (tinyformat::format_error &fmterr) { \ - /* Original format string will have newline so don't add one here */ \ - _log_msg_ = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + FormatStringFromLogArgs(__VA_ARGS__); \ - } \ - g_logger->LogPrintStr(_log_msg_); \ - } \ -} while(0) - -#define LogPrint(category, ...) do { \ - if (LogAcceptCategory((category))) { \ - LogPrintf(__VA_ARGS__); \ - } \ -} while(0) -#endif +template <typename... Args> +static inline void LogPrintf(const char* fmt, const Args&... args) +{ + if (g_logger->Enabled()) { + std::string log_msg; + try { + log_msg = tfm::format(fmt, args...); + } catch (tinyformat::format_error& fmterr) { + /* Original format string will have newline so don't add one here */ + log_msg = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + fmt; + } + g_logger->LogPrintStr(log_msg); + } +} + +template <typename... Args> +static inline void LogPrint(const BCLog::LogFlags& category, const Args&... args) +{ + if (LogAcceptCategory((category))) { + LogPrintf(args...); + } +} #endif // BITCOIN_LOGGING_H diff --git a/src/net.cpp b/src/net.cpp index ec4fa6c267..f83f39a67d 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -344,7 +344,7 @@ CNode* CConnman::FindNode(const CService& addr) bool CConnman::CheckIncomingNonce(uint64_t nonce) { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) { + for (const CNode* pnode : vNodes) { if (!pnode->fSuccessfullyConnected && !pnode->fInbound && pnode->GetLocalNonce() == nonce) return false; } @@ -715,6 +715,7 @@ void CNode::copyStats(CNodeStats &stats) X(nRecvBytes); } X(fWhitelisted); + X(minFeeFilter); // It is common for nodes with good ping times to suddenly become lagged, // due to a new block arriving or other large transfer. @@ -1226,7 +1227,7 @@ void CConnman::ThreadSocketHandler() if(vNodesSize != nPrevNodeCount) { nPrevNodeCount = vNodesSize; if(clientInterface) - clientInterface->NotifyNumConnectionsChanged(nPrevNodeCount); + clientInterface->NotifyNumConnectionsChanged(vNodesSize); } // @@ -1613,7 +1614,7 @@ void CConnman::ThreadDNSAddressSeed() LOCK(cs_vNodes); int nRelevant = 0; - for (auto pnode : vNodes) { + for (const CNode* pnode : vNodes) { nRelevant += pnode->fSuccessfullyConnected && !pnode->fFeeler && !pnode->fOneShot && !pnode->m_manual_connection && !pnode->fInbound; } if (nRelevant >= 2) { @@ -1732,7 +1733,7 @@ int CConnman::GetExtraOutboundCount() int nOutbound = 0; { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) { + for (const CNode* pnode : vNodes) { if (!pnode->fInbound && !pnode->m_manual_connection && !pnode->fFeeler && !pnode->fDisconnect && !pnode->fOneShot && pnode->fSuccessfullyConnected) { ++nOutbound; } @@ -1802,7 +1803,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) std::set<std::vector<unsigned char> > setConnected; { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) { + for (const CNode* pnode : vNodes) { if (!pnode->fInbound && !pnode->m_manual_connection) { // Netgroups for inbound and addnode peers are not excluded because our goal here // is to not use multiple of our limited outbound slots on a single netgroup @@ -2064,7 +2065,7 @@ void CConnman::ThreadMessageHandler() pnode->Release(); } - std::unique_lock<std::mutex> lock(mutexMsgProc); + WAIT_LOCK(mutexMsgProc, lock); if (!fMoreWork) { condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this] { return fMsgProcWake; }); } @@ -2166,7 +2167,7 @@ void Discover() } } } -#else +#elif (HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS) // Get local host ip struct ifaddrs* myaddrs; if (getifaddrs(&myaddrs) == 0) @@ -2346,7 +2347,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) flagInterruptMsgProc = false; { - std::unique_lock<std::mutex> lock(mutexMsgProc); + LOCK(mutexMsgProc); fMsgProcWake = false; } @@ -430,7 +430,7 @@ private: bool fMsgProcWake; std::condition_variable condMsgProc; - std::mutex mutexMsgProc; + Mutex mutexMsgProc; std::atomic<bool> flagInterruptMsgProc; CThreadInterrupt interruptNet; @@ -561,6 +561,7 @@ public: double dPingTime; double dPingWait; double dMinPing; + CAmount minFeeFilter; // Our address, as reported by the peer std::string addrLocal; // Address of this peer diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 2a0ba3970d..a1b6e021ae 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -70,7 +70,7 @@ struct COrphanTx { NodeId fromPeer; int64_t nTimeExpire; }; -static CCriticalSection g_cs_orphans; +CCriticalSection g_cs_orphans; std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans); void EraseOrphansFor(NodeId peer); @@ -442,7 +442,7 @@ static void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) EXCLUSIV * lNodesAnnouncingHeaderAndIDs, and keeping that list under a certain size by * removing the first element if necessary. */ -static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) +static void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); CNodeState* nodestate = State(nodeid); @@ -831,7 +831,7 @@ void Misbehaving(NodeId pnode, int howmuch, const std::string& message) EXCLUSIV // active chain if they are no more than a month older (both in time, and in // best equivalent proof of work) than the best header chain we know about and // we fully-validated them at some point. -static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) +static bool BlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); if (chainActive.Contains(pindex)) return true; @@ -882,7 +882,7 @@ void PeerLogicValidation::BlockConnected(const std::shared_ptr<const CBlock>& pb // Erase orphan transactions included or precluded by this block if (vOrphanErase.size()) { int nErased = 0; - for (uint256 &orphanHash : vOrphanErase) { + for (const uint256& orphanHash : vOrphanErase) { nErased += EraseOrphanTx(orphanHash); } LogPrint(BCLog::MEMPOOL, "Erased %d orphan tx included or conflicted by block\n", nErased); @@ -1260,7 +1260,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c } } -void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc) +void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc) LOCKS_EXCLUDED(cs_main) { AssertLockNotHeld(cs_main); @@ -1618,8 +1618,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr return true; } - else if (strCommand == NetMsgType::VERSION) - { + if (strCommand == NetMsgType::VERSION) { // Each connection can only send one version message if (pfrom->nVersion != 0) { @@ -1662,8 +1661,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr return false; } - if (nVersion < MIN_PEER_PROTO_VERSION) - { + if (nVersion < MIN_PEER_PROTO_VERSION) { // disconnect from peers older than this proto version LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion); if (enable_bip61) { @@ -1674,8 +1672,6 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr return false; } - if (nVersion == 10300) - nVersion = 300; if (!vRecv.empty()) vRecv >> addrFrom >> nNonce; if (!vRecv.empty()) { @@ -1796,9 +1792,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr return true; } - - else if (pfrom->nVersion == 0) - { + if (pfrom->nVersion == 0) { // Must have a version message before anything else LOCK(cs_main); Misbehaving(pfrom->GetId(), 1); @@ -1842,18 +1836,17 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); } pfrom->fSuccessfullyConnected = true; + return true; } - else if (!pfrom->fSuccessfullyConnected) - { + if (!pfrom->fSuccessfullyConnected) { // Must have a verack message before anything else LOCK(cs_main); Misbehaving(pfrom->GetId(), 1); return false; } - else if (strCommand == NetMsgType::ADDR) - { + if (strCommand == NetMsgType::ADDR) { std::vector<CAddress> vAddr; vRecv >> vAddr; @@ -1900,16 +1893,16 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr pfrom->fGetAddr = false; if (pfrom->fOneShot) pfrom->fDisconnect = true; + return true; } - else if (strCommand == NetMsgType::SENDHEADERS) - { + if (strCommand == NetMsgType::SENDHEADERS) { LOCK(cs_main); State(pfrom->GetId())->fPreferHeaders = true; + return true; } - else if (strCommand == NetMsgType::SENDCMPCT) - { + if (strCommand == NetMsgType::SENDCMPCT) { bool fAnnounceUsingCMPCTBLOCK = false; uint64_t nCMPCTBLOCKVersion = 0; vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion; @@ -1929,11 +1922,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr State(pfrom->GetId())->fSupportsDesiredCmpctVersion = (nCMPCTBLOCKVersion == 1); } } + return true; } - - else if (strCommand == NetMsgType::INV) - { + if (strCommand == NetMsgType::INV) { std::vector<CInv> vInv; vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) @@ -1987,11 +1979,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } } } + return true; } - - else if (strCommand == NetMsgType::GETDATA) - { + if (strCommand == NetMsgType::GETDATA) { std::vector<CInv> vInv; vRecv >> vInv; if (vInv.size() > MAX_INV_SZ) @@ -2009,11 +2000,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); ProcessGetData(pfrom, chainparams, connman, interruptMsgProc); + return true; } - - else if (strCommand == NetMsgType::GETBLOCKS) - { + if (strCommand == NetMsgType::GETBLOCKS) { CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; @@ -2078,11 +2068,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr break; } } + return true; } - - else if (strCommand == NetMsgType::GETBLOCKTXN) - { + if (strCommand == NetMsgType::GETBLOCKTXN) { BlockTransactionsRequest req; vRecv >> req; @@ -2128,11 +2117,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr assert(ret); SendBlockTransactions(block, req, pfrom, connman); + return true; } - - else if (strCommand == NetMsgType::GETHEADERS) - { + if (strCommand == NetMsgType::GETHEADERS) { CBlockLocator locator; uint256 hashStop; vRecv >> locator >> hashStop; @@ -2196,11 +2184,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // in the SendMessages logic. nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip(); connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); + return true; } - - else if (strCommand == NetMsgType::TX) - { + if (strCommand == NetMsgType::TX) { // Stop processing the transaction early if // We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off if (!fRelayTxes && (!pfrom->fWhitelisted || !gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))) @@ -2301,7 +2288,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } } - for (uint256 hash : vEraseQueue) + for (const uint256& hash : vEraseQueue) EraseOrphanTx(hash); } else if (fMissingInputs) @@ -2384,10 +2371,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr Misbehaving(pfrom->GetId(), nDoS); } } + return true; } - - else if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex) // Ignore blocks received while importing + if (strCommand == NetMsgType::CMPCTBLOCK && !fImporting && !fReindex) // Ignore blocks received while importing { CBlockHeaderAndShortTxIDs cmpctblock; vRecv >> cmpctblock; @@ -2605,10 +2592,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr MarkBlockAsReceived(pblock->GetHash()); } } - + return true; } - else if (strCommand == NetMsgType::BLOCKTXN && !fImporting && !fReindex) // Ignore blocks received while importing + if (strCommand == NetMsgType::BLOCKTXN && !fImporting && !fReindex) // Ignore blocks received while importing { BlockTransactions resp; vRecv >> resp; @@ -2680,10 +2667,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr mapBlockSource.erase(pblock->GetHash()); } } + return true; } - - else if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) // Ignore headers received while importing + if (strCommand == NetMsgType::HEADERS && !fImporting && !fReindex) // Ignore headers received while importing { std::vector<CBlockHeader> headers; @@ -2708,7 +2695,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr return ProcessHeadersMessage(pfrom, connman, headers, chainparams, should_punish); } - else if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing + if (strCommand == NetMsgType::BLOCK && !fImporting && !fReindex) // Ignore blocks received while importing { std::shared_ptr<CBlock> pblock = std::make_shared<CBlock>(); vRecv >> *pblock; @@ -2734,11 +2721,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr LOCK(cs_main); mapBlockSource.erase(pblock->GetHash()); } + return true; } - - else if (strCommand == NetMsgType::GETADDR) - { + if (strCommand == NetMsgType::GETADDR) { // This asymmetric behavior for inbound and outbound connections was introduced // to prevent a fingerprinting attack: an attacker can send specific fake addresses // to users' AddrMan and later request them by sending getaddr messages. @@ -2762,11 +2748,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr FastRandomContext insecure_rand; for (const CAddress &addr : vAddr) pfrom->PushAddress(addr, insecure_rand); + return true; } - - else if (strCommand == NetMsgType::MEMPOOL) - { + if (strCommand == NetMsgType::MEMPOOL) { if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->fWhitelisted) { LogPrint(BCLog::NET, "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId()); @@ -2783,11 +2768,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr LOCK(pfrom->cs_inventory); pfrom->fSendMempool = true; + return true; } - - else if (strCommand == NetMsgType::PING) - { + if (strCommand == NetMsgType::PING) { if (pfrom->nVersion > BIP0031_VERSION) { uint64_t nonce = 0; @@ -2805,11 +2789,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // return very quickly. connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::PONG, nonce)); } + return true; } - - else if (strCommand == NetMsgType::PONG) - { + if (strCommand == NetMsgType::PONG) { int64_t pingUsecEnd = nTimeReceived; uint64_t nonce = 0; size_t nAvail = vRecv.in_avail(); @@ -2862,11 +2845,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (bPingFinished) { pfrom->nPingNonceSent = 0; } + return true; } - - else if (strCommand == NetMsgType::FILTERLOAD) - { + if (strCommand == NetMsgType::FILTERLOAD) { CBloomFilter filter; vRecv >> filter; @@ -2883,11 +2865,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr pfrom->pfilter->UpdateEmptyFull(); pfrom->fRelayTxes = true; } + return true; } - - else if (strCommand == NetMsgType::FILTERADD) - { + if (strCommand == NetMsgType::FILTERADD) { std::vector<unsigned char> vData; vRecv >> vData; @@ -2908,19 +2889,19 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr LOCK(cs_main); Misbehaving(pfrom->GetId(), 100); } + return true; } - - else if (strCommand == NetMsgType::FILTERCLEAR) - { + if (strCommand == NetMsgType::FILTERCLEAR) { LOCK(pfrom->cs_filter); if (pfrom->GetLocalServices() & NODE_BLOOM) { pfrom->pfilter.reset(new CBloomFilter()); } pfrom->fRelayTxes = true; + return true; } - else if (strCommand == NetMsgType::FEEFILTER) { + if (strCommand == NetMsgType::FEEFILTER) { CAmount newFeeFilter = 0; vRecv >> newFeeFilter; if (MoneyRange(newFeeFilter)) { @@ -2930,24 +2911,21 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } LogPrint(BCLog::NET, "received: feefilter of %s from peer=%d\n", CFeeRate(newFeeFilter).ToString(), pfrom->GetId()); } + return true; } - else if (strCommand == NetMsgType::NOTFOUND) { + 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. + return true; } - else { - // Ignore unknown commands for extensibility - LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->GetId()); - } - - - + // Ignore unknown commands for extensibility + LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->GetId()); return true; } -static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman, bool enable_bip61) +static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman, bool enable_bip61) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); CNodeState &state = *State(pnode->GetId()); diff --git a/src/net_processing.h b/src/net_processing.h index 01b8e97075..88dbdf556d 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -18,7 +18,7 @@ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100; /** Default number of orphan+recently-replaced txn to keep around for block reconstruction */ static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100; /** Default for BIP61 (sending reject messages) */ -static constexpr bool DEFAULT_ENABLE_BIP61 = true; +static constexpr bool DEFAULT_ENABLE_BIP61{false}; class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface { private: @@ -64,7 +64,7 @@ public: bool SendMessages(CNode* pto) override EXCLUSIVE_LOCKS_REQUIRED(pto->cs_sendProcessing); /** Consider evicting an outbound peer based on the amount of time they've been behind our tip */ - void ConsiderEviction(CNode *pto, int64_t time_in_seconds); + void ConsiderEviction(CNode *pto, int64_t time_in_seconds) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Evict extra outbound peers. If we think our tip may be stale, connect to an extra outbound */ void CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams); /** If we have extra outbound peers, try to disconnect the one with the oldest block announcement */ diff --git a/src/netbase.cpp b/src/netbase.cpp index 9750173987..093fd0bdb7 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -17,10 +17,10 @@ #ifndef WIN32 #include <fcntl.h> +#else +#include <codecvt> #endif -#include <boost/algorithm/string/case_conv.hpp> // for to_lower() - #if !defined(MSG_NOSIGNAL) #define MSG_NOSIGNAL 0 #endif @@ -37,7 +37,7 @@ static const int SOCKS5_RECV_TIMEOUT = 20 * 1000; static std::atomic<bool> interruptSocks5Recv(false); enum Network ParseNetwork(std::string net) { - boost::to_lower(net); + Downcase(net); if (net == "ipv4") return NET_IPV4; if (net == "ipv6") return NET_IPV6; if (net == "onion") return NET_ONION; @@ -651,13 +651,13 @@ bool LookupSubNet(const char* pszName, CSubNet& ret) #ifdef WIN32 std::string NetworkErrorString(int err) { - char buf[256]; + wchar_t buf[256]; buf[0] = 0; - if(FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, + if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - buf, sizeof(buf), nullptr)) + buf, ARRAYSIZE(buf), nullptr)) { - return strprintf("%s (%d)", buf, err); + return strprintf("%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf), err); } else { diff --git a/src/noui.cpp b/src/noui.cpp index e6d01e7b26..df4bfabb66 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -12,7 +12,9 @@ #include <stdint.h> #include <string> -static bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) +#include <boost/signals2/connection.hpp> + +bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) { bool fSecure = style & CClientUIInterface::SECURE; style &= ~CClientUIInterface::SECURE; @@ -39,19 +41,18 @@ static bool noui_ThreadSafeMessageBox(const std::string& message, const std::str return false; } -static bool noui_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style) +bool noui_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style) { return noui_ThreadSafeMessageBox(message, caption, style); } -static void noui_InitMessage(const std::string& message) +void noui_InitMessage(const std::string& message) { LogPrintf("init message: %s\n", message); } void noui_connect() { - // Connect bitcoind signal handlers uiInterface.ThreadSafeMessageBox_connect(noui_ThreadSafeMessageBox); uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestion); uiInterface.InitMessage_connect(noui_InitMessage); diff --git a/src/noui.h b/src/noui.h index ff16cc9aa8..169c2bbd7f 100644 --- a/src/noui.h +++ b/src/noui.h @@ -5,6 +5,16 @@ #ifndef BITCOIN_NOUI_H #define BITCOIN_NOUI_H -extern void noui_connect(); +#include <string> + +/** Non-GUI handler, which logs and prints messages. */ +bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style); +/** Non-GUI handler, which logs and prints questions. */ +bool noui_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style); +/** Non-GUI handler, which only logs a message. */ +void noui_InitMessage(const std::string& message); + +/** Connect all bitcoind signal handlers */ +void noui_connect(); #endif // BITCOIN_NOUI_H diff --git a/src/policy/fees.h b/src/policy/fees.h index 136fb481f7..2733c5a7de 100644 --- a/src/policy/fees.h +++ b/src/policy/fees.h @@ -22,51 +22,6 @@ class CTxMemPoolEntry; class CTxMemPool; class TxConfirmStats; -/** \class CBlockPolicyEstimator - * The BlockPolicyEstimator is used for estimating the feerate needed - * for a transaction to be included in a block within a certain number of - * blocks. - * - * At a high level the algorithm works by grouping transactions into buckets - * based on having similar feerates and then tracking how long it - * takes transactions in the various buckets to be mined. It operates under - * the assumption that in general transactions of higher feerate will be - * included in blocks before transactions of lower feerate. So for - * example if you wanted to know what feerate you should put on a transaction to - * be included in a block within the next 5 blocks, you would start by looking - * at the bucket with the highest feerate transactions and verifying that a - * sufficiently high percentage of them were confirmed within 5 blocks and - * then you would look at the next highest feerate bucket, and so on, stopping at - * the last bucket to pass the test. The average feerate of transactions in this - * bucket will give you an indication of the lowest feerate you can put on a - * transaction and still have a sufficiently high chance of being confirmed - * within your desired 5 blocks. - * - * Here is a brief description of the implementation: - * When a transaction enters the mempool, we track the height of the block chain - * at entry. All further calculations are conducted only on this set of "seen" - * transactions. Whenever a block comes in, we count the number of transactions - * in each bucket and the total amount of feerate paid in each bucket. Then we - * calculate how many blocks Y it took each transaction to be mined. We convert - * from a number of blocks to a number of periods Y' each encompassing "scale" - * blocks. This is tracked in 3 different data sets each up to a maximum - * number of periods. Within each data set we have an array of counters in each - * feerate bucket and we increment all the counters from Y' up to max periods - * representing that a tx was successfully confirmed in less than or equal to - * that many periods. We want to save a history of this information, so at any - * time we have a counter of the total number of transactions that happened in a - * given feerate bucket and the total number that were confirmed in each of the - * periods or less for any bucket. We save this history by keeping an - * exponentially decaying moving average of each one of these stats. This is - * done for a different decay in each of the 3 data sets to keep relevant data - * from different time horizons. Furthermore we also keep track of the number - * unmined (in mempool or left mempool without being included in a block) - * transactions in each bucket and for how many blocks they have been - * outstanding and use both of these numbers to increase the number of transactions - * we've seen in that feerate bucket when calculating an estimate for any number - * of confirmations below the number of blocks they've been outstanding. - */ - /* Identifier for each of the 3 different TxConfirmStats which will track * history over different time horizons. */ enum class FeeEstimateHorizon { @@ -130,7 +85,50 @@ struct FeeCalculation int returnedTarget = 0; }; -/** +/** \class CBlockPolicyEstimator + * The BlockPolicyEstimator is used for estimating the feerate needed + * for a transaction to be included in a block within a certain number of + * blocks. + * + * At a high level the algorithm works by grouping transactions into buckets + * based on having similar feerates and then tracking how long it + * takes transactions in the various buckets to be mined. It operates under + * the assumption that in general transactions of higher feerate will be + * included in blocks before transactions of lower feerate. So for + * example if you wanted to know what feerate you should put on a transaction to + * be included in a block within the next 5 blocks, you would start by looking + * at the bucket with the highest feerate transactions and verifying that a + * sufficiently high percentage of them were confirmed within 5 blocks and + * then you would look at the next highest feerate bucket, and so on, stopping at + * the last bucket to pass the test. The average feerate of transactions in this + * bucket will give you an indication of the lowest feerate you can put on a + * transaction and still have a sufficiently high chance of being confirmed + * within your desired 5 blocks. + * + * Here is a brief description of the implementation: + * When a transaction enters the mempool, we track the height of the block chain + * at entry. All further calculations are conducted only on this set of "seen" + * transactions. Whenever a block comes in, we count the number of transactions + * in each bucket and the total amount of feerate paid in each bucket. Then we + * calculate how many blocks Y it took each transaction to be mined. We convert + * from a number of blocks to a number of periods Y' each encompassing "scale" + * blocks. This is tracked in 3 different data sets each up to a maximum + * number of periods. Within each data set we have an array of counters in each + * feerate bucket and we increment all the counters from Y' up to max periods + * representing that a tx was successfully confirmed in less than or equal to + * that many periods. We want to save a history of this information, so at any + * time we have a counter of the total number of transactions that happened in a + * given feerate bucket and the total number that were confirmed in each of the + * periods or less for any bucket. We save this history by keeping an + * exponentially decaying moving average of each one of these stats. This is + * done for a different decay in each of the 3 data sets to keep relevant data + * from different time horizons. Furthermore we also keep track of the number + * unmined (in mempool or left mempool without being included in a block) + * transactions in each bucket and for how many blocks they have been + * outstanding and use both of these numbers to increase the number of transactions + * we've seen in that feerate bucket when calculating an estimate for any number + * of confirmations below the number of blocks they've been outstanding. + * * We want to be able to estimate feerates that are needed on tx's to be included in * a certain number of blocks. Every time a block is added to the best chain, this class records * stats on the transactions included in that block diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index b8f7963a0c..ac1b75edb4 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -34,7 +34,7 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) if (txout.scriptPubKey.IsUnspendable()) return 0; - size_t nSize = GetSerializeSize(txout, SER_DISK, 0); + size_t nSize = GetSerializeSize(txout); int witnessversion = 0; std::vector<unsigned char> witnessprogram; @@ -57,11 +57,11 @@ bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) { std::vector<std::vector<unsigned char> > vSolutions; - if (!Solver(scriptPubKey, whichType, vSolutions)) - return false; + whichType = Solver(scriptPubKey, vSolutions); - if (whichType == TX_MULTISIG) - { + if (whichType == TX_NONSTANDARD || whichType == TX_WITNESS_UNKNOWN) { + return false; + } else if (whichType == TX_MULTISIG) { unsigned char m = vSolutions.front()[0]; unsigned char n = vSolutions.back()[0]; // Support up to x-of-3 multisig txns as standard @@ -70,10 +70,11 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) if (m < 1 || m > n) return false; } else if (whichType == TX_NULL_DATA && - (!fAcceptDatacarrier || scriptPubKey.size() > nMaxDatacarrierBytes)) + (!fAcceptDatacarrier || scriptPubKey.size() > nMaxDatacarrierBytes)) { return false; + } - return whichType != TX_NONSTANDARD && whichType != TX_WITNESS_UNKNOWN; + return true; } bool IsStandardTx(const CTransaction& tx, std::string& reason) @@ -166,14 +167,10 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) const CTxOut& prev = mapInputs.AccessCoin(tx.vin[i].prevout).out; std::vector<std::vector<unsigned char> > vSolutions; - txnouttype whichType; - // get the scriptPubKey corresponding to this input: - const CScript& prevScript = prev.scriptPubKey; - if (!Solver(prevScript, whichType, vSolutions)) + txnouttype whichType = Solver(prev.scriptPubKey, vSolutions); + if (whichType == TX_NONSTANDARD) { return false; - - if (whichType == TX_SCRIPTHASH) - { + } else if (whichType == TX_SCRIPTHASH) { std::vector<std::vector<unsigned char> > stack; // convert the scriptSig into a stack, so we can inspect the redeemScript if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SigVersion::BASE)) diff --git a/src/prevector.h b/src/prevector.h index 033952c959..6ddb6f321f 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -248,32 +248,32 @@ public: prevector() : _size(0), _union{{}} {} - explicit prevector(size_type n) : _size(0) { + explicit prevector(size_type n) : prevector() { resize(n); } - explicit prevector(size_type n, const T& val = T()) : _size(0) { + explicit prevector(size_type n, const T& val) : prevector() { change_capacity(n); _size += n; fill(item_ptr(0), n, val); } template<typename InputIterator> - prevector(InputIterator first, InputIterator last) : _size(0) { + prevector(InputIterator first, InputIterator last) : prevector() { size_type n = last - first; change_capacity(n); _size += n; fill(item_ptr(0), first, last); } - prevector(const prevector<N, T, Size, Diff>& other) : _size(0) { + prevector(const prevector<N, T, Size, Diff>& other) : prevector() { size_type n = other.size(); change_capacity(n); _size += n; fill(item_ptr(0), other.begin(), other.end()); } - prevector(prevector<N, T, Size, Diff>&& other) : _size(0) { + prevector(prevector<N, T, Size, Diff>&& other) : prevector() { swap(other); } diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 59865a5eab..bdb470470e 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -93,7 +93,7 @@ CAmount CTransaction::GetValueOut() const unsigned int CTransaction::GetTotalSize() const { - return ::GetSerializeSize(*this, SER_NETWORK, PROTOCOL_VERSION); + return ::GetSerializeSize(*this, PROTOCOL_VERSION); } std::string CTransaction::ToString() const diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 380a3ddb84..2f88e15d21 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -71,7 +71,7 @@ public: QList<AddressTableEntry> cachedAddressTable; AddressTableModel *parent; - AddressTablePriv(AddressTableModel *_parent): + explicit AddressTablePriv(AddressTableModel *_parent): parent(_parent) {} void refreshAddressTable(interfaces::Wallet& wallet) diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index 673f63a19b..a0270daf99 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -123,16 +123,15 @@ void AskPassphraseDialog::accept() { QMessageBox::warning(this, tr("Wallet encrypted"), "<qt>" + - tr("%1 will close now to finish the encryption process. " + tr("Your wallet is now encrypted. " "Remember that encrypting your wallet cannot fully protect " - "your bitcoins from being stolen by malware infecting your computer.").arg(tr(PACKAGE_NAME)) + + "your bitcoins from being stolen by malware infecting your computer.") + "<br><br><b>" + tr("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.") + "</b></qt>"); - QApplication::quit(); } else { diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 85fb88d338..1e950e2686 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -28,6 +28,7 @@ #include <interfaces/handler.h> #include <interfaces/node.h> +#include <noui.h> #include <rpc/server.h> #include <ui_interface.h> #include <uint256.h> @@ -71,18 +72,15 @@ Q_DECLARE_METATYPE(bool*) Q_DECLARE_METATYPE(CAmount) Q_DECLARE_METATYPE(uint256) -static void InitMessage(const std::string &message) +static void InitMessage(const std::string& message) { - LogPrintf("init message: %s\n", message); + noui_InitMessage(message); } -/* - Translate string to current locale using Qt. - */ -static std::string Translate(const char* psz) -{ +/** Translate string to current locale using Qt. */ +const std::function<std::string(const char*)> G_TRANSLATION_FUN = [](const char* psz) { return QCoreApplication::translate("bitcoin-core", psz).toStdString(); -} +}; static QString GetLangTerritory() { @@ -419,7 +417,7 @@ void BitcoinApplication::requestShutdown() #ifdef ENABLE_WALLET window->removeAllWallets(); - for (WalletModel *walletModel : m_wallet_models) { + for (const WalletModel* walletModel : m_wallet_models) { delete walletModel; } m_wallet_models.clear(); @@ -588,7 +586,7 @@ int main(int argc, char *argv[]) // Need to pass name here as CAmount is a typedef (see http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType) // IMPORTANT if it is no longer a typedef use the normal variant above qRegisterMetaType< CAmount >("CAmount"); - qRegisterMetaType< std::function<void(void)> >("std::function<void(void)>"); + qRegisterMetaType< std::function<void()> >("std::function<void()>"); #ifdef ENABLE_WALLET qRegisterMetaType<WalletModel*>("WalletModel*"); #endif @@ -618,7 +616,6 @@ int main(int argc, char *argv[]) // Now that QSettings are accessible, initialize translations QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator; initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); - translationInterface.Translate.connect(Translate); // Show help message immediately after parsing command-line options (for "-lang") and setting locale, // but before showing splash screen. diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index cefa9004ed..51aff08c42 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -31,6 +31,7 @@ #include <chainparams.h> #include <interfaces/handler.h> #include <interfaces/node.h> +#include <noui.h> #include <ui_interface.h> #include <util.h> @@ -57,6 +58,8 @@ #include <QUrlQuery> #include <QVBoxLayout> +#include <boost/bind.hpp> + const std::string BitcoinGUI::DEFAULT_UIPLATFORM = #if defined(Q_OS_MAC) "macosx" @@ -612,8 +615,11 @@ void BitcoinGUI::createTrayIconMenu() #endif // Configuration of the tray icon (or dock icon) icon menu +#ifndef Q_OS_MAC + // Note: On Mac, the dock icon's menu already has show / hide action. trayIconMenu->addAction(toggleHideAction); trayIconMenu->addSeparator(); +#endif trayIconMenu->addAction(sendCoinsMenuAction); trayIconMenu->addAction(receiveCoinsMenuAction); trayIconMenu->addSeparator(); @@ -1212,8 +1218,11 @@ void BitcoinGUI::showModalOverlay() modalOverlay->toggleVisibility(); } -static bool ThreadSafeMessageBox(BitcoinGUI *gui, const std::string& message, const std::string& caption, unsigned int style) +static bool ThreadSafeMessageBox(BitcoinGUI* gui, const std::string& message, const std::string& caption, unsigned int style) { + // Redundantly log and print message in non-gui fashion + noui_ThreadSafeMessageBox(message, caption, style); + bool modal = (style & CClientUIInterface::MODAL); // The SECURE flag has no effect in the Qt GUI. // bool secure = (style & CClientUIInterface::SECURE); @@ -1271,7 +1280,7 @@ void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event) void UnitDisplayStatusBarControl::createContextMenu() { menu = new QMenu(this); - for (BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) + for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) { QAction *menuAction = new QAction(QString(BitcoinUnits::longName(u)), this); menuAction->setData(QVariant(u)); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 61cd6f76cc..dcaca10557 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -212,7 +212,7 @@ private: void setEncryptionStatus(int status); /** Set the hd-enabled status as shown in the UI. - @param[in] status current hd enabled status + @param[in] hdEnabled current hd enabled status @see WalletModel::EncryptionStatus */ void setHDStatus(int hdEnabled); diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index bbf60d96fd..b894fc8166 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -38,8 +38,6 @@ #include <shlwapi.h> #endif -#include <boost/scoped_array.hpp> - #include <QAbstractItemView> #include <QApplication> #include <QClipboard> @@ -62,8 +60,6 @@ #include <QFontDatabase> #endif -static fs::detail::utf8_codecvt_facet utf8; - namespace GUIUtil { QString dateTimeStr(const QDateTime &date) @@ -548,40 +544,28 @@ bool SetStartOnSystemStartup(bool fAutoStart) CoInitialize(nullptr); // Get a pointer to the IShellLink interface. - IShellLink* psl = nullptr; + IShellLinkW* psl = nullptr; HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, - CLSCTX_INPROC_SERVER, IID_IShellLink, + CLSCTX_INPROC_SERVER, IID_IShellLinkW, reinterpret_cast<void**>(&psl)); if (SUCCEEDED(hres)) { // Get the current executable path - TCHAR pszExePath[MAX_PATH]; - GetModuleFileName(nullptr, pszExePath, sizeof(pszExePath)); + WCHAR pszExePath[MAX_PATH]; + GetModuleFileNameW(nullptr, pszExePath, ARRAYSIZE(pszExePath)); // Start client minimized QString strArgs = "-min"; // Set -testnet /-regtest options strArgs += QString::fromStdString(strprintf(" -testnet=%d -regtest=%d", gArgs.GetBoolArg("-testnet", false), gArgs.GetBoolArg("-regtest", false))); -#ifdef UNICODE - boost::scoped_array<TCHAR> args(new TCHAR[strArgs.length() + 1]); - // Convert the QString to TCHAR* - strArgs.toWCharArray(args.get()); - // Add missing '\0'-termination to string - args[strArgs.length()] = '\0'; -#endif - // Set the path to the shortcut target psl->SetPath(pszExePath); - PathRemoveFileSpec(pszExePath); + PathRemoveFileSpecW(pszExePath); psl->SetWorkingDirectory(pszExePath); psl->SetShowCmd(SW_SHOWMINNOACTIVE); -#ifndef UNICODE - psl->SetArguments(strArgs.toStdString().c_str()); -#else - psl->SetArguments(args.get()); -#endif + psl->SetArguments(strArgs.toStdWString().c_str()); // Query IShellLink for the IPersistFile interface for // saving the shortcut in persistent storage. @@ -589,11 +573,8 @@ bool SetStartOnSystemStartup(bool fAutoStart) hres = psl->QueryInterface(IID_IPersistFile, reinterpret_cast<void**>(&ppf)); if (SUCCEEDED(hres)) { - WCHAR pwsz[MAX_PATH]; - // Ensure that the string is ANSI. - MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().string().c_str(), -1, pwsz, MAX_PATH); // Save the link by calling IPersistFile::Save. - hres = ppf->Save(pwsz, TRUE); + hres = ppf->Save(StartupShortcutPath().wstring().c_str(), TRUE); ppf->Release(); psl->Release(); CoUninitialize(); @@ -781,12 +762,12 @@ void setClipboard(const QString& str) fs::path qstringToBoostPath(const QString &path) { - return fs::path(path.toStdString(), utf8); + return fs::path(path.toStdString()); } QString boostPathToQString(const fs::path &path) { - return QString::fromStdString(path.string(utf8)); + return QString::fromStdString(path.string()); } QString formatDurationStr(int secs) diff --git a/src/qt/macnotificationhandler.h b/src/qt/macnotificationhandler.h index 23993adc2e..03c744c12e 100644 --- a/src/qt/macnotificationhandler.h +++ b/src/qt/macnotificationhandler.h @@ -19,7 +19,7 @@ public: void showNotification(const QString &title, const QString &text); /** check if OS can handle UserNotifications */ - bool hasUserNotificationCenterSupport(void); + bool hasUserNotificationCenterSupport(); static MacNotificationHandler *instance(); }; diff --git a/src/qt/paymentrequestplus.cpp b/src/qt/paymentrequestplus.cpp index 944bcc8ad0..a989988c45 100644 --- a/src/qt/paymentrequestplus.cpp +++ b/src/qt/paymentrequestplus.cpp @@ -142,7 +142,6 @@ bool PaymentRequestPlus::getMerchant(X509_STORE* certStore, QString& merchant) c if (result != 1) { int error = X509_STORE_CTX_get_error(store_ctx); // For testing payment requests, we allow self signed root certs! - // This option is just shown in the UI options, if -help-debug is enabled. if (!(error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT && gArgs.GetBoolArg("-allowselfsignedrootcertificates", DEFAULT_SELFSIGNED_ROOTCERTS))) { throw SSLVerifyError(X509_verify_cert_error_string(error)); } else { diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index 59a751fb12..8cfedca57f 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -65,7 +65,7 @@ public: interfaces::Node::NodesStats nodes_stats; node.getNodesStats(nodes_stats); cachedNodeStats.reserve(nodes_stats.size()); - for (auto& node_stats : nodes_stats) + for (const auto& node_stats : nodes_stats) { CNodeCombinedStats stats; stats.nodeStats = std::get<0>(node_stats); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 66e36f0b67..3857befdf2 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -82,7 +82,7 @@ class RPCExecutor : public QObject { Q_OBJECT public: - RPCExecutor(interfaces::Node& node) : m_node(node) {} + explicit RPCExecutor(interfaces::Node& node) : m_node(node) {} public Q_SLOTS: void request(const QString &command, const QString &walletID); @@ -101,7 +101,7 @@ class QtRPCTimerBase: public QObject, public RPCTimerBase { Q_OBJECT public: - QtRPCTimerBase(std::function<void(void)>& _func, int64_t millis): + QtRPCTimerBase(std::function<void()>& _func, int64_t millis): func(_func) { timer.setSingleShot(true); @@ -111,7 +111,7 @@ public: ~QtRPCTimerBase() {} private: QTimer timer; - std::function<void(void)> func; + std::function<void()> func; }; class QtRPCTimerInterface: public RPCTimerInterface @@ -119,7 +119,7 @@ class QtRPCTimerInterface: public RPCTimerInterface public: ~QtRPCTimerInterface() {} const char *Name() { return "Qt"; } - RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis) + RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis) { return new QtRPCTimerBase(func, millis); } @@ -142,7 +142,7 @@ public: * - Within single quotes, no escaping is possible and no special interpretation takes place * * @param[in] node optional node to execute command on - * @param[out] result stringified Result from the executed command(chain) + * @param[out] strResult stringified result from the executed command(chain) * @param[in] strCommand Command line to split * @param[in] fExecute set true if you want the command to be executed * @param[out] pstrFilteredOut Command line, filtered to remove any sensitive data diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index eed71397ab..6f66bc19e1 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -348,7 +348,7 @@ void SendCoinsDialog::on_sendButton_clicked() questionString.append("<hr />"); CAmount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee; QStringList alternativeUnits; - for (BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) + for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits()) { if(u != model->getOptionsModel()->getDisplayUnit()) alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount)); diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index e438d22919..1eff4f6b65 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -14,8 +14,8 @@ #include <interfaces/handler.h> #include <interfaces/node.h> #include <interfaces/wallet.h> -#include <util.h> #include <ui_interface.h> +#include <util.h> #include <version.h> #include <QApplication> @@ -24,6 +24,8 @@ #include <QPainter> #include <QRadialGradient> +#include <boost/bind.hpp> + SplashScreen::SplashScreen(interfaces::Node& node, Qt::WindowFlags f, const NetworkStyle *networkStyle) : QWidget(0, f), curAlignment(0), m_node(node) { @@ -198,7 +200,7 @@ void SplashScreen::unsubscribeFromCoreSignals() // Disconnect signals from client m_handler_init_message->disconnect(); m_handler_show_progress->disconnect(); - for (auto& handler : m_connected_wallet_handlers) { + for (const auto& handler : m_connected_wallet_handlers) { handler->disconnect(); } m_connected_wallet_handlers.clear(); diff --git a/src/qt/test/util.h b/src/qt/test/util.h index 324386c139..5363c94547 100644 --- a/src/qt/test/util.h +++ b/src/qt/test/util.h @@ -5,7 +5,7 @@ * Press "Ok" button in message box dialog. * * @param text - Optionally store dialog text. - * @param msec - Number of miliseconds to pause before triggering the callback. + * @param msec - Number of milliseconds to pause before triggering the callback. */ void ConfirmMessage(QString* text = nullptr, int msec = 0); diff --git a/src/qt/trafficgraphwidget.cpp b/src/qt/trafficgraphwidget.cpp index f087e7569c..3aed3f2b97 100644 --- a/src/qt/trafficgraphwidget.cpp +++ b/src/qt/trafficgraphwidget.cpp @@ -141,10 +141,10 @@ void TrafficGraphWidget::updateRates() } float tmax = 0.0f; - for (float f : vSamplesIn) { + for (const float f : vSamplesIn) { if(f > tmax) tmax = f; } - for (float f : vSamplesOut) { + for (const float f : vSamplesOut) { if(f > tmax) tmax = f; } fMax = tmax; diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 6286ddc0e0..59332be754 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -152,13 +152,13 @@ QString TransactionDesc::toHTML(interfaces::Node& node, interfaces::Wallet& wall else { isminetype fAllFromMe = ISMINE_SPENDABLE; - for (isminetype mine : wtx.txin_is_mine) + for (const isminetype mine : wtx.txin_is_mine) { if(fAllFromMe > mine) fAllFromMe = mine; } isminetype fAllToMe = ISMINE_SPENDABLE; - for (isminetype mine : wtx.txout_is_mine) + for (const isminetype mine : wtx.txout_is_mine) { if(fAllToMe > mine) fAllToMe = mine; } diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index c767de5eda..d1a7527ac7 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -77,14 +77,14 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const interface { bool involvesWatchAddress = false; isminetype fAllFromMe = ISMINE_SPENDABLE; - for (isminetype mine : wtx.txin_is_mine) + for (const isminetype mine : wtx.txin_is_mine) { if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true; if(fAllFromMe > mine) fAllFromMe = mine; } isminetype fAllToMe = ISMINE_SPENDABLE; - for (isminetype mine : wtx.txout_is_mine) + for (const isminetype mine : wtx.txout_is_mine) { if(mine & ISMINE_WATCH_ONLY) involvesWatchAddress = true; if(fAllToMe > mine) fAllToMe = mine; diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index e379b70553..b4be068904 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -16,10 +16,10 @@ #include <core_io.h> #include <interfaces/handler.h> #include <interfaces/node.h> -#include <validation.h> #include <sync.h> #include <uint256.h> #include <util.h> +#include <validation.h> #include <QColor> #include <QDateTime> @@ -27,6 +27,8 @@ #include <QIcon> #include <QList> +#include <boost/bind.hpp> + // Amount column is right-aligned it contains numbers static int column_alignments[] = { Qt::AlignLeft|Qt::AlignVCenter, /* status */ @@ -58,7 +60,7 @@ struct TxLessThan class TransactionTablePriv { public: - TransactionTablePriv(TransactionTableModel *_parent) : + explicit TransactionTablePriv(TransactionTableModel *_parent) : parent(_parent) { } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 18a5bda9c3..f7cc94ae32 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -253,7 +253,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran auto& newTx = transaction.getWtx(); std::string rejectReason; - if (!newTx->commit({} /* mapValue */, std::move(vOrderForm), {} /* fromAccount */, rejectReason)) + if (!newTx->commit({} /* mapValue */, std::move(vOrderForm), rejectReason)) return SendCoinsReturn(TransactionCommitFailed, QString::fromStdString(rejectReason)); CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); diff --git a/src/random.cpp b/src/random.cpp index 6c5ad5ac96..503d5b3636 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -12,6 +12,7 @@ #include <wincrypt.h> #endif #include <logging.h> // for LogPrint() +#include <sync.h> // for WAIT_LOCK #include <utiltime.h> // for GetTime() #include <stdlib.h> @@ -295,7 +296,7 @@ void RandAddSeedSleep() } -static std::mutex cs_rng_state; +static Mutex cs_rng_state; static unsigned char rng_state[32] = {0}; static uint64_t rng_counter = 0; @@ -305,7 +306,7 @@ static void AddDataToRng(void* data, size_t len) { hasher.Write((const unsigned char*)data, len); unsigned char buf[64]; { - std::unique_lock<std::mutex> lock(cs_rng_state); + WAIT_LOCK(cs_rng_state, lock); hasher.Write(rng_state, sizeof(rng_state)); hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter)); ++rng_counter; @@ -337,7 +338,7 @@ void GetStrongRandBytes(unsigned char* out, int num) // Combine with and update state { - std::unique_lock<std::mutex> lock(cs_rng_state); + WAIT_LOCK(cs_rng_state, lock); hasher.Write(rng_state, sizeof(rng_state)); hasher.Write((const unsigned char*)&rng_counter, sizeof(rng_counter)); ++rng_counter; diff --git a/src/rest.cpp b/src/rest.cpp index 12358cf50d..7792844992 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -157,13 +157,13 @@ static bool rest_headers(HTTPRequest* req, } } - CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION); - for (const CBlockIndex *pindex : headers) { - ssHeader << pindex->GetBlockHeader(); - } - switch (rf) { case RetFormat::BINARY: { + CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION); + for (const CBlockIndex *pindex : headers) { + ssHeader << pindex->GetBlockHeader(); + } + std::string binaryHeader = ssHeader.str(); req->WriteHeader("Content-Type", "application/octet-stream"); req->WriteReply(HTTP_OK, binaryHeader); @@ -171,6 +171,11 @@ static bool rest_headers(HTTPRequest* req, } case RetFormat::HEX: { + CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION); + for (const CBlockIndex *pindex : headers) { + ssHeader << pindex->GetBlockHeader(); + } + std::string strHex = HexStr(ssHeader.begin(), ssHeader.end()) + "\n"; req->WriteHeader("Content-Type", "text/plain"); req->WriteReply(HTTP_OK, strHex); @@ -224,11 +229,10 @@ static bool rest_block(HTTPRequest* req, return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); } - CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags()); - ssBlock << block; - switch (rf) { case RetFormat::BINARY: { + CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags()); + ssBlock << block; std::string binaryBlock = ssBlock.str(); req->WriteHeader("Content-Type", "application/octet-stream"); req->WriteReply(HTTP_OK, binaryBlock); @@ -236,6 +240,8 @@ static bool rest_block(HTTPRequest* req, } case RetFormat::HEX: { + CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags()); + ssBlock << block; std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n"; req->WriteHeader("Content-Type", "text/plain"); req->WriteReply(HTTP_OK, strHex); @@ -360,11 +366,11 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart) if (!GetTransaction(hash, tx, Params().GetConsensus(), hashBlock, true)) return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); - CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags()); - ssTx << tx; - switch (rf) { case RetFormat::BINARY: { + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags()); + ssTx << tx; + std::string binaryTx = ssTx.str(); req->WriteHeader("Content-Type", "application/octet-stream"); req->WriteReply(HTTP_OK, binaryTx); @@ -372,6 +378,9 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart) } case RetFormat::HEX: { + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags()); + ssTx << tx; + std::string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n"; req->WriteHeader("Content-Type", "text/plain"); req->WriteReply(HTTP_OK, strHex); @@ -464,7 +473,7 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) oss >> fCheckMemPool; oss >> vOutPoints; } - } catch (const std::ios_base::failure& e) { + } catch (const std::ios_base::failure&) { // abort in case of unreadable binary data return RESTERR(req, HTTP_BAD_REQUEST, "Parse error"); } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 51bc218d39..1eca0277b0 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -18,6 +18,7 @@ #include <key_io.h> #include <policy/feerate.h> #include <policy/policy.h> +#include <policy/rbf.h> #include <primitives/transaction.h> #include <rpc/server.h> #include <script/descriptor.h> @@ -29,6 +30,7 @@ #include <utilstrencodings.h> #include <hash.h> #include <validationinterface.h> +#include <versionbitsinfo.h> #include <warnings.h> #include <assert.h> @@ -49,7 +51,7 @@ struct CUpdatedBlock int height; }; -static std::mutex cs_blockchange; +static Mutex cs_blockchange; static std::condition_variable cond_blockchange; static CUpdatedBlock latestblock; @@ -120,8 +122,8 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx if (chainActive.Contains(blockindex)) confirmations = chainActive.Height() - blockindex->nHeight + 1; result.pushKV("confirmations", confirmations); - result.pushKV("strippedsize", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS)); - result.pushKV("size", (int)::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION)); + result.pushKV("strippedsize", (int)::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS)); + result.pushKV("size", (int)::GetSerializeSize(block, PROTOCOL_VERSION)); result.pushKV("weight", (int)::GetBlockWeight(block)); result.pushKV("height", blockindex->nHeight); result.pushKV("version", block.nVersion); @@ -224,7 +226,7 @@ static UniValue waitfornewblock(const JSONRPCRequest& request) CUpdatedBlock block; { - std::unique_lock<std::mutex> lock(cs_blockchange); + WAIT_LOCK(cs_blockchange, lock); block = latestblock; if(timeout) cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&block]{return latestblock.height != block.height || latestblock.hash != block.hash || !IsRPCRunning(); }); @@ -259,14 +261,14 @@ static UniValue waitforblock(const JSONRPCRequest& request) ); int timeout = 0; - uint256 hash = uint256S(request.params[0].get_str()); + uint256 hash(ParseHashV(request.params[0], "blockhash")); if (!request.params[1].isNull()) timeout = request.params[1].get_int(); CUpdatedBlock block; { - std::unique_lock<std::mutex> lock(cs_blockchange); + WAIT_LOCK(cs_blockchange, lock); if(timeout) cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&hash]{return latestblock.hash == hash || !IsRPCRunning();}); else @@ -309,7 +311,7 @@ static UniValue waitforblockheight(const JSONRPCRequest& request) CUpdatedBlock block; { - std::unique_lock<std::mutex> lock(cs_blockchange); + WAIT_LOCK(cs_blockchange, lock); if(timeout) cond_blockchange.wait_for(lock, std::chrono::milliseconds(timeout), [&height]{return latestblock.height >= height || !IsRPCRunning();}); else @@ -379,7 +381,8 @@ static std::string EntryDescriptionString() " ... ]\n" " \"spentby\" : [ (array) unconfirmed transactions spending outputs from this transaction\n" " \"transactionid\", (string) child transaction id\n" - " ... ]\n"; + " ... ]\n" + " \"bip125-replaceable\" : true|false, (boolean) Whether this transaction could be replaced due to BIP125 (replace-by-fee)\n"; } static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCKS_REQUIRED(::mempool.cs) @@ -424,11 +427,22 @@ static void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) EXCLUSIVE_LOCK UniValue spent(UniValue::VARR); const CTxMemPool::txiter &it = mempool.mapTx.find(tx.GetHash()); const CTxMemPool::setEntries &setChildren = mempool.GetMemPoolChildren(it); - for (const CTxMemPool::txiter &childiter : setChildren) { + for (CTxMemPool::txiter childiter : setChildren) { spent.push_back(childiter->GetTx().GetHash().ToString()); } info.pushKV("spentby", spent); + + // Add opt-in RBF status + bool rbfStatus = false; + RBFTransactionState rbfState = IsRBFOptIn(tx, mempool); + if (rbfState == RBFTransactionState::UNKNOWN) { + throw JSONRPCError(RPC_MISC_ERROR, "Transaction is not in mempool"); + } else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125) { + rbfStatus = true; + } + + info.pushKV("bip125-replaceable", rbfStatus); } UniValue mempoolToJSON(bool fVerbose) @@ -714,8 +728,7 @@ static UniValue getblockheader(const JSONRPCRequest& request) LOCK(cs_main); - std::string strHash = request.params[0].get_str(); - uint256 hash(uint256S(strHash)); + uint256 hash(ParseHashV(request.params[0], "hash")); bool fVerbose = true; if (!request.params[1].isNull()) @@ -809,8 +822,7 @@ static UniValue getblock(const JSONRPCRequest& request) LOCK(cs_main); - std::string strHash = request.params[0].get_str(); - uint256 hash(uint256S(strHash)); + uint256 hash(ParseHashV(request.params[0], "blockhash")); int verbosity = 1; if (!request.params[1].isNull()) { @@ -1042,8 +1054,7 @@ UniValue gettxout(const JSONRPCRequest& request) UniValue ret(UniValue::VOBJ); - std::string strHash = request.params[0].get_str(); - uint256 hash(uint256S(strHash)); + uint256 hash(ParseHashV(request.params[0], "txid")); int n = request.params[1].get_int(); COutPoint out(hash, n); bool fMempool = true; @@ -1451,8 +1462,7 @@ static UniValue preciousblock(const JSONRPCRequest& request) + HelpExampleRpc("preciousblock", "\"blockhash\"") ); - std::string strHash = request.params[0].get_str(); - uint256 hash(uint256S(strHash)); + uint256 hash(ParseHashV(request.params[0], "blockhash")); CBlockIndex* pblockindex; { @@ -1487,8 +1497,7 @@ static UniValue invalidateblock(const JSONRPCRequest& request) + HelpExampleRpc("invalidateblock", "\"blockhash\"") ); - std::string strHash = request.params[0].get_str(); - uint256 hash(uint256S(strHash)); + uint256 hash(ParseHashV(request.params[0], "blockhash")); CValidationState state; { @@ -1527,8 +1536,7 @@ static UniValue reconsiderblock(const JSONRPCRequest& request) + HelpExampleRpc("reconsiderblock", "\"blockhash\"") ); - std::string strHash = request.params[0].get_str(); - uint256 hash(uint256S(strHash)); + uint256 hash(ParseHashV(request.params[0], "blockhash")); { LOCK(cs_main); @@ -1581,7 +1589,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) LOCK(cs_main); pindex = chainActive.Tip(); } else { - uint256 hash = uint256S(request.params[1].get_str()); + uint256 hash(ParseHashV(request.params[1], "blockhash")); LOCK(cs_main); pindex = LookupBlockIndex(hash); if (!pindex) { @@ -1755,8 +1763,7 @@ static UniValue getblockstats(const JSONRPCRequest& request) pindex = chainActive[height]; } else { - const std::string strHash = request.params[0].get_str(); - const uint256 hash(uint256S(strHash)); + const uint256 hash(ParseHashV(request.params[0], "hash_or_height")); pindex = LookupBlockIndex(hash); if (!pindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); @@ -1818,7 +1825,7 @@ static UniValue getblockstats(const JSONRPCRequest& request) if (loop_outputs) { for (const CTxOut& out : tx->vout) { tx_total_out += out.nValue; - utxo_size_inc += GetSerializeSize(out, SER_NETWORK, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD; + utxo_size_inc += GetSerializeSize(out, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD; } } @@ -1869,7 +1876,7 @@ static UniValue getblockstats(const JSONRPCRequest& request) CTxOut prevoutput = tx_in->vout[in.prevout.n]; tx_total_in += prevoutput.nValue; - utxo_size_inc -= GetSerializeSize(prevoutput, SER_NETWORK, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD; + utxo_size_inc -= GetSerializeSize(prevoutput, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD; } CAmount txfee = tx_total_in - tx_total_out; @@ -2045,7 +2052,7 @@ UniValue scantxoutset(const JSONRPCRequest& request) "or more path elements separated by \"/\", and optionally ending in \"/*\" (unhardened), or \"/*'\" or \"/*h\" (hardened) to specify all\n" "unhardened or hardened child keys.\n" "In the latter case, a range needs to be specified by below if different from 1000.\n" - "For more information on output descriptors, see the documentation at TODO\n" + "For more information on output descriptors, see the documentation in the doc/descriptors.md file.\n" "\nArguments:\n" "1. \"action\" (string, required) The action to execute\n" " \"start\" for starting a scan\n" @@ -2179,6 +2186,7 @@ UniValue scantxoutset(const JSONRPCRequest& request) return result; } +// clang-format off static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- @@ -2214,6 +2222,7 @@ static const CRPCCommand commands[] = { "hidden", "waitforblockheight", &waitforblockheight, {"height","timeout"} }, { "hidden", "syncwithvalidationinterfacequeue", &syncwithvalidationinterfacequeue, {} }, }; +// clang-format on void RegisterBlockchainRPCCommands(CRPCTable &t) { diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index 544bc62c36..add335eb8a 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -16,8 +16,7 @@ class UniValue; static constexpr int NUM_GETBLOCKSTATS_PERCENTILES = 5; /** - * Get the difficulty of the net wrt to the given block index, or the chain tip if - * not provided. + * Get the difficulty of the net wrt to the given block index. * * @return A floating point number that is a multiple of the main net minimum * difficulty (4295032833 hashes). diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index c7f3e38ac0..649e222c39 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -18,6 +18,7 @@ public: std::string paramName; //!< parameter name }; +// clang-format off /** * Specify a (method, idx, name) here if the argument is a non-string RPC * argument and needs to be converted from JSON. @@ -40,15 +41,11 @@ static const CRPCConvertParam vRPCConvertParams[] = { "settxfee", 0, "amount" }, { "sethdseed", 0, "newkeypool" }, { "getreceivedbyaddress", 1, "minconf" }, - { "getreceivedbyaccount", 1, "minconf" }, { "getreceivedbylabel", 1, "minconf" }, { "listreceivedbyaddress", 0, "minconf" }, { "listreceivedbyaddress", 1, "include_empty" }, { "listreceivedbyaddress", 2, "include_watchonly" }, { "listreceivedbyaddress", 3, "address_filter" }, - { "listreceivedbyaccount", 0, "minconf" }, - { "listreceivedbyaccount", 1, "include_empty" }, - { "listreceivedbyaccount", 2, "include_watchonly" }, { "listreceivedbylabel", 0, "minconf" }, { "listreceivedbylabel", 1, "include_empty" }, { "listreceivedbylabel", 2, "include_watchonly" }, @@ -59,15 +56,9 @@ static const CRPCConvertParam vRPCConvertParams[] = { "waitforblockheight", 1, "timeout" }, { "waitforblock", 1, "timeout" }, { "waitfornewblock", 0, "timeout" }, - { "move", 2, "amount" }, - { "move", 3, "minconf" }, - { "sendfrom", 2, "amount" }, - { "sendfrom", 3, "minconf" }, { "listtransactions", 1, "count" }, { "listtransactions", 2, "skip" }, { "listtransactions", 3, "include_watchonly" }, - { "listaccounts", 0, "minconf" }, - { "listaccounts", 1, "include_watchonly" }, { "walletpassphrase", 1, "timeout" }, { "getblocktemplate", 0, "template_request" }, { "listsinceblock", 1, "target_confirmations" }, @@ -172,7 +163,9 @@ static const CRPCConvertParam vRPCConvertParams[] = { "rescanblockchain", 0, "start_height"}, { "rescanblockchain", 1, "stop_height"}, { "createwallet", 1, "disable_private_keys"}, + { "getnodeaddresses", 0, "count"}, }; +// clang-format on class CRPCConvertTable { diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 623b0bd86a..1b2fc2c156 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -24,6 +24,7 @@ #include <utilstrencodings.h> #include <validation.h> #include <validationinterface.h> +#include <versionbitsinfo.h> #include <warnings.h> #include <memory> @@ -247,7 +248,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request) LOCK(cs_main); - uint256 hash = ParseHashStr(request.params[0].get_str(), "txid"); + uint256 hash(ParseHashV(request.params[0], "txid")); CAmount nAmount = request.params[2].get_int64(); if (!(request.params[1].isNull() || request.params[1].get_real() == 0)) { @@ -455,7 +456,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) // Format: <hashBestChain><nTransactionsUpdatedLast> std::string lpstr = lpval.get_str(); - hashWatchedChain.SetHex(lpstr.substr(0, 64)); + hashWatchedChain = ParseHashV(lpstr.substr(0, 64), "longpollid"); nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64)); } else @@ -470,7 +471,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) { checktxtime = std::chrono::steady_clock::now() + std::chrono::minutes(1); - WaitableLock lock(g_best_block_mutex); + WAIT_LOCK(g_best_block_mutex, lock); while (g_best_block == hashWatchedChain && IsRPCRunning()) { if (g_best_block_cv.wait_until(lock, checktxtime) == std::cv_status::timeout) @@ -750,11 +751,7 @@ static UniValue submitblock(const JSONRPCRequest& request) RegisterValidationInterface(&sc); bool accepted = ProcessNewBlock(Params(), blockptr, /* fForceProcessing */ true, /* fNewBlock */ &new_block); UnregisterValidationInterface(&sc); - if (!new_block) { - if (!accepted) { - // TODO Maybe pass down fNewBlock to AcceptBlockHeader, so it is properly set to true in this case? - return "invalid"; - } + if (!new_block && accepted) { return "duplicate"; } if (!sc.found) { @@ -799,12 +796,6 @@ static UniValue submitheader(const JSONRPCRequest& request) throw JSONRPCError(RPC_VERIFY_ERROR, state.GetRejectReason()); } -static UniValue estimatefee(const JSONRPCRequest& request) -{ - throw JSONRPCError(RPC_METHOD_DEPRECATED, "estimatefee was removed in v0.17.\n" - "Clients should use estimatesmartfee."); -} - static UniValue estimatesmartfee(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) @@ -922,7 +913,7 @@ static UniValue estimaterawfee(const JSONRPCRequest& request) UniValue result(UniValue::VOBJ); - for (FeeEstimateHorizon horizon : {FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}) { + for (const FeeEstimateHorizon horizon : {FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}) { CFeeRate feeRate; EstimationResult buckets; @@ -968,6 +959,7 @@ static UniValue estimaterawfee(const JSONRPCRequest& request) return result; } +// clang-format off static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- @@ -981,11 +973,11 @@ static const CRPCCommand commands[] = { "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} }, - { "hidden", "estimatefee", &estimatefee, {} }, { "util", "estimatesmartfee", &estimatesmartfee, {"conf_target", "estimate_mode"} }, { "hidden", "estimaterawfee", &estimaterawfee, {"conf_target", "threshold"} }, }; +// clang-format on void RegisterMiningRPCCommands(CRPCTable &t) { diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 40418545c1..0f3b601414 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -19,11 +19,6 @@ #include <timedata.h> #include <util.h> #include <utilstrencodings.h> -#ifdef ENABLE_WALLET -#include <wallet/rpcwallet.h> -#include <wallet/wallet.h> -#include <wallet/walletdb.h> -#endif #include <warnings.h> #include <stdint.h> @@ -67,29 +62,18 @@ static UniValue validateaddress(const JSONRPCRequest& request) ret.pushKV("isvalid", isValid); if (isValid) { + std::string currentAddress = EncodeDestination(dest); + ret.pushKV("address", currentAddress); -#ifdef ENABLE_WALLET - if (HasWallets() && IsDeprecatedRPCEnabled("validateaddress")) { - ret.pushKVs(getaddressinfo(request)); - } -#endif - if (ret["address"].isNull()) { - std::string currentAddress = EncodeDestination(dest); - ret.pushKV("address", currentAddress); - - CScript scriptPubKey = GetScriptForDestination(dest); - ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); + CScript scriptPubKey = GetScriptForDestination(dest); + ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); - UniValue detail = DescribeAddress(dest); - ret.pushKVs(detail); - } + UniValue detail = DescribeAddress(dest); + ret.pushKVs(detail); } return ret; } -// Needed even with !ENABLE_WALLET, to pass (ignored) pointers around -class CWallet; - static UniValue createmultisig(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 2 || request.params.size() > 3) @@ -130,8 +114,7 @@ static UniValue createmultisig(const JSONRPCRequest& request) if (IsHex(keys[i].get_str()) && (keys[i].get_str().length() == 66 || keys[i].get_str().length() == 130)) { pubkeys.push_back(HexToPubKey(keys[i].get_str())); } else { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid public key: %s\nNote that from v0.16, createmultisig no longer accepts addresses." - " Users must use addmultisigaddress to create multisig addresses with addresses known to the wallet.", keys[i].get_str())); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid public key: %s\n.", keys[i].get_str())); } } @@ -456,12 +439,13 @@ static UniValue echo(const JSONRPCRequest& request) return request.params; } +// clang-format off static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- { "control", "getmemoryinfo", &getmemoryinfo, {"mode"} }, { "control", "logging", &logging, {"include", "exclude"}}, - { "util", "validateaddress", &validateaddress, {"address"} }, /* uses wallet if enabled */ + { "util", "validateaddress", &validateaddress, {"address"} }, { "util", "createmultisig", &createmultisig, {"nrequired","keys"} }, { "util", "verifymessage", &verifymessage, {"address","signature","message"} }, { "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} }, @@ -471,6 +455,7 @@ static const CRPCCommand commands[] = { "hidden", "echo", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, { "hidden", "echojson", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, }; +// clang-format on void RegisterMiscRPCCommands(CRPCTable &t) { diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 610a1fee6f..846d90cd0a 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -102,6 +102,7 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) " ...\n" " ],\n" " \"whitelisted\": true|false, (boolean) Whether the peer is whitelisted\n" + " \"minfeefilter\": n, (numeric) The minimum fee rate for transactions this peer accepts\n" " \"bytessent_per_msg\": {\n" " \"addr\": n, (numeric) The total bytes sent aggregated by message type\n" " ...\n" @@ -163,12 +164,13 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) obj.pushKV("synced_headers", statestats.nSyncHeight); obj.pushKV("synced_blocks", statestats.nCommonHeight); UniValue heights(UniValue::VARR); - for (int height : statestats.vHeightInFlight) { + for (const int height : statestats.vHeightInFlight) { heights.push_back(height); } obj.pushKV("inflight", heights); } obj.pushKV("whitelisted", stats.fWhitelisted); + obj.pushKV("minfeefilter", ValueFromAmount(stats.minFeeFilter)); UniValue sendPerMsgCmd(UniValue::VOBJ); for (const mapMsgCmdSize::value_type &i : stats.mapSendBytesPerMsgCmd) { @@ -624,6 +626,59 @@ static UniValue setnetworkactive(const JSONRPCRequest& request) return g_connman->GetNetworkActive(); } +static UniValue getnodeaddresses(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() > 1) { + throw std::runtime_error( + "getnodeaddresses ( count )\n" + "\nReturn known addresses which can potentially be used to find new nodes in the network\n" + "\nArguments:\n" + "1. \"count\" (numeric, optional) How many addresses to return. Limited to the smaller of " + std::to_string(ADDRMAN_GETADDR_MAX) + + " or " + std::to_string(ADDRMAN_GETADDR_MAX_PCT) + "% of all known addresses. (default = 1)\n" + "\nResult:\n" + "[\n" + " {\n" + " \"time\": ttt, (numeric) Timestamp in seconds since epoch (Jan 1 1970 GMT) keeping track of when the node was last seen\n" + " \"services\": n, (numeric) The services offered\n" + " \"address\": \"host\", (string) The address of the node\n" + " \"port\": n (numeric) The port of the node\n" + " }\n" + " ,....\n" + "]\n" + "\nExamples:\n" + + HelpExampleCli("getnodeaddresses", "8") + + HelpExampleRpc("getnodeaddresses", "8") + ); + } + if (!g_connman) { + throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); + } + + int count = 1; + if (!request.params[0].isNull()) { + count = request.params[0].get_int(); + if (count <= 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Address count out of range"); + } + } + // returns a shuffled list of CAddress + std::vector<CAddress> vAddr = g_connman->GetAddresses(); + UniValue ret(UniValue::VARR); + + int address_return_count = std::min<int>(count, vAddr.size()); + for (int i = 0; i < address_return_count; ++i) { + UniValue obj(UniValue::VOBJ); + const CAddress& addr = vAddr[i]; + obj.pushKV("time", (int)addr.nTime); + obj.pushKV("services", (uint64_t)addr.nServices); + obj.pushKV("address", addr.ToStringIP()); + obj.pushKV("port", addr.GetPort()); + ret.push_back(obj); + } + return ret; +} + +// clang-format off static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- @@ -639,7 +694,9 @@ static const CRPCCommand commands[] = { "network", "listbanned", &listbanned, {} }, { "network", "clearbanned", &clearbanned, {} }, { "network", "setnetworkactive", &setnetworkactive, {"state"} }, + { "network", "getnodeaddresses", &getnodeaddresses, {"count"} }, }; +// clang-format on void RegisterNetRPCCommands(CRPCTable &t) { diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index dab63ab263..55bebb5662 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -128,7 +128,7 @@ void DeleteAuthCookie() try { fs::remove(GetAuthCookieFile()); } catch (const fs::filesystem_error& e) { - LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, e.what()); + LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, fsbridge::get_filesystem_error_message(e)); } } diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 314184ab06..7397216506 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -27,9 +27,6 @@ #include <txmempool.h> #include <uint256.h> #include <utilstrencodings.h> -#ifdef ENABLE_WALLET -#include <wallet/rpcwallet.h> -#endif #include <future> #include <stdint.h> @@ -229,9 +226,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request) UniValue txids = request.params[0].get_array(); for (unsigned int idx = 0; idx < txids.size(); idx++) { const UniValue& txid = txids[idx]; - if (txid.get_str().length() != 64 || !IsHex(txid.get_str())) - throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid txid ")+txid.get_str()); - uint256 hash(uint256S(txid.get_str())); + uint256 hash(ParseHashV(txid, "txid")); if (setTxids.count(hash)) throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated txid: ")+txid.get_str()); setTxids.insert(hash); @@ -242,7 +237,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request) uint256 hashBlock; if (!request.params[1].isNull()) { LOCK(cs_main); - hashBlock = uint256S(request.params[1].get_str()); + hashBlock = ParseHashV(request.params[1], "blockhash"); pblockindex = LookupBlockIndex(hashBlock); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); @@ -562,7 +557,6 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request) + HelpExampleRpc("decoderawtransaction", "\"hexstring\"") ); - LOCK(cs_main); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}); CMutableTransaction mtx; @@ -627,9 +621,8 @@ static UniValue decodescript(const JSONRPCRequest& request) // P2SH and witness programs cannot be wrapped in P2WSH, if this script // is a witness program, don't return addresses for a segwit programs. if (type.get_str() == "pubkey" || type.get_str() == "pubkeyhash" || type.get_str() == "multisig" || type.get_str() == "nonstandard") { - txnouttype which_type; std::vector<std::vector<unsigned char>> solutions_data; - Solver(script, which_type, solutions_data); + txnouttype which_type = Solver(script, solutions_data); // Uncompressed pubkeys cannot be used with segwit checksigs. // If the script contains an uncompressed pubkey, skip encoding of a segwit program. if ((which_type == TX_PUBKEY) || (which_type == TX_MULTISIG)) { @@ -826,8 +819,7 @@ UniValue SignTransaction(CMutableTransaction& mtx, const UniValue& prevTxsUnival view.AddCoin(out, std::move(newcoin), true); } - // if redeemScript given and not using the local wallet (private keys - // given), add redeemScript to the keystore so it can be signed: + // if redeemScript and private keys were given, add redeemScript to the keystore so it can be signed if (is_temp_keystore && (scriptPubKey.IsPayToScriptHash() || scriptPubKey.IsPayToWitnessScriptHash())) { RPCTypeCheckObj(prevOut, { @@ -982,102 +974,10 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request) UniValue signrawtransaction(const JSONRPCRequest& request) { -#ifdef ENABLE_WALLET - std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); - CWallet* const pwallet = wallet.get(); -#endif - - if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) - throw std::runtime_error( - "signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n" - "\nDEPRECATED. Sign inputs for raw transaction (serialized, hex-encoded).\n" - "The second optional argument (may be null) is an array of previous transaction outputs that\n" - "this transaction depends on but may not yet be in the block chain.\n" - "The third optional argument (may be null) is an array of base58-encoded private\n" - "keys that, if given, will be the only keys used to sign the transaction.\n" -#ifdef ENABLE_WALLET - + HelpRequiringPassphrase(pwallet) + "\n" -#endif - "\nArguments:\n" - "1. \"hexstring\" (string, required) The transaction hex string\n" - "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n" - " [ (json array of json objects, or 'null' if none provided)\n" - " {\n" - " \"txid\":\"id\", (string, required) The transaction id\n" - " \"vout\":n, (numeric, required) The output number\n" - " \"scriptPubKey\": \"hex\", (string, required) script key\n" - " \"redeemScript\": \"hex\", (string, required for P2SH or P2WSH) redeem script\n" - " \"amount\": value (numeric, required) The amount spent\n" - " }\n" - " ,...\n" - " ]\n" - "3. \"privkeys\" (string, optional) A json array of base58-encoded private keys for signing\n" - " [ (json array of strings, or 'null' if none provided)\n" - " \"privatekey\" (string) private key in base58-encoding\n" - " ,...\n" - " ]\n" - "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n" - " \"ALL\"\n" - " \"NONE\"\n" - " \"SINGLE\"\n" - " \"ALL|ANYONECANPAY\"\n" - " \"NONE|ANYONECANPAY\"\n" - " \"SINGLE|ANYONECANPAY\"\n" - - "\nResult:\n" - "{\n" - " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" - " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" - " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n" - " {\n" - " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n" - " \"vout\" : n, (numeric) The index of the output to spent and used as input\n" - " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n" - " \"sequence\" : n, (numeric) Script sequence number\n" - " \"error\" : \"text\" (string) Verification or signing error related to the input\n" - " }\n" - " ,...\n" - " ]\n" - "}\n" - - "\nExamples:\n" - + HelpExampleCli("signrawtransaction", "\"myhex\"") - + HelpExampleRpc("signrawtransaction", "\"myhex\"") - ); - - if (!IsDeprecatedRPCEnabled("signrawtransaction")) { - throw JSONRPCError(RPC_METHOD_DEPRECATED, "signrawtransaction is deprecated and will be fully removed in v0.18. " - "To use signrawtransaction in v0.17, restart bitcoind with -deprecatedrpc=signrawtransaction.\n" - "Projects should transition to using signrawtransactionwithkey and signrawtransactionwithwallet before upgrading to v0.18"); - } - - RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR, UniValue::VARR, UniValue::VSTR}, true); - - // Make a JSONRPCRequest to pass on to the right signrawtransaction* command - JSONRPCRequest new_request; - new_request.id = request.id; - new_request.params.setArray(); - - // For signing with private keys - if (!request.params[2].isNull()) { - new_request.params.push_back(request.params[0]); - // Note: the prevtxs and privkeys are reversed for signrawtransactionwithkey - new_request.params.push_back(request.params[2]); - new_request.params.push_back(request.params[1]); - new_request.params.push_back(request.params[3]); - return signrawtransactionwithkey(new_request); - } else { -#ifdef ENABLE_WALLET - // Otherwise sign with the wallet which does not take a privkeys parameter - new_request.params.push_back(request.params[0]); - new_request.params.push_back(request.params[1]); - new_request.params.push_back(request.params[3]); - return signrawtransactionwithwallet(new_request); -#else - // If we have made it this far, then wallet is disabled and no private keys were given, so fail here. - throw JSONRPCError(RPC_INVALID_PARAMETER, "No private keys available."); -#endif - } + // This method should be removed entirely in V0.19, along with the entries in the + // CRPCCommand table and rpc/client.cpp. + throw JSONRPCError(RPC_METHOD_DEPRECATED, "signrawtransaction was removed in v0.18.\n" + "Clients should transition to using signrawtransactionwithkey and signrawtransactionwithwallet"); } static UniValue sendrawtransaction(const JSONRPCRequest& request) @@ -1086,7 +986,7 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request) throw std::runtime_error( "sendrawtransaction \"hexstring\" ( allowhighfees )\n" "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n" - "\nAlso see createrawtransaction and signrawtransaction calls.\n" + "\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n" "\nArguments:\n" "1. \"hexstring\" (string, required) The hex string of the raw transaction)\n" "2. allowhighfees (boolean, optional, default=false) Allow high fees\n" @@ -1096,7 +996,7 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request) "\nCreate a transaction\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") + "Sign the transaction, and get back the hex\n" - + HelpExampleCli("signrawtransaction", "\"myhex\"") + + + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") + "\nSend the transaction (signed hex)\n" + HelpExampleCli("sendrawtransaction", "\"signedhex\"") + "\nAs a json rpc call\n" @@ -1201,7 +1101,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) "\nCreate a transaction\n" + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") + "Sign the transaction, and get back the hex\n" - + HelpExampleCli("signrawtransaction", "\"myhex\"") + + + HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"") + "\nTest acceptance of the transaction (signed hex)\n" + HelpExampleCli("testmempoolaccept", "\"signedhex\"") + "\nAs a json rpc call\n" @@ -1460,11 +1360,8 @@ UniValue decodepsbt(const JSONRPCRequest& request) UniValue keypath(UniValue::VOBJ); keypath.pushKV("pubkey", HexStr(entry.first)); - uint32_t fingerprint = entry.second.at(0); - keypath.pushKV("master_fingerprint", strprintf("%08x", bswap_32(fingerprint))); - - entry.second.erase(entry.second.begin()); - keypath.pushKV("path", WriteHDKeypath(entry.second)); + keypath.pushKV("master_fingerprint", strprintf("%08x", ReadBE32(entry.second.fingerprint))); + keypath.pushKV("path", WriteHDKeypath(entry.second.path)); keypaths.push_back(keypath); } in.pushKV("bip32_derivs", keypaths); @@ -1522,12 +1419,8 @@ UniValue decodepsbt(const JSONRPCRequest& request) for (auto entry : output.hd_keypaths) { UniValue keypath(UniValue::VOBJ); keypath.pushKV("pubkey", HexStr(entry.first)); - - uint32_t fingerprint = entry.second.at(0); - keypath.pushKV("master_fingerprint", strprintf("%08x", bswap_32(fingerprint))); - - entry.second.erase(entry.second.begin()); - keypath.pushKV("path", WriteHDKeypath(entry.second)); + keypath.pushKV("master_fingerprint", strprintf("%08x", ReadBE32(entry.second.fingerprint))); + keypath.pushKV("path", WriteHDKeypath(entry.second.path)); keypaths.push_back(keypath); } out.pushKV("bip32_derivs", keypaths); @@ -1648,8 +1541,7 @@ UniValue finalizepsbt(const JSONRPCRequest& request) for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { PSBTInput& input = psbtx.inputs.at(i); - SignatureData sigdata; - complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, *psbtx.tx, input, sigdata, i, 1); + complete &= SignPSBTInput(DUMMY_SIGNING_PROVIDER, *psbtx.tx, input, i, 1); } UniValue result(UniValue::VOBJ); @@ -1750,7 +1642,7 @@ UniValue converttopsbt(const JSONRPCRequest& request) " will continue. If false, RPC will fail if any signatures are present.\n" "3. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction.\n" " If iswitness is not present, heuristic tests will be used in decoding. If true, only witness deserializaion\n" - " will be tried. If false, only non-witness deserialization wil be tried. Only has an effect if\n" + " will be tried. If false, only non-witness deserialization will be tried. Only has an effect if\n" " permitsigdata is true.\n" "\nResult:\n" " \"psbt\" (string) The resulting raw transaction (base64-encoded string)\n" @@ -1801,6 +1693,7 @@ UniValue converttopsbt(const JSONRPCRequest& request) return EncodeBase64((unsigned char*)ssTx.data(), ssTx.size()); } +// clang-format off static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- @@ -1810,7 +1703,7 @@ static const CRPCCommand commands[] = { "rawtransactions", "decodescript", &decodescript, {"hexstring"} }, { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} }, { "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} }, - { "rawtransactions", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */ + { "hidden", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, { "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} }, { "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees"} }, { "rawtransactions", "decodepsbt", &decodepsbt, {"psbt"} }, @@ -1822,6 +1715,7 @@ static const CRPCCommand commands[] = { "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} }, { "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} }, }; +// clang-format on void RegisterRawTransactionRPCCommands(CRPCTable &t) { diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index e46bf2f765..60bf3c28c0 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -16,7 +16,6 @@ #include <boost/bind.hpp> #include <boost/signals2/signal.hpp> -#include <boost/algorithm/string/case_conv.hpp> // for to_upper() #include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/split.hpp> @@ -117,16 +116,12 @@ CAmount AmountFromValue(const UniValue& value) uint256 ParseHashV(const UniValue& v, std::string strName) { - std::string strHex; - if (v.isStr()) - strHex = v.get_str(); + std::string strHex(v.get_str()); + if (64 != strHex.length()) + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex)); if (!IsHex(strHex)) // Note: IsHex("") is false throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); - if (64 != strHex.length()) - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d)", strName, 64, strHex.length())); - uint256 result; - result.SetHex(strHex); - return result; + return uint256S(strHex); } uint256 ParseHashO(const UniValue& o, std::string strKey) { @@ -192,9 +187,7 @@ std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& if (!category.empty()) strRet += "\n"; category = pcmd->category; - std::string firstLetter = category.substr(0,1); - boost::to_upper(firstLetter); - strRet += "== " + firstLetter + category.substr(1) + " ==\n"; + strRet += "== " + Capitalize(category) + " ==\n"; } } strRet += strHelp + "\n"; @@ -255,6 +248,7 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest) return GetTime() - GetStartupTime(); } +// clang-format off /** * Call Table */ @@ -266,6 +260,7 @@ static const CRPCCommand vRPCCommands[] = { "control", "stop", &stop, {} }, { "control", "uptime", &uptime, {} }, }; +// clang-format on CRPCTable::CRPCTable() { @@ -541,7 +536,7 @@ void RPCUnsetTimerInterface(RPCTimerInterface *iface) timerInterface = nullptr; } -void RPCRunLater(const std::string& name, std::function<void(void)> func, int64_t nSeconds) +void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nSeconds) { if (!timerInterface) throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC"); diff --git a/src/rpc/server.h b/src/rpc/server.h index 15d0ec80f5..2d62a76f3c 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -110,7 +110,7 @@ public: * This is needed to cope with the case in which there is no HTTP server, but * only GUI RPC console, and to break the dependency of pcserver on httprpc. */ - virtual RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis) = 0; + virtual RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis) = 0; }; /** Set the factory function for timers */ @@ -124,7 +124,7 @@ void RPCUnsetTimerInterface(RPCTimerInterface *iface); * Run func nSeconds from now. * Overrides previous timer <name> (if any). */ -void RPCRunLater(const std::string& name, std::function<void(void)> func, int64_t nSeconds); +void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nSeconds); typedef UniValue(*rpcfn_type)(const JSONRPCRequest& jsonRequest); diff --git a/src/scheduler.cpp b/src/scheduler.cpp index 89dfc2b363..552391d7d0 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -159,7 +159,7 @@ void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue() { } void SingleThreadedSchedulerClient::ProcessQueue() { - std::function<void (void)> callback; + std::function<void ()> callback; { LOCK(m_cs_callbacks_pending); if (m_are_callbacks_running) return; @@ -187,7 +187,7 @@ void SingleThreadedSchedulerClient::ProcessQueue() { callback(); } -void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void (void)> func) { +void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void ()> func) { assert(m_pscheduler); { diff --git a/src/scheduler.h b/src/scheduler.h index 0c2551cf4f..6c45f508ec 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -40,7 +40,7 @@ public: CScheduler(); ~CScheduler(); - typedef std::function<void(void)> Function; + typedef std::function<void()> Function; // Call func at/after time t void schedule(Function f, boost::chrono::system_clock::time_point t=boost::chrono::system_clock::now()); @@ -99,7 +99,7 @@ private: CScheduler *m_pscheduler; CCriticalSection m_cs_callbacks_pending; - std::list<std::function<void (void)>> m_callbacks_pending GUARDED_BY(m_cs_callbacks_pending); + std::list<std::function<void ()>> m_callbacks_pending GUARDED_BY(m_cs_callbacks_pending); bool m_are_callbacks_running GUARDED_BY(m_cs_callbacks_pending) = false; void MaybeScheduleProcessQueue(); @@ -111,10 +111,10 @@ public: /** * Add a callback to be executed. Callbacks are executed serially * and memory is release-acquire consistent between callback executions. - * Practially, this means that callbacks can behave as if they are executed + * Practically, this means that callbacks can behave as if they are executed * in order by a single thread. */ - void AddToProcessQueue(std::function<void (void)> func); + void AddToProcessQueue(std::function<void ()> func); // Processes all remaining queue members on the calling thread, blocking until queue is empty // Must be called after the CScheduler has no remaining processing threads! diff --git a/src/script/bitcoinconsensus.cpp b/src/script/bitcoinconsensus.cpp index 01cfeb23f1..15e204062f 100644 --- a/src/script/bitcoinconsensus.cpp +++ b/src/script/bitcoinconsensus.cpp @@ -88,7 +88,7 @@ static int verify_script(const unsigned char *scriptPubKey, unsigned int scriptP CTransaction tx(deserialize, stream); if (nIn >= tx.vin.size()) return set_error(err, bitcoinconsensus_ERR_TX_INDEX); - if (GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION) != txToLen) + if (GetSerializeSize(tx, PROTOCOL_VERSION) != txToLen) return set_error(err, bitcoinconsensus_ERR_TX_SIZE_MISMATCH); // Regardless of the verification result, the tx did not error. diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index f366b99ec3..45b097dde6 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -373,7 +373,7 @@ enum class ParseScriptContext { P2WSH, }; -/** Parse a constant. If succesful, sp is updated to skip the constant and return true. */ +/** Parse a constant. If successful, sp is updated to skip the constant and return true. */ bool Const(const std::string& str, Span<const char>& sp) { if ((size_t)sp.size() >= str.size() && std::equal(str.begin(), str.end(), sp.begin())) { @@ -383,7 +383,7 @@ bool Const(const std::string& str, Span<const char>& sp) return false; } -/** Parse a function call. If succesful, sp is updated to be the function's argument(s). */ +/** Parse a function call. If successful, sp is updated to be the function's argument(s). */ bool Func(const std::string& str, Span<const char>& sp) { if ((size_t)sp.size() >= str.size() + 2 && sp[str.size()] == '(' && sp[sp.size() - 1] == ')' && std::equal(str.begin(), str.end(), sp.begin())) { diff --git a/src/script/descriptor.h b/src/script/descriptor.h index e079c72e92..87e07369c7 100644 --- a/src/script/descriptor.h +++ b/src/script/descriptor.h @@ -22,55 +22,8 @@ // they can be included inside by changing public keys to private keys (WIF // format), and changing xpubs by xprvs. // -// 1. Examples -// -// A P2PK descriptor with a fixed public key: -// - pk(0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798) -// -// A P2SH-P2WSH-P2PKH descriptor with a fixed public key: -// - sh(wsh(pkh(02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13))) -// -// A bare 1-of-2 multisig descriptor: -// - multi(1,022f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc) -// -// A chain of P2PKH outputs (this needs the corresponding private key to derive): -// - pkh(xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw/1'/2/*) -// -// 2. Grammar description: -// -// X: xpub or xprv encoded extended key -// I: decimal encoded integer -// H: Hex encoded byte array -// A: Address in P2PKH, P2SH, or Bech32 encoding -// -// S (Scripts): -// * pk(P): Pay-to-pubkey (P2PK) output for public key P. -// * pkh(P): Pay-to-pubkey-hash (P2PKH) output for public key P. -// * wpkh(P): Pay-to-witness-pubkey-hash (P2WPKH) output for public key P. -// * sh(S): Pay-to-script-hash (P2SH) output for script S -// * wsh(S): Pay-to-witness-script-hash (P2WSH) output for script S -// * combo(P): combination of P2PK, P2PKH, P2WPKH, and P2SH-P2WPKH for public key P. -// * multi(I,L): k-of-n multisig for given public keys -// * addr(A): Output to address -// * raw(H): scriptPubKey with raw bytes -// -// P (Public keys): -// * H: fixed public key (or WIF-encoded private key) -// * E: extended public key -// * E/*: (ranged) all unhardened direct children of an extended public key -// * E/*': (ranged) all hardened direct children of an extended public key -// -// L (Comma-separated lists of public keys): -// * P -// * L,P -// -// E (Extended public keys): -// * X -// * E/I: unhardened child -// * E/I': hardened child -// * E/Ih: hardened child (alternative notation) -// -// The top level is S. +// Reference documentation about the descriptor language can be found in +// doc/descriptors.md. /** Interface for parsed descriptor objects. */ struct Descriptor { diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index e3a6278c75..1433ebf42f 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -60,8 +60,7 @@ IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, IsMineResult ret = IsMineResult::NO; std::vector<valtype> vSolutions; - txnouttype whichType; - Solver(scriptPubKey, whichType, vSolutions); + txnouttype whichType = Solver(scriptPubKey, vSolutions); CKeyID keyID; switch (whichType) diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 1982e8a832..d779910425 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -50,10 +50,6 @@ static bool GetCScript(const SigningProvider& provider, const SignatureData& sig static bool GetPubKey(const SigningProvider& provider, SignatureData& sigdata, const CKeyID& address, CPubKey& pubkey) { - if (provider.GetPubKey(address, pubkey)) { - sigdata.misc_pubkeys.emplace(pubkey.GetID(), pubkey); - return true; - } // Look for pubkey in all partial sigs const auto it = sigdata.signatures.find(address); if (it != sigdata.signatures.end()) { @@ -63,7 +59,15 @@ static bool GetPubKey(const SigningProvider& provider, SignatureData& sigdata, c // Look for pubkey in pubkey list const auto& pk_it = sigdata.misc_pubkeys.find(address); if (pk_it != sigdata.misc_pubkeys.end()) { - pubkey = pk_it->second; + pubkey = pk_it->second.first; + return true; + } + // Query the underlying provider + if (provider.GetPubKey(address, pubkey)) { + KeyOriginInfo info; + if (provider.GetKeyOrigin(address, info)) { + sigdata.misc_pubkeys.emplace(address, std::make_pair(pubkey, std::move(info))); + } return true; } return false; @@ -101,8 +105,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator std::vector<unsigned char> sig; std::vector<valtype> vSolutions; - if (!Solver(scriptPubKey, whichTypeRet, vSolutions)) - return false; + whichTypeRet = Solver(scriptPubKey, vSolutions); switch (whichTypeRet) { @@ -233,7 +236,7 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato return sigdata.complete; } -bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, SignatureData& sigdata, int index, int sighash) +bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, int index, int sighash) { // if this input has a final scriptsig or scriptwitness, don't do anything with it if (!input.final_script_sig.empty() || !input.final_script_witness.IsNull()) { @@ -241,6 +244,7 @@ bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& t } // Fill SignatureData with input info + SignatureData sigdata; input.FillSignatureData(sigdata); // Get UTXO @@ -272,6 +276,16 @@ bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& t // Verify that a witness signature was produced in case one was required. if (require_witness_sig && !sigdata.witness) return false; input.FromSignatureData(sigdata); + + // If both UTXO types are present, drop the unnecessary one. + if (input.non_witness_utxo && !input.witness_utxo.IsNull()) { + if (sigdata.witness) { + input.non_witness_utxo = nullptr; + } else { + input.witness_utxo.SetNull(); + } + } + return sig_complete; } @@ -329,9 +343,8 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI } // Get scripts - txnouttype script_type; std::vector<std::vector<unsigned char>> solutions; - Solver(txout.scriptPubKey, script_type, solutions); + txnouttype script_type = Solver(txout.scriptPubKey, solutions); SigVersion sigversion = SigVersion::BASE; CScript next_script = txout.scriptPubKey; @@ -342,7 +355,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI next_script = std::move(redeem_script); // Get redeemScript type - Solver(next_script, script_type, solutions); + script_type = Solver(next_script, solutions); stack.script.pop_back(); } if (script_type == TX_WITNESS_V0_SCRIPTHASH && !stack.witness.empty() && !stack.witness.back().empty()) { @@ -352,7 +365,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI next_script = std::move(witness_script); // Get witnessScript type - Solver(next_script, script_type, solutions); + script_type = Solver(next_script, solutions); stack.witness.pop_back(); stack.script = std::move(stack.witness); stack.witness.clear(); @@ -543,7 +556,7 @@ void PSBTInput::FillSignatureData(SignatureData& sigdata) const sigdata.witness_script = witness_script; } for (const auto& key_pair : hd_keypaths) { - sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair.first); + sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair); } } @@ -571,6 +584,9 @@ void PSBTInput::FromSignatureData(const SignatureData& sigdata) if (witness_script.empty() && !sigdata.witness_script.empty()) { witness_script = sigdata.witness_script; } + for (const auto& entry : sigdata.misc_pubkeys) { + hd_keypaths.emplace(entry.second); + } } void PSBTInput::Merge(const PSBTInput& input) @@ -612,7 +628,7 @@ void PSBTOutput::FillSignatureData(SignatureData& sigdata) const sigdata.witness_script = witness_script; } for (const auto& key_pair : hd_keypaths) { - sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair.first); + sigdata.misc_pubkeys.emplace(key_pair.first.GetID(), key_pair); } } @@ -624,6 +640,9 @@ void PSBTOutput::FromSignatureData(const SignatureData& sigdata) if (witness_script.empty() && !sigdata.witness_script.empty()) { witness_script = sigdata.witness_script; } + for (const auto& entry : sigdata.misc_pubkeys) { + hd_keypaths.emplace(entry.second); + } } bool PSBTOutput::IsNull() const @@ -640,14 +659,26 @@ void PSBTOutput::Merge(const PSBTOutput& output) if (witness_script.empty() && !output.witness_script.empty()) witness_script = output.witness_script; } -bool PublicOnlySigningProvider::GetCScript(const CScriptID &scriptid, CScript& script) const +bool HidingSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return m_provider->GetCScript(scriptid, script); } -bool PublicOnlySigningProvider::GetPubKey(const CKeyID &address, CPubKey& pubkey) const +bool HidingSigningProvider::GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const +{ + return m_provider->GetPubKey(keyid, pubkey); +} + +bool HidingSigningProvider::GetKey(const CKeyID& keyid, CKey& key) const +{ + if (m_hide_secret) return false; + return m_provider->GetKey(keyid, key); +} + +bool HidingSigningProvider::GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const { - return m_provider->GetPubKey(address, pubkey); + if (m_hide_origin) return false; + return m_provider->GetKeyOrigin(keyid, info); } bool FlatSigningProvider::GetCScript(const CScriptID& scriptid, CScript& script) const { return LookupHelper(scripts, scriptid, script); } diff --git a/src/script/sign.h b/src/script/sign.h index 7ade715ee2..2fc4575e59 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -20,6 +20,12 @@ class CTransaction; struct CMutableTransaction; +struct KeyOriginInfo +{ + unsigned char fingerprint[4]; + std::vector<uint32_t> path; +}; + /** An interface to be implemented by keystores that support signing. */ class SigningProvider { @@ -28,19 +34,24 @@ public: virtual bool GetCScript(const CScriptID &scriptid, CScript& script) const { return false; } virtual bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const { return false; } virtual bool GetKey(const CKeyID &address, CKey& key) const { return false; } + virtual bool GetKeyOrigin(const CKeyID& id, KeyOriginInfo& info) const { return false; } }; extern const SigningProvider& DUMMY_SIGNING_PROVIDER; -class PublicOnlySigningProvider : public SigningProvider +class HidingSigningProvider : public SigningProvider { private: + const bool m_hide_secret; + const bool m_hide_origin; const SigningProvider* m_provider; public: - PublicOnlySigningProvider(const SigningProvider* provider) : m_provider(provider) {} - bool GetCScript(const CScriptID &scriptid, CScript& script) const; - bool GetPubKey(const CKeyID &address, CPubKey& pubkey) const; + HidingSigningProvider(const SigningProvider* provider, bool hide_secret, bool hide_origin) : m_hide_secret(hide_secret), m_hide_origin(hide_origin), m_provider(provider) {} + bool GetCScript(const CScriptID& scriptid, CScript& script) const override; + bool GetPubKey(const CKeyID& keyid, CPubKey& pubkey) const override; + bool GetKey(const CKeyID& keyid, CKey& key) const override; + bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override; }; struct FlatSigningProvider final : public SigningProvider @@ -98,7 +109,7 @@ struct SignatureData { CScript witness_script; ///< The witnessScript (if any) for the input. witnessScripts are used in P2WSH outputs. CScriptWitness scriptWitness; ///< The scriptWitness of an input. Contains complete signatures or the traditional partial signatures format. scriptWitness is part of a transaction input per BIP 144. std::map<CKeyID, SigPair> signatures; ///< BIP 174 style partial signatures for the input. May contain all signatures necessary for producing a final scriptSig or scriptWitness. - std::map<CKeyID, CPubKey> misc_pubkeys; + std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>> misc_pubkeys; SignatureData() {} explicit SignatureData(const CScript& script) : scriptSig(script) {} @@ -136,7 +147,7 @@ static constexpr uint8_t PSBT_SEPARATOR = 0x00; template<typename Stream, typename... X> void SerializeToVector(Stream& s, const X&... args) { - WriteCompactSize(s, GetSerializeSizeMany(s, args...)); + WriteCompactSize(s, GetSerializeSizeMany(s.GetVersion(), args...)); SerializeMany(s, args...); } @@ -155,7 +166,7 @@ void UnserializeFromVector(Stream& s, X&... args) // Deserialize HD keypaths into a map template<typename Stream> -void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std::map<CPubKey, std::vector<uint32_t>>& hd_keypaths) +void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std::map<CPubKey, KeyOriginInfo>& hd_keypaths) { // Make sure that the key is the size of pubkey + 1 if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) { @@ -172,25 +183,31 @@ void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std // Read in key path uint64_t value_len = ReadCompactSize(s); - std::vector<uint32_t> keypath; - for (unsigned int i = 0; i < value_len; i += sizeof(uint32_t)) { + if (value_len % 4 || value_len == 0) { + throw std::ios_base::failure("Invalid length for HD key path"); + } + + KeyOriginInfo keypath; + s >> keypath.fingerprint; + for (unsigned int i = 4; i < value_len; i += sizeof(uint32_t)) { uint32_t index; s >> index; - keypath.push_back(index); + keypath.path.push_back(index); } // Add to map - hd_keypaths.emplace(pubkey, keypath); + hd_keypaths.emplace(pubkey, std::move(keypath)); } // Serialize HD keypaths to a stream from a map template<typename Stream> -void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, std::vector<uint32_t>>& hd_keypaths, uint8_t type) +void SerializeHDKeypaths(Stream& s, const std::map<CPubKey, KeyOriginInfo>& hd_keypaths, uint8_t type) { for (auto keypath_pair : hd_keypaths) { SerializeToVector(s, type, MakeSpan(keypath_pair.first)); - WriteCompactSize(s, keypath_pair.second.size() * sizeof(uint32_t)); - for (auto& path : keypath_pair.second) { + WriteCompactSize(s, (keypath_pair.second.path.size() + 1) * sizeof(uint32_t)); + s << keypath_pair.second.fingerprint; + for (const auto& path : keypath_pair.second.path) { s << path; } } @@ -205,7 +222,7 @@ struct PSBTInput CScript witness_script; CScript final_script_sig; CScriptWitness final_script_witness; - std::map<CPubKey, std::vector<uint32_t>> hd_keypaths; + std::map<CPubKey, KeyOriginInfo> hd_keypaths; std::map<CKeyID, SigPair> partial_sigs; std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; int sighash_type = 0; @@ -418,7 +435,7 @@ struct PSBTOutput { CScript redeem_script; CScript witness_script; - std::map<CPubKey, std::vector<uint32_t>> hd_keypaths; + std::map<CPubKey, KeyOriginInfo> hd_keypaths; std::map<std::vector<unsigned char>, std::vector<unsigned char>> unknown; bool IsNull() const; @@ -687,7 +704,7 @@ bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, C bool SignSignature(const SigningProvider &provider, const CTransaction& txFrom, CMutableTransaction& txTo, unsigned int nIn, int nHashType); /** Signs a PSBTInput, verifying that all provided data matches what is being signed. */ -bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, SignatureData& sigdata, int index, int sighash = 1); +bool SignPSBTInput(const SigningProvider& provider, const CMutableTransaction& tx, PSBTInput& input, int index, int sighash = SIGHASH_ALL); /** Extract signature data from a transaction input, and insert it. */ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nIn, const CTxOut& txout); diff --git a/src/script/standard.cpp b/src/script/standard.cpp index bfbf9f13db..08ba1b1e0f 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -87,7 +87,7 @@ static bool MatchMultisig(const CScript& script, unsigned int& required, std::ve return (it + 1 == script.end()); } -bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet) +txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet) { vSolutionsRet.clear(); @@ -95,33 +95,28 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v // it is always OP_HASH160 20 [20 byte hash] OP_EQUAL if (scriptPubKey.IsPayToScriptHash()) { - typeRet = TX_SCRIPTHASH; std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22); vSolutionsRet.push_back(hashBytes); - return true; + return TX_SCRIPTHASH; } int witnessversion; std::vector<unsigned char> witnessprogram; if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) { - typeRet = TX_WITNESS_V0_KEYHASH; vSolutionsRet.push_back(witnessprogram); - return true; + return TX_WITNESS_V0_KEYHASH; } if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) { - typeRet = TX_WITNESS_V0_SCRIPTHASH; vSolutionsRet.push_back(witnessprogram); - return true; + return TX_WITNESS_V0_SCRIPTHASH; } if (witnessversion != 0) { - typeRet = TX_WITNESS_UNKNOWN; vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion}); vSolutionsRet.push_back(std::move(witnessprogram)); - return true; + return TX_WITNESS_UNKNOWN; } - typeRet = TX_NONSTANDARD; - return false; + return TX_NONSTANDARD; } // Provably prunable, data-carrying output @@ -130,47 +125,39 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v // byte passes the IsPushOnly() test we don't care what exactly is in the // script. if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) { - typeRet = TX_NULL_DATA; - return true; + return TX_NULL_DATA; } std::vector<unsigned char> data; if (MatchPayToPubkey(scriptPubKey, data)) { - typeRet = TX_PUBKEY; vSolutionsRet.push_back(std::move(data)); - return true; + return TX_PUBKEY; } if (MatchPayToPubkeyHash(scriptPubKey, data)) { - typeRet = TX_PUBKEYHASH; vSolutionsRet.push_back(std::move(data)); - return true; + return TX_PUBKEYHASH; } unsigned int required; std::vector<std::vector<unsigned char>> keys; if (MatchMultisig(scriptPubKey, required, keys)) { - typeRet = TX_MULTISIG; vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..16 vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end()); vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..16 - return true; + return TX_MULTISIG; } vSolutionsRet.clear(); - typeRet = TX_NONSTANDARD; - return false; + return TX_NONSTANDARD; } bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) { std::vector<valtype> vSolutions; - txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) - return false; + txnouttype whichType = Solver(scriptPubKey, vSolutions); - if (whichType == TX_PUBKEY) - { + if (whichType == TX_PUBKEY) { CPubKey pubKey(vSolutions[0]); if (!pubKey.IsValid()) return false; @@ -212,11 +199,11 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet) { addressRet.clear(); - typeRet = TX_NONSTANDARD; std::vector<valtype> vSolutions; - if (!Solver(scriptPubKey, typeRet, vSolutions)) + typeRet = Solver(scriptPubKey, vSolutions); + if (typeRet == TX_NONSTANDARD) { return false; - if (typeRet == TX_NULL_DATA){ + } else if (typeRet == TX_NULL_DATA) { // This is data, not addresses return false; } @@ -324,14 +311,12 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys) CScript GetScriptForWitness(const CScript& redeemscript) { - txnouttype typ; std::vector<std::vector<unsigned char> > vSolutions; - if (Solver(redeemscript, typ, vSolutions)) { - if (typ == TX_PUBKEY) { - return GetScriptForDestination(WitnessV0KeyHash(Hash160(vSolutions[0].begin(), vSolutions[0].end()))); - } else if (typ == TX_PUBKEYHASH) { - return GetScriptForDestination(WitnessV0KeyHash(vSolutions[0])); - } + txnouttype typ = Solver(redeemscript, vSolutions); + if (typ == TX_PUBKEY) { + return GetScriptForDestination(WitnessV0KeyHash(Hash160(vSolutions[0].begin(), vSolutions[0].end()))); + } else if (typ == TX_PUBKEYHASH) { + return GetScriptForDestination(WitnessV0KeyHash(vSolutions[0])); } return GetScriptForDestination(WitnessV0ScriptHash(redeemscript)); } diff --git a/src/script/standard.h b/src/script/standard.h index b16ac83e77..fc20fb6a08 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -135,11 +135,10 @@ const char* GetTxnOutputType(txnouttype t); * script hash, for P2PKH it will contain the key hash, etc. * * @param[in] scriptPubKey Script to parse - * @param[out] typeRet The script type * @param[out] vSolutionsRet Vector of parsed pubkeys and hashes - * @return True if script matches standard template + * @return The script type. TX_NONSTANDARD represents a failed solve. */ -bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet); +txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet); /** * Parse a standard scriptPubKey for the destination address. Assigns result to diff --git a/src/serialize.h b/src/serialize.h index 8bd3ef5d76..2d0cfbbbf0 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -901,10 +901,9 @@ class CSizeComputer protected: size_t nSize; - const int nType; const int nVersion; public: - CSizeComputer(int nTypeIn, int nVersionIn) : nSize(0), nType(nTypeIn), nVersion(nVersionIn) {} + explicit CSizeComputer(int nVersionIn) : nSize(0), nVersion(nVersionIn) {} void write(const char *psz, size_t _nSize) { @@ -929,7 +928,6 @@ public: } int GetVersion() const { return nVersion; } - int GetType() const { return nType; } }; template<typename Stream> @@ -980,21 +978,15 @@ inline void WriteCompactSize(CSizeComputer &s, uint64_t nSize) } template <typename T> -size_t GetSerializeSize(const T& t, int nType, int nVersion = 0) +size_t GetSerializeSize(const T& t, int nVersion = 0) { - return (CSizeComputer(nType, nVersion) << t).size(); + return (CSizeComputer(nVersion) << t).size(); } -template <typename S, typename T> -size_t GetSerializeSize(const S& s, const T& t) +template <typename... T> +size_t GetSerializeSizeMany(int nVersion, const T&... t) { - return (CSizeComputer(s.GetType(), s.GetVersion()) << t).size(); -} - -template <typename S, typename... T> -size_t GetSerializeSizeMany(const S& s, const T&... t) -{ - CSizeComputer sc(s.GetType(), s.GetVersion()); + CSizeComputer sc(nVersion); SerializeMany(sc, t...); return sc.size(); } diff --git a/src/streams.h b/src/streams.h index 096ebfc9c2..dc20f7a9da 100644 --- a/src/streams.h +++ b/src/streams.h @@ -139,6 +139,80 @@ private: size_t nPos; }; +/** Minimal stream for reading from an existing vector by reference + */ +class VectorReader +{ +private: + const int m_type; + const int m_version; + const std::vector<unsigned char>& m_data; + size_t m_pos = 0; + +public: + + /* + * @param[in] type Serialization Type + * @param[in] version Serialization Version (including any flags) + * @param[in] data Referenced byte vector to overwrite/append + * @param[in] pos Starting position. Vector index where reads should start. + */ + VectorReader(int type, int version, const std::vector<unsigned char>& data, size_t pos) + : m_type(type), m_version(version), m_data(data) + { + seek(pos); + } + + /* + * (other params same as above) + * @param[in] args A list of items to deserialize starting at pos. + */ + template <typename... Args> + VectorReader(int type, int version, const std::vector<unsigned char>& data, size_t pos, + Args&&... args) + : VectorReader(type, version, data, pos) + { + ::UnserializeMany(*this, std::forward<Args>(args)...); + } + + template<typename T> + VectorReader& operator>>(T& obj) + { + // Unserialize from this stream + ::Unserialize(*this, obj); + return (*this); + } + + int GetVersion() const { return m_version; } + int GetType() const { return m_type; } + + size_t size() const { return m_data.size() - m_pos; } + bool empty() const { return m_data.size() == m_pos; } + + void read(char* dst, size_t n) + { + if (n == 0) { + return; + } + + // Read from the beginning of the buffer + size_t pos_next = m_pos + n; + if (pos_next > m_data.size()) { + throw std::ios_base::failure("VectorReader::read(): end of data"); + } + memcpy(dst, m_data.data() + m_pos, n); + m_pos = pos_next; + } + + void seek(size_t n) + { + m_pos += n; + if (m_pos > m_data.size()) { + throw std::ios_base::failure("VectorReader::seek(): end of data"); + } + } +}; + /** Double ended buffer combining vector and stream-like interfaces. * * >> and << read and write unformatted data using the above serialization templates. @@ -436,12 +510,105 @@ public: } }; +template <typename IStream> +class BitStreamReader +{ +private: + IStream& m_istream; + + /// Buffered byte read in from the input stream. A new byte is read into the + /// buffer when m_offset reaches 8. + uint8_t m_buffer{0}; + + /// Number of high order bits in m_buffer already returned by previous + /// Read() calls. The next bit to be returned is at this offset from the + /// most significant bit position. + int m_offset{8}; + +public: + explicit BitStreamReader(IStream& istream) : m_istream(istream) {} + + /** Read the specified number of bits from the stream. The data is returned + * in the nbits least significant bits of a 64-bit uint. + */ + uint64_t Read(int nbits) { + if (nbits < 0 || nbits > 64) { + throw std::out_of_range("nbits must be between 0 and 64"); + } + + uint64_t data = 0; + while (nbits > 0) { + if (m_offset == 8) { + m_istream >> m_buffer; + m_offset = 0; + } + int bits = std::min(8 - m_offset, nbits); + data <<= bits; + data |= static_cast<uint8_t>(m_buffer << m_offset) >> (8 - bits); + m_offset += bits; + nbits -= bits; + } + return data; + } +}; +template <typename OStream> +class BitStreamWriter +{ +private: + OStream& m_ostream; + /// Buffered byte waiting to be written to the output stream. The byte is + /// written buffer when m_offset reaches 8 or Flush() is called. + uint8_t m_buffer{0}; + /// Number of high order bits in m_buffer already written by previous + /// Write() calls and not yet flushed to the stream. The next bit to be + /// written to is at this offset from the most significant bit position. + int m_offset{0}; +public: + explicit BitStreamWriter(OStream& ostream) : m_ostream(ostream) {} + ~BitStreamWriter() + { + Flush(); + } + + /** Write the nbits least significant bits of a 64-bit int to the output + * stream. Data is buffered until it completes an octet. + */ + void Write(uint64_t data, int nbits) { + if (nbits < 0 || nbits > 64) { + throw std::out_of_range("nbits must be between 0 and 64"); + } + + while (nbits > 0) { + int bits = std::min(8 - m_offset, nbits); + m_buffer |= (data << (64 - nbits)) >> (64 - 8 + m_offset); + m_offset += bits; + nbits -= bits; + + if (m_offset == 8) { + Flush(); + } + } + } + + /** Flush any unwritten bits to the output stream, padding with 0's to the + * next byte boundary. + */ + void Flush() { + if (m_offset == 0) { + return; + } + + m_ostream << m_buffer; + m_buffer = 0; + m_offset = 0; + } +}; diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp index 070b3ed80e..8d577cf521 100644 --- a/src/support/lockedpool.cpp +++ b/src/support/lockedpool.cpp @@ -75,7 +75,7 @@ void* Arena::alloc(size_t size) // Create the used-chunk, taking its space from the end of the free-chunk const size_t size_remaining = size_ptr_it->first - size; - auto alloced = chunks_used.emplace(size_ptr_it->second + size_remaining, size).first; + auto allocated = chunks_used.emplace(size_ptr_it->second + size_remaining, size).first; chunks_free_end.erase(size_ptr_it->second + size_ptr_it->first); if (size_ptr_it->first == size) { // whole chunk is used up @@ -88,7 +88,7 @@ void* Arena::alloc(size_t size) } size_to_free_chunk.erase(size_ptr_it); - return reinterpret_cast<void*>(alloced->first); + return reinterpret_cast<void*>(allocated->first); } void Arena::free(void *ptr) diff --git a/src/sync.cpp b/src/sync.cpp index 255eb4f00b..c9aa98dcd6 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -100,7 +100,11 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, } LogPrintf(" %s\n", i.second.ToString()); } - assert(false); + if (g_debug_lockorder_abort) { + fprintf(stderr, "Assertion failed: detected inconsistent lock order at %s:%i, details in debug log.\n", __FILE__, __LINE__); + abort(); + } + throw std::logic_error("potential deadlock detected"); } static void push_lock(void* c, const CLockLocation& locklocation) @@ -189,4 +193,6 @@ void DeleteLock(void* cs) } } +bool g_debug_lockorder_abort = true; + #endif /* DEBUG_LOCKORDER */ diff --git a/src/sync.h b/src/sync.h index 11c1253757..40709bdd7f 100644 --- a/src/sync.h +++ b/src/sync.h @@ -46,14 +46,42 @@ LEAVE_CRITICAL_SECTION(mutex); // no RAII // // /////////////////////////////// +#ifdef DEBUG_LOCKORDER +void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); +void LeaveCritical(); +std::string LocksHeld(); +void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs); +void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs); +void DeleteLock(void* cs); + /** - * Template mixin that adds -Wthread-safety locking - * annotations to a subset of the mutex API. + * Call abort() if a potential lock order deadlock bug is detected, instead of + * just logging information and throwing a logic_error. Defaults to true, and + * set to false in DEBUG_LOCKORDER unit tests. + */ +extern bool g_debug_lockorder_abort; +#else +void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} +void static inline LeaveCritical() {} +void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs) {} +void static inline AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {} +void static inline DeleteLock(void* cs) {} +#endif +#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) +#define AssertLockNotHeld(cs) AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs) + +/** + * Template mixin that adds -Wthread-safety locking annotations and lock order + * checking to a subset of the mutex API. */ template <typename PARENT> class LOCKABLE AnnotatedMixin : public PARENT { public: + ~AnnotatedMixin() { + DeleteLock((void*)this); + } + void lock() EXCLUSIVE_LOCK_FUNCTION() { PARENT::lock(); @@ -68,64 +96,36 @@ public: { return PARENT::try_lock(); } -}; -#ifdef DEBUG_LOCKORDER -void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); -void LeaveCritical(); -std::string LocksHeld(); -void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs); -void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs); -void DeleteLock(void* cs); -#else -void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} -void static inline LeaveCritical() {} -void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) ASSERT_EXCLUSIVE_LOCK(cs) {} -void static inline AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {} -void static inline DeleteLock(void* cs) {} -#endif -#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) -#define AssertLockNotHeld(cs) AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs) + using UniqueLock = std::unique_lock<PARENT>; +}; /** * Wrapped mutex: supports recursive locking, but no waiting * TODO: We should move away from using the recursive lock by default. */ -class CCriticalSection : public AnnotatedMixin<std::recursive_mutex> -{ -public: - ~CCriticalSection() { - DeleteLock((void*)this); - } -}; +typedef AnnotatedMixin<std::recursive_mutex> CCriticalSection; /** Wrapped mutex: supports waiting but not recursive locking */ -typedef AnnotatedMixin<std::mutex> CWaitableCriticalSection; - -/** Just a typedef for std::condition_variable, can be wrapped later if desired */ -typedef std::condition_variable CConditionVariable; - -/** Just a typedef for std::unique_lock, can be wrapped later if desired */ -typedef std::unique_lock<std::mutex> WaitableLock; +typedef AnnotatedMixin<std::mutex> Mutex; #ifdef DEBUG_LOCKCONTENTION void PrintLockContention(const char* pszName, const char* pszFile, int nLine); #endif -/** Wrapper around std::unique_lock<CCriticalSection> */ -class SCOPED_LOCKABLE CCriticalBlock +/** Wrapper around std::unique_lock style lock for Mutex. */ +template <typename Mutex, typename Base = typename Mutex::UniqueLock> +class SCOPED_LOCKABLE UniqueLock : public Base { private: - std::unique_lock<CCriticalSection> lock; - void Enter(const char* pszName, const char* pszFile, int nLine) { - EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex())); + EnterCritical(pszName, pszFile, nLine, (void*)(Base::mutex())); #ifdef DEBUG_LOCKCONTENTION - if (!lock.try_lock()) { + if (!Base::try_lock()) { PrintLockContention(pszName, pszFile, nLine); #endif - lock.lock(); + Base::lock(); #ifdef DEBUG_LOCKCONTENTION } #endif @@ -133,15 +133,15 @@ private: bool TryEnter(const char* pszName, const char* pszFile, int nLine) { - EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex()), true); - lock.try_lock(); - if (!lock.owns_lock()) + EnterCritical(pszName, pszFile, nLine, (void*)(Base::mutex()), true); + Base::try_lock(); + if (!Base::owns_lock()) LeaveCritical(); - return lock.owns_lock(); + return Base::owns_lock(); } public: - CCriticalBlock(CCriticalSection& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : lock(mutexIn, std::defer_lock) + UniqueLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock) { if (fTry) TryEnter(pszName, pszFile, nLine); @@ -149,35 +149,41 @@ public: Enter(pszName, pszFile, nLine); } - CCriticalBlock(CCriticalSection* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) + UniqueLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) { if (!pmutexIn) return; - lock = std::unique_lock<CCriticalSection>(*pmutexIn, std::defer_lock); + *static_cast<Base*>(this) = Base(*pmutexIn, std::defer_lock); if (fTry) TryEnter(pszName, pszFile, nLine); else Enter(pszName, pszFile, nLine); } - ~CCriticalBlock() UNLOCK_FUNCTION() + ~UniqueLock() UNLOCK_FUNCTION() { - if (lock.owns_lock()) + if (Base::owns_lock()) LeaveCritical(); } operator bool() { - return lock.owns_lock(); + return Base::owns_lock(); } }; +template<typename MutexArg> +using DebugLock = UniqueLock<typename std::remove_reference<typename std::remove_pointer<MutexArg>::type>::type>; + #define PASTE(x, y) x ## y #define PASTE2(x, y) PASTE(x, y) -#define LOCK(cs) CCriticalBlock PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__) -#define LOCK2(cs1, cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__), criticalblock2(cs2, #cs2, __FILE__, __LINE__) -#define TRY_LOCK(cs, name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true) +#define LOCK(cs) DebugLock<decltype(cs)> PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__) +#define LOCK2(cs1, cs2) \ + DebugLock<decltype(cs1)> criticalblock1(cs1, #cs1, __FILE__, __LINE__); \ + DebugLock<decltype(cs2)> criticalblock2(cs2, #cs2, __FILE__, __LINE__); +#define TRY_LOCK(cs, name) DebugLock<decltype(cs)> name(cs, #cs, __FILE__, __LINE__, true) +#define WAIT_LOCK(cs, name) DebugLock<decltype(cs)> name(cs, #cs, __FILE__, __LINE__) #define ENTER_CRITICAL_SECTION(cs) \ { \ diff --git a/src/test/allocator_tests.cpp b/src/test/allocator_tests.cpp index 1052ada232..c72c062b81 100644 --- a/src/test/allocator_tests.cpp +++ b/src/test/allocator_tests.cpp @@ -163,7 +163,7 @@ private: BOOST_AUTO_TEST_CASE(lockedpool_tests_mock) { // Test over three virtual arenas, of which one will succeed being locked - std::unique_ptr<LockedPageAllocator> x(new TestLockedPageAllocator(3, 1)); + std::unique_ptr<LockedPageAllocator> x = MakeUnique<TestLockedPageAllocator>(3, 1); LockedPool pool(std::move(x)); BOOST_CHECK(pool.stats().total == 0); BOOST_CHECK(pool.stats().locked == 0); diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp index d2c7c8cb1d..5131fe8235 100644 --- a/src/test/blockencodings_tests.cpp +++ b/src/test/blockencodings_tests.cpp @@ -63,7 +63,7 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest) CBlock block(BuildBlockTestCase()); LOCK(pool.cs); - pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(block.vtx[2])); + pool.addUnchecked(entry.FromTx(block.vtx[2])); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); // Do a simple ShortTxIDs RT @@ -163,7 +163,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest) CBlock block(BuildBlockTestCase()); LOCK(pool.cs); - pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(block.vtx[2])); + pool.addUnchecked(entry.FromTx(block.vtx[2])); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); uint256 txhash; @@ -233,7 +233,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest) CBlock block(BuildBlockTestCase()); LOCK(pool.cs); - pool.addUnchecked(block.vtx[1]->GetHash(), entry.FromTx(block.vtx[1])); + pool.addUnchecked(entry.FromTx(block.vtx[1])); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); uint256 txhash; diff --git a/src/test/blockfilter_tests.cpp b/src/test/blockfilter_tests.cpp new file mode 100644 index 0000000000..773de343ea --- /dev/null +++ b/src/test/blockfilter_tests.cpp @@ -0,0 +1,144 @@ +// Copyright (c) 2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <test/data/blockfilters.json.h> +#include <test/test_bitcoin.h> + +#include <blockfilter.h> +#include <core_io.h> +#include <serialize.h> +#include <streams.h> +#include <univalue.h> +#include <utilstrencodings.h> + +#include <boost/test/unit_test.hpp> + +BOOST_AUTO_TEST_SUITE(blockfilter_tests) + +BOOST_AUTO_TEST_CASE(gcsfilter_test) +{ + GCSFilter::ElementSet included_elements, excluded_elements; + for (int i = 0; i < 100; ++i) { + GCSFilter::Element element1(32); + element1[0] = i; + included_elements.insert(std::move(element1)); + + GCSFilter::Element element2(32); + element2[1] = i; + excluded_elements.insert(std::move(element2)); + } + + GCSFilter filter(0, 0, 10, 1 << 10, included_elements); + for (const auto& element : included_elements) { + BOOST_CHECK(filter.Match(element)); + + auto insertion = excluded_elements.insert(element); + BOOST_CHECK(filter.MatchAny(excluded_elements)); + excluded_elements.erase(insertion.first); + } +} + +BOOST_AUTO_TEST_CASE(blockfilter_basic_test) +{ + CScript included_scripts[5], excluded_scripts[3]; + + // First two are outputs on a single transaction. + included_scripts[0] << std::vector<unsigned char>(0, 65) << OP_CHECKSIG; + included_scripts[1] << OP_DUP << OP_HASH160 << std::vector<unsigned char>(1, 20) << OP_EQUALVERIFY << OP_CHECKSIG; + + // Third is an output on in a second transaction. + included_scripts[2] << OP_1 << std::vector<unsigned char>(2, 33) << OP_1 << OP_CHECKMULTISIG; + + // Last two are spent by a single transaction. + included_scripts[3] << OP_0 << std::vector<unsigned char>(3, 32); + included_scripts[4] << OP_4 << OP_ADD << OP_8 << OP_EQUAL; + + // OP_RETURN output is an output on the second transaction. + excluded_scripts[0] << OP_RETURN << std::vector<unsigned char>(4, 40); + + // This script is not related to the block at all. + excluded_scripts[1] << std::vector<unsigned char>(5, 33) << OP_CHECKSIG; + + CMutableTransaction tx_1; + tx_1.vout.emplace_back(100, included_scripts[0]); + tx_1.vout.emplace_back(200, included_scripts[1]); + + CMutableTransaction tx_2; + tx_2.vout.emplace_back(300, included_scripts[2]); + tx_2.vout.emplace_back(0, excluded_scripts[0]); + tx_2.vout.emplace_back(400, excluded_scripts[2]); // Script is empty + + CBlock block; + block.vtx.push_back(MakeTransactionRef(tx_1)); + block.vtx.push_back(MakeTransactionRef(tx_2)); + + CBlockUndo block_undo; + block_undo.vtxundo.emplace_back(); + block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(500, included_scripts[3]), 1000, true); + block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(600, included_scripts[4]), 10000, false); + block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(700, excluded_scripts[2]), 100000, false); + + BlockFilter block_filter(BlockFilterType::BASIC, block, block_undo); + const GCSFilter& filter = block_filter.GetFilter(); + + for (const CScript& script : included_scripts) { + BOOST_CHECK(filter.Match(GCSFilter::Element(script.begin(), script.end()))); + } + for (const CScript& script : excluded_scripts) { + BOOST_CHECK(!filter.Match(GCSFilter::Element(script.begin(), script.end()))); + } +} + +BOOST_AUTO_TEST_CASE(blockfilters_json_test) +{ + UniValue json; + std::string json_data(json_tests::blockfilters, + json_tests::blockfilters + sizeof(json_tests::blockfilters)); + if (!json.read(json_data) || !json.isArray()) { + BOOST_ERROR("Parse error."); + return; + } + + const UniValue& tests = json.get_array(); + for (unsigned int i = 0; i < tests.size(); i++) { + UniValue test = tests[i]; + std::string strTest = test.write(); + + if (test.size() == 1) { + continue; + } else if (test.size() < 7) { + BOOST_ERROR("Bad test: " << strTest); + continue; + } + + unsigned int pos = 0; + /*int block_height =*/ test[pos++].get_int(); + /*uint256 block_hash =*/ ParseHashStr(test[pos++].get_str(), "block_hash"); + + CBlock block; + BOOST_REQUIRE(DecodeHexBlk(block, test[pos++].get_str())); + + CBlockUndo block_undo; + block_undo.vtxundo.emplace_back(); + CTxUndo& tx_undo = block_undo.vtxundo.back(); + const UniValue& prev_scripts = test[pos++].get_array(); + for (unsigned int ii = 0; ii < prev_scripts.size(); ii++) { + std::vector<unsigned char> raw_script = ParseHex(prev_scripts[ii].get_str()); + CTxOut txout(0, CScript(raw_script.begin(), raw_script.end())); + tx_undo.vprevout.emplace_back(txout, 0, false); + } + + uint256 prev_filter_header_basic = ParseHashStr(test[pos++].get_str(), "prev_filter_header_basic"); + std::vector<unsigned char> filter_basic = ParseHex(test[pos++].get_str()); + uint256 filter_header_basic = ParseHashStr(test[pos++].get_str(), "filter_header_basic"); + + BlockFilter computed_filter_basic(BlockFilterType::BASIC, block, block_undo); + BOOST_CHECK(computed_filter_basic.GetFilter().GetEncoded() == filter_basic); + + uint256 computed_header_basic = computed_filter_basic.ComputeHeader(prev_filter_header_basic); + BOOST_CHECK(computed_header_basic == filter_header_basic); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index 39d0061c1c..f4b416c4ca 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -148,14 +148,14 @@ typedef CCheckQueue<FrozenCleanupCheck> FrozenCleanup_Queue; */ static void Correct_Queue_range(std::vector<size_t> range) { - auto small_queue = std::unique_ptr<Correct_Queue>(new Correct_Queue {QUEUE_BATCH_SIZE}); + auto small_queue = MakeUnique<Correct_Queue>(QUEUE_BATCH_SIZE); boost::thread_group tg; for (auto x = 0; x < nScriptCheckThreads; ++x) { tg.create_thread([&]{small_queue->Thread();}); } // Make vChecks here to save on malloc (this test can be slow...) std::vector<FakeCheckCheckCompletion> vChecks; - for (auto i : range) { + for (const size_t i : range) { size_t total = i; FakeCheckCheckCompletion::n_calls = 0; CCheckQueueControl<FakeCheckCheckCompletion> control(small_queue.get()); @@ -213,7 +213,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Random) /** Test that failing checks are caught */ BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure) { - auto fail_queue = std::unique_ptr<Failing_Queue>(new Failing_Queue {QUEUE_BATCH_SIZE}); + auto fail_queue = MakeUnique<Failing_Queue>(QUEUE_BATCH_SIZE); boost::thread_group tg; for (auto x = 0; x < nScriptCheckThreads; ++x) { @@ -246,14 +246,14 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure) // future blocks, ie, the bad state is cleared. BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure) { - auto fail_queue = std::unique_ptr<Failing_Queue>(new Failing_Queue {QUEUE_BATCH_SIZE}); + auto fail_queue = MakeUnique<Failing_Queue>(QUEUE_BATCH_SIZE); boost::thread_group tg; for (auto x = 0; x < nScriptCheckThreads; ++x) { tg.create_thread([&]{fail_queue->Thread();}); } for (auto times = 0; times < 10; ++times) { - for (bool end_fails : {true, false}) { + for (const bool end_fails : {true, false}) { CCheckQueueControl<FailingCheck> control(fail_queue.get()); { std::vector<FailingCheck> vChecks; @@ -274,7 +274,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure) // more than once as well BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck) { - auto queue = std::unique_ptr<Unique_Queue>(new Unique_Queue {QUEUE_BATCH_SIZE}); + auto queue = MakeUnique<Unique_Queue>(QUEUE_BATCH_SIZE); boost::thread_group tg; for (auto x = 0; x < nScriptCheckThreads; ++x) { tg.create_thread([&]{queue->Thread();}); @@ -310,7 +310,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck) // time could leave the data hanging across a sequence of blocks. BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) { - auto queue = std::unique_ptr<Memory_Queue>(new Memory_Queue {QUEUE_BATCH_SIZE}); + auto queue = MakeUnique<Memory_Queue>(QUEUE_BATCH_SIZE); boost::thread_group tg; for (auto x = 0; x < nScriptCheckThreads; ++x) { tg.create_thread([&]{queue->Thread();}); @@ -341,7 +341,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory) // have been destructed BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) { - auto queue = std::unique_ptr<FrozenCleanup_Queue>(new FrozenCleanup_Queue {QUEUE_BATCH_SIZE}); + auto queue = MakeUnique<FrozenCleanup_Queue>(QUEUE_BATCH_SIZE); boost::thread_group tg; bool fails = false; for (auto x = 0; x < nScriptCheckThreads; ++x) { @@ -384,7 +384,7 @@ BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup) /** Test that CCheckQueueControl is threadsafe */ BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks) { - auto queue = std::unique_ptr<Standard_Queue>(new Standard_Queue{QUEUE_BATCH_SIZE}); + auto queue = MakeUnique<Standard_Queue>(QUEUE_BATCH_SIZE); { boost::thread_group tg; std::atomic<int> nThreads {0}; diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 63b91b74c0..6f4b5ecd26 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -508,7 +508,7 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization) Coin cc4; ss4 >> cc4; BOOST_CHECK_MESSAGE(false, "We should have thrown"); - } catch (const std::ios_base::failure& e) { + } catch (const std::ios_base::failure&) { } // Very large scriptPubKey (3*10^9 bytes) past the end of the stream @@ -521,7 +521,7 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization) Coin cc5; ss5 >> cc5; BOOST_CHECK_MESSAGE(false, "We should have thrown"); - } catch (const std::ios_base::failure& e) { + } catch (const std::ios_base::failure&) { } } @@ -719,7 +719,7 @@ static void CheckAddCoinBase(CAmount base_value, CAmount cache_value, CAmount mo test.cache.AddCoin(OUTPOINT, Coin(std::move(output), 1, coinbase), coinbase); test.cache.SelfTest(); GetCoinsMapEntry(test.cache.map(), result_value, result_flags); - } catch (std::logic_error& e) { + } catch (std::logic_error&) { result_value = FAIL; result_flags = NO_ENTRY; } @@ -736,7 +736,7 @@ static void CheckAddCoinBase(CAmount base_value, CAmount cache_value, CAmount mo template <typename... Args> static void CheckAddCoin(Args&&... args) { - for (CAmount base_value : {ABSENT, PRUNED, VALUE1}) + for (const CAmount base_value : {ABSENT, PRUNED, VALUE1}) CheckAddCoinBase(base_value, std::forward<Args>(args)...); } @@ -780,7 +780,7 @@ void CheckWriteCoins(CAmount parent_value, CAmount child_value, CAmount expected WriteCoinsViewEntry(test.cache, child_value, child_flags); test.cache.SelfTest(); GetCoinsMapEntry(test.cache.map(), result_value, result_flags); - } catch (std::logic_error& e) { + } catch (std::logic_error&) { result_value = FAIL; result_flags = NO_ENTRY; } @@ -848,10 +848,10 @@ BOOST_AUTO_TEST_CASE(ccoins_write) // they would be too repetitive (the parent cache is never updated in these // cases). The loop below covers these cases and makes sure the parent cache // is always left unchanged. - for (CAmount parent_value : {ABSENT, PRUNED, VALUE1}) - for (CAmount child_value : {ABSENT, PRUNED, VALUE2}) - for (char parent_flags : parent_value == ABSENT ? ABSENT_FLAGS : FLAGS) - for (char child_flags : child_value == ABSENT ? ABSENT_FLAGS : CLEAN_FLAGS) + for (const CAmount parent_value : {ABSENT, PRUNED, VALUE1}) + for (const CAmount child_value : {ABSENT, PRUNED, VALUE2}) + for (const char parent_flags : parent_value == ABSENT ? ABSENT_FLAGS : FLAGS) + for (const char child_flags : child_value == ABSENT ? ABSENT_FLAGS : CLEAN_FLAGS) CheckWriteCoins(parent_value, child_value, parent_value, parent_flags, child_flags, parent_flags); } diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 19521027a9..713e3e2ded 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -200,7 +200,7 @@ static void TestChaCha20(const std::string &hexkey, uint64_t nonce, uint64_t see BOOST_CHECK(out == outres); } -static std::string LongTestString(void) { +static std::string LongTestString() { std::string ret; for (int i=0; i<200000; i++) { ret += (unsigned char)(i); diff --git a/src/test/cuckoocache_tests.cpp b/src/test/cuckoocache_tests.cpp index ec7aba34ca..dbceb9d2e0 100644 --- a/src/test/cuckoocache_tests.cpp +++ b/src/test/cuckoocache_tests.cpp @@ -82,11 +82,11 @@ static double test_cache(size_t megabytes, double load) */ std::vector<uint256> hashes_insert_copy = hashes; /** Do the insert */ - for (uint256& h : hashes_insert_copy) + for (const uint256& h : hashes_insert_copy) set.insert(h); /** Count the hits */ uint32_t count = 0; - for (uint256& h : hashes) + for (const uint256& h : hashes) count += set.contains(h, false); double hit_rate = ((double)count) / ((double)n_insert); return hit_rate; @@ -323,7 +323,7 @@ static void test_cache_generations() reads.push_back(inserts[i]); for (uint32_t i = n_insert - (n_insert / 4); i < n_insert; ++i) reads.push_back(inserts[i]); - for (auto h : inserts) + for (const auto& h : inserts) c.insert(h); } }; diff --git a/src/test/data/blockfilters.json b/src/test/data/blockfilters.json new file mode 100644 index 0000000000..134b788eed --- /dev/null +++ b/src/test/data/blockfilters.json @@ -0,0 +1,11 @@ +[ +["Block Height,Block Hash,Block,[Prev Output Scripts for Block],Previous Basic Header,Basic Filter,Basic Header,Notes"], +[0,"000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943","0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae180101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000",[],"0000000000000000000000000000000000000000000000000000000000000000","019dfca8","21584579b7eb08997773e5aeff3a7f932700042d0ed2a6129012b7d7ae81b750","Genesis block"], +[2,"000000006c02c8ea6e4ff69651f7fcde348fb9d557a06e6957b65552002a7820","0100000006128e87be8b1b4dea47a7247d5528d2702c96826c7a648497e773b800000000e241352e3bec0a95a6217e10c3abb54adfa05abb12c126695595580fb92e222032e7494dffff001d00d235340101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0432e7494d010e062f503253482fffffffff0100f2052a010000002321038a7f6ef1c8ca0c588aa53fa860128077c9e6c11e6830f4d7ee4e763a56b7718fac00000000",[],"d7bdac13a59d745b1add0d2ce852f1a0442e8945fc1bf3848d3cbffd88c24fe1","0174a170","186afd11ef2b5e7e3504f2e8cbf8df28a1fd251fe53d60dff8b1467d1b386cf0",""], +[3,"000000008b896e272758da5297bcd98fdc6d97c9b765ecec401e286dc1fdbe10","0100000020782a005255b657696ea057d5b98f34defcf75196f64f6eeac8026c0000000041ba5afc532aae03151b8aa87b65e1594f97504a768e010c98c0add79216247186e7494dffff001d058dc2b60101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0486e7494d0151062f503253482fffffffff0100f2052a01000000232103f6d9ff4c12959445ca5549c811683bf9c88e637b222dd2e0311154c4c85cf423ac00000000",[],"186afd11ef2b5e7e3504f2e8cbf8df28a1fd251fe53d60dff8b1467d1b386cf0","016cf7a0","8d63aadf5ab7257cb6d2316a57b16f517bff1c6388f124ec4c04af1212729d2a",""], +[49291,"0000000018b07dca1b28b4b5a119f6d6e71698ce1ed96f143f54179ce177a19c","02000000abfaf47274223ca2fea22797e44498240e482cb4c2f2baea088962f800000000604b5b52c32305b15d7542071d8b04e750a547500005d4010727694b6e72a776e55d0d51ffff001d211806480201000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0d038bc0000102062f503253482fffffffff01a078072a01000000232102971dd6034ed0cf52450b608d196c07d6345184fcb14deb277a6b82d526a6163dac0000000001000000081cefd96060ecb1c4fbe675ad8a4f8bdc61d634c52b3a1c4116dee23749fe80ff000000009300493046022100866859c21f306538152e83f115bcfbf59ab4bb34887a88c03483a5dff9895f96022100a6dfd83caa609bf0516debc2bf65c3df91813a4842650a1858b3f61cfa8af249014730440220296d4b818bb037d0f83f9f7111665f49532dfdcbec1e6b784526e9ac4046eaa602204acf3a5cb2695e8404d80bf49ab04828bcbe6fc31d25a2844ced7a8d24afbdff01ffffffff1cefd96060ecb1c4fbe675ad8a4f8bdc61d634c52b3a1c4116dee23749fe80ff020000009400483045022100e87899175991aa008176cb553c6f2badbb5b741f328c9845fcab89f8b18cae2302200acce689896dc82933015e7230e5230d5cff8a1ffe82d334d60162ac2c5b0c9601493046022100994ad29d1e7b03e41731a4316e5f4992f0d9b6e2efc40a1ccd2c949b461175c502210099b69fdc2db00fbba214f16e286f6a49e2d8a0d5ffc6409d87796add475478d601ffffffff1e4a6d2d280ea06680d6cf8788ac90344a9c67cca9b06005bbd6d3f6945c8272010000009500493046022100a27400ba52fd842ce07398a1de102f710a10c5599545e6c95798934352c2e4df022100f6383b0b14c9f64b6718139f55b6b9494374755b86bae7d63f5d3e583b57255a01493046022100fdf543292f34e1eeb1703b264965339ec4a450ec47585009c606b3edbc5b617b022100a5fbb1c8de8aaaa582988cdb23622838e38de90bebcaab3928d949aa502a65d401ffffffff1e4a6d2d280ea06680d6cf8788ac90344a9c67cca9b06005bbd6d3f6945c8272020000009400493046022100ac626ac3051f875145b4fe4cfe089ea895aac73f65ab837b1ac30f5d875874fa022100bc03e79fa4b7eb707fb735b95ff6613ca33adeaf3a0607cdcead4cfd3b51729801483045022100b720b04a5c5e2f61b7df0fcf334ab6fea167b7aaede5695d3f7c6973496adbf1022043328c4cc1cdc3e5db7bb895ccc37133e960b2fd3ece98350f774596badb387201ffffffff23a8733e349c97d6cd90f520fdd084ba15ce0a395aad03cd51370602bb9e5db3010000004a00483045022100e8556b72c5e9c0da7371913a45861a61c5df434dfd962de7b23848e1a28c86ca02205d41ceda00136267281be0974be132ac4cda1459fe2090ce455619d8b91045e901ffffffff6856d609b881e875a5ee141c235e2a82f6b039f2b9babe82333677a5570285a6000000006a473044022040a1c631554b8b210fbdf2a73f191b2851afb51d5171fb53502a3a040a38d2c0022040d11cf6e7b41fe1b66c3d08f6ada1aee07a047cb77f242b8ecc63812c832c9a012102bcfad931b502761e452962a5976c79158a0f6d307ad31b739611dac6a297c256ffffffff6856d609b881e875a5ee141c235e2a82f6b039f2b9babe82333677a5570285a601000000930048304502205b109df098f7e932fbf71a45869c3f80323974a826ee2770789eae178a21bfc8022100c0e75615e53ee4b6e32b9bb5faa36ac539e9c05fa2ae6b6de5d09c08455c8b9601483045022009fb7d27375c47bea23b24818634df6a54ecf72d52e0c1268fb2a2c84f1885de022100e0ed4f15d62e7f537da0d0f1863498f9c7c0c0a4e00e4679588c8d1a9eb20bb801ffffffffa563c3722b7b39481836d5edfc1461f97335d5d1e9a23ade13680d0e2c1c371f030000006c493046022100ecc38ae2b1565643dc3c0dad5e961a5f0ea09cab28d024f92fa05c922924157e022100ebc166edf6fbe4004c72bfe8cf40130263f98ddff728c8e67b113dbd621906a601210211a4ed241174708c07206601b44a4c1c29e5ad8b1f731c50ca7e1d4b2a06dc1fffffffff02d0223a00000000001976a91445db0b779c0b9fa207f12a8218c94fc77aff504588ac80f0fa02000000000000000000",["5221033423007d8f263819a2e42becaaf5b06f34cb09919e06304349d950668209eaed21021d69e2b68c3960903b702af7829fadcd80bd89b158150c85c4a75b2c8cb9c39452ae","52210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821021d69e2b68c3960903b702af7829fadcd80bd89b158150c85c4a75b2c8cb9c39452ae","522102a7ae1e0971fc1689bd66d2a7296da3a1662fd21a53c9e38979e0f090a375c12d21022adb62335f41eb4e27056ac37d462cda5ad783fa8e0e526ed79c752475db285d52ae","52210279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179821022adb62335f41eb4e27056ac37d462cda5ad783fa8e0e526ed79c752475db285d52ae","512103b9d1d0e2b4355ec3cdef7c11a5c0beff9e8b8d8372ab4b4e0aaf30e80173001951ae","76a9149144761ebaccd5b4bbdc2a35453585b5637b2f8588ac","522103f1848b40621c5d48471d9784c8174ca060555891ace6d2b03c58eece946b1a9121020ee5d32b54d429c152fdc7b1db84f2074b0564d35400d89d11870f9273ec140c52ae","76a914f4fa1cc7de742d135ea82c17adf0bb9cf5f4fb8388ac"],"ed47705334f4643892ca46396eb3f4196a5e30880589e4009ef38eae895d4a13","0afbc2920af1b027f31f87b592276eb4c32094bb4d3697021b4c6380","b6d98692cec5145f67585f3434ec3c2b3030182e1cb3ec58b855c5c164dfaaa3","Tx pays to empty output script"], +[180480,"00000000fd3ceb2404ff07a785c7fdcc76619edc8ed61bd25134eaa22084366a","020000006058aa080a655aa991a444bd7d1f2defd9a3bbe68aabb69030cf3b4e00000000d2e826bfd7ef0beaa891a7eedbc92cd6a544a6cb61c7bdaa436762eb2123ef9790f5f552ffff001d0002c90f0501000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0e0300c102024608062f503253482fffffffff01c0c6072a01000000232102e769e60137a4df6b0df8ebd387cca44c4c57ae74cc0114a8e8317c8f3bfd85e9ac00000000010000000381a0802911a01ffb025c4dea0bc77963e8c1bb46313b71164c53f72f37fe5248010000000151ffffffffc904b267833d215e2128bd9575242232ac2bc311550c7fc1f0ef6f264b40d14c010000000151ffffffffdf0915666649dba81886519c531649b7b02180b4af67d6885e871299e9d5f775000000000151ffffffff0180817dcb00000000232103bb52138972c48a132fc1f637858c5189607dd0f7fe40c4f20f6ad65f2d389ba4ac0000000001000000018da38b434fba82d66052af74fc5e4e94301b114d9bc03f819dc876398404c8b4010000006c493046022100fe738b7580dc5fb5168e51fc61b5aed211125eb71068031009a22d9bbad752c5022100be5086baa384d40bcab0fa586e4f728397388d86e18b66cc417dc4f7fa4f9878012103f233299455134caa2687bdf15cb0becdfb03bd0ff2ff38e65ec6b7834295c34fffffffff022ebc1400000000001976a9147779b7fba1c1e06b717069b80ca170e8b04458a488ac9879c40f000000001976a9142a0307cd925dbb66b534c4db33003dd18c57015788ac0000000001000000026139a62e3422a602de36c873a225c1d3ca5aeee598539ceecb9f0dc8d1ad0f83010000006b483045022100ad9f32b4a0a2ddc19b5a74eba78123e57616f1b3cfd72ce68c03ea35a3dda1f002200dbd22aa6da17213df5e70dfc3b2611d40f70c98ed9626aa5e2cde9d97461f0a012103ddb295d2f1e8319187738fb4b230fdd9aa29d0e01647f69f6d770b9ab24eea90ffffffff983c82c87cf020040d671956525014d5c2b28c6d948c85e1a522362c0059eeae010000006b4830450221009ca544274c786d30a5d5d25e17759201ea16d3aedddf0b9e9721246f7ef6b32e02202cfa5564b6e87dfd9fd98957820e4d4e6238baeb0f65fe305d91506bb13f5f4f012103c99113deac0d5d044e3ac0346abc02501542af8c8d3759f1382c72ff84e704f7ffffffff02c0c62d00000000001976a914ae19d27efe12f5a886dc79af37ad6805db6f922d88ac70ce2000000000001976a9143b8d051d37a07ea1042067e93efe63dbf73920b988ac000000000100000002be566e8cd9933f0c75c4a82c027f7d0c544d5c101d0607ef6ae5d07b98e7f1dc000000006b483045022036a8cdfd5ea7ebc06c2bfb6e4f942bbf9a1caeded41680d11a3a9f5d8284abad022100cacb92a5be3f39e8bc14db1710910ef7b395fa1e18f45d41c28d914fcdde33be012102bf59abf110b5131fae0a3ce1ec379329b4c896a6ae5d443edb68529cc2bc7816ffffffff96cf67645b76ceb23fe922874847456a15feee1655082ff32d25a6bf2c0dfc90000000006a47304402203471ca2001784a5ac0abab583581f2613523da47ec5f53df833c117b5abd81500220618a2847723d57324f2984678db556dbca1a72230fc7e39df04c2239942ba942012102925c9794fd7bb9f8b29e207d5fc491b1150135a21f505041858889fa4edf436fffffffff026c840f00000000001976a914797fb8777d7991d8284d88bfd421ce520f0f843188ac00ca9a3b000000001976a9146d10f3f592699265d10b106eda37c3ce793f7a8588ac00000000",["","","","76a9142903b138c24be9e070b3e73ec495d77a204615e788ac","76a91433a1941fd9a37b9821d376f5a51bd4b52fa50e2888ac","76a914e4374e8155d0865742ca12b8d4d14d41b57d682f88ac","76a914001fa7459a6cfc64bdc178ba7e7a21603bb2568f88ac","76a914f6039952bc2b307aeec5371bfb96b66078ec17f688ac"],"b109139671dbedc2b6fcd499a5480a7461ae458af8ff9411d819aa64ba6995d1","0db414c859a07e8205876354a210a75042d0463404913d61a8e068e58a3ae2aa080026","a0af77e0a7ed20ea78d2def3200cc24f08217dcd51755c7c7feb0e2ba8316c2d","Tx spends from empty output script"], +[926485,"000000000000015d6077a411a8f5cc95caf775ccf11c54e27df75ce58d187313","0000002060bbab0edbf3ef8a49608ee326f8fd75c473b7e3982095e2d100000000000000c30134f8c9b6d2470488d7a67a888f6fa12f8692e0c3411fbfb92f0f68f67eedae03ca57ef13021acc22dc4105010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2f0315230e0004ae03ca57043e3d1e1d0c8796bf579aef0c0000000000122f4e696e6a61506f6f6c2f5345475749542fffffffff038427a112000000001976a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac0000000000000000266a24aa21a9ed5c748e121c0fe146d973a4ac26fa4a68b0549d46ee22d25f50a5e46fe1b377ee00000000000000002952534b424c4f434b3acd16772ad61a3c5f00287480b720f6035d5e54c9efc71be94bb5e3727f10909001200000000000000000000000000000000000000000000000000000000000000000000000000100000000010145310e878941a1b2bc2d33797ee4d89d95eaaf2e13488063a2aa9a74490f510a0100000023220020b6744de4f6ec63cc92f7c220cdefeeb1b1bed2b66c8e5706d80ec247d37e65a1ffffffff01002d3101000000001976a9143ebc40e411ed3c76f86711507ab952300890397288ac0400473044022001dd489a5d4e2fbd8a3ade27177f6b49296ba7695c40dbbe650ea83f106415fd02200b23a0602d8ff1bdf79dee118205fc7e9b40672bf31563e5741feb53fb86388501483045022100f88f040e90cc5dc6c6189d04718376ac19ed996bf9e4a3c29c3718d90ffd27180220761711f16c9e3a44f71aab55cbc0634907a1fa8bb635d971a9a01d368727bea10169522103b3623117e988b76aaabe3d63f56a4fc88b228a71e64c4cc551d1204822fe85cb2103dd823066e096f72ed617a41d3ca56717db335b1ea47a1b4c5c9dbdd0963acba621033d7c89bd9da29fa8d44db7906a9778b53121f72191184a9fee785c39180e4be153ae00000000010000000120925534261de4dcebb1ed5ab1b62bfe7a3ef968fb111dc2c910adfebc6e3bdf010000006b483045022100f50198f5ae66211a4f485190abe4dc7accdabe3bc214ebc9ea7069b97097d46e0220316a70a03014887086e335fc1b48358d46cd6bdc9af3b57c109c94af76fc915101210316cff587a01a2736d5e12e53551b18d73780b83c3bfb4fcf209c869b11b6415effffffff0220a10700000000001976a91450333046115eaa0ac9e0216565f945070e44573988ac2e7cd01a000000001976a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac00000000010000000203a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322010000006a47304402204efc3d70e4ca3049c2a425025edf22d5ca355f9ec899dbfbbeeb2268533a0f2b02204780d3739653035af4814ea52e1396d021953f948c29754edd0ee537364603dc012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff03a25f58630d7a1ea52550365fd2156683f56daf6ca73a4b4bbd097e66516322000000006a47304402202d96defdc5b4af71d6ba28c9a6042c2d5ee7bc6de565d4db84ef517445626e03022022da80320e9e489c8f41b74833dfb6a54a4eb5087cdb46eb663eef0b25caa526012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a914b7e6f7ff8658b2d1fb107e3d7be7af4742e6b1b3876f88fc00000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac0000000001000000043ffd60d3818431c495b89be84afac205d5d1ed663009291c560758bbd0a66df5010000006b483045022100f344607de9df42049688dcae8ff1db34c0c7cd25ec05516e30d2bc8f12ac9b2f022060b648f6a21745ea6d9782e17bcc4277b5808326488a1f40d41e125879723d3a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffffa5379401cce30f84731ef1ba65ce27edf2cc7ce57704507ebe8714aa16a96b92010000006a473044022020c37a63bf4d7f564c2192528709b6a38ab8271bd96898c6c2e335e5208661580220435c6f1ad4d9305d2c0a818b2feb5e45d443f2f162c0f61953a14d097fd07064012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff70e731e193235ff12c3184510895731a099112ffca4b00246c60003c40f843ce000000006a473044022053760f74c29a879e30a17b5f03a5bb057a5751a39f86fa6ecdedc36a1b7db04c022041d41c9b95f00d2d10a0373322a9025dba66c942196bc9d8adeb0e12d3024728012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff66b7a71b3e50379c8e85fc18fe3f1a408fc985f257036c34702ba205cef09f6f000000006a4730440220499bf9e2db3db6e930228d0661395f65431acae466634d098612fd80b08459ee022040e069fc9e3c60009f521cef54c38aadbd1251aee37940e6018aadb10f194d6a012103f7a897e4dbecab2264b21917f90664ea8256189ea725d28740cf7ba5d85b5763ffffffff0200e1f5050000000017a9148fc37ad460fdfbd2b44fe446f6e3071a4f64faa6878f447f0b000000001976a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac00000000",["a914feb8a29635c56d9cd913122f90678756bf23887687","76a914c01a7ca16b47be50cbdbc60724f701d52d75156688ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac","76a914913bcc2be49cb534c20474c4dee1e9c4c317e7eb88ac"],"da49977ba1ee0d620a2c4f8f646b03cd0d230f5c6c994722e3ba884889f0be1a","09027acea61b6cc3fb33f5d52f7d088a6b2f75d234e89ca800","4cd9dd007a325199102f1fc0b7d77ca25ee3c84d46018c4353ecfcb56c0d3e7a","Duplicate pushdata 913bcc2be49cb534c20474c4dee1e9c4c317e7eb"], +[987876,"0000000000000c00901f2049055e2a437c819d79a3d54fd63e6af796cd7b8a79","000000202694f74969fdb542090e95a56bc8aa2d646e27033850e32f1c5f000000000000f7e53676b3f12d5beb524ed617f2d25f5a93b5f4f52c1ba2678260d72712f8dd0a6dfe5740257e1a4b1768960101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff1603e4120ff9c30a1c216900002f424d4920546573742fffffff0001205fa012000000001e76a914c486de584a735ec2f22da7cd9681614681f92173d83d0aa68688ac00000000",[],"e9d729b72d533c29abe5276d5cf6c152f3723f10efe000b1e0c9ca5265a8beb6","010c0b40","e6137ae5a8424c40da1e5023c16975cc97b09300b4c050e6b1c713add3836c40","Coinbase tx has unparseable output script"], +[1263442,"000000006f27ddfe1dd680044a34548f41bed47eba9e6f0b310da21423bc5f33","000000201c8d1a529c39a396db2db234d5ec152fa651a2872966daccbde028b400000000083f14492679151dbfaa1a825ef4c18518e780c1f91044180280a7d33f4a98ff5f45765aaddc001d38333b9a02010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff230352471300fe5f45765afe94690a000963676d696e6572343208000000000000000000ffffffff024423a804000000001976a914f2c25ac3d59f3d674b1d1d0a25c27339aaac0ba688ac0000000000000000266a24aa21a9edcb26cb3052426b9ebb4d19c819ef87c19677bbf3a7c46ef0855bd1b2abe83491012000000000000000000000000000000000000000000000000000000000000000000000000002000000000101d20978463906ba4ff5e7192494b88dd5eb0de85d900ab253af909106faa22cc5010000000004000000014777ff000000000016001446c29eabe8208a33aa1023c741fa79aa92e881ff0347304402207d7ca96134f2bcfdd6b536536fdd39ad17793632016936f777ebb32c22943fda02206014d2fb8a6aa58279797f861042ba604ebd2f8f61e5bddbd9d3be5a245047b201004b632103eeaeba7ce5dc2470221e9517fb498e8d6bd4e73b85b8be655196972eb9ccd5566754b2752103a40b74d43df244799d041f32ce1ad515a6cd99501701540e38750d883ae21d3a68ac00000000",["002027a5000c7917f785d8fc6e5a55adfca8717ecb973ebb7743849ff956d896a7ed"],"a4a4d6c6034da8aa06f01fe71f1fffbd79e032006b07f6c7a2c60a66aa310c01","0385acb4f0fe889ef0","3588f34fbbc11640f9ed40b2a66a4e096215d50389691309c1dac74d4268aa81","Includes witness data"] +] diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 4dff1e5c72..9957ac074b 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -26,7 +26,7 @@ BOOST_FIXTURE_TEST_SUITE(dbwrapper_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(dbwrapper) { // Perform tests both obfuscated and non-obfuscated. - for (bool obfuscate : {false, true}) { + for (const bool obfuscate : {false, true}) { fs::path ph = SetDataDir(std::string("dbwrapper").append(obfuscate ? "_true" : "_false")); CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate); char key = 'k'; @@ -46,7 +46,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper) BOOST_AUTO_TEST_CASE(dbwrapper_batch) { // Perform tests both obfuscated and non-obfuscated. - for (bool obfuscate : {false, true}) { + for (const bool obfuscate : {false, true}) { fs::path ph = SetDataDir(std::string("dbwrapper_batch").append(obfuscate ? "_true" : "_false")); CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate); @@ -82,7 +82,7 @@ BOOST_AUTO_TEST_CASE(dbwrapper_batch) BOOST_AUTO_TEST_CASE(dbwrapper_iterator) { // Perform tests both obfuscated and non-obfuscated. - for (bool obfuscate : {false, true}) { + for (const bool obfuscate : {false, true}) { fs::path ph = SetDataDir(std::string("dbwrapper_iterator").append(obfuscate ? "_true" : "_false")); CDBWrapper dbw(ph, (1 << 20), true, false, obfuscate); @@ -216,7 +216,7 @@ BOOST_AUTO_TEST_CASE(iterator_ordering) if (x & 1) BOOST_CHECK(dbw.Write(key, value)); } - for (int seek_start : {0x00, 0x80}) { + for (const int seek_start : {0x00, 0x80}) { it->Seek((uint8_t)seek_start); for (unsigned int x=seek_start; x<255; ++x) { uint8_t key; @@ -262,7 +262,7 @@ struct StringContentsSerializer { try { READWRITE(c); str.push_back(c); - } catch (const std::ios_base::failure& e) { + } catch (const std::ios_base::failure&) { break; } } @@ -291,7 +291,7 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering) } std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator()); - for (int seek_start : {0, 5}) { + for (const int seek_start : {0, 5}) { snprintf(buf, sizeof(buf), "%d", seek_start); StringContentsSerializer seek_key(buf); it->Seek(seek_key); diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp index fdf7397bff..52bbe96b96 100644 --- a/src/test/denialofservice_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -31,7 +31,8 @@ struct COrphanTx { NodeId fromPeer; int64_t nTimeExpire; }; -extern std::map<uint256, COrphanTx> mapOrphanTransactions; +extern CCriticalSection g_cs_orphans; +extern std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans); static CService ip(uint32_t i) { @@ -324,7 +325,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) static CTransactionRef RandomOrphan() { std::map<uint256, COrphanTx>::iterator it; - LOCK(cs_main); + LOCK2(cs_main, g_cs_orphans); it = mapOrphanTransactions.lower_bound(InsecureRand256()); if (it == mapOrphanTransactions.end()) it = mapOrphanTransactions.begin(); @@ -394,7 +395,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans) BOOST_CHECK(!AddOrphanTx(MakeTransactionRef(tx), i)); } - LOCK(cs_main); + LOCK2(cs_main, g_cs_orphans); // Test EraseOrphansFor: for (NodeId i = 0; i < 3; i++) { diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp index f189222be8..e739b84a48 100644 --- a/src/test/descriptor_tests.cpp +++ b/src/test/descriptor_tests.cpp @@ -64,7 +64,7 @@ void Check(const std::string& prv, const std::string& pub, int flags, const std: BOOST_CHECK_EQUAL(pub, pub2); // Check that both can be serialized with private key back to the private version, but not without private key. - std::string prv1, prv2; + std::string prv1; BOOST_CHECK(parse_priv->ToPrivateString(keys_priv, prv1)); BOOST_CHECK_EQUAL(prv, prv1); BOOST_CHECK(!parse_priv->ToPrivateString(keys_pub, prv1)); diff --git a/src/test/gen/crypto_gen.cpp b/src/test/gen/crypto_gen.cpp new file mode 100644 index 0000000000..ca8c65806f --- /dev/null +++ b/src/test/gen/crypto_gen.cpp @@ -0,0 +1,19 @@ +// Copyright (c) 2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <test/gen/crypto_gen.h> + +#include <key.h> + +#include <rapidcheck/gen/Arbitrary.h> +#include <rapidcheck/Gen.h> +#include <rapidcheck/gen/Predicate.h> +#include <rapidcheck/gen/Container.h> + +/** Generates 1 to 20 keys for OP_CHECKMULTISIG */ +rc::Gen<std::vector<CKey>> MultisigKeys() +{ + return rc::gen::suchThat(rc::gen::arbitrary<std::vector<CKey>>(), [](const std::vector<CKey>& keys) { + return keys.size() >= 1 && keys.size() <= 15; + }); +}; diff --git a/src/test/gen/crypto_gen.h b/src/test/gen/crypto_gen.h new file mode 100644 index 0000000000..7c2fb0350f --- /dev/null +++ b/src/test/gen/crypto_gen.h @@ -0,0 +1,63 @@ +// Copyright (c) 2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#ifndef BITCOIN_TEST_GEN_CRYPTO_GEN_H +#define BITCOIN_TEST_GEN_CRYPTO_GEN_H + +#include <key.h> +#include <random.h> +#include <uint256.h> +#include <rapidcheck/gen/Arbitrary.h> +#include <rapidcheck/Gen.h> +#include <rapidcheck/gen/Create.h> +#include <rapidcheck/gen/Numeric.h> + +/** Generates 1 to 15 keys for OP_CHECKMULTISIG */ +rc::Gen<std::vector<CKey>> MultisigKeys(); + +namespace rc +{ +/** Generator for a new CKey */ +template <> +struct Arbitrary<CKey> { + static Gen<CKey> arbitrary() + { + return rc::gen::map<int>([](int x) { + CKey key; + key.MakeNewKey(true); + return key; + }); + }; +}; + +/** Generator for a CPrivKey */ +template <> +struct Arbitrary<CPrivKey> { + static Gen<CPrivKey> arbitrary() + { + return gen::map(gen::arbitrary<CKey>(), [](const CKey& key) { + return key.GetPrivKey(); + }); + }; +}; + +/** Generator for a new CPubKey */ +template <> +struct Arbitrary<CPubKey> { + static Gen<CPubKey> arbitrary() + { + return gen::map(gen::arbitrary<CKey>(), [](const CKey& key) { + return key.GetPubKey(); + }); + }; +}; +/** Generates a arbitrary uint256 */ +template <> +struct Arbitrary<uint256> { + static Gen<uint256> arbitrary() + { + return rc::gen::just(GetRandHash()); + }; +}; +} //namespace rc +#endif diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index eec7b38af7..7592330b10 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -24,7 +24,7 @@ static void ResetArgs(const std::string& strArg) // Convert to char*: std::vector<const char*> vecChar; - for (std::string& s : vecArg) + for (const std::string& s : vecArg) vecChar.push_back(s.c_str()); std::string error; diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp index 7e475ac610..a0c10d8ddd 100644 --- a/src/test/key_io_tests.cpp +++ b/src/test/key_io_tests.cpp @@ -136,7 +136,7 @@ BOOST_AUTO_TEST_CASE(key_io_invalid) std::string exp_base58string = test[0].get_str(); // must be invalid as public and as private key - for (auto chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST }) { + for (const auto& chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST }) { SelectParams(chain); destination = DecodeDestination(exp_base58string); BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey in mainnet:" + strTest); diff --git a/src/test/key_properties.cpp b/src/test/key_properties.cpp new file mode 100644 index 0000000000..14e3c85359 --- /dev/null +++ b/src/test/key_properties.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <key.h> + +#include <base58.h> +#include <script/script.h> +#include <uint256.h> +#include <util.h> +#include <utilstrencodings.h> +#include <test/test_bitcoin.h> +#include <string> +#include <vector> + +#include <boost/test/unit_test.hpp> +#include <rapidcheck/boost_test.h> +#include <rapidcheck/gen/Arbitrary.h> +#include <rapidcheck/Gen.h> + +#include <test/gen/crypto_gen.h> + +BOOST_FIXTURE_TEST_SUITE(key_properties, BasicTestingSetup) + +/** Check CKey uniqueness */ +RC_BOOST_PROP(key_uniqueness, (const CKey& key1, const CKey& key2)) +{ + RC_ASSERT(!(key1 == key2)); +} + +/** Verify that a private key generates the correct public key */ +RC_BOOST_PROP(key_generates_correct_pubkey, (const CKey& key)) +{ + CPubKey pubKey = key.GetPubKey(); + RC_ASSERT(key.VerifyPubKey(pubKey)); +} + +/** Create a CKey using the 'Set' function must give us the same key */ +RC_BOOST_PROP(key_set_symmetry, (const CKey& key)) +{ + CKey key1; + key1.Set(key.begin(), key.end(), key.IsCompressed()); + RC_ASSERT(key1 == key); +} + +/** Create a CKey, sign a piece of data, then verify it with the public key */ +RC_BOOST_PROP(key_sign_symmetry, (const CKey& key, const uint256& hash)) +{ + std::vector<unsigned char> vchSig; + key.Sign(hash, vchSig, 0); + const CPubKey& pubKey = key.GetPubKey(); + RC_ASSERT(pubKey.Verify(hash, vchSig)); +} +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index e416de098e..0e15464fd9 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -63,17 +63,17 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest) BOOST_CHECK_EQUAL(testPool.size(), poolSize); // Just the parent: - testPool.addUnchecked(txParent.GetHash(), entry.FromTx(txParent)); + testPool.addUnchecked(entry.FromTx(txParent)); poolSize = testPool.size(); testPool.removeRecursive(txParent); BOOST_CHECK_EQUAL(testPool.size(), poolSize - 1); // Parent, children, grandchildren: - testPool.addUnchecked(txParent.GetHash(), entry.FromTx(txParent)); + testPool.addUnchecked(entry.FromTx(txParent)); for (int i = 0; i < 3; i++) { - testPool.addUnchecked(txChild[i].GetHash(), entry.FromTx(txChild[i])); - testPool.addUnchecked(txGrandChild[i].GetHash(), entry.FromTx(txGrandChild[i])); + testPool.addUnchecked(entry.FromTx(txChild[i])); + testPool.addUnchecked(entry.FromTx(txGrandChild[i])); } // Remove Child[0], GrandChild[0] should be removed: poolSize = testPool.size(); @@ -95,8 +95,8 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest) // Add children and grandchildren, but NOT the parent (simulate the parent being in a block) for (int i = 0; i < 3; i++) { - testPool.addUnchecked(txChild[i].GetHash(), entry.FromTx(txChild[i])); - testPool.addUnchecked(txGrandChild[i].GetHash(), entry.FromTx(txGrandChild[i])); + testPool.addUnchecked(entry.FromTx(txChild[i])); + testPool.addUnchecked(entry.FromTx(txGrandChild[i])); } // Now remove the parent, as might happen if a block-re-org occurs but the parent cannot be // put into the mempool (maybe because it is non-standard): @@ -128,28 +128,28 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) tx1.vout.resize(1); tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx1.vout[0].nValue = 10 * COIN; - pool.addUnchecked(tx1.GetHash(), entry.Fee(10000LL).FromTx(tx1)); + pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1)); /* highest fee */ CMutableTransaction tx2 = CMutableTransaction(); tx2.vout.resize(1); tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx2.vout[0].nValue = 2 * COIN; - pool.addUnchecked(tx2.GetHash(), entry.Fee(20000LL).FromTx(tx2)); + pool.addUnchecked(entry.Fee(20000LL).FromTx(tx2)); /* lowest fee */ CMutableTransaction tx3 = CMutableTransaction(); tx3.vout.resize(1); tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx3.vout[0].nValue = 5 * COIN; - pool.addUnchecked(tx3.GetHash(), entry.Fee(0LL).FromTx(tx3)); + pool.addUnchecked(entry.Fee(0LL).FromTx(tx3)); /* 2nd highest fee */ CMutableTransaction tx4 = CMutableTransaction(); tx4.vout.resize(1); tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx4.vout[0].nValue = 6 * COIN; - pool.addUnchecked(tx4.GetHash(), entry.Fee(15000LL).FromTx(tx4)); + pool.addUnchecked(entry.Fee(15000LL).FromTx(tx4)); /* equal fee rate to tx1, but newer */ CMutableTransaction tx5 = CMutableTransaction(); @@ -157,7 +157,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx5.vout[0].nValue = 11 * COIN; entry.nTime = 1; - pool.addUnchecked(tx5.GetHash(), entry.Fee(10000LL).FromTx(tx5)); + pool.addUnchecked(entry.Fee(10000LL).FromTx(tx5)); BOOST_CHECK_EQUAL(pool.size(), 5U); std::vector<std::string> sortedOrder; @@ -175,7 +175,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) tx6.vout.resize(1); tx6.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx6.vout[0].nValue = 20 * COIN; - pool.addUnchecked(tx6.GetHash(), entry.Fee(0LL).FromTx(tx6)); + pool.addUnchecked(entry.Fee(0LL).FromTx(tx6)); BOOST_CHECK_EQUAL(pool.size(), 6U); // Check that at this point, tx6 is sorted low sortedOrder.insert(sortedOrder.begin(), tx6.GetHash().ToString()); @@ -198,7 +198,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors(entry.Fee(2000000LL).FromTx(tx7), setAncestorsCalculated, 100, 1000000, 1000, 1000000, dummy), true); BOOST_CHECK(setAncestorsCalculated == setAncestors); - pool.addUnchecked(tx7.GetHash(), entry.FromTx(tx7), setAncestors); + pool.addUnchecked(entry.FromTx(tx7), setAncestors); BOOST_CHECK_EQUAL(pool.size(), 7U); // Now tx6 should be sorted higher (high fee child): tx7, tx6, tx2, ... @@ -216,7 +216,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx8.vout[0].nValue = 10 * COIN; setAncestors.insert(pool.mapTx.find(tx7.GetHash())); - pool.addUnchecked(tx8.GetHash(), entry.Fee(0LL).Time(2).FromTx(tx8), setAncestors); + pool.addUnchecked(entry.Fee(0LL).Time(2).FromTx(tx8), setAncestors); // Now tx8 should be sorted low, but tx6/tx both high sortedOrder.insert(sortedOrder.begin(), tx8.GetHash().ToString()); @@ -230,7 +230,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) tx9.vout.resize(1); tx9.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx9.vout[0].nValue = 1 * COIN; - pool.addUnchecked(tx9.GetHash(), entry.Fee(0LL).Time(3).FromTx(tx9), setAncestors); + pool.addUnchecked(entry.Fee(0LL).Time(3).FromTx(tx9), setAncestors); // tx9 should be sorted low BOOST_CHECK_EQUAL(pool.size(), 9U); @@ -256,7 +256,7 @@ BOOST_AUTO_TEST_CASE(MempoolIndexingTest) BOOST_CHECK_EQUAL(pool.CalculateMemPoolAncestors(entry.Fee(200000LL).Time(4).FromTx(tx10), setAncestorsCalculated, 100, 1000000, 1000, 1000000, dummy), true); BOOST_CHECK(setAncestorsCalculated == setAncestors); - pool.addUnchecked(tx10.GetHash(), entry.FromTx(tx10), setAncestors); + pool.addUnchecked(entry.FromTx(tx10), setAncestors); /** * tx8 and tx9 should both now be sorted higher @@ -301,14 +301,14 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest) tx1.vout.resize(1); tx1.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx1.vout[0].nValue = 10 * COIN; - pool.addUnchecked(tx1.GetHash(), entry.Fee(10000LL).FromTx(tx1)); + pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1)); /* highest fee */ CMutableTransaction tx2 = CMutableTransaction(); tx2.vout.resize(1); tx2.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx2.vout[0].nValue = 2 * COIN; - pool.addUnchecked(tx2.GetHash(), entry.Fee(20000LL).FromTx(tx2)); + pool.addUnchecked(entry.Fee(20000LL).FromTx(tx2)); uint64_t tx2Size = GetVirtualTransactionSize(tx2); /* lowest fee */ @@ -316,21 +316,21 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest) tx3.vout.resize(1); tx3.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx3.vout[0].nValue = 5 * COIN; - pool.addUnchecked(tx3.GetHash(), entry.Fee(0LL).FromTx(tx3)); + pool.addUnchecked(entry.Fee(0LL).FromTx(tx3)); /* 2nd highest fee */ CMutableTransaction tx4 = CMutableTransaction(); tx4.vout.resize(1); tx4.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx4.vout[0].nValue = 6 * COIN; - pool.addUnchecked(tx4.GetHash(), entry.Fee(15000LL).FromTx(tx4)); + pool.addUnchecked(entry.Fee(15000LL).FromTx(tx4)); /* equal fee rate to tx1, but newer */ CMutableTransaction tx5 = CMutableTransaction(); tx5.vout.resize(1); tx5.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL; tx5.vout[0].nValue = 11 * COIN; - pool.addUnchecked(tx5.GetHash(), entry.Fee(10000LL).FromTx(tx5)); + pool.addUnchecked(entry.Fee(10000LL).FromTx(tx5)); BOOST_CHECK_EQUAL(pool.size(), 5U); std::vector<std::string> sortedOrder; @@ -359,7 +359,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest) tx6.vout[0].nValue = 20 * COIN; uint64_t tx6Size = GetVirtualTransactionSize(tx6); - pool.addUnchecked(tx6.GetHash(), entry.Fee(0LL).FromTx(tx6)); + pool.addUnchecked(entry.Fee(0LL).FromTx(tx6)); BOOST_CHECK_EQUAL(pool.size(), 6U); // Ties are broken by hash if (tx3.GetHash() < tx6.GetHash()) @@ -381,7 +381,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest) /* set the fee to just below tx2's feerate when including ancestor */ CAmount fee = (20000/tx2Size)*(tx7Size + tx6Size) - 1; - pool.addUnchecked(tx7.GetHash(), entry.Fee(fee).FromTx(tx7)); + pool.addUnchecked(entry.Fee(fee).FromTx(tx7)); BOOST_CHECK_EQUAL(pool.size(), 7U); sortedOrder.insert(sortedOrder.begin()+1, tx7.GetHash().ToString()); CheckSort<ancestor_score>(pool, sortedOrder); @@ -413,7 +413,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest) // Check that we sort by min(feerate, ancestor_feerate): // set the fee so that the ancestor feerate is above tx1/5, // but the transaction's own feerate is lower - pool.addUnchecked(tx8.GetHash(), entry.Fee(5000LL).FromTx(tx8)); + pool.addUnchecked(entry.Fee(5000LL).FromTx(tx8)); sortedOrder.insert(sortedOrder.end()-1, tx8.GetHash().ToString()); CheckSort<ancestor_score>(pool, sortedOrder); } @@ -431,7 +431,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) tx1.vout.resize(1); tx1.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL; tx1.vout[0].nValue = 10 * COIN; - pool.addUnchecked(tx1.GetHash(), entry.Fee(10000LL).FromTx(tx1)); + pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1)); CMutableTransaction tx2 = CMutableTransaction(); tx2.vin.resize(1); @@ -439,7 +439,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) tx2.vout.resize(1); tx2.vout[0].scriptPubKey = CScript() << OP_2 << OP_EQUAL; tx2.vout[0].nValue = 10 * COIN; - pool.addUnchecked(tx2.GetHash(), entry.Fee(5000LL).FromTx(tx2)); + pool.addUnchecked(entry.Fee(5000LL).FromTx(tx2)); pool.TrimToSize(pool.DynamicMemoryUsage()); // should do nothing BOOST_CHECK(pool.exists(tx1.GetHash())); @@ -449,7 +449,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) BOOST_CHECK(pool.exists(tx1.GetHash())); BOOST_CHECK(!pool.exists(tx2.GetHash())); - pool.addUnchecked(tx2.GetHash(), entry.FromTx(tx2)); + pool.addUnchecked(entry.FromTx(tx2)); CMutableTransaction tx3 = CMutableTransaction(); tx3.vin.resize(1); tx3.vin[0].prevout = COutPoint(tx2.GetHash(), 0); @@ -457,7 +457,7 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) tx3.vout.resize(1); tx3.vout[0].scriptPubKey = CScript() << OP_3 << OP_EQUAL; tx3.vout[0].nValue = 10 * COIN; - pool.addUnchecked(tx3.GetHash(), entry.Fee(20000LL).FromTx(tx3)); + pool.addUnchecked(entry.Fee(20000LL).FromTx(tx3)); pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4); // tx3 should pay for tx2 (CPFP) BOOST_CHECK(!pool.exists(tx1.GetHash())); @@ -520,10 +520,10 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) tx7.vout[1].scriptPubKey = CScript() << OP_7 << OP_EQUAL; tx7.vout[1].nValue = 10 * COIN; - pool.addUnchecked(tx4.GetHash(), entry.Fee(7000LL).FromTx(tx4)); - pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5)); - pool.addUnchecked(tx6.GetHash(), entry.Fee(1100LL).FromTx(tx6)); - pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7)); + pool.addUnchecked(entry.Fee(7000LL).FromTx(tx4)); + pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5)); + pool.addUnchecked(entry.Fee(1100LL).FromTx(tx6)); + pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7)); // we only require this to remove, at max, 2 txn, because it's not clear what we're really optimizing for aside from that pool.TrimToSize(pool.DynamicMemoryUsage() - 1); @@ -532,8 +532,8 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) BOOST_CHECK(!pool.exists(tx7.GetHash())); if (!pool.exists(tx5.GetHash())) - pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5)); - pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7)); + pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5)); + pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7)); pool.TrimToSize(pool.DynamicMemoryUsage() / 2); // should maximize mempool size by only removing 5/7 BOOST_CHECK(pool.exists(tx4.GetHash())); @@ -541,8 +541,8 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) BOOST_CHECK(pool.exists(tx6.GetHash())); BOOST_CHECK(!pool.exists(tx7.GetHash())); - pool.addUnchecked(tx5.GetHash(), entry.Fee(1000LL).FromTx(tx5)); - pool.addUnchecked(tx7.GetHash(), entry.Fee(9000LL).FromTx(tx7)); + pool.addUnchecked(entry.Fee(1000LL).FromTx(tx5)); + pool.addUnchecked(entry.Fee(9000LL).FromTx(tx7)); std::vector<CTransactionRef> vtx; SetMockTime(42); @@ -603,7 +603,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests) // [tx1] // CTransactionRef tx1 = make_tx(/* output_values */ {10 * COIN}); - pool.addUnchecked(tx1->GetHash(), entry.Fee(10000LL).FromTx(tx1)); + pool.addUnchecked(entry.Fee(10000LL).FromTx(tx1)); // Ancestors / descendants should be 1 / 1 (itself / itself) pool.GetTransactionAncestry(tx1->GetHash(), ancestors, descendants); @@ -615,7 +615,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests) // [tx1].0 <- [tx2] // CTransactionRef tx2 = make_tx(/* output_values */ {495 * CENT, 5 * COIN}, /* inputs */ {tx1}); - pool.addUnchecked(tx2->GetHash(), entry.Fee(10000LL).FromTx(tx2)); + pool.addUnchecked(entry.Fee(10000LL).FromTx(tx2)); // Ancestors / descendants should be: // transaction ancestors descendants @@ -634,7 +634,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests) // [tx1].0 <- [tx2].0 <- [tx3] // CTransactionRef tx3 = make_tx(/* output_values */ {290 * CENT, 200 * CENT}, /* inputs */ {tx2}); - pool.addUnchecked(tx3->GetHash(), entry.Fee(10000LL).FromTx(tx3)); + pool.addUnchecked(entry.Fee(10000LL).FromTx(tx3)); // Ancestors / descendants should be: // transaction ancestors descendants @@ -659,7 +659,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests) // \---1 <- [tx4] // CTransactionRef tx4 = make_tx(/* output_values */ {290 * CENT, 250 * CENT}, /* inputs */ {tx2}, /* input_indices */ {1}); - pool.addUnchecked(tx4->GetHash(), entry.Fee(10000LL).FromTx(tx4)); + pool.addUnchecked(entry.Fee(10000LL).FromTx(tx4)); // Ancestors / descendants should be: // transaction ancestors descendants @@ -696,13 +696,13 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests) CTransactionRef& tyi = *ty[i]; tyi = make_tx(/* output_values */ {v}, /* inputs */ i > 0 ? std::vector<CTransactionRef>{*ty[i - 1]} : std::vector<CTransactionRef>{}); v -= 50 * CENT; - pool.addUnchecked(tyi->GetHash(), entry.Fee(10000LL).FromTx(tyi)); + pool.addUnchecked(entry.Fee(10000LL).FromTx(tyi)); pool.GetTransactionAncestry(tyi->GetHash(), ancestors, descendants); BOOST_CHECK_EQUAL(ancestors, i+1); BOOST_CHECK_EQUAL(descendants, i+1); } CTransactionRef ty6 = make_tx(/* output_values */ {5 * COIN}, /* inputs */ {tx3, ty5}); - pool.addUnchecked(ty6->GetHash(), entry.Fee(10000LL).FromTx(ty6)); + pool.addUnchecked(entry.Fee(10000LL).FromTx(ty6)); // Ancestors / descendants should be: // transaction ancestors descendants diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 2da06b8d31..3eb8aa14fd 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -29,7 +29,7 @@ BOOST_FIXTURE_TEST_SUITE(miner_tests, TestingSetup) // BOOST_CHECK_EXCEPTION predicates to check the specific validation error class HasReason { public: - HasReason(const std::string& reason) : m_reason(reason) {} + explicit HasReason(const std::string& reason) : m_reason(reason) {} bool operator() (const std::runtime_error& e) const { return std::string(e.what()).find(m_reason) != std::string::npos; }; @@ -90,7 +90,7 @@ static CBlockIndex CreateBlockIndex(int nHeight) return index; } -static bool TestSequenceLocks(const CTransaction &tx, int flags) +static bool TestSequenceLocks(const CTransaction &tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { LOCK(mempool.cs); return CheckSequenceLocks(tx, flags); @@ -115,19 +115,19 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript& tx.vout[0].nValue = 5000000000LL - 1000; // This tx has a low fee: 1000 satoshis uint256 hashParentTx = tx.GetHash(); // save this txid for later use - mempool.addUnchecked(hashParentTx, entry.Fee(1000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(entry.Fee(1000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); // This tx has a medium fee: 10000 satoshis tx.vin[0].prevout.hash = txFirst[1]->GetHash(); tx.vout[0].nValue = 5000000000LL - 10000; uint256 hashMediumFeeTx = tx.GetHash(); - mempool.addUnchecked(hashMediumFeeTx, entry.Fee(10000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(entry.Fee(10000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); // This tx has a high fee, but depends on the first transaction tx.vin[0].prevout.hash = hashParentTx; tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 50k satoshi fee uint256 hashHighFeeTx = tx.GetHash(); - mempool.addUnchecked(hashHighFeeTx, entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + mempool.addUnchecked(entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx); @@ -138,8 +138,8 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript& tx.vin[0].prevout.hash = hashHighFeeTx; tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee uint256 hashFreeTx = tx.GetHash(); - mempool.addUnchecked(hashFreeTx, entry.Fee(0).FromTx(tx)); - size_t freeTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + mempool.addUnchecked(entry.Fee(0).FromTx(tx)); + size_t freeTxSize = ::GetSerializeSize(tx, PROTOCOL_VERSION); // Calculate a fee on child transaction that will put the package just // below the block min tx fee (assuming 1 child tx of the same size). @@ -148,7 +148,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript& tx.vin[0].prevout.hash = hashFreeTx; tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse; uint256 hashLowFeeTx = tx.GetHash(); - mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse).FromTx(tx)); + mempool.addUnchecked(entry.Fee(feeToUse).FromTx(tx)); pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); // Verify that the free tx and the low fee tx didn't get selected for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) { @@ -162,7 +162,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript& mempool.removeRecursive(tx); tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee hashLowFeeTx = tx.GetHash(); - mempool.addUnchecked(hashLowFeeTx, entry.Fee(feeToUse+2).FromTx(tx)); + mempool.addUnchecked(entry.Fee(feeToUse+2).FromTx(tx)); pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx); BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashLowFeeTx); @@ -175,7 +175,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript& tx.vout[0].nValue = 5000000000LL - 100000000; tx.vout[1].nValue = 100000000; // 1BTC output uint256 hashFreeTx2 = tx.GetHash(); - mempool.addUnchecked(hashFreeTx2, entry.Fee(0).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx)); // This tx can't be mined by itself tx.vin[0].prevout.hash = hashFreeTx2; @@ -183,7 +183,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript& feeToUse = blockMinFeeRate.GetFee(freeTxSize); tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse; uint256 hashLowFeeTx2 = tx.GetHash(); - mempool.addUnchecked(hashLowFeeTx2, entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx)); + mempool.addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx)); pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); // Verify that this tx isn't selected. @@ -196,7 +196,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript& // as well. tx.vin[0].prevout.n = 1; tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee - mempool.addUnchecked(tx.GetHash(), entry.Fee(10000).FromTx(tx)); + mempool.addUnchecked(entry.Fee(10000).FromTx(tx)); pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2); } @@ -277,7 +277,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) hash = tx.GetHash(); bool spendsCoinbase = i == 0; // only first tx spends coinbase // If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails - mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); + mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } @@ -292,7 +292,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) hash = tx.GetHash(); bool spendsCoinbase = i == 0; // only first tx spends coinbase // If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes - mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx)); + mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx)); tx.vin[0].prevout.hash = hash; } BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); @@ -312,7 +312,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); bool spendsCoinbase = i == 0; // only first tx spends coinbase - mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); + mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); @@ -320,7 +320,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // orphan in mempool, template creation fails hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx)); + mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx)); BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); mempool.clear(); @@ -329,7 +329,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].prevout.hash = txFirst[1]->GetHash(); tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; tx.vin.resize(2); tx.vin[1].scriptSig = CScript() << OP_1; @@ -337,7 +337,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[1].prevout.n = 0; tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); mempool.clear(); @@ -348,7 +348,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = 0; hash = tx.GetHash(); // give it a fee so it'll get mined - mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); // Should throw bad-cb-multiple BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple")); mempool.clear(); @@ -359,10 +359,10 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE; tx.vout[0].scriptPubKey = CScript() << OP_1; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); mempool.clear(); @@ -401,12 +401,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) script = CScript() << OP_0; tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script)); hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vin[0].prevout.hash = hash; tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end()); tx.vout[0].nValue -= LOWFEE; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); // Should throw block-validation-failed BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed")); mempool.clear(); @@ -440,7 +440,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].scriptPubKey = CScript() << OP_1; tx.nLockTime = 0; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(CheckFinalTx(tx, flags)); // Locktime passes BOOST_CHECK(!TestSequenceLocks(tx, flags)); // Sequence locks fail BOOST_CHECK(SequenceLocks(tx, flags, &prevheights, CreateBlockIndex(chainActive.Tip()->nHeight + 2))); // Sequence locks pass on 2nd block @@ -450,7 +450,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((chainActive.Tip()->GetMedianTimePast()+1-chainActive[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block prevheights[0] = baseheight + 2; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); + mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx)); BOOST_CHECK(CheckFinalTx(tx, flags)); // Locktime passes BOOST_CHECK(!TestSequenceLocks(tx, flags)); // Sequence locks fail @@ -466,7 +466,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) prevheights[0] = baseheight + 3; tx.nLockTime = chainActive.Tip()->nHeight + 1; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); + mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx)); BOOST_CHECK(!CheckFinalTx(tx, flags)); // Locktime fails BOOST_CHECK(TestSequenceLocks(tx, flags)); // Sequence locks pass BOOST_CHECK(IsFinalTx(tx, chainActive.Tip()->nHeight + 2, chainActive.Tip()->GetMedianTimePast())); // Locktime passes on 2nd block @@ -477,7 +477,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) prevheights.resize(1); prevheights[0] = baseheight + 4; hash = tx.GetHash(); - mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx)); + mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx)); BOOST_CHECK(!CheckFinalTx(tx, flags)); // Locktime fails BOOST_CHECK(TestSequenceLocks(tx, flags)); // Sequence locks pass BOOST_CHECK(IsFinalTx(tx, chainActive.Tip()->nHeight + 2, chainActive.Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index 4c56938ec9..35a143957e 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -115,7 +115,7 @@ BOOST_AUTO_TEST_CASE(caddrdb_read) unsigned char pchMsgTmp[4]; ssPeers1 >> pchMsgTmp; ssPeers1 >> addrman1; - } catch (const std::exception& e) { + } catch (const std::exception&) { exceptionThrown = true; } @@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(caddrdb_read_corrupted) unsigned char pchMsgTmp[4]; ssPeers1 >> pchMsgTmp; ssPeers1 >> addrman1; - } catch (const std::exception& e) { + } catch (const std::exception&) { exceptionThrown = true; } // Even through de-serialization failed addrman is not left in a clean state. @@ -179,12 +179,12 @@ BOOST_AUTO_TEST_CASE(cnode_simple_test) bool fInboundIn = false; // Test that fFeeler is false by default. - std::unique_ptr<CNode> pnode1(new CNode(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, CAddress(), pszDest, fInboundIn)); + std::unique_ptr<CNode> pnode1 = MakeUnique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 0, 0, CAddress(), pszDest, fInboundIn); BOOST_CHECK(pnode1->fInbound == false); BOOST_CHECK(pnode1->fFeeler == false); fInboundIn = true; - std::unique_ptr<CNode> pnode2(new CNode(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, CAddress(), pszDest, fInboundIn)); + std::unique_ptr<CNode> pnode2 = MakeUnique<CNode>(id++, NODE_NETWORK, height, hSocket, addr, 1, 1, CAddress(), pszDest, fInboundIn); BOOST_CHECK(pnode2->fInbound == true); BOOST_CHECK(pnode2->fFeeler == false); } diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index 9c1af6bdf6..8072eb922d 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -302,4 +302,22 @@ BOOST_AUTO_TEST_CASE(netbase_getgroup) BOOST_CHECK(CreateInternal("baz.net").GetGroup() == internal_group); } +BOOST_AUTO_TEST_CASE(netbase_parsenetwork) +{ + BOOST_CHECK_EQUAL(ParseNetwork("ipv4"), NET_IPV4); + BOOST_CHECK_EQUAL(ParseNetwork("ipv6"), NET_IPV6); + BOOST_CHECK_EQUAL(ParseNetwork("onion"), NET_ONION); + BOOST_CHECK_EQUAL(ParseNetwork("tor"), NET_ONION); + + BOOST_CHECK_EQUAL(ParseNetwork("IPv4"), NET_IPV4); + BOOST_CHECK_EQUAL(ParseNetwork("IPv6"), NET_IPV6); + BOOST_CHECK_EQUAL(ParseNetwork("ONION"), NET_ONION); + BOOST_CHECK_EQUAL(ParseNetwork("TOR"), NET_ONION); + + BOOST_CHECK_EQUAL(ParseNetwork(":)"), NET_UNROUTABLE); + BOOST_CHECK_EQUAL(ParseNetwork("tÖr"), NET_UNROUTABLE); + BOOST_CHECK_EQUAL(ParseNetwork("\xfe\xff"), NET_UNROUTABLE); + BOOST_CHECK_EQUAL(ParseNetwork(""), NET_UNROUTABLE); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/policyestimator_tests.cpp b/src/test/policyestimator_tests.cpp index 26a0b495d9..2022ed6659 100644 --- a/src/test/policyestimator_tests.cpp +++ b/src/test/policyestimator_tests.cpp @@ -58,7 +58,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) for (int k = 0; k < 4; k++) { // add 4 fee txs tx.vin[0].prevout.n = 10000*blocknum+100*j+k; // make transaction unique uint256 hash = tx.GetHash(); - mpool.addUnchecked(hash, entry.Fee(feeV[j]).Time(GetTime()).Height(blocknum).FromTx(tx)); + mpool.addUnchecked(entry.Fee(feeV[j]).Time(GetTime()).Height(blocknum).FromTx(tx)); txHashes[j].push_back(hash); } } @@ -129,7 +129,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) for (int k = 0; k < 4; k++) { // add 4 fee txs tx.vin[0].prevout.n = 10000*blocknum+100*j+k; uint256 hash = tx.GetHash(); - mpool.addUnchecked(hash, entry.Fee(feeV[j]).Time(GetTime()).Height(blocknum).FromTx(tx)); + mpool.addUnchecked(entry.Fee(feeV[j]).Time(GetTime()).Height(blocknum).FromTx(tx)); txHashes[j].push_back(hash); } } @@ -164,7 +164,7 @@ BOOST_AUTO_TEST_CASE(BlockPolicyEstimates) for (int k = 0; k < 4; k++) { // add 4 fee txs tx.vin[0].prevout.n = 10000*blocknum+100*j+k; uint256 hash = tx.GetHash(); - mpool.addUnchecked(hash, entry.Fee(feeV[j]).Time(GetTime()).Height(blocknum).FromTx(tx)); + mpool.addUnchecked(entry.Fee(feeV[j]).Time(GetTime()).Height(blocknum).FromTx(tx)); CTransactionRef ptx = mpool.get(hash); if (ptx) block.push_back(ptx); diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp index 001d8eb64b..12ec00ce02 100644 --- a/src/test/scheduler_tests.cpp +++ b/src/test/scheduler_tests.cpp @@ -54,7 +54,7 @@ BOOST_AUTO_TEST_CASE(manythreads) boost::mutex counterMutex[10]; int counter[10] = { 0 }; - FastRandomContext rng(42); + FastRandomContext rng{/* fDeterministic */ true}; auto zeroToNine = [](FastRandomContext& rc) -> int { return rc.randrange(10); }; // [0, 9] auto randomMsec = [](FastRandomContext& rc) -> int { return -11 + (int)rc.randrange(1012); }; // [-11, 1000] auto randomDelta = [](FastRandomContext& rc) -> int { return -1000 + (int)rc.randrange(2001); }; // [-1000, 1000] @@ -138,11 +138,13 @@ BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered) // the callbacks should run in exactly the order in which they were enqueued for (int i = 0; i < 100; ++i) { queue1.AddToProcessQueue([i, &counter1]() { - BOOST_CHECK_EQUAL(i, counter1++); + bool expectation = i == counter1++; + assert(expectation); }); queue2.AddToProcessQueue([i, &counter2]() { - BOOST_CHECK_EQUAL(i, counter2++); + bool expectation = i == counter2++; + assert(expectation); }); } diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp index bf57272ab2..b3e4b12918 100644 --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -25,22 +25,19 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) } CScript s; - txnouttype whichType; std::vector<std::vector<unsigned char> > solutions; // TX_PUBKEY s.clear(); s << ToByteVector(pubkeys[0]) << OP_CHECKSIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_PUBKEY); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_PUBKEY); BOOST_CHECK_EQUAL(solutions.size(), 1U); BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0])); // TX_PUBKEYHASH s.clear(); s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_PUBKEYHASH); BOOST_CHECK_EQUAL(solutions.size(), 1U); BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID())); @@ -48,8 +45,7 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) CScript redeemScript(s); // initialize with leftover P2PKH script s.clear(); s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_SCRIPTHASH); BOOST_CHECK_EQUAL(solutions.size(), 1U); BOOST_CHECK(solutions[0] == ToByteVector(CScriptID(redeemScript))); @@ -59,8 +55,7 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) ToByteVector(pubkeys[0]) << ToByteVector(pubkeys[1]) << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_MULTISIG); BOOST_CHECK_EQUAL(solutions.size(), 4U); BOOST_CHECK(solutions[0] == std::vector<unsigned char>({1})); BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0])); @@ -73,8 +68,7 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) ToByteVector(pubkeys[1]) << ToByteVector(pubkeys[2]) << OP_3 << OP_CHECKMULTISIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_MULTISIG); BOOST_CHECK_EQUAL(solutions.size(), 5U); BOOST_CHECK(solutions[0] == std::vector<unsigned char>({2})); BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0])); @@ -88,15 +82,13 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) std::vector<unsigned char>({0}) << std::vector<unsigned char>({75}) << std::vector<unsigned char>({255}); - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_NULL_DATA); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NULL_DATA); BOOST_CHECK_EQUAL(solutions.size(), 0U); // TX_WITNESS_V0_KEYHASH s.clear(); s << OP_0 << ToByteVector(pubkeys[0].GetID()); - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_WITNESS_V0_KEYHASH); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_WITNESS_V0_KEYHASH); BOOST_CHECK_EQUAL(solutions.size(), 1U); BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID())); @@ -107,16 +99,14 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success) s.clear(); s << OP_0 << ToByteVector(scriptHash); - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_WITNESS_V0_SCRIPTHASH); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_WITNESS_V0_SCRIPTHASH); BOOST_CHECK_EQUAL(solutions.size(), 1U); BOOST_CHECK(solutions[0] == ToByteVector(scriptHash)); // TX_NONSTANDARD s.clear(); s << OP_9 << OP_ADD << OP_11 << OP_EQUAL; - BOOST_CHECK(!Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); } BOOST_AUTO_TEST_CASE(script_standard_Solver_failure) @@ -127,53 +117,52 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_failure) pubkey = key.GetPubKey(); CScript s; - txnouttype whichType; std::vector<std::vector<unsigned char> > solutions; // TX_PUBKEY with incorrectly sized pubkey s.clear(); s << std::vector<unsigned char>(30, 0x01) << OP_CHECKSIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_PUBKEYHASH with incorrectly sized key hash s.clear(); s << OP_DUP << OP_HASH160 << ToByteVector(pubkey) << OP_EQUALVERIFY << OP_CHECKSIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_SCRIPTHASH with incorrectly sized script hash s.clear(); s << OP_HASH160 << std::vector<unsigned char>(21, 0x01) << OP_EQUAL; - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_MULTISIG 0/2 s.clear(); s << OP_0 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_MULTISIG 2/1 s.clear(); s << OP_2 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_MULTISIG n = 2 with 1 pubkey s.clear(); s << OP_1 << ToByteVector(pubkey) << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_MULTISIG n = 1 with 0 pubkeys s.clear(); s << OP_1 << OP_1 << OP_CHECKMULTISIG; - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_NULL_DATA with other opcodes s.clear(); s << OP_RETURN << std::vector<unsigned char>({75}) << OP_ADD; - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); // TX_WITNESS with incorrect program size s.clear(); s << OP_0 << std::vector<unsigned char>(19, 0x01); - BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD); } BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index bc671394c0..67c377778f 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -937,17 +937,19 @@ BOOST_AUTO_TEST_CASE(script_build) } } +#ifdef UPDATE_JSON_TESTS std::string strGen; - +#endif for (TestBuilder& test : tests) { test.Test(); std::string str = JSONPrettyPrint(test.GetJSON()); -#ifndef UPDATE_JSON_TESTS +#ifdef UPDATE_JSON_TESTS + strGen += str + ",\n"; +#else if (tests_set.count(str) == 0) { BOOST_CHECK_MESSAGE(false, "Missing auto script_valid test: " + test.GetComment()); } #endif - strGen += str + ",\n"; } #ifdef UPDATE_JSON_TESTS diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp index 3a06145861..e754996d2f 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -182,13 +182,13 @@ BOOST_AUTO_TEST_CASE(varints) CDataStream::size_type size = 0; for (int i = 0; i < 100000; i++) { ss << VARINT(i, VarIntMode::NONNEGATIVE_SIGNED); - size += ::GetSerializeSize(VARINT(i, VarIntMode::NONNEGATIVE_SIGNED), 0, 0); + size += ::GetSerializeSize(VARINT(i, VarIntMode::NONNEGATIVE_SIGNED), 0); BOOST_CHECK(size == ss.size()); } for (uint64_t i = 0; i < 100000000000ULL; i += 999999937) { ss << VARINT(i); - size += ::GetSerializeSize(VARINT(i), 0, 0); + size += ::GetSerializeSize(VARINT(i), 0); BOOST_CHECK(size == ss.size()); } diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp index bcd2a9c7b6..c0754618fb 100644 --- a/src/test/skiplist_tests.cpp +++ b/src/test/skiplist_tests.cpp @@ -146,7 +146,7 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_test) BOOST_AUTO_TEST_CASE(findearliestatleast_edge_test) { std::list<CBlockIndex> blocks; - for (unsigned int timeMax : {100, 100, 100, 200, 200, 200, 300, 300, 300}) { + for (const unsigned int timeMax : {100, 100, 100, 200, 200, 200, 300, 300, 300}) { CBlockIndex* prev = blocks.empty() ? nullptr : &blocks.back(); blocks.emplace_back(); blocks.back().nHeight = prev ? prev->nHeight + 1 : 0; diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp index 6a0aa864d9..26cf74830d 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -68,6 +68,86 @@ BOOST_AUTO_TEST_CASE(streams_vector_writer) vch.clear(); } +BOOST_AUTO_TEST_CASE(streams_vector_reader) +{ + std::vector<unsigned char> vch = {1, 255, 3, 4, 5, 6}; + + VectorReader reader(SER_NETWORK, INIT_PROTO_VERSION, vch, 0); + BOOST_CHECK_EQUAL(reader.size(), 6); + BOOST_CHECK(!reader.empty()); + + // Read a single byte as an unsigned char. + unsigned char a; + reader >> a; + BOOST_CHECK_EQUAL(a, 1); + BOOST_CHECK_EQUAL(reader.size(), 5); + BOOST_CHECK(!reader.empty()); + + // Read a single byte as a signed char. + signed char b; + reader >> b; + BOOST_CHECK_EQUAL(b, -1); + BOOST_CHECK_EQUAL(reader.size(), 4); + BOOST_CHECK(!reader.empty()); + + // Read a 4 bytes as an unsigned int. + unsigned int c; + reader >> c; + BOOST_CHECK_EQUAL(c, 100992003); // 3,4,5,6 in little-endian base-256 + BOOST_CHECK_EQUAL(reader.size(), 0); + BOOST_CHECK(reader.empty()); + + // Reading after end of byte vector throws an error. + signed int d; + BOOST_CHECK_THROW(reader >> d, std::ios_base::failure); + + // Read a 4 bytes as a signed int from the beginning of the buffer. + reader.seek(-6); + reader >> d; + BOOST_CHECK_EQUAL(d, 67370753); // 1,255,3,4 in little-endian base-256 + BOOST_CHECK_EQUAL(reader.size(), 2); + BOOST_CHECK(!reader.empty()); + + // Reading after end of byte vector throws an error even if the reader is + // not totally empty. + BOOST_CHECK_THROW(reader >> d, std::ios_base::failure); +} + +BOOST_AUTO_TEST_CASE(bitstream_reader_writer) +{ + CDataStream data(SER_NETWORK, INIT_PROTO_VERSION); + + BitStreamWriter<CDataStream> bit_writer(data); + bit_writer.Write(0, 1); + bit_writer.Write(2, 2); + bit_writer.Write(6, 3); + bit_writer.Write(11, 4); + bit_writer.Write(1, 5); + bit_writer.Write(32, 6); + bit_writer.Write(7, 7); + bit_writer.Write(30497, 16); + bit_writer.Flush(); + + CDataStream data_copy(data); + uint32_t serialized_int1; + data >> serialized_int1; + BOOST_CHECK_EQUAL(serialized_int1, (uint32_t)0x7700C35A); // NOTE: Serialized as LE + uint16_t serialized_int2; + data >> serialized_int2; + BOOST_CHECK_EQUAL(serialized_int2, (uint16_t)0x1072); // NOTE: Serialized as LE + + BitStreamReader<CDataStream> bit_reader(data_copy); + BOOST_CHECK_EQUAL(bit_reader.Read(1), 0); + BOOST_CHECK_EQUAL(bit_reader.Read(2), 2); + BOOST_CHECK_EQUAL(bit_reader.Read(3), 6); + BOOST_CHECK_EQUAL(bit_reader.Read(4), 11); + BOOST_CHECK_EQUAL(bit_reader.Read(5), 1); + BOOST_CHECK_EQUAL(bit_reader.Read(6), 32); + BOOST_CHECK_EQUAL(bit_reader.Read(7), 7); + BOOST_CHECK_EQUAL(bit_reader.Read(16), 30497); + BOOST_CHECK_THROW(bit_reader.Read(8), std::ios_base::failure); +} + BOOST_AUTO_TEST_CASE(streams_serializedata_xor) { std::vector<char> in; diff --git a/src/test/sync_tests.cpp b/src/test/sync_tests.cpp new file mode 100644 index 0000000000..df0380546e --- /dev/null +++ b/src/test/sync_tests.cpp @@ -0,0 +1,52 @@ +// Copyright (c) 2012-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <sync.h> +#include <test/test_bitcoin.h> + +#include <boost/test/unit_test.hpp> + +namespace { +template <typename MutexType> +void TestPotentialDeadLockDetected(MutexType& mutex1, MutexType& mutex2) +{ + { + LOCK2(mutex1, mutex2); + } + bool error_thrown = false; + try { + LOCK2(mutex2, mutex1); + } catch (const std::logic_error& e) { + BOOST_CHECK_EQUAL(e.what(), "potential deadlock detected"); + error_thrown = true; + } + #ifdef DEBUG_LOCKORDER + BOOST_CHECK(error_thrown); + #else + BOOST_CHECK(!error_thrown); + #endif +} +} // namespace + +BOOST_FIXTURE_TEST_SUITE(sync_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(potential_deadlock_detected) +{ + #ifdef DEBUG_LOCKORDER + bool prev = g_debug_lockorder_abort; + g_debug_lockorder_abort = false; + #endif + + CCriticalSection rmutex1, rmutex2; + TestPotentialDeadLockDetected(rmutex1, rmutex2); + + Mutex mutex1, mutex2; + TestPotentialDeadLockDetected(mutex1, mutex2); + + #ifdef DEBUG_LOCKORDER + g_debug_lockorder_abort = prev; + #endif +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 9c3285fb0c..f7874e6882 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -6,17 +6,20 @@ #include <chainparams.h> #include <consensus/consensus.h> +#include <consensus/params.h> #include <consensus/validation.h> #include <crypto/sha256.h> -#include <validation.h> #include <miner.h> #include <net_processing.h> #include <pow.h> -#include <ui_interface.h> -#include <streams.h> -#include <rpc/server.h> #include <rpc/register.h> +#include <rpc/server.h> #include <script/sigcache.h> +#include <streams.h> +#include <ui_interface.h> +#include <validation.h> + +const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr; void CConnmanTest::AddNode(CNode& node) { @@ -27,7 +30,7 @@ void CConnmanTest::AddNode(CNode& node) void CConnmanTest::ClearNodes() { LOCK(g_connman->cs_vNodes); - for (CNode* node : g_connman->vNodes) { + for (const CNode* node : g_connman->vNodes) { delete node; } g_connman->vNodes.clear(); @@ -56,6 +59,9 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName) InitSignatureCache(); InitScriptExecutionCache(); fCheckBlockIndex = true; + // CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests. + // TODO: fix the code to support SegWit blocks. + gArgs.ForceSetArg("-vbparams", strprintf("segwit:0:%d", (int64_t)Consensus::BIP9Deployment::NO_TIMEOUT)); SelectParams(chainName); noui_connect(); } @@ -105,7 +111,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha nScriptCheckThreads = 3; for (int i=0; i < nScriptCheckThreads-1; i++) threadGroup.create_thread(&ThreadScriptCheck); - g_connman = std::unique_ptr<CConnman>(new CConnman(0x1337, 0x1337)); // Deterministic randomness for tests. + g_connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests. connman = g_connman.get(); peerLogic.reset(new PeerLogicValidation(connman, scheduler, /*enable_bip61=*/true)); } @@ -126,9 +132,6 @@ TestingSetup::~TestingSetup() TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST) { - // CreateAndProcessBlock() does not support building SegWit blocks, so don't activate in these tests. - // TODO: fix the code to support SegWit blocks. - UpdateVersionBitsParameters(Consensus::DEPLOYMENT_SEGWIT, 0, Consensus::BIP9Deployment::NO_TIMEOUT); // Generate a 100-block chain: coinbaseKey.MakeNewKey(true); CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; diff --git a/src/test/test_bitcoin_fuzzy.cpp b/src/test/test_bitcoin_fuzzy.cpp index 4d835db7be..88c082ff66 100644 --- a/src/test/test_bitcoin_fuzzy.cpp +++ b/src/test/test_bitcoin_fuzzy.cpp @@ -6,20 +6,20 @@ #include <config/bitcoin-config.h> #endif -#include <consensus/merkle.h> -#include <primitives/block.h> -#include <script/script.h> #include <addrman.h> +#include <blockencodings.h> #include <chain.h> #include <coins.h> #include <compressor.h> +#include <consensus/merkle.h> #include <net.h> +#include <primitives/block.h> #include <protocol.h> +#include <pubkey.h> +#include <script/script.h> #include <streams.h> #include <undo.h> #include <version.h> -#include <pubkey.h> -#include <blockencodings.h> #include <stdint.h> #include <unistd.h> @@ -28,6 +28,8 @@ #include <memory> #include <vector> +const std::function<std::string(const char*)> G_TRANSLATION_FUN = nullptr; + enum TEST_ID { CBLOCK_DESERIALIZE=0, CTRANSACTION_DESERIALIZE, @@ -277,7 +279,7 @@ static int test_one_input(std::vector<uint8_t> buffer) { static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle; void initialize() { - globalVerifyHandle = std::unique_ptr<ECCVerifyHandle>(new ECCVerifyHandle()); + globalVerifyHandle = MakeUnique<ECCVerifyHandle>(); } // This function is used by libFuzzer diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 33f0a83b7e..c527ad448c 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -65,7 +65,7 @@ unsigned int ParseScriptFlags(std::string strFlags) std::vector<std::string> words; boost::algorithm::split(words, strFlags, boost::algorithm::is_any_of(",")); - for (std::string word : words) + for (const std::string& word : words) { if (!mapFlagNames.count(word)) BOOST_ERROR("Bad test: unknown verification flag '" << word << "'"); diff --git a/src/test/txindex_tests.cpp b/src/test/txindex_tests.cpp index be7ee2428b..2a160b9988 100644 --- a/src/test/txindex_tests.cpp +++ b/src/test/txindex_tests.cpp @@ -61,6 +61,8 @@ BOOST_FIXTURE_TEST_CASE(txindex_initial_sync, TestChain100Setup) BOOST_ERROR("Read incorrect tx"); } } + + txindex.Stop(); // Stop thread before calling destructor } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/uint256_tests.cpp b/src/test/uint256_tests.cpp index f91b633b83..cca5e20296 100644 --- a/src/test/uint256_tests.cpp +++ b/src/test/uint256_tests.cpp @@ -184,8 +184,8 @@ BOOST_AUTO_TEST_CASE( methods ) // GetHex SetHex begin() end() size() GetLow64 G BOOST_CHECK(OneL.begin() + 32 == OneL.end()); BOOST_CHECK(MaxL.begin() + 32 == MaxL.end()); BOOST_CHECK(TmpL.begin() + 32 == TmpL.end()); - BOOST_CHECK(GetSerializeSize(R1L, 0, PROTOCOL_VERSION) == 32); - BOOST_CHECK(GetSerializeSize(ZeroL, 0, PROTOCOL_VERSION) == 32); + BOOST_CHECK(GetSerializeSize(R1L, PROTOCOL_VERSION) == 32); + BOOST_CHECK(GetSerializeSize(ZeroL, PROTOCOL_VERSION) == 32); CDataStream ss(0, PROTOCOL_VERSION); ss << R1L; @@ -230,8 +230,8 @@ BOOST_AUTO_TEST_CASE( methods ) // GetHex SetHex begin() end() size() GetLow64 G BOOST_CHECK(OneS.begin() + 20 == OneS.end()); BOOST_CHECK(MaxS.begin() + 20 == MaxS.end()); BOOST_CHECK(TmpS.begin() + 20 == TmpS.end()); - BOOST_CHECK(GetSerializeSize(R1S, 0, PROTOCOL_VERSION) == 20); - BOOST_CHECK(GetSerializeSize(ZeroS, 0, PROTOCOL_VERSION) == 20); + BOOST_CHECK(GetSerializeSize(R1S, PROTOCOL_VERSION) == 20); + BOOST_CHECK(GetSerializeSize(ZeroS, PROTOCOL_VERSION) == 20); ss << R1S; BOOST_CHECK(ss.str() == std::string(R1Array,R1Array+20)); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index b2960446eb..c74eb7531a 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -245,7 +245,7 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArg) testArgs.ParseParameters(7, (char**)argv_test, error); // Each letter should be set. - for (char opt : "abcdef") + for (const char opt : "abcdef") BOOST_CHECK(testArgs.IsArgSet({'-', opt}) || !opt); // Nothing else should be in the map @@ -394,7 +394,7 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream) && test_args.GetArg("-iii", "xxx") == "xxx" ); - for (bool def : {false, true}) { + for (const bool def : {false, true}) { BOOST_CHECK(test_args.GetBoolArg("-a", def) && test_args.GetBoolArg("-b", def) && !test_args.GetBoolArg("-ccc", def) @@ -1217,4 +1217,43 @@ BOOST_AUTO_TEST_CASE(test_DirIsWritable) fs::remove(tmpdirname); } +BOOST_AUTO_TEST_CASE(test_ToLower) +{ + BOOST_CHECK_EQUAL(ToLower('@'), '@'); + BOOST_CHECK_EQUAL(ToLower('A'), 'a'); + BOOST_CHECK_EQUAL(ToLower('Z'), 'z'); + BOOST_CHECK_EQUAL(ToLower('['), '['); + BOOST_CHECK_EQUAL(ToLower(0), 0); + BOOST_CHECK_EQUAL(ToLower(255), 255); + + std::string testVector; + Downcase(testVector); + BOOST_CHECK_EQUAL(testVector, ""); + + testVector = "#HODL"; + Downcase(testVector); + BOOST_CHECK_EQUAL(testVector, "#hodl"); + + testVector = "\x00\xfe\xff"; + Downcase(testVector); + BOOST_CHECK_EQUAL(testVector, "\x00\xfe\xff"); +} + +BOOST_AUTO_TEST_CASE(test_ToUpper) +{ + BOOST_CHECK_EQUAL(ToUpper('`'), '`'); + BOOST_CHECK_EQUAL(ToUpper('a'), 'A'); + BOOST_CHECK_EQUAL(ToUpper('z'), 'Z'); + BOOST_CHECK_EQUAL(ToUpper('{'), '{'); + BOOST_CHECK_EQUAL(ToUpper(0), 0); + BOOST_CHECK_EQUAL(ToUpper(255), 255); +} + +BOOST_AUTO_TEST_CASE(test_Capitalize) +{ + BOOST_CHECK_EQUAL(Capitalize(""), ""); + BOOST_CHECK_EQUAL(Capitalize("bitcoin"), "Bitcoin"); + BOOST_CHECK_EQUAL(Capitalize("\x00\xfe\xff"), "\x00\xfe\xff"); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp index 37c4f79133..4316f37999 100644 --- a/src/test/validation_block_tests.cpp +++ b/src/test/validation_block_tests.cpp @@ -23,7 +23,7 @@ BOOST_FIXTURE_TEST_SUITE(validation_block_tests, RegtestingSetup) struct TestSubscriber : public CValidationInterface { uint256 m_expected_tip; - TestSubscriber(uint256 tip) : m_expected_tip(tip) {} + explicit TestSubscriber(uint256 tip) : m_expected_tip(tip) {} void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) override { diff --git a/src/threadinterrupt.cpp b/src/threadinterrupt.cpp index d08d52e4a9..340106ed99 100644 --- a/src/threadinterrupt.cpp +++ b/src/threadinterrupt.cpp @@ -5,6 +5,8 @@ #include <threadinterrupt.h> +#include <sync.h> + CThreadInterrupt::CThreadInterrupt() : flag(false) {} CThreadInterrupt::operator bool() const @@ -20,7 +22,7 @@ void CThreadInterrupt::reset() void CThreadInterrupt::operator()() { { - std::unique_lock<std::mutex> lock(mut); + LOCK(mut); flag.store(true, std::memory_order_release); } cond.notify_all(); @@ -28,7 +30,7 @@ void CThreadInterrupt::operator()() bool CThreadInterrupt::sleep_for(std::chrono::milliseconds rel_time) { - std::unique_lock<std::mutex> lock(mut); + WAIT_LOCK(mut, lock); return !cond.wait_for(lock, rel_time, [this]() { return flag.load(std::memory_order_acquire); }); } diff --git a/src/threadinterrupt.h b/src/threadinterrupt.h index c30bd3d657..9c6fccfcde 100644 --- a/src/threadinterrupt.h +++ b/src/threadinterrupt.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_THREADINTERRUPT_H #define BITCOIN_THREADINTERRUPT_H +#include <sync.h> + #include <atomic> #include <chrono> #include <condition_variable> @@ -28,7 +30,7 @@ public: private: std::condition_variable cond; - std::mutex mut; + Mutex mut; std::atomic<bool> flag; }; diff --git a/src/threadsafety.h b/src/threadsafety.h index a2f45e5fce..47e6b2ea38 100644 --- a/src/threadsafety.h +++ b/src/threadsafety.h @@ -10,7 +10,7 @@ // TL;DR Add GUARDED_BY(mutex) to member variables. The others are // rarely necessary. Ex: int nFoo GUARDED_BY(cs_foo); // -// See http://clang.llvm.org/docs/LanguageExtensions.html#threadsafety +// See https://clang.llvm.org/docs/ThreadSafetyAnalysis.html // for documentation. The clang compiler can do advanced static analysis // of locking when given the -Wthread-safety option. #define LOCKABLE __attribute__((lockable)) diff --git a/src/timedata.cpp b/src/timedata.cpp index 1599508306..291111feb2 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -94,7 +94,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample) { // If nobody has a time different than ours but within 5 minutes of ours, give a warning bool fMatch = false; - for (int64_t nOffset : vSorted) + for (const int64_t nOffset : vSorted) if (nOffset != 0 && abs64(nOffset) < 5 * 60) fMatch = true; @@ -109,7 +109,7 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample) } if (LogAcceptCategory(BCLog::NET)) { - for (int64_t n : vSorted) { + for (const int64_t n : vSorted) { LogPrint(BCLog::NET, "%+d ", n); /* Continued */ } LogPrint(BCLog::NET, "| "); /* Continued */ diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 8bada4dce8..3ad93342c4 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -20,13 +20,10 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee, int64_t _nTime, unsigned int _entryHeight, - bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp): - tx(_tx), nFee(_nFee), nTime(_nTime), entryHeight(_entryHeight), + bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp) + : tx(_tx), nFee(_nFee), nTxWeight(GetTransactionWeight(*tx)), nUsageSize(RecursiveDynamicUsage(tx)), nTime(_nTime), entryHeight(_entryHeight), spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp) { - nTxWeight = GetTransactionWeight(*tx); - nUsageSize = RecursiveDynamicUsage(tx); - nCountWithDescendants = 1; nSizeWithDescendants = GetTxSize(); nModFeesWithDescendants = nFee; @@ -159,9 +156,9 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntr // GetMemPoolParents() is only valid for entries in the mempool, so we // iterate mapTx to find parents. for (unsigned int i = 0; i < tx.vin.size(); i++) { - txiter piter = mapTx.find(tx.vin[i].prevout.hash); - if (piter != mapTx.end()) { - parentHashes.insert(piter); + boost::optional<txiter> piter = GetIter(tx.vin[i].prevout.hash); + if (piter) { + parentHashes.insert(*piter); if (parentHashes.size() + 1 > limitAncestorCount) { errString = strprintf("too many unconfirmed parents [limit: %u]", limitAncestorCount); return false; @@ -196,7 +193,7 @@ bool CTxMemPool::CalculateMemPoolAncestors(const CTxMemPoolEntry &entry, setEntr } const setEntries & setMemPoolParents = GetMemPoolParents(stageit); - for (const txiter &phash : setMemPoolParents) { + for (txiter phash : setMemPoolParents) { // If this is a new ancestor, add it. if (setAncestors.count(phash) == 0) { parentHashes.insert(phash); @@ -355,7 +352,7 @@ void CTxMemPool::AddTransactionsUpdated(unsigned int n) nTransactionsUpdated += n; } -void CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate) +void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAncestors, bool validFeeEstimate) { NotifyEntryAdded(entry.GetSharedTx()); // Add to memory pool without checking anything. @@ -367,12 +364,10 @@ void CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, // Update transaction for any feeDelta created by PrioritiseTransaction // TODO: refactor so that the fee delta is calculated before inserting // into mapTx. - std::map<uint256, CAmount>::const_iterator pos = mapDeltas.find(hash); - if (pos != mapDeltas.end()) { - const CAmount &delta = pos->second; - if (delta) { + CAmount delta{0}; + ApplyDelta(entry.GetTx().GetHash(), delta); + if (delta) { mapTx.modify(newit, update_fee_delta(delta)); - } } // Update cachedInnerUsage to include contained transaction's usage. @@ -394,11 +389,8 @@ void CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, // to clean up the mess we're leaving here. // Update ancestors with information about this tx - for (const uint256 &phash : setParentTransactions) { - txiter pit = mapTx.find(phash); - if (pit != mapTx.end()) { + for (const auto& pit : GetIterSet(setParentTransactions)) { UpdateParent(newit, pit, true); - } } UpdateAncestorsOf(true, newit, setAncestors); UpdateEntryForAncestors(newit, setAncestors); @@ -457,7 +449,7 @@ void CTxMemPool::CalculateDescendants(txiter entryit, setEntries& setDescendants stage.erase(it); const setEntries &setChildren = GetMemPoolChildren(it); - for (const txiter &childiter : setChildren) { + for (txiter childiter : setChildren) { if (!setDescendants.count(childiter)) { stage.insert(childiter); } @@ -640,8 +632,6 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const innerUsage += memusage::DynamicUsage(links.parents) + memusage::DynamicUsage(links.children); bool fDependsWait = false; setEntries setParentCheck; - int64_t parentSizes = 0; - int64_t parentSigOpCost = 0; for (const CTxIn &txin : tx.vin) { // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash); @@ -649,10 +639,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const const CTransaction& tx2 = it2->GetTx(); assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull()); fDependsWait = true; - if (setParentCheck.insert(it2).second) { - parentSizes += it2->GetTxSize(); - parentSigOpCost += it2->GetSigOpCost(); - } + setParentCheck.insert(it2); } else { assert(pcoins->HaveCoin(txin.prevout)); } @@ -872,6 +859,29 @@ void CTxMemPool::ClearPrioritisation(const uint256 hash) mapDeltas.erase(hash); } +const CTransaction* CTxMemPool::GetConflictTx(const COutPoint& prevout) const +{ + const auto it = mapNextTx.find(prevout); + return it == mapNextTx.end() ? nullptr : it->second; +} + +boost::optional<CTxMemPool::txiter> CTxMemPool::GetIter(const uint256& txid) const +{ + auto it = mapTx.find(txid); + if (it != mapTx.end()) return it; + return boost::optional<txiter>{}; +} + +CTxMemPool::setEntries CTxMemPool::GetIterSet(const std::set<uint256>& hashes) const +{ + CTxMemPool::setEntries ret; + for (const auto& h : hashes) { + const auto mi = GetIter(h); + if (mi) ret.insert(*mi); + } + return ret; +} + bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const { for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -907,7 +917,7 @@ size_t CTxMemPool::DynamicMemoryUsage() const { void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason) { AssertLockHeld(cs); UpdateForRemoveFromMempool(stage, updateDescendants); - for (const txiter& it : stage) { + for (txiter it : stage) { removeUnchecked(it, reason); } } @@ -928,13 +938,13 @@ int CTxMemPool::Expire(int64_t time) { return stage.size(); } -void CTxMemPool::addUnchecked(const uint256&hash, const CTxMemPoolEntry &entry, bool validFeeEstimate) +void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, bool validFeeEstimate) { setEntries setAncestors; uint64_t nNoLimit = std::numeric_limits<uint64_t>::max(); std::string dummy; CalculateMemPoolAncestors(entry, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy); - return addUnchecked(hash, entry, setAncestors, validFeeEstimate); + return addUnchecked(entry, setAncestors, validFeeEstimate); } void CTxMemPool::UpdateChild(txiter entry, txiter child, bool add) diff --git a/src/txmempool.h b/src/txmempool.h index 587d931d95..913501fd66 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -28,6 +28,7 @@ #include <boost/signals2/signal.hpp> class CBlockIndex; +extern CCriticalSection cs_main; /** Fake height value used in Coin to signify they are only in the memory pool (since 0.8) */ static const uint32_t MEMPOOL_HEIGHT = 0x7FFFFFFF; @@ -64,14 +65,14 @@ class CTxMemPool; class CTxMemPoolEntry { private: - CTransactionRef tx; - CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups - size_t nTxWeight; //!< ... and avoid recomputing tx weight (also used for GetTxSize()) - size_t nUsageSize; //!< ... and total memory usage - int64_t nTime; //!< Local time when entering the mempool - unsigned int entryHeight; //!< Chain height when entering the mempool - bool spendsCoinbase; //!< keep track of transactions that spend a coinbase - int64_t sigOpCost; //!< Total sigop cost + const CTransactionRef tx; + const CAmount nFee; //!< Cached to avoid expensive parent-transaction lookups + const size_t nTxWeight; //!< ... and avoid recomputing tx weight (also used for GetTxSize()) + const size_t nUsageSize; //!< ... and total memory usage + const int64_t nTime; //!< Local time when entering the mempool + const unsigned int entryHeight; //!< Chain height when entering the mempool + const bool spendsCoinbase; //!< keep track of transactions that spend a coinbase + const int64_t sigOpCost; //!< Total sigop cost int64_t feeDelta; //!< Used for determining the priority of the transaction for mining in a block LockPoints lockPoints; //!< Track the height and time at which tx was final @@ -539,11 +540,11 @@ public: // Note that addUnchecked is ONLY called from ATMP outside of tests // and any other callers may break wallet's in-mempool tracking (due to // lack of CValidationInterface::TransactionAddedToMempool callbacks). - void addUnchecked(const uint256& hash, const CTxMemPoolEntry& entry, bool validFeeEstimate = true) EXCLUSIVE_LOCKS_REQUIRED(cs); - void addUnchecked(const uint256& hash, const CTxMemPoolEntry& entry, setEntries& setAncestors, bool validFeeEstimate = true) EXCLUSIVE_LOCKS_REQUIRED(cs); + void addUnchecked(const CTxMemPoolEntry& entry, bool validFeeEstimate = true) EXCLUSIVE_LOCKS_REQUIRED(cs); + void addUnchecked(const CTxMemPoolEntry& entry, setEntries& setAncestors, bool validFeeEstimate = true) EXCLUSIVE_LOCKS_REQUIRED(cs); void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN); - void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags); + void removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main); void removeConflicts(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(cs); void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight); @@ -565,7 +566,15 @@ public: void ApplyDelta(const uint256 hash, CAmount &nFeeDelta) const; void ClearPrioritisation(const uint256 hash); -public: + /** Get the transaction in the pool that spends the same prevout */ + const CTransaction* GetConflictTx(const COutPoint& prevout) const EXCLUSIVE_LOCKS_REQUIRED(cs); + + /** Returns an iterator to the given hash, if found */ + boost::optional<txiter> GetIter(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs); + + /** Translate a set of hashes into a set of pool iterators to avoid repeated lookups */ + setEntries GetIterSet(const std::set<uint256>& hashes) const EXCLUSIVE_LOCKS_REQUIRED(cs); + /** Remove a set of transactions from the mempool. * If a transaction is in this set, then all in-mempool descendants must * also be in the set, unless this transaction is being removed for being @@ -638,7 +647,7 @@ public: return totalTxSize; } - bool exists(uint256 hash) const + bool exists(const uint256& hash) const { LOCK(cs); return (mapTx.count(hash) != 0); diff --git a/src/undo.h b/src/undo.h index a1398b7624..4ed3dc4ca0 100644 --- a/src/undo.h +++ b/src/undo.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_UNDO_H #define BITCOIN_UNDO_H +#include <coins.h> #include <compressor.h> #include <consensus/consensus.h> #include <primitives/transaction.h> @@ -60,7 +61,7 @@ public: explicit TxInUndoDeserializer(Coin* coin) : txout(coin) {} }; -static const size_t MIN_TRANSACTION_INPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxIn(), SER_NETWORK, PROTOCOL_VERSION); +static const size_t MIN_TRANSACTION_INPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxIn(), PROTOCOL_VERSION); static const size_t MAX_INPUTS_PER_BLOCK = MAX_BLOCK_WEIGHT / MIN_TRANSACTION_INPUT_WEIGHT; /** Undo information for a CTransaction */ diff --git a/src/univalue/gen/gen.cpp b/src/univalue/gen/gen.cpp index 17f361941d..85fe20924a 100644 --- a/src/univalue/gen/gen.cpp +++ b/src/univalue/gen/gen.cpp @@ -12,8 +12,6 @@ #include <string.h> #include "univalue.h" -using namespace std; - static bool initEscapes; static std::string escapes[256]; diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h index c15b2f051e..91b104e56e 100644 --- a/src/univalue/include/univalue.h +++ b/src/univalue/include/univalue.h @@ -15,7 +15,6 @@ #include <cassert> #include <sstream> // .get_int64() -#include <utility> // std::pair class UniValue { public: @@ -177,76 +176,9 @@ public: const UniValue& get_array() const; enum VType type() const { return getType(); } - bool push_back(std::pair<std::string,UniValue> pear) { - return pushKV(pear.first, pear.second); - } friend const UniValue& find_value( const UniValue& obj, const std::string& name); }; -// -// The following were added for compatibility with json_spirit. -// Most duplicate other methods, and should be removed. -// -static inline std::pair<std::string,UniValue> Pair(const char *cKey, const char *cVal) -{ - std::string key(cKey); - UniValue uVal(cVal); - return std::make_pair(key, uVal); -} - -static inline std::pair<std::string,UniValue> Pair(const char *cKey, std::string strVal) -{ - std::string key(cKey); - UniValue uVal(strVal); - return std::make_pair(key, uVal); -} - -static inline std::pair<std::string,UniValue> Pair(const char *cKey, uint64_t u64Val) -{ - std::string key(cKey); - UniValue uVal(u64Val); - return std::make_pair(key, uVal); -} - -static inline std::pair<std::string,UniValue> Pair(const char *cKey, int64_t i64Val) -{ - std::string key(cKey); - UniValue uVal(i64Val); - return std::make_pair(key, uVal); -} - -static inline std::pair<std::string,UniValue> Pair(const char *cKey, bool iVal) -{ - std::string key(cKey); - UniValue uVal(iVal); - return std::make_pair(key, uVal); -} - -static inline std::pair<std::string,UniValue> Pair(const char *cKey, int iVal) -{ - std::string key(cKey); - UniValue uVal(iVal); - return std::make_pair(key, uVal); -} - -static inline std::pair<std::string,UniValue> Pair(const char *cKey, double dVal) -{ - std::string key(cKey); - UniValue uVal(dVal); - return std::make_pair(key, uVal); -} - -static inline std::pair<std::string,UniValue> Pair(const char *cKey, const UniValue& uVal) -{ - std::string key(cKey); - return std::make_pair(key, uVal); -} - -static inline std::pair<std::string,UniValue> Pair(std::string key, const UniValue& uVal) -{ - return std::make_pair(key, uVal); -} - enum jtokentype { JTOK_ERR = -1, JTOK_NONE = 0, // eof diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp index d8ad7c4b90..4c9c15d63e 100644 --- a/src/univalue/lib/univalue.cpp +++ b/src/univalue/lib/univalue.cpp @@ -10,8 +10,6 @@ #include "univalue.h" -using namespace std; - const UniValue NullUniValue; void UniValue::clear() @@ -37,15 +35,15 @@ bool UniValue::setBool(bool val_) return true; } -static bool validNumStr(const string& s) +static bool validNumStr(const std::string& s) { - string tokenVal; + std::string tokenVal; unsigned int consumed; enum jtokentype tt = getJsonToken(tokenVal, consumed, s.data(), s.data() + s.size()); return (tt == JTOK_NUMBER); } -bool UniValue::setNumStr(const string& val_) +bool UniValue::setNumStr(const std::string& val_) { if (!validNumStr(val_)) return false; @@ -58,7 +56,7 @@ bool UniValue::setNumStr(const string& val_) bool UniValue::setInt(uint64_t val_) { - ostringstream oss; + std::ostringstream oss; oss << val_; @@ -67,7 +65,7 @@ bool UniValue::setInt(uint64_t val_) bool UniValue::setInt(int64_t val_) { - ostringstream oss; + std::ostringstream oss; oss << val_; @@ -76,7 +74,7 @@ bool UniValue::setInt(int64_t val_) bool UniValue::setFloat(double val_) { - ostringstream oss; + std::ostringstream oss; oss << std::setprecision(16) << val_; @@ -85,7 +83,7 @@ bool UniValue::setFloat(double val_) return ret; } -bool UniValue::setStr(const string& val_) +bool UniValue::setStr(const std::string& val_) { clear(); typ = VSTR; diff --git a/src/univalue/lib/univalue_read.cpp b/src/univalue/lib/univalue_read.cpp index ae75cb462a..14834db24d 100644 --- a/src/univalue/lib/univalue_read.cpp +++ b/src/univalue/lib/univalue_read.cpp @@ -8,8 +8,6 @@ #include "univalue.h" #include "univalue_utffilter.h" -using namespace std; - static bool json_isdigit(int ch) { return ((ch >= '0') && (ch <= '9')); @@ -42,7 +40,7 @@ static const char *hatoui(const char *first, const char *last, return first; } -enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, +enum jtokentype getJsonToken(std::string& tokenVal, unsigned int& consumed, const char *raw, const char *end) { tokenVal.clear(); @@ -114,7 +112,7 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, case '8': case '9': { // part 1: int - string numStr; + std::string numStr; const char *first = raw; @@ -174,7 +172,7 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, case '"': { raw++; // skip " - string valStr; + std::string valStr; JSONUTF8StringFilter writer(valStr); while (true) { @@ -255,9 +253,9 @@ bool UniValue::read(const char *raw, size_t size) clear(); uint32_t expectMask = 0; - vector<UniValue*> stack; + std::vector<UniValue*> stack; - string tokenVal; + std::string tokenVal; unsigned int consumed; enum jtokentype tok = JTOK_NONE; enum jtokentype last_tok = JTOK_NONE; diff --git a/src/univalue/lib/univalue_write.cpp b/src/univalue/lib/univalue_write.cpp index cf27835991..827eb9b271 100644 --- a/src/univalue/lib/univalue_write.cpp +++ b/src/univalue/lib/univalue_write.cpp @@ -8,11 +8,9 @@ #include "univalue.h" #include "univalue_escapes.h" -using namespace std; - -static string json_escape(const string& inS) +static std::string json_escape(const std::string& inS) { - string outS; + std::string outS; outS.reserve(inS.size() * 2); for (unsigned int i = 0; i < inS.size(); i++) { @@ -28,10 +26,10 @@ static string json_escape(const string& inS) return outS; } -string UniValue::write(unsigned int prettyIndent, - unsigned int indentLevel) const +std::string UniValue::write(unsigned int prettyIndent, + unsigned int indentLevel) const { - string s; + std::string s; s.reserve(1024); unsigned int modIndent = indentLevel; @@ -62,12 +60,12 @@ string UniValue::write(unsigned int prettyIndent, return s; } -static void indentStr(unsigned int prettyIndent, unsigned int indentLevel, string& s) +static void indentStr(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) { s.append(prettyIndent * indentLevel, ' '); } -void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, string& s) const +void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const { s += "["; if (prettyIndent) @@ -89,7 +87,7 @@ void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, s s += "]"; } -void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, string& s) const +void UniValue::writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const { s += "{"; if (prettyIndent) diff --git a/src/univalue/test/unitester.cpp b/src/univalue/test/unitester.cpp index 2c37794a4b..75c0dc225a 100644 --- a/src/univalue/test/unitester.cpp +++ b/src/univalue/test/unitester.cpp @@ -17,8 +17,7 @@ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #endif -using namespace std; -string srcdir(JSON_TEST_SRC); +std::string srcdir(JSON_TEST_SRC); static bool test_failed = false; #define d_assert(expr) { if (!(expr)) { test_failed = true; fprintf(stderr, "%s failed\n", filename.c_str()); } } @@ -30,9 +29,9 @@ static std::string rtrim(std::string s) return s; } -static void runtest(string filename, const string& jdata) +static void runtest(std::string filename, const std::string& jdata) { - string prefix = filename.substr(0, 4); + std::string prefix = filename.substr(0, 4); bool wantPass = (prefix == "pass") || (prefix == "roun"); bool wantFail = (prefix == "fail"); @@ -56,19 +55,19 @@ static void runtest(string filename, const string& jdata) static void runtest_file(const char *filename_) { - string basename(filename_); - string filename = srcdir + "/" + basename; + std::string basename(filename_); + std::string filename = srcdir + "/" + basename; FILE *f = fopen(filename.c_str(), "r"); assert(f != NULL); - string jdata; + std::string jdata; char buf[4096]; while (!feof(f)) { int bread = fread(buf, 1, sizeof(buf), f); assert(!ferror(f)); - string s(buf, bread); + std::string s(buf, bread); jdata += s; } diff --git a/src/util.cpp b/src/util.cpp index 95bc427bb2..fa624aee90 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -58,6 +58,7 @@ #ifndef NOMINMAX #define NOMINMAX #endif +#include <codecvt> #include <io.h> /* for _commit */ #include <shlobj.h> @@ -71,7 +72,6 @@ #include <malloc.h> #endif -#include <boost/interprocess/sync/file_lock.hpp> #include <boost/thread.hpp> #include <openssl/crypto.h> #include <openssl/rand.h> @@ -86,8 +86,6 @@ const char * const BITCOIN_PID_FILENAME = "bitcoind.pid"; ArgsManager gArgs; -CTranslationInterface translationInterface; - /** Init OpenSSL library multithreading support */ static std::unique_ptr<CCriticalSection[]> ppmutexOpenSSL; void locking_callback(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS @@ -141,7 +139,7 @@ instance_of_cinit; * cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks * is called. */ -static std::map<std::string, std::unique_ptr<boost::interprocess::file_lock>> dir_locks; +static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks; /** Mutex to protect dir_locks. */ static std::mutex cs_dir_locks; @@ -158,18 +156,13 @@ bool LockDirectory(const fs::path& directory, const std::string lockfile_name, b // Create empty lock file if it doesn't exist. FILE* file = fsbridge::fopen(pathLockFile, "a"); if (file) fclose(file); - - try { - auto lock = MakeUnique<boost::interprocess::file_lock>(pathLockFile.string().c_str()); - if (!lock->try_lock()) { - return false; - } - if (!probe_only) { - // Lock successful and we're not just probing, put it into the map - dir_locks.emplace(pathLockFile.string(), std::move(lock)); - } - } catch (const boost::interprocess::interprocess_exception& e) { - return error("Error while attempting to lock directory %s: %s", directory.string(), e.what()); + auto lock = MakeUnique<fsbridge::FileLock>(pathLockFile); + if (!lock->TryLock()) { + return error("Error while attempting to lock directory %s: %s", directory.string(), lock->GetReason()); + } + if (!probe_only) { + // Lock successful and we're not just probing, put it into the map + dir_locks.emplace(pathLockFile.string(), std::move(lock)); } return true; } @@ -224,7 +217,7 @@ public: /** Determine whether to use config settings in the default section, * See also comments around ArgsManager::ArgsManager() below. */ - static inline bool UseDefaultSection(const ArgsManager& am, const std::string& arg) + static inline bool UseDefaultSection(const ArgsManager& am, const std::string& arg) EXCLUSIVE_LOCKS_REQUIRED(am.cs_args) { return (am.m_network == CBaseChainParams::MAIN || am.m_network_only_args.count(arg) == 0); } @@ -303,7 +296,7 @@ public: /* Special test for -testnet and -regtest args, because we * don't want to be confused by craziness like "[regtest] testnet=1" */ - static inline bool GetNetBoolArg(const ArgsManager &am, const std::string& net_arg) + static inline bool GetNetBoolArg(const ArgsManager &am, const std::string& net_arg) EXCLUSIVE_LOCKS_REQUIRED(am.cs_args) { std::pair<bool,std::string> found_result(false,std::string()); found_result = GetArgHelper(am.m_override_args, net_arg, true); @@ -380,6 +373,8 @@ ArgsManager::ArgsManager() : void ArgsManager::WarnForSectionOnlyArgs() { + LOCK(cs_args); + // if there's no section selected, don't worry if (m_network.empty()) return; @@ -408,6 +403,7 @@ void ArgsManager::WarnForSectionOnlyArgs() void ArgsManager::SelectConfigNetwork(const std::string& network) { + LOCK(cs_args); m_network = network; } @@ -476,6 +472,7 @@ bool ArgsManager::IsArgKnown(const std::string& key) const arg_no_net = std::string("-") + key.substr(option_index + 1, std::string::npos); } + LOCK(cs_args); for (const auto& arg_map : m_available_args) { if (arg_map.second.count(arg_no_net)) return true; } @@ -579,6 +576,7 @@ void ArgsManager::AddArg(const std::string& name, const std::string& help, const eq_index = name.size(); } + LOCK(cs_args); std::map<std::string, Arg>& arg_map = m_available_args[cat]; auto ret = arg_map.emplace(name.substr(0, eq_index), Arg(name.substr(eq_index, name.size() - eq_index), help, debug_only)); assert(ret.second); // Make sure an insertion actually happened @@ -596,6 +594,7 @@ std::string ArgsManager::GetHelpMessage() const const bool show_debug = gArgs.GetBoolArg("-help-debug", false); std::string usage = ""; + LOCK(cs_args); for (const auto& arg_map : m_available_args) { switch(arg_map.first) { case OptionsCategory::OPTIONS: @@ -661,7 +660,7 @@ std::string ArgsManager::GetHelpMessage() const bool HelpRequested(const ArgsManager& args) { - return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help"); + return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help") || args.IsArgSet("-help-debug"); } static const int screenWidth = 79; @@ -820,11 +819,11 @@ static std::string TrimString(const std::string& str, const std::string& pattern return str.substr(front, end - front + 1); } -static std::vector<std::pair<std::string, std::string>> GetConfigOptions(std::istream& stream) +static bool GetConfigOptions(std::istream& stream, std::string& error, std::vector<std::pair<std::string, std::string>> &options) { - std::vector<std::pair<std::string, std::string>> options; std::string str, prefix; std::string::size_type pos; + int linenr = 1; while (std::getline(stream, str)) { if ((pos = str.find('#')) != std::string::npos) { str = str.substr(0, pos); @@ -834,21 +833,34 @@ static std::vector<std::pair<std::string, std::string>> GetConfigOptions(std::is if (!str.empty()) { if (*str.begin() == '[' && *str.rbegin() == ']') { prefix = str.substr(1, str.size() - 2) + '.'; + } else if (*str.begin() == '-') { + error = strprintf("parse error on line %i: %s, options in configuration file must be specified without leading -", linenr, str); + return false; } else if ((pos = str.find('=')) != std::string::npos) { std::string name = prefix + TrimString(str.substr(0, pos), pattern); std::string value = TrimString(str.substr(pos + 1), pattern); options.emplace_back(name, value); + } else { + error = strprintf("parse error on line %i: %s", linenr, str); + if (str.size() >= 2 && str.substr(0, 2) == "no") { + error += strprintf(", if you intended to specify a negated option, use %s=1 instead", str); + } + return false; } } + ++linenr; } - return options; + return true; } bool ArgsManager::ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys) { LOCK(cs_args); - - for (const std::pair<std::string, std::string>& option : GetConfigOptions(stream)) { + std::vector<std::pair<std::string, std::string>> options; + if (!GetConfigOptions(stream, error, options)) { + return false; + } + for (const std::pair<std::string, std::string>& option : options) { std::string strKey = std::string("-") + option.first; std::string strValue = option.second; @@ -888,7 +900,12 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) } // if there is an -includeconf in the override args, but it is empty, that means the user // passed '-noincludeconf' on the command line, in which case we should not include anything - if (m_override_args.count("-includeconf") == 0) { + bool emptyIncludeConf; + { + LOCK(cs_args); + emptyIncludeConf = m_override_args.count("-includeconf") == 0; + } + if (emptyIncludeConf) { std::string chain_id = GetChainName(); std::vector<std::string> includeconf(GetArgs("-includeconf")); { @@ -948,6 +965,7 @@ bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) std::string ArgsManager::GetChainName() const { + LOCK(cs_args); bool fRegTest = ArgsManagerHelper::GetNetBoolArg(*this, "-regtest"); bool fTestNet = ArgsManagerHelper::GetNetBoolArg(*this, "-testnet"); @@ -980,7 +998,7 @@ void CreatePidFile(const fs::path &path, pid_t pid) bool RenameOver(fs::path src, fs::path dest) { #ifdef WIN32 - return MoveFileExA(src.string().c_str(), dest.string().c_str(), + return MoveFileExW(src.wstring().c_str(), dest.wstring().c_str(), MOVEFILE_REPLACE_EXISTING) != 0; #else int rc = std::rename(src.string().c_str(), dest.string().c_str()); @@ -1122,14 +1140,14 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { #ifdef WIN32 fs::path GetSpecialFolderPath(int nFolder, bool fCreate) { - char pszPath[MAX_PATH] = ""; + WCHAR pszPath[MAX_PATH] = L""; - if(SHGetSpecialFolderPathA(nullptr, pszPath, nFolder, fCreate)) + if(SHGetSpecialFolderPathW(nullptr, pszPath, nFolder, fCreate)) { return fs::path(pszPath); } - LogPrintf("SHGetSpecialFolderPathA() failed, could not obtain requested path.\n"); + LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path.\n"); return fs::path(""); } #endif @@ -1137,7 +1155,11 @@ fs::path GetSpecialFolderPath(int nFolder, bool fCreate) void runCommand(const std::string& strCommand) { if (strCommand.empty()) return; +#ifndef WIN32 int nErr = ::system(strCommand.c_str()); +#else + int nErr = ::_wsystem(std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().from_bytes(strCommand).c_str()); +#endif if (nErr) LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr); } @@ -1184,7 +1206,11 @@ void SetupEnvironment() // A dummy locale is used to extract the internal default locale, used by // fs::path, which is then used to explicitly imbue the path. std::locale loc = fs::path::imbue(std::locale::classic()); +#ifndef WIN32 fs::path::imbue(loc); +#else + fs::path::imbue(std::locale(loc, new std::codecvt_utf8_utf16<wchar_t>())); +#endif } bool SetupNetworking() @@ -1226,7 +1252,7 @@ fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific) return fs::absolute(path, GetDataDir(net_specific)); } -int ScheduleBatchPriority(void) +int ScheduleBatchPriority() { #ifdef SCHED_BATCH const static sched_param param{0}; diff --git a/src/util.h b/src/util.h index 23b2a787e4..f119385e48 100644 --- a/src/util.h +++ b/src/util.h @@ -19,8 +19,8 @@ #include <logging.h> #include <sync.h> #include <tinyformat.h> -#include <utiltime.h> #include <utilmemory.h> +#include <utiltime.h> #include <atomic> #include <exception> @@ -31,33 +31,24 @@ #include <unordered_set> #include <vector> -#include <boost/signals2/signal.hpp> #include <boost/thread/condition_variable.hpp> // for boost::thread_interrupted // Application startup time (used for uptime calculation) int64_t GetStartupTime(); -/** Signals for translation. */ -class CTranslationInterface -{ -public: - /** Translate a message to the native language of the user. */ - boost::signals2::signal<std::string (const char* psz)> Translate; -}; - -extern CTranslationInterface translationInterface; - extern const char * const BITCOIN_CONF_FILENAME; extern const char * const BITCOIN_PID_FILENAME; +/** Translate a message to the native language of the user. */ +const extern std::function<std::string(const char*)> G_TRANSLATION_FUN; + /** - * Translation function: Call Translate signal on UI interface, which returns a boost::optional result. - * If no translation slot is registered, nothing is returned, and simply return the input. + * Translation function. + * If no translation function is set, simply return the input. */ inline std::string _(const char* psz) { - boost::optional<std::string> rv = translationInterface.Translate(psz); - return rv ? (*rv) : psz; + return G_TRANSLATION_FUN ? (G_TRANSLATION_FUN)(psz) : psz; } void SetupEnvironment(); @@ -151,11 +142,11 @@ protected: }; mutable CCriticalSection cs_args; - std::map<std::string, std::vector<std::string>> m_override_args; - std::map<std::string, std::vector<std::string>> m_config_args; - std::string m_network; - std::set<std::string> m_network_only_args; - std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args; + std::map<std::string, std::vector<std::string>> m_override_args GUARDED_BY(cs_args); + std::map<std::string, std::vector<std::string>> m_config_args GUARDED_BY(cs_args); + std::string m_network GUARDED_BY(cs_args); + std::set<std::string> m_network_only_args GUARDED_BY(cs_args); + std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args GUARDED_BY(cs_args); bool ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys = false); @@ -271,7 +262,10 @@ public: /** * Clear available arguments */ - void ClearArgs() { m_available_args.clear(); } + void ClearArgs() { + LOCK(cs_args); + m_available_args.clear(); + } /** * Get the help string @@ -353,7 +347,7 @@ std::string CopyrightHolders(const std::string& strPrefix); * @return The return value of sched_setschedule(), or 1 on systems without * sched_setschedule(). */ -int ScheduleBatchPriority(void); +int ScheduleBatchPriority(); namespace util { diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index a06d88cb19..4940267bae 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -7,6 +7,7 @@ #include <tinyformat.h> +#include <algorithm> #include <cstdlib> #include <cstring> #include <errno.h> @@ -71,7 +72,7 @@ bool IsHexNumber(const std::string& str) if (str.size() > 2 && *str.begin() == '0' && *(str.begin()+1) == 'x') { starting_location = 2; } - for (auto c : str.substr(starting_location)) { + for (const char c : str.substr(starting_location)) { if (HexDigit(c) < 0) return false; } // Return false for empty string or "0x". @@ -544,3 +545,55 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out) return true; } +bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath) +{ + std::stringstream ss(keypath_str); + std::string item; + bool first = true; + while (std::getline(ss, item, '/')) { + if (item.compare("m") == 0) { + if (first) { + first = false; + continue; + } + return false; + } + // Finds whether it is hardened + uint32_t path = 0; + size_t pos = item.find("'"); + if (pos != std::string::npos) { + // The hardened tick can only be in the last index of the string + if (pos != item.size() - 1) { + return false; + } + path |= 0x80000000; + item = item.substr(0, item.size() - 1); // Drop the last character which is the hardened tick + } + + // Ensure this is only numbers + if (item.find_first_not_of( "0123456789" ) != std::string::npos) { + return false; + } + uint32_t number; + if (!ParseUInt32(item, &number)) { + return false; + } + path |= number; + + keypath.push_back(path); + first = false; + } + return true; +} + +void Downcase(std::string& str) +{ + std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c){return ToLower(c);}); +} + +std::string Capitalize(std::string str) +{ + if (str.empty()) return str; + str[0] = ToUpper(str.front()); + return str; +} diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index 5f2211b5dc..846a3917a2 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -183,4 +183,51 @@ bool ConvertBits(const O& outfn, I it, I end) { return true; } +/** Parse an HD keypaths like "m/7/0'/2000". */ +bool ParseHDKeypath(const std::string& keypath_str, std::vector<uint32_t>& keypath); + +/** + * Converts the given character to its lowercase equivalent. + * This function is locale independent. It only converts uppercase + * characters in the standard 7-bit ASCII range. + * @param[in] c the character to convert to lowercase. + * @return the lowercase equivalent of c; or the argument + * if no conversion is possible. + */ +constexpr unsigned char ToLower(unsigned char c) +{ + return (c >= 'A' && c <= 'Z' ? (c - 'A') + 'a' : c); +} + +/** + * Converts the given string to its lowercase equivalent. + * This function is locale independent. It only converts uppercase + * characters in the standard 7-bit ASCII range. + * @param[in,out] str the string to convert to lowercase. + */ +void Downcase(std::string& str); + +/** + * Converts the given character to its uppercase equivalent. + * This function is locale independent. It only converts lowercase + * characters in the standard 7-bit ASCII range. + * @param[in] c the character to convert to uppercase. + * @return the uppercase equivalent of c; or the argument + * if no conversion is possible. + */ +constexpr unsigned char ToUpper(unsigned char c) +{ + return (c >= 'a' && c <= 'z' ? (c - 'a') + 'A' : c); +} + +/** + * Capitalizes the first character of the given string. + * This function is locale independent. It only capitalizes the + * first character of the argument if it has an uppercase equivalent + * in the standard 7-bit ASCII range. + * @param[in] str the string to capitalize. + * @return string with the first letter capitalized. + */ +std::string Capitalize(std::string str); + #endif // BITCOIN_UTILSTRENCODINGS_H diff --git a/src/validation.cpp b/src/validation.cpp index fceb135854..458458d85d 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -170,7 +170,7 @@ public: // Block (dis)connection on a given view: DisconnectResult DisconnectBlock(const CBlock& block, const CBlockIndex* pindex, CCoinsViewCache& view); bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pindex, - CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false); + CCoinsViewCache& view, const CChainParams& chainparams, bool fJustCheck = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); // Block disconnection on our pcoinsTip: bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions *disconnectpool); @@ -189,8 +189,8 @@ public: void UnloadBlockIndex(); private: - bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace); - bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool); + bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main); CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Create a new block index entry for a given block hash */ @@ -202,7 +202,7 @@ private: */ void CheckBlockIndex(const Consensus::Params& consensusParams); - void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state); + void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); CBlockIndex* FindMostWorkChain() EXCLUSIVE_LOCKS_REQUIRED(cs_main); void ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pindexNew, const CDiskBlockPos& pos, const Consensus::Params& consensusParams) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -217,8 +217,8 @@ CCriticalSection cs_main; BlockMap& mapBlockIndex = g_chainstate.mapBlockIndex; CChain& chainActive = g_chainstate.chainActive; CBlockIndex *pindexBestHeader = nullptr; -CWaitableCriticalSection g_best_block_mutex; -CConditionVariable g_best_block_cv; +Mutex g_best_block_mutex; +std::condition_variable g_best_block_cv; uint256 g_best_block; int nScriptCheckThreads = 0; std::atomic_bool fImporting(false); @@ -421,7 +421,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool // lock on a mempool input, so we can use the return value of // CheckSequenceLocks to indicate the LockPoints validity int maxInputHeight = 0; - for (int height : prevheights) { + for (const int height : prevheights) { // Can ignore mempool inputs since we'll fail if they had non-zero locks if (height != tip->nHeight+1) { maxInputHeight = std::max(maxInputHeight, height); @@ -457,7 +457,7 @@ std::string FormatStateMessage(const CValidationState &state) state.GetRejectCode()); } -static bool IsCurrentForFeeEstimation() +static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); if (IsInitialBlockDownload()) @@ -482,7 +482,7 @@ static bool IsCurrentForFeeEstimation() * and instead just erase from the mempool as needed. */ -static void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool fAddToMempool) +static void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); std::vector<uint256> vHashUpdate; @@ -524,7 +524,7 @@ static void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, // Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool // were somehow broken and returning the wrong scriptPubKeys static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& view, const CTxMemPool& pool, - unsigned int flags, bool cacheSigStore, PrecomputedTransactionData& txdata) { + unsigned int flags, bool cacheSigStore, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); // pool.cs should be locked already, but go ahead and re-take the lock here @@ -559,7 +559,7 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced, - bool bypass_limits, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache, bool test_accept) + bool bypass_limits, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { const CTransaction& tx = *ptx; const uint256 hash = tx.GetHash(); @@ -584,7 +584,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // Do not work on transactions that are too small. // A transaction with 1 segwit input and 1 P2WPHK output has non-witness size of 82 bytes. // Transactions smaller than this are not relayed to reduce unnecessary malloc overhead. - if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) < MIN_STANDARD_TX_NONWITNESS_SIZE) + if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) < MIN_STANDARD_TX_NONWITNESS_SIZE) return state.DoS(0, false, REJECT_NONSTANDARD, "tx-size-small"); // Only accept nLockTime-using transactions that can be mined in the next @@ -602,10 +602,8 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool std::set<uint256> setConflicts; for (const CTxIn &txin : tx.vin) { - auto itConflicting = pool.mapNextTx.find(txin.prevout); - if (itConflicting != pool.mapNextTx.end()) - { - const CTransaction *ptxConflicting = itConflicting->second; + const CTransaction* ptxConflicting = pool.GetConflictTx(txin.prevout); + if (ptxConflicting) { if (!setConflicts.count(ptxConflicting->GetHash())) { // Allow opt-out of transaction replacement by setting @@ -786,16 +784,8 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool CFeeRate newFeeRate(nModifiedFees, nSize); std::set<uint256> setConflictsParents; const int maxDescendantsToVisit = 100; - CTxMemPool::setEntries setIterConflicting; - for (const uint256 &hashConflicting : setConflicts) - { - CTxMemPool::txiter mi = pool.mapTx.find(hashConflicting); - if (mi == pool.mapTx.end()) - continue; - - // Save these to avoid repeated lookups - setIterConflicting.insert(mi); - + const CTxMemPool::setEntries setIterConflicting = pool.GetIterSet(setConflicts); + for (const auto& mi : setIterConflicting) { // Don't allow the replacement to reduce the feerate of the // mempool. // @@ -861,11 +851,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // Rather than check the UTXO set - potentially expensive - // it's cheaper to just check if the new input refers to a // tx that's in the mempool. - if (pool.mapTx.find(tx.vin[j].prevout.hash) != pool.mapTx.end()) + if (pool.exists(tx.vin[j].prevout.hash)) { return state.DoS(0, false, REJECT_NONSTANDARD, "replacement-adds-unconfirmed", false, strprintf("replacement %s adds unconfirmed input, idx %d", hash.ToString(), j)); + } } } @@ -959,7 +950,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool bool validForFeeEstimation = !fReplacementTransaction && !bypass_limits && IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); // Store transaction in memory - pool.addUnchecked(hash, entry, setAncestors, validForFeeEstimation); + pool.addUnchecked(entry, setAncestors, validForFeeEstimation); // trim mempool and check if tx was trimmed if (!bypass_limits) { @@ -977,7 +968,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool /** (try to) add transaction to memory pool with a specified acceptance time **/ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced, - bool bypass_limits, const CAmount nAbsurdFee, bool test_accept) + bool bypass_limits, const CAmount nAbsurdFee, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { std::vector<COutPoint> coins_to_uncache; bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept); @@ -1060,7 +1051,7 @@ static bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMes return error("WriteBlockToDisk: OpenBlockFile failed"); // Write index header - unsigned int nSize = GetSerializeSize(fileout, block); + unsigned int nSize = GetSerializeSize(block, fileout.GetVersion()); fileout << messageStart << nSize; // Write block @@ -1216,7 +1207,7 @@ static void AlertNotify(const std::string& strMessage) t.detach(); // thread runs free } -static void CheckForkWarningConditions() +static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); // Before we get past initial download, we cannot reliably alert about forks @@ -1257,7 +1248,7 @@ static void CheckForkWarningConditions() } } -static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) +static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); // If we are on a fork that is sufficiently large, set a warning flag @@ -1290,7 +1281,7 @@ static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) CheckForkWarningConditions(); } -void static InvalidChainFound(CBlockIndex* pindexNew) +void static InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) pindexBestInvalid = pindexNew; @@ -1377,7 +1368,7 @@ void InitScriptExecutionCache() { * * Non-static (and re-declared) in src/test/txvalidationcache_tests.cpp */ -bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks) +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector<CScriptCheck> *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { if (!tx.IsCoinBase()) { @@ -1470,7 +1461,7 @@ bool UndoWriteToDisk(const CBlockUndo& blockundo, CDiskBlockPos& pos, const uint return error("%s: OpenUndoFile failed", __func__); // Write index header - unsigned int nSize = GetSerializeSize(fileout, blockundo); + unsigned int nSize = GetSerializeSize(blockundo, fileout.GetVersion()); fileout << messageStart << nSize; // Write undo data @@ -1668,7 +1659,7 @@ static bool WriteUndoDataForBlock(const CBlockUndo& blockundo, CValidationState& // Write undo information to disk if (pindex->GetUndoPos().IsNull()) { CDiskBlockPos _pos; - if (!FindUndoPos(state, pindex->nFile, _pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) + if (!FindUndoPos(state, pindex->nFile, _pos, ::GetSerializeSize(blockundo, CLIENT_VERSION) + 40)) return error("ConnectBlock(): FindUndoPos failed"); if (!UndoWriteToDisk(blockundo, _pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart())) return AbortNode(state, "Failed to write undo data"); @@ -1743,7 +1734,7 @@ static bool IsScriptWitnessEnabled(const Consensus::Params& params) return params.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0; } -static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) { +static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); unsigned int flags = SCRIPT_VERIFY_NONE; @@ -2239,7 +2230,7 @@ void static UpdateTip(const CBlockIndex *pindexNew, const CChainParams& chainPar mempool.AddTransactionsUpdated(1); { - WaitableLock lock(g_best_block_mutex); + LOCK(g_best_block_mutex); g_best_block = pindexNew->GetBlockHash(); g_best_block_cv.notify_all(); } @@ -2448,7 +2439,7 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp if (!rv) { if (state.IsInvalid()) InvalidBlockFound(pindexNew, state); - return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); + return error("%s: ConnectBlock %s failed, %s", __func__, pindexNew->GetBlockHash().ToString(), FormatStateMessage(state)); } nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2; LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime3 - nTime2) * MILLI, nTimeConnectTotal * MICRO, nTimeConnectTotal * MILLI / nBlocksTotal); @@ -2863,6 +2854,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c } return true; } + bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) { return g_chainstate.InvalidateBlock(state, chainparams, pindex); } @@ -3118,7 +3110,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P // checks that use witness data may be performed here. // Size limits - if (block.vtx.empty() || block.vtx.size() * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT) + if (block.vtx.empty() || block.vtx.size() * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT || ::GetSerializeSize(block, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * WITNESS_SCALE_FACTOR > MAX_BLOCK_WEIGHT) return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed"); // First transaction must be coinbase, the rest must not be @@ -3130,7 +3122,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P // Check transactions for (const auto& tx : block.vtx) - if (!CheckTransaction(*tx, state, false)) + if (!CheckTransaction(*tx, state, true)) return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(), strprintf("Transaction check failed (tx hash %s) %s", tx->GetHash().ToString(), state.GetDebugMessage())); @@ -3435,7 +3427,7 @@ bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& headers, CValidatio /** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */ static CDiskBlockPos SaveBlockToDisk(const CBlock& block, int nHeight, const CChainParams& chainparams, const CDiskBlockPos* dbp) { - unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); + unsigned int nBlockSize = ::GetSerializeSize(block, CLIENT_VERSION); CDiskBlockPos blockPos; if (dbp != nullptr) blockPos = *dbp; @@ -4141,6 +4133,7 @@ bool CChainState::ReplayBlocks(const CChainParams& params, CCoinsView* view) for (int nHeight = nForkHeight + 1; nHeight <= pindexNew->nHeight; ++nHeight) { const CBlockIndex* pindex = pindexNew->GetAncestor(nHeight); LogPrintf("Rolling forward %s (%i)\n", pindex->GetBlockHash().ToString(), nHeight); + uiInterface.ShowProgress(_("Replaying blocks..."), (int) ((nHeight - nForkHeight) * 100.0 / (pindexNew->nHeight - nForkHeight)) , false); if (!RollforwardBlock(pindex, cache, params)) return false; } @@ -4290,7 +4283,7 @@ void UnloadBlockIndex() warningcache[b].clear(); } - for (BlockMap::value_type& entry : mapBlockIndex) { + for (const BlockMap::value_type& entry : mapBlockIndex) { delete entry.second; } mapBlockIndex.clear(); @@ -4491,7 +4484,7 @@ void CChainState::CheckBlockIndex(const Consensus::Params& consensusParams) // Build forward-pointing map of the entire block tree. std::multimap<CBlockIndex*,CBlockIndex*> forward; - for (auto& entry : mapBlockIndex) { + for (const std::pair<const uint256, CBlockIndex*>& entry : mapBlockIndex) { forward.insert(std::make_pair(entry.second->pprev, entry.second)); } @@ -4689,7 +4682,7 @@ int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::D static const uint64_t MEMPOOL_DUMP_VERSION = 1; -bool LoadMempool(void) +bool LoadMempool() { const CChainParams& chainparams = Params(); int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60; @@ -4766,7 +4759,7 @@ bool LoadMempool(void) return true; } -bool DumpMempool(void) +bool DumpMempool() { int64_t start = GetTimeMicros(); diff --git a/src/validation.h b/src/validation.h index 4965ff152a..1034ba4665 100644 --- a/src/validation.h +++ b/src/validation.h @@ -53,9 +53,9 @@ static const bool DEFAULT_WHITELISTFORCERELAY = true; /** Default for -minrelaytxfee, minimum relay fee for transactions */ static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000; //! -maxtxfee default -static const CAmount DEFAULT_TRANSACTION_MAXFEE = 0.1 * COIN; +static const CAmount DEFAULT_TRANSACTION_MAXFEE = COIN / 10; //! Discourage users to set fees higher than this amount (in satoshis) per kB -static const CAmount HIGH_TX_FEE_PER_KB = 0.01 * COIN; +static const CAmount HIGH_TX_FEE_PER_KB = COIN / 100; //! -maxtxfee will warn if called with a higher fee than this amount (in satoshis) static const CAmount HIGH_MAX_TX_FEE = 100 * HIGH_TX_FEE_PER_KB; /** Default for -limitancestorcount, max number of in-mempool ancestors */ @@ -151,8 +151,8 @@ extern BlockMap& mapBlockIndex; extern uint64_t nLastBlockTx; extern uint64_t nLastBlockWeight; extern const std::string strMessageMagic; -extern CWaitableCriticalSection g_best_block_mutex; -extern CConditionVariable g_best_block_cv; +extern Mutex g_best_block_mutex; +extern std::condition_variable g_best_block_cv; extern uint256 g_best_block; extern std::atomic_bool fImporting; extern std::atomic_bool fReindex; @@ -302,7 +302,7 @@ void PruneBlockFilesManual(int nManualPruneHeight); * plTxnReplaced will be appended to with all transactions replaced from mempool **/ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced, - bool bypass_limits, const CAmount nAbsurdFee, bool test_accept=false); + bool bypass_limits, const CAmount nAbsurdFee, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Convert CValidationState to a human-readable message for logging */ std::string FormatStateMessage(const CValidationState &state); @@ -329,12 +329,12 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight); * * See consensus/consensus.h for flag definitions. */ -bool CheckFinalTx(const CTransaction &tx, int flags = -1); +bool CheckFinalTx(const CTransaction &tx, int flags = -1) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** * Test whether the LockPoints height and time are still valid on the current chain */ -bool TestLockPointValidity(const LockPoints* lp); +bool TestLockPointValidity(const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** * Check if transaction will be BIP 68 final in the next block to be created. @@ -347,7 +347,7 @@ bool TestLockPointValidity(const LockPoints* lp); * * See consensus/consensus.h for flag definitions. */ -bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp = nullptr, bool useExistingLockPoints = false); +bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp = nullptr, bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** * Closure representing one script verification diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 248e774a8d..e25eca2368 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -7,7 +7,6 @@ #include <primitives/block.h> #include <scheduler.h> -#include <sync.h> #include <txmempool.h> #include <util.h> #include <validation.h> diff --git a/src/validationinterface.h b/src/validationinterface.h index 6e96755ea3..f0374e8e78 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -7,10 +7,12 @@ #define BITCOIN_VALIDATIONINTERFACE_H #include <primitives/transaction.h> // CTransaction(Ref) +#include <sync.h> #include <functional> #include <memory> +extern CCriticalSection cs_main; class CBlock; class CBlockIndex; struct CBlockLocator; @@ -51,7 +53,7 @@ void CallFunctionInValidationInterfaceQueue(std::function<void ()> func); * }); * promise.get_future().wait(); */ -void SyncWithValidationInterfaceQueue(); +void SyncWithValidationInterfaceQueue() LOCKS_EXCLUDED(cs_main); /** * Implement this to subscribe to events generated in validation diff --git a/src/versionbits.cpp b/src/versionbits.cpp index b0d2bc8a14..3f297c0ebb 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -5,21 +5,6 @@ #include <versionbits.h> #include <consensus/params.h> -const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = { - { - /*.name =*/ "testdummy", - /*.gbt_force =*/ true, - }, - { - /*.name =*/ "csv", - /*.gbt_force =*/ true, - }, - { - /*.name =*/ "segwit", - /*.gbt_force =*/ true, - } -}; - ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const { int nPeriod = Period(params); diff --git a/src/versionbits.h b/src/versionbits.h index e4bf9cb9be..cdc947cd9e 100644 --- a/src/versionbits.h +++ b/src/versionbits.h @@ -30,13 +30,6 @@ enum class ThresholdState { // will either be nullptr or a block with (height + 1) % Period() == 0. typedef std::map<const CBlockIndex*, ThresholdState> ThresholdConditionCache; -struct VBDeploymentInfo { - /** Deployment name */ - const char *name; - /** Whether GBT clients can safely ignore this rule in simplified usage */ - bool gbt_force; -}; - struct BIP9Stats { int period; int threshold; @@ -45,8 +38,6 @@ struct BIP9Stats { bool possible; }; -extern const struct VBDeploymentInfo VersionBitsDeploymentInfo[]; - /** * Abstract class that implements BIP9-style threshold logic, and caches results. */ diff --git a/src/versionbitsinfo.cpp b/src/versionbitsinfo.cpp new file mode 100644 index 0000000000..ecf3482927 --- /dev/null +++ b/src/versionbitsinfo.cpp @@ -0,0 +1,22 @@ +// Copyright (c) 2016-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <versionbitsinfo.h> + +#include <consensus/params.h> + +const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = { + { + /*.name =*/ "testdummy", + /*.gbt_force =*/ true, + }, + { + /*.name =*/ "csv", + /*.gbt_force =*/ true, + }, + { + /*.name =*/ "segwit", + /*.gbt_force =*/ true, + } +}; diff --git a/src/versionbitsinfo.h b/src/versionbitsinfo.h new file mode 100644 index 0000000000..a7822bc747 --- /dev/null +++ b/src/versionbitsinfo.h @@ -0,0 +1,17 @@ +// Copyright (c) 2016-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_VERSIONBITSINFO_H +#define BITCOIN_VERSIONBITSINFO_H + +struct VBDeploymentInfo { + /** Deployment name */ + const char *name; + /** Whether GBT clients can safely ignore this rule in simplified usage */ + bool gbt_force; +}; + +extern const struct VBDeploymentInfo VersionBitsDeploymentInfo[]; + +#endif // BITCOIN_VERSIONBITSINFO_H diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp index 3734ba66b6..fdeb89553b 100644 --- a/src/wallet/coinselection.cpp +++ b/src/wallet/coinselection.cpp @@ -3,9 +3,12 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <wallet/coinselection.h> + #include <util.h> #include <utilmoneystr.h> +#include <boost/optional.hpp> + // Descending order comparator struct { bool operator()(const OutputGroup& a, const OutputGroup& b) const diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 4fa9603011..729e4e39b0 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -311,7 +311,7 @@ bool CCryptoKeyStore::EncryptKeys(CKeyingMaterial& vMasterKeyIn) return false; fUseCrypto = true; - for (KeyMap::value_type& mKey : mapKeys) + for (const KeyMap::value_type& mKey : mapKeys) { const CKey &key = mKey.second; CPubKey vchPubKey = key.GetPubKey(); diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index d0fe51801e..a7bf89c572 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -72,7 +72,7 @@ BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& data database_filename = "wallet.dat"; } LOCK(cs_db); - // Note: An ununsed temporary BerkeleyEnvironment object may be created inside the + // Note: An unused temporary BerkeleyEnvironment object may be created inside the // emplace function if the key already exists. This is a little inefficient, // but not a big concern since the map will be changed in the future to hold // pointers instead of objects, anyway. @@ -503,7 +503,7 @@ BerkeleyBatch::BerkeleyBatch(BerkeleyDatabase& database, const char* pszMode, bo // be implemented, so no equality checks are needed at all. (Newer // versions of BDB have an set_lk_exclusive method for this // purpose, but the older version we use does not.) - for (auto& env : g_dbenvs) { + for (const auto& env : g_dbenvs) { CheckUniqueFileid(env.second, strFilename, *pdb_temp); } @@ -556,6 +556,7 @@ void BerkeleyBatch::Close() LOCK(cs_db); --env->mapFileUseCount[strFile]; } + env->m_db_in_use.notify_all(); } void BerkeleyEnvironment::CloseDb(const std::string& strFile) @@ -572,6 +573,32 @@ void BerkeleyEnvironment::CloseDb(const std::string& strFile) } } +void BerkeleyEnvironment::ReloadDbEnv() +{ + // Make sure that no Db's are in use + AssertLockNotHeld(cs_db); + std::unique_lock<CCriticalSection> lock(cs_db); + m_db_in_use.wait(lock, [this](){ + for (auto& count : mapFileUseCount) { + if (count.second > 0) return false; + } + return true; + }); + + std::vector<std::string> filenames; + for (auto it : mapDb) { + filenames.push_back(it.first); + } + // Close the individual Db's + for (const std::string& filename : filenames) { + CloseDb(filename); + } + // Reset the environment + Flush(true); // This will flush and close the environment + Reset(); + Open(true); +} + bool BerkeleyBatch::Rewrite(BerkeleyDatabase& database, const char* pszSkip) { if (database.IsDummy()) { @@ -697,7 +724,6 @@ void BerkeleyEnvironment::Flush(bool fShutdown) if (!fMockDb) { fs::remove_all(fs::path(strPath) / "database"); } - g_dbenvs.erase(strPath); } } } @@ -783,7 +809,7 @@ bool BerkeleyDatabase::Backup(const std::string& strDest) LogPrintf("copied %s to %s\n", strFile, pathDest.string()); return true; } catch (const fs::filesystem_error& e) { - LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), e.what()); + LogPrintf("error copying %s to %s - %s\n", strFile, pathDest.string(), fsbridge::get_filesystem_error_message(e)); return false; } } @@ -796,6 +822,17 @@ void BerkeleyDatabase::Flush(bool shutdown) { if (!IsDummy()) { env->Flush(shutdown); - if (shutdown) env = nullptr; + if (shutdown) { + LOCK(cs_db); + g_dbenvs.erase(env->Directory().string()); + env = nullptr; + } + } +} + +void BerkeleyDatabase::ReloadDbEnv() +{ + if (!IsDummy()) { + env->ReloadDbEnv(); } } diff --git a/src/wallet/db.h b/src/wallet/db.h index b078edab7b..467ed13b45 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -38,6 +38,7 @@ public: std::unique_ptr<DbEnv> dbenv; std::map<std::string, int> mapFileUseCount; std::map<std::string, Db*> mapDb; + std::condition_variable_any m_db_in_use; BerkeleyEnvironment(const fs::path& env_directory); ~BerkeleyEnvironment(); @@ -75,6 +76,7 @@ public: void CheckpointLSN(const std::string& strFile); void CloseDb(const std::string& strFile); + void ReloadDbEnv(); DbTxn* TxnBegin(int flags = DB_TXN_WRITE_NOSYNC) { @@ -145,6 +147,8 @@ public: void IncrementUpdateCounter(); + void ReloadDbEnv(); + std::atomic<unsigned int> nUpdateCounter; unsigned int nLastSeen; unsigned int nLastFlushed; diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index d5d4021305..677bf74b5d 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -18,7 +18,7 @@ //! Check whether transaction has descendant in wallet or mempool, or has been //! mined, or conflicts with a mined transaction. Return a feebumper::Result. -static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet) +static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors) EXCLUSIVE_LOCKS_REQUIRED(cs_main, wallet->cs_wallet) { if (wallet->HasWalletSpend(wtx.GetHash())) { errors.push_back("Transaction has descendants in the wallet"); @@ -242,7 +242,7 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti CReserveKey reservekey(wallet); CValidationState state; - if (!wallet->CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm, oldWtx.strFromAccount, reservekey, g_connman.get(), state)) { + if (!wallet->CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm, reservekey, g_connman.get(), state)) { // NOTE: CommitTransaction never returns false, so this should never happen. errors.push_back(strprintf("The transaction was rejected: %s", FormatStateMessage(state))); return Result::WALLET_ERROR; diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index b9f267210e..a299a4ee44 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -19,6 +19,9 @@ class WalletInit : public WalletInitInterface { public: + //! Was the wallet component compiled in. + bool HasWalletSupport() const override {return true;} + //! Return the wallets help message. void AddWalletOptions() const override; @@ -78,7 +81,7 @@ void WalletInit::AddWalletOptions() const gArgs.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)", false, OptionsCategory::WALLET); gArgs.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), false, OptionsCategory::WALLET); gArgs.AddArg("-zapwallettxes=<mode>", "Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup" - " (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)", false, OptionsCategory::WALLET); + " (1 = keep tx meta data e.g. payment request information, 2 = drop tx meta data)", false, OptionsCategory::WALLET); gArgs.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), true, OptionsCategory::WALLET_DEBUG_TEST); gArgs.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), true, OptionsCategory::WALLET_DEBUG_TEST); diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 7cb41da39e..c97bc38e6f 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -41,7 +41,7 @@ int64_t static DecodeDumpTime(const std::string &str) { std::string static EncodeDumpString(const std::string &str) { std::stringstream ret; - for (unsigned char c : str) { + for (const unsigned char c : str) { if (c <= 32 || c >= 128 || c == '%') { ret << '%' << HexStr(&c, &c + 1); } else { @@ -415,8 +415,7 @@ UniValue removeprunedfunds(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - uint256 hash; - hash.SetHex(request.params[0].get_str()); + uint256 hash(ParseHashV(request.params[0], "txid")); std::vector<uint256> vHash; vHash.push_back(hash); std::vector<uint256> vHashOut; @@ -849,6 +848,9 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con std::vector<unsigned char> vData(ParseHex(output)); script = CScript(vData.begin(), vData.end()); + if (!ExtractDestination(script, dest) && !internal) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set to true for nonstandard scriptPubKey imports."); + } } // Watchonly and private keys @@ -861,11 +863,6 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con throw JSONRPCError(RPC_INVALID_PARAMETER, "Incompatibility found between internal and label"); } - // Not having Internal + Script - if (!internal && isScript) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Internal must be set for hex scriptPubKey"); - } - // Keys / PubKeys size check. if (!isP2SH && (keys.size() > 1 || pubKeys.size() > 1)) { // Address / scriptPubKey throw JSONRPCError(RPC_INVALID_PARAMETER, "More than private key given for one address"); @@ -969,21 +966,10 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con CTxDestination pubkey_dest = pubKey.GetID(); // Consistency check. - if (!isScript && !(pubkey_dest == dest)) { + if (!(pubkey_dest == dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } - // Consistency check. - if (isScript) { - CTxDestination destination; - - if (ExtractDestination(script, destination)) { - if (!(destination == pubkey_dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); - } - } - } - CScript pubKeyScript = GetScriptForDestination(pubkey_dest); if (::IsMine(*pwallet, pubKeyScript) == ISMINE_SPENDABLE) { @@ -1034,21 +1020,10 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con CTxDestination pubkey_dest = pubKey.GetID(); // Consistency check. - if (!isScript && !(pubkey_dest == dest)) { + if (!(pubkey_dest == dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } - // Consistency check. - if (isScript) { - CTxDestination destination; - - if (ExtractDestination(script, destination)) { - if (!(destination == pubkey_dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); - } - } - } - CKeyID vchAddress = pubKey.GetID(); pwallet->MarkDirty(); pwallet->SetAddressBook(vchAddress, label, "receive"); @@ -1080,11 +1055,9 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con throw JSONRPCError(RPC_WALLET_ERROR, "Error adding address to wallet"); } - if (scriptPubKey.getType() == UniValue::VOBJ) { - // add to address book or update label - if (IsValidDestination(dest)) { - pwallet->SetAddressBook(dest, label, "receive"); - } + // add to address book or update label + if (IsValidDestination(dest)) { + pwallet->SetAddressBook(dest, label, "receive"); } success = true; @@ -1150,7 +1123,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) " \"keys\": [\"<key>\", ... ] , (array, optional) Array of strings giving private keys whose corresponding public keys must occur in the output or redeemscript\n" " \"internal\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be treated as not incoming payments\n" " \"watchonly\": <true> , (boolean, optional, default: false) Stating whether matching outputs should be considered watched even when they're not spendable, only allowed if keys are empty\n" - " \"label\": <label> , (string, optional, default: '') Label to assign to the address (aka account name, for now), only allowed with internal=false\n" + " \"label\": <label> , (string, optional, default: '') Label to assign to the address, only allowed with internal=false\n" " }\n" " ,...\n" " ]\n" diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index b108d6df53..7d0219201e 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -89,7 +89,7 @@ void EnsureWalletIsUnlocked(CWallet * const pwallet) } } -static void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) +static void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { int confirms = wtx.GetDepthInMainChain(); entry.pushKV("confirmations", confirms); @@ -165,7 +165,7 @@ static UniValue getnewaddress(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet"); } - LOCK2(cs_main, pwallet->cs_wallet); + LOCK(pwallet->cs_wallet); // Parse the label first so we don't generate a key if there's an error std::string label; @@ -196,59 +196,6 @@ static UniValue getnewaddress(const JSONRPCRequest& request) return EncodeDestination(dest); } -CTxDestination GetLabelDestination(CWallet* const pwallet, const std::string& label, bool bForceNew=false) -{ - CTxDestination dest; - if (!pwallet->GetLabelDestination(dest, label, bForceNew)) { - throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); - } - - return dest; -} - -static UniValue getaccountaddress(const JSONRPCRequest& request) -{ - std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); - CWallet* const pwallet = wallet.get(); - - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (!IsDeprecatedRPCEnabled("accounts")) { - if (request.fHelp) { - throw std::runtime_error("getaccountaddress (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); - } - throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaccountaddress is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); - } - - if (request.fHelp || request.params.size() != 1) - throw std::runtime_error( - "getaccountaddress \"account\"\n" - "\n\nDEPRECATED. Returns the current Bitcoin address for receiving payments to this account.\n" - "\nArguments:\n" - "1. \"account\" (string, required) The account for the address. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created and a new address created if there is no account by the given name.\n" - "\nResult:\n" - "\"address\" (string) The account bitcoin address\n" - "\nExamples:\n" - + HelpExampleCli("getaccountaddress", "") - + HelpExampleCli("getaccountaddress", "\"\"") - + HelpExampleCli("getaccountaddress", "\"myaccount\"") - + HelpExampleRpc("getaccountaddress", "\"myaccount\"") - ); - - LOCK2(cs_main, pwallet->cs_wallet); - - // Parse the account first so we don't generate a key if there's an error - std::string account = LabelFromValue(request.params[0]); - - UniValue ret(UniValue::VSTR); - - ret = EncodeDestination(GetLabelDestination(pwallet, account)); - return ret; -} - - static UniValue getrawchangeaddress(const JSONRPCRequest& request) { std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); @@ -276,7 +223,7 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_ERROR, "Error: Private keys are disabled for this wallet"); } - LOCK2(cs_main, pwallet->cs_wallet); + LOCK(pwallet->cs_wallet); if (!pwallet->IsLocked()) { pwallet->TopUpKeyPool(); @@ -312,13 +259,6 @@ static UniValue setlabel(const JSONRPCRequest& request) return NullUniValue; } - if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "setaccount") { - if (request.fHelp) { - throw std::runtime_error("setaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); - } - throw JSONRPCError(RPC_METHOD_DEPRECATED, "setaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); - } - if (request.fHelp || request.params.size() != 2) throw std::runtime_error( "setlabel \"address\" \"label\"\n" @@ -331,138 +271,26 @@ static UniValue setlabel(const JSONRPCRequest& request) + HelpExampleRpc("setlabel", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\"") ); - LOCK2(cs_main, pwallet->cs_wallet); + LOCK(pwallet->cs_wallet); CTxDestination dest = DecodeDestination(request.params[0].get_str()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); } - std::string old_label = pwallet->mapAddressBook[dest].name; std::string label = LabelFromValue(request.params[1]); if (IsMine(*pwallet, dest)) { pwallet->SetAddressBook(dest, label, "receive"); - if (request.strMethod == "setaccount" && old_label != label && dest == GetLabelDestination(pwallet, old_label)) { - // for setaccount, call GetLabelDestination so a new receive address is created for the old account - GetLabelDestination(pwallet, old_label, true); - } } else { pwallet->SetAddressBook(dest, label, "send"); } - // Detect when there are no addresses using this label. - // If so, delete the account record for it. Labels, unlike addresses, can be deleted, - // and if we wouldn't do this, the record would stick around forever. - bool found_address = false; - for (const std::pair<const CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) { - if (item.second.name == label) { - found_address = true; - break; - } - } - if (!found_address) { - pwallet->DeleteLabel(old_label); - } - return NullUniValue; } -static UniValue getaccount(const JSONRPCRequest& request) -{ - std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); - CWallet* const pwallet = wallet.get(); - - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (!IsDeprecatedRPCEnabled("accounts")) { - if (request.fHelp) { - throw std::runtime_error("getaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); - } - throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); - } - - if (request.fHelp || request.params.size() != 1) - throw std::runtime_error( - "getaccount \"address\"\n" - "\nDEPRECATED. Returns the account associated with the given address.\n" - "\nArguments:\n" - "1. \"address\" (string, required) The bitcoin address for account lookup.\n" - "\nResult:\n" - "\"accountname\" (string) the account address\n" - "\nExamples:\n" - + HelpExampleCli("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") - + HelpExampleRpc("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") - ); - - LOCK2(cs_main, pwallet->cs_wallet); - - CTxDestination dest = DecodeDestination(request.params[0].get_str()); - if (!IsValidDestination(dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); - } - - std::string strAccount; - std::map<CTxDestination, CAddressBookData>::iterator mi = pwallet->mapAddressBook.find(dest); - if (mi != pwallet->mapAddressBook.end() && !(*mi).second.name.empty()) { - strAccount = (*mi).second.name; - } - return strAccount; -} - - -static UniValue getaddressesbyaccount(const JSONRPCRequest& request) -{ - std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); - CWallet* const pwallet = wallet.get(); - - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (!IsDeprecatedRPCEnabled("accounts")) { - if (request.fHelp) { - throw std::runtime_error("getaddressbyaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); - } - throw JSONRPCError(RPC_METHOD_DEPRECATED, "getaddressesbyaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); - } - - if (request.fHelp || request.params.size() != 1) - throw std::runtime_error( - "getaddressesbyaccount \"account\"\n" - "\nDEPRECATED. Returns the list of addresses for the given account.\n" - "\nArguments:\n" - "1. \"account\" (string, required) The account name.\n" - "\nResult:\n" - "[ (json array of string)\n" - " \"address\" (string) a bitcoin address associated with the given account\n" - " ,...\n" - "]\n" - "\nExamples:\n" - + HelpExampleCli("getaddressesbyaccount", "\"tabby\"") - + HelpExampleRpc("getaddressesbyaccount", "\"tabby\"") - ); - - LOCK2(cs_main, pwallet->cs_wallet); - - std::string strAccount = LabelFromValue(request.params[0]); - - // Find all addresses that have the given account - UniValue ret(UniValue::VARR); - for (const std::pair<const CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) { - const CTxDestination& dest = item.first; - const std::string& strName = item.second.name; - if (strName == strAccount) { - ret.push_back(EncodeDestination(dest)); - } - } - return ret; -} - -static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue, std::string fromAccount) +static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, const CCoinControl& coin_control, mapValue_t mapValue) { CAmount curBalance = pwallet->GetBalance(); @@ -495,7 +323,7 @@ static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination & throw JSONRPCError(RPC_WALLET_ERROR, strError); } CValidationState state; - if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, std::move(fromAccount), reservekey, g_connman.get(), state)) { + if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, reservekey, g_connman.get(), state)) { strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state)); throw JSONRPCError(RPC_WALLET_ERROR, strError); } @@ -587,7 +415,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); - CTransactionRef tx = SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, std::move(mapValue), {} /* fromAccount */); + CTransactionRef tx = SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, std::move(mapValue)); return tx->GetHash().GetHex(); } @@ -789,13 +617,6 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request) return NullUniValue; } - if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "getreceivedbyaccount") { - if (request.fHelp) { - throw std::runtime_error("getreceivedbyaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); - } - throw JSONRPCError(RPC_METHOD_DEPRECATED, "getreceivedbyaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); - } - if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) throw std::runtime_error( "getreceivedbylabel \"label\" ( minconf )\n" @@ -863,41 +684,16 @@ static UniValue getbalance(const JSONRPCRequest& request) if (request.fHelp || (request.params.size() > 3 )) throw std::runtime_error( - (IsDeprecatedRPCEnabled("accounts") ? std::string( - "getbalance ( \"account\" minconf include_watchonly )\n" - "\nIf account is not specified, returns the server's total available balance.\n" - "The available balance is what the wallet considers currently spendable, and is\n" - "thus affected by options which limit spendability such as -spendzeroconfchange.\n" - "If account is specified (DEPRECATED), returns the balance in the account.\n" - "Note that the account \"\" is not the same as leaving the parameter out.\n" - "The server total may be different to the balance in the default \"\" account.\n" - "\nArguments:\n" - "1. \"account\" (string, optional) DEPRECATED. This argument will be removed in V0.18. \n" - " To use this deprecated argument, start bitcoind with -deprecatedrpc=accounts. The account string may be given as a\n" - " specific account name to find the balance associated with wallet keys in\n" - " a named account, or as the empty string (\"\") to find the balance\n" - " associated with wallet keys not in any named account, or as \"*\" to find\n" - " the balance associated with all wallet keys regardless of account.\n" - " When this option is specified, it calculates the balance in a different\n" - " way than when it is not specified, and which can count spends twice when\n" - " there are conflicting pending transactions (such as those created by\n" - " the bumpfee command), temporarily resulting in low or even negative\n" - " balances. In general, account balance calculation is not considered\n" - " reliable and has resulted in confusing outcomes, so it is recommended to\n" - " avoid passing this argument.\n" - "2. minconf (numeric, optional) Only include transactions confirmed at least this many times. \n" - " The default is 1 if an account is provided or 0 if no account is provided\n") - : std::string( "getbalance ( \"(dummy)\" minconf include_watchonly )\n" "\nReturns the total available balance.\n" "The available balance is what the wallet considers currently spendable, and is\n" "thus affected by options which limit spendability such as -spendzeroconfchange.\n" "\nArguments:\n" "1. (dummy) (string, optional) Remains for backward compatibility. Must be excluded or set to \"*\".\n" - "2. minconf (numeric, optional, default=0) Only include transactions confirmed at least this many times.\n")) + + "2. minconf (numeric, optional, default=0) Only include transactions confirmed at least this many times.\n" "3. include_watchonly (bool, optional, default=false) Also include balance in watch-only addresses (see 'importaddress')\n" "\nResult:\n" - "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" + "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this wallet.\n" "\nExamples:\n" "\nThe total amount in the wallet with 1 or more confirmations\n" + HelpExampleCli("getbalance", "") + @@ -913,13 +709,12 @@ static UniValue getbalance(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - const UniValue& account_value = request.params[0]; + const UniValue& dummy_value = request.params[0]; + if (!dummy_value.isNull() && dummy_value.get_str() != "*") { + throw JSONRPCError(RPC_METHOD_DEPRECATED, "dummy first argument must be excluded or set to \"*\"."); + } int min_depth = 0; - if (IsDeprecatedRPCEnabled("accounts") && !account_value.isNull()) { - // Default min_depth to 1 when an account is provided. - min_depth = 1; - } if (!request.params[1].isNull()) { min_depth = request.params[1].get_int(); } @@ -929,18 +724,6 @@ static UniValue getbalance(const JSONRPCRequest& request) filter = filter | ISMINE_WATCH_ONLY; } - if (!account_value.isNull()) { - - const std::string& account_param = account_value.get_str(); - const std::string* account = account_param != "*" ? &account_param : nullptr; - - if (!IsDeprecatedRPCEnabled("accounts") && account_param != "*") { - throw JSONRPCError(RPC_METHOD_DEPRECATED, "dummy first argument must be excluded or set to \"*\"."); - } else if (IsDeprecatedRPCEnabled("accounts")) { - return ValueFromAmount(pwallet->GetLegacyBalance(filter, min_depth, account)); - } - } - return ValueFromAmount(pwallet->GetBalance(filter, min_depth)); } @@ -968,148 +751,6 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request) } -static UniValue movecmd(const JSONRPCRequest& request) -{ - std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); - CWallet* const pwallet = wallet.get(); - - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (!IsDeprecatedRPCEnabled("accounts")) { - if (request.fHelp) { - throw std::runtime_error("move (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); - } - throw JSONRPCError(RPC_METHOD_DEPRECATED, "move is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); - } - - if (request.fHelp || request.params.size() < 3 || request.params.size() > 5) - throw std::runtime_error( - "move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" )\n" - "\nDEPRECATED. Move a specified amount from one account in your wallet to another.\n" - "\nArguments:\n" - "1. \"fromaccount\" (string, required) The name of the account to move funds from. May be the default account using \"\".\n" - "2. \"toaccount\" (string, required) The name of the account to move funds to. May be the default account using \"\".\n" - "3. amount (numeric) Quantity of " + CURRENCY_UNIT + " to move between accounts.\n" - "4. (dummy) (numeric, optional) Ignored. Remains for backward compatibility.\n" - "5. \"comment\" (string, optional) An optional comment, stored in the wallet only.\n" - "\nResult:\n" - "true|false (boolean) true if successful.\n" - "\nExamples:\n" - "\nMove 0.01 " + CURRENCY_UNIT + " from the default account to the account named tabby\n" - + HelpExampleCli("move", "\"\" \"tabby\" 0.01") + - "\nMove 0.01 " + CURRENCY_UNIT + " timotei to akiko with a comment and funds have 6 confirmations\n" - + HelpExampleCli("move", "\"timotei\" \"akiko\" 0.01 6 \"happy birthday!\"") + - "\nAs a json rpc call\n" - + HelpExampleRpc("move", "\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\"") - ); - - LOCK2(cs_main, pwallet->cs_wallet); - - std::string strFrom = LabelFromValue(request.params[0]); - std::string strTo = LabelFromValue(request.params[1]); - CAmount nAmount = AmountFromValue(request.params[2]); - if (nAmount <= 0) - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); - if (!request.params[3].isNull()) - // unused parameter, used to be nMinDepth, keep type-checking it though - (void)request.params[3].get_int(); - std::string strComment; - if (!request.params[4].isNull()) - strComment = request.params[4].get_str(); - - if (!pwallet->AccountMove(strFrom, strTo, nAmount, strComment)) { - throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); - } - - return true; -} - - -static UniValue sendfrom(const JSONRPCRequest& request) -{ - std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); - CWallet* const pwallet = wallet.get(); - - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (!IsDeprecatedRPCEnabled("accounts")) { - if (request.fHelp) { - throw std::runtime_error("sendfrom (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); - } - throw JSONRPCError(RPC_METHOD_DEPRECATED, "sendfrom is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); - } - - - if (request.fHelp || request.params.size() < 3 || request.params.size() > 6) - throw std::runtime_error( - "sendfrom \"fromaccount\" \"toaddress\" amount ( minconf \"comment\" \"comment_to\" )\n" - "\nDEPRECATED (use sendtoaddress). Sent an amount from an account to a bitcoin address." - + HelpRequiringPassphrase(pwallet) + "\n" - "\nArguments:\n" - "1. \"fromaccount\" (string, required) The name of the account to send funds from. May be the default account using \"\".\n" - " Specifying an account does not influence coin selection, but it does associate the newly created\n" - " transaction with the account, so the account's balance computation and transaction history can reflect\n" - " the spend.\n" - "2. \"toaddress\" (string, required) The bitcoin address to send funds to.\n" - "3. amount (numeric or string, required) The amount in " + CURRENCY_UNIT + " (transaction fee is added on top).\n" - "4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" - "5. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" - " This is not part of the transaction, just kept in your wallet.\n" - "6. \"comment_to\" (string, optional) An optional comment to store the name of the person or organization \n" - " to which you're sending the transaction. This is not part of the transaction, \n" - " it is just kept in your wallet.\n" - "\nResult:\n" - "\"txid\" (string) The transaction id.\n" - "\nExamples:\n" - "\nSend 0.01 " + CURRENCY_UNIT + " from the default account to the address, must have at least 1 confirmation\n" - + HelpExampleCli("sendfrom", "\"\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01") + - "\nSend 0.01 from the tabby account to the given address, funds must have at least 6 confirmations\n" - + HelpExampleCli("sendfrom", "\"tabby\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01 6 \"donation\" \"seans outpost\"") + - "\nAs a json rpc call\n" - + HelpExampleRpc("sendfrom", "\"tabby\", \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.01, 6, \"donation\", \"seans outpost\"") - ); - - // Make sure the results are valid at least up to the most recent block - // the user could have gotten from another RPC command prior to now - pwallet->BlockUntilSyncedToCurrentChain(); - - LOCK2(cs_main, pwallet->cs_wallet); - - std::string strAccount = LabelFromValue(request.params[0]); - CTxDestination dest = DecodeDestination(request.params[1].get_str()); - if (!IsValidDestination(dest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); - } - CAmount nAmount = AmountFromValue(request.params[2]); - if (nAmount <= 0) - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); - int nMinDepth = 1; - if (!request.params[3].isNull()) - nMinDepth = request.params[3].get_int(); - - mapValue_t mapValue; - if (!request.params[4].isNull() && !request.params[4].get_str().empty()) - mapValue["comment"] = request.params[4].get_str(); - if (!request.params[5].isNull() && !request.params[5].get_str().empty()) - mapValue["to"] = request.params[5].get_str(); - - EnsureWalletIsUnlocked(pwallet); - - // Check funds - CAmount nBalance = pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount); - if (nAmount > nBalance) - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); - - CCoinControl no_coin_control; // This is a deprecated API - CTransactionRef tx = SendMoney(pwallet, dest, nAmount, false, no_coin_control, std::move(mapValue), std::move(strAccount)); - return tx->GetHash().GetHex(); -} - - static UniValue sendmany(const JSONRPCRequest& request) { std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); @@ -1119,12 +760,10 @@ static UniValue sendmany(const JSONRPCRequest& request) return NullUniValue; } - std::string help_text; - if (!IsDeprecatedRPCEnabled("accounts")) { - help_text = "sendmany \"\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n" + if (request.fHelp || request.params.size() < 2 || request.params.size() > 8) + throw std::runtime_error( + "sendmany \"\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n" "\nSend multiple times. Amounts are double-precision floating point numbers.\n" - "Note that the \"fromaccount\" argument has been removed in V0.17. To use this RPC with a \"fromaccount\" argument, restart\n" - "bitcoind with -deprecatedrpc=accounts\n" + HelpRequiringPassphrase(pwallet) + "\n" "\nArguments:\n" "1. \"dummy\" (string, required) Must be set to \"\" for backwards compatibility.\n" @@ -1160,49 +799,8 @@ static UniValue sendmany(const JSONRPCRequest& request) "\nSend two amounts to two different addresses, subtract fee from amount:\n" + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") + "\nAs a json rpc call\n" - + HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\""); - } else { - help_text = "sendmany \"\" \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] replaceable conf_target \"estimate_mode\")\n" - "\nSend multiple times. Amounts are double-precision floating point numbers." - + HelpRequiringPassphrase(pwallet) + "\n" - "\nArguments:\n" - "1. \"fromaccount\" (string, required) DEPRECATED. The account to send the funds from. Should be \"\" for the default account\n" - "2. \"amounts\" (string, required) A json object with addresses and amounts\n" - " {\n" - " \"address\":amount (numeric or string) The bitcoin address is the key, the numeric amount (can be string) in " + CURRENCY_UNIT + " is the value\n" - " ,...\n" - " }\n" - "3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n" - "4. \"comment\" (string, optional) A comment\n" - "5. subtractfeefrom (array, optional) A json array with addresses.\n" - " The fee will be equally deducted from the amount of each selected address.\n" - " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n" - " If no addresses are specified here, the sender pays the fee.\n" - " [\n" - " \"address\" (string) Subtract fee from this address\n" - " ,...\n" - " ]\n" - "6. replaceable (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees via BIP 125\n" - "7. conf_target (numeric, optional) Confirmation target (in blocks)\n" - "8. \"estimate_mode\" (string, optional, default=UNSET) The fee estimate mode, must be one of:\n" - " \"UNSET\"\n" - " \"ECONOMICAL\"\n" - " \"CONSERVATIVE\"\n" - "\nResult:\n" - "\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" - " the number of addresses.\n" - "\nExamples:\n" - "\nSend two amounts to two different addresses:\n" - + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + - "\nSend two amounts to two different addresses setting the confirmation and comment:\n" - + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") + - "\nSend two amounts to two different addresses, subtract fee from amount:\n" - + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") + - "\nAs a json rpc call\n" - + HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\""); - } - - if (request.fHelp || request.params.size() < 2 || request.params.size() > 8) throw std::runtime_error(help_text); + + HelpExampleRpc("sendmany", "\"\", {\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\":0.01,\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\":0.02}, 6, \"testing\"") + ); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now @@ -1214,10 +812,9 @@ static UniValue sendmany(const JSONRPCRequest& request) throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } - if (!IsDeprecatedRPCEnabled("accounts") && !request.params[0].get_str().empty()) { + if (!request.params[0].isNull() && !request.params[0].get_str().empty()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"\""); } - std::string strAccount = LabelFromValue(request.params[0]); UniValue sendTo = request.params[1].get_obj(); int nMinDepth = 1; if (!request.params[2].isNull()) @@ -1282,9 +879,7 @@ static UniValue sendmany(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); // Check funds - if (IsDeprecatedRPCEnabled("accounts") && totalAmount > pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount)) { - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); - } else if (!IsDeprecatedRPCEnabled("accounts") && totalAmount > pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, nullptr)) { + if (totalAmount > pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth)) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Wallet has insufficient funds"); } @@ -1301,7 +896,7 @@ static UniValue sendmany(const JSONRPCRequest& request) if (!fCreated) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); CValidationState state; - if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, std::move(strAccount), keyChange, g_connman.get(), state)) { + if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, keyChange, g_connman.get(), state)) { strFailReason = strprintf("Transaction commit failed:: %s", FormatStateMessage(state)); throw JSONRPCError(RPC_WALLET_ERROR, strFailReason); } @@ -1526,7 +1121,7 @@ struct tallyitem } }; -static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool by_label) +static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { // Minimum confirmations int nMinDepth = 1; @@ -1635,7 +1230,6 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo if(fIsWatchonly) obj.pushKV("involvesWatchonly", true); obj.pushKV("address", EncodeDestination(address)); - obj.pushKV("account", label); obj.pushKV("amount", ValueFromAmount(nAmount)); obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)); obj.pushKV("label", label); @@ -1661,7 +1255,6 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo UniValue obj(UniValue::VOBJ); if (entry.second.fIsWatchonly) obj.pushKV("involvesWatchonly", true); - obj.pushKV("account", entry.first); obj.pushKV("amount", ValueFromAmount(nAmount)); obj.pushKV("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf)); obj.pushKV("label", entry.first); @@ -1695,7 +1288,6 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request) " {\n" " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" " \"address\" : \"receivingaddress\", (string) The receiving address\n" - " \"account\" : \"accountname\", (string) DEPRECATED. Backwards compatible alias for label.\n" " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n" " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n" " \"label\" : \"label\", (string) The label of the receiving address. The default label is \"\".\n" @@ -1732,13 +1324,6 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request) return NullUniValue; } - if (!IsDeprecatedRPCEnabled("accounts") && request.strMethod == "listreceivedbyaccount") { - if (request.fHelp) { - throw std::runtime_error("listreceivedbyaccount (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); - } - throw JSONRPCError(RPC_METHOD_DEPRECATED, "listreceivedbyaccount is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); - } - if (request.fHelp || request.params.size() > 3) throw std::runtime_error( "listreceivedbylabel ( minconf include_empty include_watchonly)\n" @@ -1752,7 +1337,6 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request) "[\n" " {\n" " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" - " \"account\" : \"accountname\", (string) DEPRECATED. Backwards compatible alias for label.\n" " \"amount\" : x.xxx, (numeric) The total amount received by addresses with this label\n" " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n" " \"label\" : \"label\" (string) The label of the receiving address. The default label is \"\".\n" @@ -1787,26 +1371,23 @@ static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) * * @param pwallet The wallet. * @param wtx The wallet transaction. - * @param strAccount The account, if any, or "*" for all. * @param nMinDepth The minimum confirmation depth. * @param fLong Whether to include the JSON version of the transaction. * @param ret The UniValue into which the result is stored. * @param filter The "is mine" filter bool. */ -static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const std::string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) +static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { CAmount nFee; - std::string strSentAccount; std::list<COutputEntry> listReceived; std::list<COutputEntry> listSent; - wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter); + wtx.GetAmounts(listReceived, listSent, nFee, filter); - bool fAllAccounts = (strAccount == std::string("*")); bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY); // Sent - if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) + if ((!listSent.empty() || nFee != 0)) { for (const COutputEntry& s : listSent) { @@ -1814,7 +1395,6 @@ static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const if (involvesWatchonly || (::IsMine(*pwallet, s.destination) & ISMINE_WATCH_ONLY)) { entry.pushKV("involvesWatchonly", true); } - if (IsDeprecatedRPCEnabled("accounts")) entry.pushKV("account", strSentAccount); MaybePushAddress(entry, s.destination); entry.pushKV("category", "send"); entry.pushKV("amount", ValueFromAmount(-s.amount)); @@ -1835,61 +1415,40 @@ static void ListTransactions(CWallet* const pwallet, const CWalletTx& wtx, const { for (const COutputEntry& r : listReceived) { - std::string account; + std::string label; if (pwallet->mapAddressBook.count(r.destination)) { - account = pwallet->mapAddressBook[r.destination].name; + label = pwallet->mapAddressBook[r.destination].name; } - if (fAllAccounts || (account == strAccount)) + UniValue entry(UniValue::VOBJ); + if (involvesWatchonly || (::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) { + entry.pushKV("involvesWatchonly", true); + } + MaybePushAddress(entry, r.destination); + if (wtx.IsCoinBase()) { - UniValue entry(UniValue::VOBJ); - if (involvesWatchonly || (::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) { - entry.pushKV("involvesWatchonly", true); - } - if (IsDeprecatedRPCEnabled("accounts")) entry.pushKV("account", account); - MaybePushAddress(entry, r.destination); - if (wtx.IsCoinBase()) - { - if (wtx.GetDepthInMainChain() < 1) - entry.pushKV("category", "orphan"); - else if (wtx.GetBlocksToMaturity() > 0) - entry.pushKV("category", "immature"); - else - entry.pushKV("category", "generate"); - } + if (wtx.GetDepthInMainChain() < 1) + entry.pushKV("category", "orphan"); + else if (wtx.IsImmatureCoinBase()) + entry.pushKV("category", "immature"); else - { - entry.pushKV("category", "receive"); - } - entry.pushKV("amount", ValueFromAmount(r.amount)); - if (pwallet->mapAddressBook.count(r.destination)) { - entry.pushKV("label", account); - } - entry.pushKV("vout", r.vout); - if (fLong) - WalletTxToJSON(wtx, entry); - ret.push_back(entry); + entry.pushKV("category", "generate"); + } + else + { + entry.pushKV("category", "receive"); + } + entry.pushKV("amount", ValueFromAmount(r.amount)); + if (pwallet->mapAddressBook.count(r.destination)) { + entry.pushKV("label", label); } + entry.pushKV("vout", r.vout); + if (fLong) + WalletTxToJSON(wtx, entry); + ret.push_back(entry); } } } -static void AcentryToJSON(const CAccountingEntry& acentry, const std::string& strAccount, UniValue& ret) -{ - bool fAllAccounts = (strAccount == std::string("*")); - - if (fAllAccounts || acentry.strAccount == strAccount) - { - UniValue entry(UniValue::VOBJ); - entry.pushKV("account", acentry.strAccount); - entry.pushKV("category", "move"); - entry.pushKV("time", acentry.nTime); - entry.pushKV("amount", ValueFromAmount(acentry.nCreditDebit)); - if (IsDeprecatedRPCEnabled("accounts")) entry.pushKV("otheraccount", acentry.strOtherAccount); - entry.pushKV("comment", acentry.strComment); - ret.push_back(entry); - } -} - UniValue listtransactions(const JSONRPCRequest& request) { std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); @@ -1899,12 +1458,10 @@ UniValue listtransactions(const JSONRPCRequest& request) return NullUniValue; } - std::string help_text {}; - if (!IsDeprecatedRPCEnabled("accounts")) { - help_text = "listtransactions (dummy count skip include_watchonly)\n" - "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n" - "Note that the \"account\" argument and \"otheraccount\" return value have been removed in V0.17. To use this RPC with an \"account\" argument, restart\n" - "bitcoind with -deprecatedrpc=accounts\n" + if (request.fHelp || request.params.size() > 4) + throw std::runtime_error( + "listtransactions (dummy count skip include_watchonly)\n" + "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions.\n" "\nArguments:\n" "1. \"dummy\" (string, optional) If set, should be \"*\" for backwards compatibility.\n" "2. count (numeric, optional, default=10) The number of transactions to return\n" @@ -1944,77 +1501,15 @@ UniValue listtransactions(const JSONRPCRequest& request) "\nList transactions 100 to 120\n" + HelpExampleCli("listtransactions", "\"*\" 20 100") + "\nAs a json rpc call\n" - + HelpExampleRpc("listtransactions", "\"*\", 20, 100"); - } else { - help_text = "listtransactions ( \"account\" count skip include_watchonly)\n" - "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n" - "\nArguments:\n" - "1. \"account\" (string, optional) DEPRECATED. This argument will be removed in V0.18. The account name. Should be \"*\".\n" - "2. count (numeric, optional, default=10) The number of transactions to return\n" - "3. skip (numeric, optional, default=0) The number of transactions to skip\n" - "4. include_watchonly (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n" - "\nResult:\n" - "[\n" - " {\n" - " \"account\":\"accountname\", (string) DEPRECATED. This field will be removed in V0.18. The account name associated with the transaction. \n" - " It will be \"\" for the default account.\n" - " \"address\":\"address\", (string) The bitcoin address of the transaction. Not present for \n" - " move transactions (category = move).\n" - " \"category\":\"send|receive|move\", (string) The transaction category. 'move' is a local (off blockchain)\n" - " transaction between accounts, and not associated with an address,\n" - " transaction id or block. 'send' and 'receive' transactions are \n" - " associated with an address, transaction id and block details\n" - " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the\n" - " 'move' category for moves outbound. It is positive for the 'receive' category,\n" - " and for the 'move' category for inbound funds.\n" - " \"label\": \"label\", (string) A comment for the address/transaction, if any\n" - " \"vout\": n, (numeric) the vout value\n" - " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" - " 'send' category of transactions.\n" - " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n" - " 'receive' category of transactions. Negative confirmations indicate the\n" - " transaction conflicts with the block chain\n" - " \"trusted\": xxx, (bool) Whether we consider the outputs of this unconfirmed transaction safe to spend.\n" - " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive'\n" - " category of transactions.\n" - " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it. Available for 'send' and 'receive'\n" - " category of transactions.\n" - " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n" - " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" - " \"time\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n" - " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n" - " for 'send' and 'receive' category of transactions.\n" - " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" - " \"otheraccount\": \"accountname\", (string) DEPRECATED. This field will be removed in V0.18. For the 'move' category of transactions, the account the funds came \n" - " from (for receiving funds, positive amounts), or went to (for sending funds,\n" - " negative amounts).\n" - " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n" - " may be unknown for unconfirmed transactions not in the mempool\n" - " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n" - " 'send' category of transactions.\n" - " }\n" - "]\n" - - "\nExamples:\n" - "\nList the most recent 10 transactions in the systems\n" - + HelpExampleCli("listtransactions", "") + - "\nList transactions 100 to 120\n" - + HelpExampleCli("listtransactions", "\"*\" 20 100") + - "\nAs a json rpc call\n" - + HelpExampleRpc("listtransactions", "\"*\", 20, 100"); - } - if (request.fHelp || request.params.size() > 4) throw std::runtime_error(help_text); + + HelpExampleRpc("listtransactions", "\"*\", 20, 100") + ); // Make sure the results are valid at least up to the most recent block // the user could have gotten from another RPC command prior to now pwallet->BlockUntilSyncedToCurrentChain(); - std::string strAccount = "*"; - if (!request.params[0].isNull()) { - strAccount = request.params[0].get_str(); - if (!IsDeprecatedRPCEnabled("accounts") && strAccount != "*") { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"*\""); - } + if (!request.params[0].isNull() && request.params[0].get_str() != "*") { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Dummy value must be set to \"*\""); } int nCount = 10; if (!request.params[1].isNull()) @@ -2042,14 +1537,8 @@ UniValue listtransactions(const JSONRPCRequest& request) // iterate backwards until we have nCount items to return: for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { - CWalletTx *const pwtx = (*it).second.first; - if (pwtx != nullptr) - ListTransactions(pwallet, *pwtx, strAccount, 0, true, ret, filter); - if (IsDeprecatedRPCEnabled("accounts")) { - CAccountingEntry *const pacentry = (*it).second.second; - if (pacentry != nullptr) AcentryToJSON(*pacentry, strAccount, ret); - } - + CWalletTx *const pwtx = (*it).second; + ListTransactions(pwallet, *pwtx, 0, true, ret, filter); if ((int)ret.size() >= (nCount+nFrom)) break; } } @@ -2080,101 +1569,6 @@ UniValue listtransactions(const JSONRPCRequest& request) return ret; } -static UniValue listaccounts(const JSONRPCRequest& request) -{ - std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); - CWallet* const pwallet = wallet.get(); - - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { - return NullUniValue; - } - - if (!IsDeprecatedRPCEnabled("accounts")) { - if (request.fHelp) { - throw std::runtime_error("listaccounts (Deprecated, will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts)"); - } - throw JSONRPCError(RPC_METHOD_DEPRECATED, "listaccounts is deprecated and will be removed in V0.18. To use this command, start bitcoind with -deprecatedrpc=accounts."); - } - - if (request.fHelp || request.params.size() > 2) - throw std::runtime_error( - "listaccounts ( minconf include_watchonly)\n" - "\nDEPRECATED. Returns Object that has account names as keys, account balances as values.\n" - "\nArguments:\n" - "1. minconf (numeric, optional, default=1) Only include transactions with at least this many confirmations\n" - "2. include_watchonly (bool, optional, default=false) Include balances in watch-only addresses (see 'importaddress')\n" - "\nResult:\n" - "{ (json object where keys are account names, and values are numeric balances\n" - " \"account\": x.xxx, (numeric) The property name is the account name, and the value is the total balance for the account.\n" - " ...\n" - "}\n" - "\nExamples:\n" - "\nList account balances where there at least 1 confirmation\n" - + HelpExampleCli("listaccounts", "") + - "\nList account balances including zero confirmation transactions\n" - + HelpExampleCli("listaccounts", "0") + - "\nList account balances for 6 or more confirmations\n" - + HelpExampleCli("listaccounts", "6") + - "\nAs json rpc call\n" - + HelpExampleRpc("listaccounts", "6") - ); - - // Make sure the results are valid at least up to the most recent block - // the user could have gotten from another RPC command prior to now - pwallet->BlockUntilSyncedToCurrentChain(); - - LOCK2(cs_main, pwallet->cs_wallet); - - int nMinDepth = 1; - if (!request.params[0].isNull()) - nMinDepth = request.params[0].get_int(); - isminefilter includeWatchonly = ISMINE_SPENDABLE; - if(!request.params[1].isNull()) - if(request.params[1].get_bool()) - includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY; - - std::map<std::string, CAmount> mapAccountBalances; - for (const std::pair<const CTxDestination, CAddressBookData>& entry : pwallet->mapAddressBook) { - if (IsMine(*pwallet, entry.first) & includeWatchonly) { // This address belongs to me - mapAccountBalances[entry.second.name] = 0; - } - } - - for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) { - const CWalletTx& wtx = pairWtx.second; - CAmount nFee; - std::string strSentAccount; - std::list<COutputEntry> listReceived; - std::list<COutputEntry> listSent; - int nDepth = wtx.GetDepthInMainChain(); - if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) - continue; - wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly); - mapAccountBalances[strSentAccount] -= nFee; - for (const COutputEntry& s : listSent) - mapAccountBalances[strSentAccount] -= s.amount; - if (nDepth >= nMinDepth) - { - for (const COutputEntry& r : listReceived) - if (pwallet->mapAddressBook.count(r.destination)) { - mapAccountBalances[pwallet->mapAddressBook[r.destination].name] += r.amount; - } - else - mapAccountBalances[""] += r.amount; - } - } - - const std::list<CAccountingEntry>& acentries = pwallet->laccentries; - for (const CAccountingEntry& entry : acentries) - mapAccountBalances[entry.strAccount] += entry.nCreditDebit; - - UniValue ret(UniValue::VOBJ); - for (const std::pair<const std::string, CAmount>& accountBalance : mapAccountBalances) { - ret.pushKV(accountBalance.first, ValueFromAmount(accountBalance.second)); - } - return ret; -} - static UniValue listsinceblock(const JSONRPCRequest& request) { std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); @@ -2199,7 +1593,6 @@ static UniValue listsinceblock(const JSONRPCRequest& request) "\nResult:\n" "{\n" " \"transactions\": [\n" - " \"account\":\"accountname\", (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The account name associated with the transaction. Will be \"\" for the default account.\n" " \"address\":\"address\", (string) The bitcoin address of the transaction. Not present for move transactions (category = move).\n" " \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n" " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the 'move' category for moves \n" @@ -2245,9 +1638,8 @@ static UniValue listsinceblock(const JSONRPCRequest& request) isminefilter filter = ISMINE_SPENDABLE; if (!request.params[0].isNull() && !request.params[0].get_str().empty()) { - uint256 blockId; + uint256 blockId(ParseHashV(request.params[0], "blockhash")); - blockId.SetHex(request.params[0].get_str()); paltindex = pindex = LookupBlockIndex(blockId); if (!pindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); @@ -2282,7 +1674,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request) CWalletTx tx = pairWtx.second; if (depth == -1 || tx.GetDepthInMainChain() < depth) { - ListTransactions(pwallet, tx, "*", 0, true, transactions, filter); + ListTransactions(pwallet, tx, 0, true, transactions, filter); } } @@ -2299,7 +1691,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request) if (it != pwallet->mapWallet.end()) { // We want all transactions regardless of confirmation count to appear here, // even negative confirmation ones, hence the big negative. - ListTransactions(pwallet, it->second, "*", -100000000, true, removed, filter); + ListTransactions(pwallet, it->second, -100000000, true, removed, filter); } } paltindex = paltindex->pprev; @@ -2348,7 +1740,6 @@ static UniValue gettransaction(const JSONRPCRequest& request) " may be unknown for unconfirmed transactions not in the mempool\n" " \"details\" : [\n" " {\n" - " \"account\" : \"accountname\", (string) DEPRECATED. This field will be removed in a V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The account name involved in the transaction, can be \"\" for the default account.\n" " \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n" " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n" @@ -2376,8 +1767,7 @@ static UniValue gettransaction(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - uint256 hash; - hash.SetHex(request.params[0].get_str()); + uint256 hash(ParseHashV(request.params[0], "txid")); isminefilter filter = ISMINE_SPENDABLE; if(!request.params[1].isNull()) @@ -2403,7 +1793,7 @@ static UniValue gettransaction(const JSONRPCRequest& request) WalletTxToJSON(wtx, entry); UniValue details(UniValue::VARR); - ListTransactions(pwallet, wtx, "*", 0, false, details, filter); + ListTransactions(pwallet, wtx, 0, false, details, filter); entry.pushKV("details", details); std::string strHex = EncodeHexTx(*wtx.tx, RPCSerializationFlags()); @@ -2444,8 +1834,7 @@ static UniValue abandontransaction(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - uint256 hash; - hash.SetHex(request.params[0].get_str()); + uint256 hash(ParseHashV(request.params[0], "txid")); if (!pwallet->mapWallet.count(hash)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); @@ -2729,7 +2118,6 @@ static UniValue encryptwallet(const JSONRPCRequest& request) "will require the passphrase to be set prior the making these calls.\n" "Use the walletpassphrase call for this, and then walletlock call.\n" "If the wallet is already encrypted, use the walletpassphrasechange call.\n" - "Note that this will shutdown the server.\n" "\nArguments:\n" "1. \"passphrase\" (string) The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long.\n" "\nExamples:\n" @@ -2767,11 +2155,7 @@ static UniValue encryptwallet(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet."); } - // BDB seems to have a bad habit of writing old data into - // slack space in .dat files; that is bad if the old data is - // unencrypted private keys. So: - StartShutdown(); - return "wallet encrypted; Bitcoin server stopping, restart to run with encrypted wallet. The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup."; + return "wallet encrypted; The keypool has been flushed and a new HD seed was generated (if you are using HD). You need to make a new backup."; } static UniValue lockunspent(const JSONRPCRequest& request) @@ -2854,17 +2238,13 @@ static UniValue lockunspent(const JSONRPCRequest& request) {"vout", UniValueType(UniValue::VNUM)}, }); - const std::string& txid = find_value(o, "txid").get_str(); - if (!IsHex(txid)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); - } - + const uint256 txid(ParseHashO(o, "txid")); const int nOutput = find_value(o, "vout").get_int(); if (nOutput < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); } - const COutPoint outpt(uint256S(txid), nOutput); + const COutPoint outpt(txid, nOutput); const auto it = pwallet->mapWallet.find(outpt.hash); if (it == pwallet->mapWallet.end()) { @@ -2945,7 +2325,7 @@ static UniValue listlockunspent(const JSONRPCRequest& request) UniValue ret(UniValue::VARR); - for (COutPoint &outpt : vOutpts) { + for (const COutPoint& outpt : vOutpts) { UniValue o(UniValue::VOBJ); o.pushKV("txid", outpt.hash.GetHex()); @@ -3326,7 +2706,6 @@ static UniValue listunspent(const JSONRPCRequest& request) " \"vout\" : n, (numeric) the vout value\n" " \"address\" : \"address\", (string) the bitcoin address\n" " \"label\" : \"label\", (string) The associated label, or \"\" for the default label\n" - " \"account\" : \"account\", (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The associated account, or \"\" for the default account\n" " \"scriptPubKey\" : \"key\", (string) the script key\n" " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n" " \"confirmations\" : n, (numeric) The number of confirmations\n" @@ -3434,9 +2813,6 @@ static UniValue listunspent(const JSONRPCRequest& request) auto i = pwallet->mapAddressBook.find(address); if (i != pwallet->mapAddressBook.end()) { entry.pushKV("label", i->second.name); - if (IsDeprecatedRPCEnabled("accounts")) { - entry.pushKV("account", i->second.name); - } } if (scriptPubKey.IsPayToScriptHash()) { @@ -3733,6 +3109,8 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request) // Sign the transaction LOCK2(cs_main, pwallet->cs_wallet); + EnsureWalletIsUnlocked(pwallet); + return SignTransaction(mtx, request.params[1], pwallet, false, request.params[2]); } @@ -3793,8 +3171,7 @@ static UniValue bumpfee(const JSONRPCRequest& request) } RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ}); - uint256 hash; - hash.SetHex(request.params[0].get_str()); + uint256 hash(ParseHashV(request.params[0], "txid")); // optional parameters CAmount totalFee = 0; @@ -4029,9 +3406,8 @@ public: void ProcessSubScript(const CScript& subscript, UniValue& obj, bool include_addresses = false) const { // Always present: script type and redeemscript - txnouttype which_type; std::vector<std::vector<unsigned char>> solutions_data; - Solver(subscript, which_type, solutions_data); + txnouttype which_type = Solver(subscript, solutions_data); obj.pushKV("script", GetTxnOutputType(which_type)); obj.pushKV("hex", HexStr(subscript.begin(), subscript.end())); @@ -4176,10 +3552,9 @@ UniValue getaddressinfo(const JSONRPCRequest& request) " ]\n" " \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output (only if \"script\" is \"multisig\")\n" " \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)\n" - " \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all getaddressinfo output fields for the embedded address, excluding metadata (\"timestamp\", \"hdkeypath\", \"hdseedid\") and relation to the wallet (\"ismine\", \"iswatchonly\", \"account\").\n" + " \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all getaddressinfo output fields for the embedded address, excluding metadata (\"timestamp\", \"hdkeypath\", \"hdseedid\") and relation to the wallet (\"ismine\", \"iswatchonly\").\n" " \"iscompressed\" : true|false, (boolean) If the address is compressed\n" - " \"label\" : \"label\" (string) The label associated with the address, \"\" is the default account\n" - " \"account\" : \"account\" (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The account associated with the address, \"\" is the default account\n" + " \"label\" : \"label\" (string) The label associated with the address, \"\" is the default label\n" " \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n" " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n" " \"hdseedid\" : \"<hash160>\" (string, optional) The Hash160 of the HD seed\n" @@ -4221,9 +3596,6 @@ UniValue getaddressinfo(const JSONRPCRequest& request) ret.pushKVs(detail); if (pwallet->mapAddressBook.count(dest)) { ret.pushKV("label", pwallet->mapAddressBook[dest].name); - if (IsDeprecatedRPCEnabled("accounts")) { - ret.pushKV("account", pwallet->mapAddressBook[dest].name); - } } const CKeyMetadata* meta = nullptr; CKeyID key_id = GetKeyForDestination(*pwallet, dest); @@ -4431,74 +3803,17 @@ UniValue sethdseed(const JSONRPCRequest& request) return NullUniValue; } -bool ParseHDKeypath(std::string keypath_str, std::vector<uint32_t>& keypath) -{ - std::stringstream ss(keypath_str); - std::string item; - bool first = true; - while (std::getline(ss, item, '/')) { - if (item.compare("m") == 0) { - if (first) { - first = false; - continue; - } - return false; - } - // Finds whether it is hardened - uint32_t path = 0; - size_t pos = item.find("'"); - if (pos != std::string::npos) { - // The hardened tick can only be in the last index of the string - if (pos != item.size() - 1) { - return false; - } - path |= 0x80000000; - item = item.substr(0, item.size() - 1); // Drop the last character which is the hardened tick - } - - // Ensure this is only numbers - if (item.find_first_not_of( "0123456789" ) != std::string::npos) { - return false; - } - uint32_t number; - if (!ParseUInt32(item, &number)) { - return false; - } - path |= number; - - keypath.push_back(path); - first = false; - } - return true; -} - -void AddKeypathToMap(const CWallet* pwallet, const CKeyID& keyID, std::map<CPubKey, std::vector<uint32_t>>& hd_keypaths) +void AddKeypathToMap(const CWallet* pwallet, const CKeyID& keyID, std::map<CPubKey, KeyOriginInfo>& hd_keypaths) { CPubKey vchPubKey; if (!pwallet->GetPubKey(keyID, vchPubKey)) { return; } - CKeyMetadata meta; - auto it = pwallet->mapKeyMetadata.find(keyID); - if (it != pwallet->mapKeyMetadata.end()) { - meta = it->second; + KeyOriginInfo info; + if (!pwallet->GetKeyOrigin(keyID, info)) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Internal keypath is broken"); } - std::vector<uint32_t> keypath; - if (!meta.hdKeypath.empty()) { - if (!ParseHDKeypath(meta.hdKeypath, keypath)) { - throw JSONRPCError(RPC_INTERNAL_ERROR, "Internal keypath is broken"); - } - // Get the proper master key id - CKey key; - pwallet->GetKey(meta.hd_seed_id, key); - CExtKey masterKey; - masterKey.SetSeed(key.begin(), key.size()); - // Add to map - keypath.insert(keypath.begin(), ReadLE32(masterKey.key.GetPubKey().GetID().begin())); - } else { // Single pubkeys get the master fingerprint of themselves - keypath.insert(keypath.begin(), ReadLE32(vchPubKey.GetID().begin())); - } - hd_keypaths.emplace(vchPubKey, keypath); + hd_keypaths.emplace(vchPubKey, std::move(info)); } bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const CTransaction* txConst, int sighash_type, bool sign, bool bip32derivs) @@ -4526,28 +3841,7 @@ bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const C throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Specified Sighash and sighash in PSBT do not match."); } - SignatureData sigdata; - if (sign) { - complete &= SignPSBTInput(*pwallet, *psbtx.tx, input, sigdata, i, sighash_type); - } else { - complete &= SignPSBTInput(PublicOnlySigningProvider(pwallet), *psbtx.tx, input, sigdata, i, sighash_type); - } - - if (it != pwallet->mapWallet.end()) { - // Drop the unnecessary UTXO if we added both from the wallet. - if (sigdata.witness) { - input.non_witness_utxo = nullptr; - } else { - input.witness_utxo.SetNull(); - } - } - - // Get public key paths - if (bip32derivs) { - for (const auto& pubkey_it : sigdata.misc_pubkeys) { - AddKeypathToMap(pwallet, pubkey_it.first, input.hd_keypaths); - } - } + complete &= SignPSBTInput(HidingSigningProvider(pwallet, !sign, !bip32derivs), *psbtx.tx, input, i, sighash_type); } // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change @@ -4560,15 +3854,8 @@ bool FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& psbtx, const C psbt_out.FillSignatureData(sigdata); MutableTransactionSignatureCreator creator(psbtx.tx.get_ptr(), 0, out.nValue, 1); - ProduceSignature(*pwallet, creator, out.scriptPubKey, sigdata); + ProduceSignature(HidingSigningProvider(pwallet, true, !bip32derivs), creator, out.scriptPubKey, sigdata); psbt_out.FromSignatureData(sigdata); - - // Get public key paths - if (bip32derivs) { - for (const auto& pubkey_it : sigdata.misc_pubkeys) { - AddKeypathToMap(pwallet, pubkey_it.first, psbt_out.hd_keypaths); - } - } } return complete; } @@ -4599,7 +3886,7 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request) " \"ALL|ANYONECANPAY\"\n" " \"NONE|ANYONECANPAY\"\n" " \"SINGLE|ANYONECANPAY\"\n" - "4. bip32derivs (boolean, optiona, default=false) If true, includes the BIP 32 derivation paths for public keys if we know them\n" + "4. bip32derivs (boolean, optional, default=false) If true, includes the BIP 32 derivation paths for public keys if we know them\n" "\nResult:\n" "{\n" @@ -4701,7 +3988,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) " \"ECONOMICAL\"\n" " \"CONSERVATIVE\"\n" " }\n" - "5. bip32derivs (boolean, optiona, default=false) If true, includes the BIP 32 derivation paths for public keys if we know them\n" + "5. bip32derivs (boolean, optional, default=false) If true, includes the BIP 32 derivation paths for public keys if we know them\n" "\nResult:\n" "{\n" " \"psbt\": \"value\", (string) The resulting raw transaction (base64-encoded string)\n" @@ -4742,7 +4029,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request) const CTransaction txConst(*psbtx.tx); // Fill transaction with out data but don't sign - bool bip32derivs = request.params[4].isNull() ? false : request.params[5].get_bool(); + bool bip32derivs = request.params[4].isNull() ? false : request.params[4].get_bool(); FillPSBT(pwallet, psbtx, &txConst, 1, false, bip32derivs); // Serialize the PSBT @@ -4767,80 +4054,68 @@ UniValue importprunedfunds(const JSONRPCRequest& request); UniValue removeprunedfunds(const JSONRPCRequest& request); UniValue importmulti(const JSONRPCRequest& request); +// clang-format off static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- - { "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} }, - { "wallet", "walletprocesspsbt", &walletprocesspsbt, {"psbt","sign","sighashtype","bip32derivs"} }, - { "wallet", "walletcreatefundedpsbt", &walletcreatefundedpsbt, {"inputs","outputs","locktime","options","bip32derivs"} }, + { "generating", "generate", &generate, {"nblocks","maxtries"} }, + { "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} }, { "hidden", "resendwallettransactions", &resendwallettransactions, {} }, + { "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} }, { "wallet", "abandontransaction", &abandontransaction, {"txid"} }, { "wallet", "abortrescan", &abortrescan, {} }, - { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","label|account","address_type"} }, - { "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} }, + { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","label","address_type"} }, { "wallet", "backupwallet", &backupwallet, {"destination"} }, { "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, { "wallet", "createwallet", &createwallet, {"wallet_name", "disable_private_keys"} }, { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, { "wallet", "dumpwallet", &dumpwallet, {"filename"} }, { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} }, + { "wallet", "getaddressesbylabel", &getaddressesbylabel, {"label"} }, { "wallet", "getaddressinfo", &getaddressinfo, {"address"} }, - { "wallet", "getbalance", &getbalance, {"account|dummy","minconf","include_watchonly"} }, - { "wallet", "getnewaddress", &getnewaddress, {"label|account","address_type"} }, + { "wallet", "getbalance", &getbalance, {"dummy","minconf","include_watchonly"} }, + { "wallet", "getnewaddress", &getnewaddress, {"label","address_type"} }, { "wallet", "getrawchangeaddress", &getrawchangeaddress, {"address_type"} }, { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} }, + { "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf"} }, { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} }, { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} }, { "wallet", "getwalletinfo", &getwalletinfo, {} }, + { "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} }, { "wallet", "importmulti", &importmulti, {"requests","options"} }, { "wallet", "importprivkey", &importprivkey, {"privkey","label","rescan"} }, - { "wallet", "importwallet", &importwallet, {"filename"} }, - { "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} }, { "wallet", "importprunedfunds", &importprunedfunds, {"rawtransaction","txoutproof"} }, { "wallet", "importpubkey", &importpubkey, {"pubkey","label","rescan"} }, + { "wallet", "importwallet", &importwallet, {"filename"} }, { "wallet", "keypoolrefill", &keypoolrefill, {"newsize"} }, { "wallet", "listaddressgroupings", &listaddressgroupings, {} }, + { "wallet", "listlabels", &listlabels, {"purpose"} }, { "wallet", "listlockunspent", &listlockunspent, {} }, { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly","address_filter"} }, + { "wallet", "listreceivedbylabel", &listreceivedbylabel, {"minconf","include_empty","include_watchonly"} }, { "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, - { "wallet", "listtransactions", &listtransactions, {"account|dummy","count","skip","include_watchonly"} }, + { "wallet", "listtransactions", &listtransactions, {"dummy","count","skip","include_watchonly"} }, { "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, { "wallet", "listwallets", &listwallets, {} }, { "wallet", "loadwallet", &loadwallet, {"filename"} }, { "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} }, - { "wallet", "sendmany", &sendmany, {"fromaccount|dummy","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} }, + { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} }, + { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} }, + { "wallet", "sendmany", &sendmany, {"dummy","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} }, { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} }, + { "wallet", "sethdseed", &sethdseed, {"newkeypool","seed"} }, + { "wallet", "setlabel", &setlabel, {"address","label"} }, { "wallet", "settxfee", &settxfee, {"amount"} }, { "wallet", "signmessage", &signmessage, {"address","message"} }, { "wallet", "signrawtransactionwithwallet", &signrawtransactionwithwallet, {"hexstring","prevtxs","sighashtype"} }, { "wallet", "unloadwallet", &unloadwallet, {"wallet_name"} }, + { "wallet", "walletcreatefundedpsbt", &walletcreatefundedpsbt, {"inputs","outputs","locktime","options","bip32derivs"} }, { "wallet", "walletlock", &walletlock, {} }, - { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} }, { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} }, - { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} }, - { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} }, - { "wallet", "sethdseed", &sethdseed, {"newkeypool","seed"} }, - - /** Account functions (deprecated) */ - { "wallet", "getaccountaddress", &getaccountaddress, {"account"} }, - { "wallet", "getaccount", &getaccount, {"address"} }, - { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, {"account"} }, - { "wallet", "getreceivedbyaccount", &getreceivedbylabel, {"account","minconf"} }, - { "wallet", "listaccounts", &listaccounts, {"minconf","include_watchonly"} }, - { "wallet", "listreceivedbyaccount", &listreceivedbylabel, {"minconf","include_empty","include_watchonly"} }, - { "wallet", "setaccount", &setlabel, {"address","account"} }, - { "wallet", "sendfrom", &sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, - { "wallet", "move", &movecmd, {"fromaccount","toaccount","amount","minconf","comment"} }, - - /** Label functions (to replace non-balance account functions) */ - { "wallet", "getaddressesbylabel", &getaddressesbylabel, {"label"} }, - { "wallet", "getreceivedbylabel", &getreceivedbylabel, {"label","minconf"} }, - { "wallet", "listlabels", &listlabels, {"purpose"} }, - { "wallet", "listreceivedbylabel", &listreceivedbylabel, {"minconf","include_empty","include_watchonly"} }, - { "wallet", "setlabel", &setlabel, {"address","label"} }, - - { "generating", "generate", &generate, {"nblocks","maxtries"} }, + { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} }, + { "wallet", "walletprocesspsbt", &walletprocesspsbt, {"psbt","sign","sighashtype","bip32derivs"} }, }; +// clang-format on void RegisterWalletRPCCommands(CRPCTable &t) { diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp deleted file mode 100644 index 79060887be..0000000000 --- a/src/wallet/test/accounting_tests.cpp +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) 2012-2018 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include <wallet/wallet.h> - -#include <wallet/test/wallet_test_fixture.h> - -#include <stdint.h> - -#include <boost/test/unit_test.hpp> - -BOOST_FIXTURE_TEST_SUITE(accounting_tests, WalletTestingSetup) - -static void -GetResults(CWallet& wallet, std::map<CAmount, CAccountingEntry>& results) -{ - std::list<CAccountingEntry> aes; - - results.clear(); - BOOST_CHECK(wallet.ReorderTransactions() == DBErrors::LOAD_OK); - wallet.ListAccountCreditDebit("", aes); - for (CAccountingEntry& ae : aes) - { - results[ae.nOrderPos] = ae; - } -} - -BOOST_AUTO_TEST_CASE(acc_orderupgrade) -{ - std::vector<CWalletTx*> vpwtx; - CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef()); - CAccountingEntry ae; - std::map<CAmount, CAccountingEntry> results; - - LOCK(m_wallet.cs_wallet); - - ae.strAccount = ""; - ae.nCreditDebit = 1; - ae.nTime = 1333333333; - ae.strOtherAccount = "b"; - ae.strComment = ""; - m_wallet.AddAccountingEntry(ae); - - wtx.mapValue["comment"] = "z"; - m_wallet.AddToWallet(wtx); - vpwtx.push_back(&m_wallet.mapWallet.at(wtx.GetHash())); - vpwtx[0]->nTimeReceived = (unsigned int)1333333335; - vpwtx[0]->nOrderPos = -1; - - ae.nTime = 1333333336; - ae.strOtherAccount = "c"; - m_wallet.AddAccountingEntry(ae); - - GetResults(m_wallet, results); - - BOOST_CHECK(m_wallet.nOrderPosNext == 3); - BOOST_CHECK(2 == results.size()); - BOOST_CHECK(results[0].nTime == 1333333333); - BOOST_CHECK(results[0].strComment.empty()); - BOOST_CHECK(1 == vpwtx[0]->nOrderPos); - BOOST_CHECK(results[2].nTime == 1333333336); - BOOST_CHECK(results[2].strOtherAccount == "c"); - - - ae.nTime = 1333333330; - ae.strOtherAccount = "d"; - ae.nOrderPos = m_wallet.IncOrderPosNext(); - m_wallet.AddAccountingEntry(ae); - - GetResults(m_wallet, results); - - BOOST_CHECK(results.size() == 3); - BOOST_CHECK(m_wallet.nOrderPosNext == 4); - BOOST_CHECK(results[0].nTime == 1333333333); - BOOST_CHECK(1 == vpwtx[0]->nOrderPos); - BOOST_CHECK(results[2].nTime == 1333333336); - BOOST_CHECK(results[3].nTime == 1333333330); - BOOST_CHECK(results[3].strComment.empty()); - - - wtx.mapValue["comment"] = "y"; - { - CMutableTransaction tx(*wtx.tx); - ++tx.nLockTime; // Just to change the hash :) - wtx.SetTx(MakeTransactionRef(std::move(tx))); - } - m_wallet.AddToWallet(wtx); - vpwtx.push_back(&m_wallet.mapWallet.at(wtx.GetHash())); - vpwtx[1]->nTimeReceived = (unsigned int)1333333336; - - wtx.mapValue["comment"] = "x"; - { - CMutableTransaction tx(*wtx.tx); - ++tx.nLockTime; // Just to change the hash :) - wtx.SetTx(MakeTransactionRef(std::move(tx))); - } - m_wallet.AddToWallet(wtx); - vpwtx.push_back(&m_wallet.mapWallet.at(wtx.GetHash())); - vpwtx[2]->nTimeReceived = (unsigned int)1333333329; - vpwtx[2]->nOrderPos = -1; - - GetResults(m_wallet, results); - - BOOST_CHECK(results.size() == 3); - BOOST_CHECK(m_wallet.nOrderPosNext == 6); - BOOST_CHECK(0 == vpwtx[2]->nOrderPos); - BOOST_CHECK(results[1].nTime == 1333333333); - BOOST_CHECK(2 == vpwtx[0]->nOrderPos); - BOOST_CHECK(results[3].nTime == 1333333336); - BOOST_CHECK(results[4].nTime == 1333333330); - BOOST_CHECK(results[4].strComment.empty()); - BOOST_CHECK(5 == vpwtx[1]->nOrderPos); - - - ae.nTime = 1333333334; - ae.strOtherAccount = "e"; - ae.nOrderPos = -1; - m_wallet.AddAccountingEntry(ae); - - GetResults(m_wallet, results); - - BOOST_CHECK(results.size() == 4); - BOOST_CHECK(m_wallet.nOrderPosNext == 7); - BOOST_CHECK(0 == vpwtx[2]->nOrderPos); - BOOST_CHECK(results[1].nTime == 1333333333); - BOOST_CHECK(2 == vpwtx[0]->nOrderPos); - BOOST_CHECK(results[3].nTime == 1333333336); - BOOST_CHECK(results[3].strComment.empty()); - BOOST_CHECK(results[4].nTime == 1333333330); - BOOST_CHECK(results[4].strComment.empty()); - BOOST_CHECK(results[5].nTime == 1333333334); - BOOST_CHECK(6 == vpwtx[1]->nOrderPos); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp index 0776ffda9c..21857df081 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -65,7 +65,7 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa // so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe() tx.vin.resize(1); } - std::unique_ptr<CWalletTx> wtx(new CWalletTx(&testWallet, MakeTransactionRef(std::move(tx)))); + std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx))); if (fIsFromMe) { wtx->fDebitCached = true; @@ -150,6 +150,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) add_coin(1 * CENT, 1, actual_selection); BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees)); BOOST_CHECK(equal_sets(selection, actual_selection)); + BOOST_CHECK_EQUAL(value_ret, 1 * CENT); actual_selection.clear(); selection.clear(); @@ -157,6 +158,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) add_coin(2 * CENT, 2, actual_selection); BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 2 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees)); BOOST_CHECK(equal_sets(selection, actual_selection)); + BOOST_CHECK_EQUAL(value_ret, 2 * CENT); actual_selection.clear(); selection.clear(); @@ -165,6 +167,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) add_coin(2 * CENT, 2, actual_selection); BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 5 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees)); BOOST_CHECK(equal_sets(selection, actual_selection)); + BOOST_CHECK_EQUAL(value_ret, 5 * CENT); actual_selection.clear(); selection.clear(); @@ -181,6 +184,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) add_coin(1 * CENT, 1, actual_selection); BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees)); BOOST_CHECK(equal_sets(selection, actual_selection)); + BOOST_CHECK_EQUAL(value_ret, 10 * CENT); actual_selection.clear(); selection.clear(); @@ -190,6 +194,9 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) add_coin(3 * CENT, 3, actual_selection); add_coin(2 * CENT, 2, actual_selection); BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 5000, selection, value_ret, not_input_fees)); + BOOST_CHECK_EQUAL(value_ret, 10 * CENT); + // FIXME: this test is redundant with the above, because 1 Cent is selected, not "too small" + // BOOST_CHECK(equal_sets(selection, actual_selection)); // Select 0.25 Cent, not possible BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.25 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees)); @@ -203,6 +210,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), target, 0, selection, value_ret, not_input_fees)); // Should not exhaust // Test same value early bailout optimization + utxo_pool.clear(); add_coin(7 * CENT, 7, actual_selection); add_coin(7 * CENT, 7, actual_selection); add_coin(7 * CENT, 7, actual_selection); @@ -217,6 +225,8 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) add_coin(5 * CENT, 7, utxo_pool); } BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 30 * CENT, 5000, selection, value_ret, not_input_fees)); + BOOST_CHECK_EQUAL(value_ret, 30 * CENT); + BOOST_CHECK(equal_sets(selection, actual_selection)); //////////////////// // Behavior tests // @@ -452,14 +462,19 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) BOOST_CHECK(testWallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); + } + + // test with many inputs + for (CAmount amt=1500; amt < COIN; amt*=10) { + empty_wallet(); + // Create 676 inputs (= (old MAX_STANDARD_TX_SIZE == 100000) / 148 bytes per input) + for (uint16_t j = 0; j < 676; j++) + add_coin(amt); - // test with many inputs - for (CAmount amt=1500; amt < COIN; amt*=10) { - empty_wallet(); - // Create 676 inputs (= (old MAX_STANDARD_TX_SIZE == 100000) / 148 bytes per input) - for (uint16_t j = 0; j < 676; j++) - add_coin(amt); + // We only create the wallet once to save time, but we still run the coin selection RUN_TESTS times. + for (int i = 0; i < RUN_TESTS; i++) { BOOST_CHECK(testWallet.SelectCoinsMinConf(2000, filter_confirmed, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + if (amt - 2000 < MIN_CHANGE) { // needs more than one input: uint16_t returnSize = std::ceil((2000.0 + MIN_CHANGE)/amt); @@ -471,14 +486,17 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) BOOST_CHECK_EQUAL(nValueRet, amt); BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); } - } + } + } - // test randomness - { - empty_wallet(); - for (int i2 = 0; i2 < 100; i2++) - add_coin(COIN); + // test randomness + { + empty_wallet(); + for (int i2 = 0; i2 < 100; i2++) + add_coin(COIN); + // Again, we only create the wallet once to save time, but we still run the coin selection RUN_TESTS times. + for (int i = 0; i < RUN_TESTS; i++) { // picking 50 from 100 coins doesn't depend on the shuffle, // but does depend on randomness in the stochastic approximation code BOOST_CHECK(testWallet.SelectCoinsMinConf(50 * COIN, filter_standard, GroupCoins(vCoins), setCoinsRet , nValueRet, coin_selection_params, bnb_used)); @@ -496,17 +514,19 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) fails++; } BOOST_CHECK_NE(fails, RANDOM_REPEATS); - - // add 75 cents in small change. not enough to make 90 cents, - // then try making 90 cents. there are multiple competing "smallest bigger" coins, - // one of which should be picked at random - add_coin(5 * CENT); - add_coin(10 * CENT); - add_coin(15 * CENT); - add_coin(20 * CENT); - add_coin(25 * CENT); - - fails = 0; + } + + // add 75 cents in small change. not enough to make 90 cents, + // then try making 90 cents. there are multiple competing "smallest bigger" coins, + // one of which should be picked at random + add_coin(5 * CENT); + add_coin(10 * CENT); + add_coin(15 * CENT); + add_coin(20 * CENT); + add_coin(25 * CENT); + + for (int i = 0; i < RUN_TESTS; i++) { + int fails = 0; for (int j = 0; j < RANDOM_REPEATS; j++) { // selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time @@ -517,8 +537,9 @@ BOOST_AUTO_TEST_CASE(knapsack_solver_test) fails++; } BOOST_CHECK_NE(fails, RANDOM_REPEATS); - } - } + } + } + empty_wallet(); } diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp index 61c3fa94a6..526f2d983f 100644 --- a/src/wallet/test/psbt_wallet_tests.cpp +++ b/src/wallet/test/psbt_wallet_tests.cpp @@ -13,8 +13,6 @@ #include <test/test_bitcoin.h> #include <wallet/test/wallet_test_fixture.h> -extern bool ParseHDKeypath(std::string keypath_str, std::vector<uint32_t>& keypath); - BOOST_FIXTURE_TEST_SUITE(psbt_wallet_tests, WalletTestingSetup) BOOST_AUTO_TEST_CASE(psbt_updater_test) diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index f44311b0be..3a8e6f751a 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -297,7 +297,7 @@ public: CCoinControl dummy; BOOST_CHECK(wallet->CreateTransaction({recipient}, tx, reservekey, fee, changePos, error, dummy)); CValidationState state; - BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, {}, reservekey, nullptr, state)); + BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, reservekey, nullptr, state)); CMutableTransaction blocktx; { LOCK(wallet->cs_wallet); @@ -320,7 +320,11 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) // Confirm ListCoins initially returns 1 coin grouped under coinbaseKey // address. - auto list = wallet->ListCoins(); + std::map<CTxDestination, std::vector<COutput>> list; + { + LOCK2(cs_main, wallet->cs_wallet); + list = wallet->ListCoins(); + } BOOST_CHECK_EQUAL(list.size(), 1U); BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(list.begin()->second.size(), 1U); @@ -333,7 +337,10 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) // coinbaseKey pubkey, even though the change address has a different // pubkey. AddTx(CRecipient{GetScriptForRawPubKey({}), 1 * COIN, false /* subtract fee */}); - list = wallet->ListCoins(); + { + LOCK2(cs_main, wallet->cs_wallet); + list = wallet->ListCoins(); + } BOOST_CHECK_EQUAL(list.size(), 1U); BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U); @@ -359,7 +366,10 @@ BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) } // Confirm ListCoins still returns same result as before, despite coins // being locked. - list = wallet->ListCoins(); + { + LOCK2(cs_main, wallet->cs_wallet); + list = wallet->ListCoins(); + } BOOST_CHECK_EQUAL(list.size(), 1U); BOOST_CHECK_EQUAL(boost::get<CKeyID>(list.begin()->first).ToString(), coinbaseAddress); BOOST_CHECK_EQUAL(list.begin()->second.size(), 2U); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 5a7fdf9a85..afe47d986e 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -585,7 +585,6 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran // nTimeReceived not copied on purpose copyTo->nTimeSmart = copyFrom->nTimeSmart; copyTo->fFromMe = copyFrom->fFromMe; - copyTo->strFromAccount = copyFrom->strFromAccount; // nOrderPos not copied on purpose // cached members not copied on purpose } @@ -688,6 +687,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) { encrypted_batch->TxnAbort(); delete encrypted_batch; + encrypted_batch = nullptr; // We now probably have half of our keys encrypted in memory, and half not... // die and let the user reload the unencrypted wallet. assert(false); @@ -698,6 +698,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) if (!encrypted_batch->TxnCommit()) { delete encrypted_batch; + encrypted_batch = nullptr; // We now have keys encrypted in memory, but not on disk... // die to avoid confusion and let the user reload the unencrypted wallet. assert(false); @@ -721,6 +722,11 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) // bits of the unencrypted private key in slack space in the database file. database->Rewrite(); + // BDB seems to have a bad habit of writing old data into + // slack space in .dat files; that is bad if the old data is + // unencrypted private keys. So: + database->ReloadDbEnv(); + } NotifyStatusChanged(this); @@ -735,44 +741,30 @@ DBErrors CWallet::ReorderTransactions() // Old wallets didn't have any defined order for transactions // Probably a bad idea to change the output of this - // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap. - typedef std::pair<CWalletTx*, CAccountingEntry*> TxPair; - typedef std::multimap<int64_t, TxPair > TxItems; + // First: get all CWalletTx into a sorted-by-time multimap. + typedef std::multimap<int64_t, CWalletTx*> TxItems; TxItems txByTime; for (auto& entry : mapWallet) { CWalletTx* wtx = &entry.second; - txByTime.insert(std::make_pair(wtx->nTimeReceived, TxPair(wtx, nullptr))); - } - std::list<CAccountingEntry> acentries; - batch.ListAccountCreditDebit("", acentries); - for (CAccountingEntry& entry : acentries) - { - txByTime.insert(std::make_pair(entry.nTime, TxPair(nullptr, &entry))); + txByTime.insert(std::make_pair(wtx->nTimeReceived, wtx)); } nOrderPosNext = 0; std::vector<int64_t> nOrderPosOffsets; for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) { - CWalletTx *const pwtx = (*it).second.first; - CAccountingEntry *const pacentry = (*it).second.second; - int64_t& nOrderPos = (pwtx != nullptr) ? pwtx->nOrderPos : pacentry->nOrderPos; + CWalletTx *const pwtx = (*it).second; + int64_t& nOrderPos = pwtx->nOrderPos; if (nOrderPos == -1) { nOrderPos = nOrderPosNext++; nOrderPosOffsets.push_back(nOrderPos); - if (pwtx) - { - if (!batch.WriteTx(*pwtx)) - return DBErrors::LOAD_FAIL; - } - else - if (!batch.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) - return DBErrors::LOAD_FAIL; + if (!batch.WriteTx(*pwtx)) + return DBErrors::LOAD_FAIL; } else { @@ -789,14 +781,8 @@ DBErrors CWallet::ReorderTransactions() continue; // Since we're changing the order, write it back - if (pwtx) - { - if (!batch.WriteTx(*pwtx)) - return DBErrors::LOAD_FAIL; - } - else - if (!batch.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) - return DBErrors::LOAD_FAIL; + if (!batch.WriteTx(*pwtx)) + return DBErrors::LOAD_FAIL; } } batch.WriteOrderPosNext(nOrderPosNext); @@ -816,80 +802,6 @@ int64_t CWallet::IncOrderPosNext(WalletBatch *batch) return nRet; } -bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment) -{ - WalletBatch batch(*database); - if (!batch.TxnBegin()) - return false; - - int64_t nNow = GetAdjustedTime(); - - // Debit - CAccountingEntry debit; - debit.nOrderPos = IncOrderPosNext(&batch); - debit.strAccount = strFrom; - debit.nCreditDebit = -nAmount; - debit.nTime = nNow; - debit.strOtherAccount = strTo; - debit.strComment = strComment; - AddAccountingEntry(debit, &batch); - - // Credit - CAccountingEntry credit; - credit.nOrderPos = IncOrderPosNext(&batch); - credit.strAccount = strTo; - credit.nCreditDebit = nAmount; - credit.nTime = nNow; - credit.strOtherAccount = strFrom; - credit.strComment = strComment; - AddAccountingEntry(credit, &batch); - - if (!batch.TxnCommit()) - return false; - - return true; -} - -bool CWallet::GetLabelDestination(CTxDestination &dest, const std::string& label, bool bForceNew) -{ - WalletBatch batch(*database); - - CAccount account; - batch.ReadAccount(label, account); - - if (!bForceNew) { - if (!account.vchPubKey.IsValid()) - bForceNew = true; - else { - // Check if the current key has been used (TODO: check other addresses with the same key) - CScript scriptPubKey = GetScriptForDestination(GetDestinationForKey(account.vchPubKey, m_default_address_type)); - for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); - it != mapWallet.end() && account.vchPubKey.IsValid(); - ++it) - for (const CTxOut& txout : (*it).second.tx->vout) - if (txout.scriptPubKey == scriptPubKey) { - bForceNew = true; - break; - } - } - } - - // Generate a new key - if (bForceNew) { - if (!GetKeyFromPool(account.vchPubKey, false)) - return false; - - LearnRelatedScripts(account.vchPubKey, m_default_address_type); - dest = GetDestinationForKey(account.vchPubKey, m_default_address_type); - SetAddressBook(dest, label, "receive"); - batch.WriteAccount(label, account); - } else { - dest = GetDestinationForKey(account.vchPubKey, m_default_address_type); - } - - return true; -} - void CWallet::MarkDirty() { { @@ -944,7 +856,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) if (fInsertedNew) { wtx.nTimeReceived = GetAdjustedTime(); wtx.nOrderPos = IncOrderPosNext(&batch); - wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); + wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx)); wtx.nTimeSmart = ComputeTimeSmart(wtx); AddToSpends(hash); } @@ -1019,7 +931,7 @@ void CWallet::LoadToWallet(const CWalletTx& wtxIn) CWalletTx& wtx = ins.first->second; wtx.BindWallet(this); if (/* insertion took place */ ins.second) { - wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr))); + wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx)); } AddToSpends(hash); for (const CTxIn& txin : wtx.tx->vin) { @@ -1579,7 +1491,7 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wall // Look up the inputs. We should have already checked that this transaction // IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our // wallet, with a valid index into the vout array, and the ability to sign. - for (auto& input : tx.vin) { + for (const CTxIn& input : tx.vin) { const auto mi = wallet->mapWallet.find(input.prevout.hash); if (mi == wallet->mapWallet.end()) { return -1; @@ -1615,12 +1527,11 @@ int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, } void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived, - std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const + std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter) const { nFee = 0; listReceived.clear(); listSent.clear(); - strSentAccount = strFromAccount; // Compute fee: CAmount nDebit = GetDebit(filter); @@ -1819,7 +1730,7 @@ void CWallet::ReacceptWalletTransactions() } // Try to add wallet transactions to memory pool - for (std::pair<const int64_t, CWalletTx*>& item : mapSorted) { + for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) { CWalletTx& wtx = *(item.second); CValidationState state; wtx.AcceptToMemoryPool(maxTxFee, state); @@ -1894,7 +1805,7 @@ CAmount CWalletTx::GetDebit(const isminefilter& filter) const CAmount CWalletTx::GetCredit(const isminefilter& filter) const { // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsCoinBase() && GetBlocksToMaturity() > 0) + if (IsImmatureCoinBase()) return 0; CAmount credit = 0; @@ -1926,8 +1837,7 @@ CAmount CWalletTx::GetCredit(const isminefilter& filter) const CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const { - if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) - { + if (IsImmatureCoinBase() && IsInMainChain()) { if (fUseCache && fImmatureCreditCached) return nImmatureCreditCached; nImmatureCreditCached = pwallet->GetCredit(*tx, ISMINE_SPENDABLE); @@ -1944,7 +1854,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter return 0; // Must wait until coinbase is safely deep enough in the chain before valuing it - if (IsCoinBase() && GetBlocksToMaturity() > 0) + if (IsImmatureCoinBase()) return 0; CAmount* cache = nullptr; @@ -1985,8 +1895,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool fUseCache) const { - if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) - { + if (IsImmatureCoinBase() && IsInMainChain()) { if (fUseCache && fImmatureWatchCreditCached) return nImmatureWatchCreditCached; nImmatureWatchCreditCached = pwallet->GetCredit(*tx, ISMINE_WATCH_ONLY); @@ -2067,7 +1976,7 @@ std::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime, CCon continue; mapSorted.insert(std::make_pair(wtx.nTimeReceived, &wtx)); } - for (std::pair<const unsigned int, CWalletTx*>& item : mapSorted) + for (const std::pair<const unsigned int, CWalletTx*>& item : mapSorted) { CWalletTx& wtx = *item.second; if (wtx.RelayWalletTransaction(connman)) @@ -2191,7 +2100,7 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const // wallet, and then subtracts the values of TxIns spending from the wallet. This // also has fewer restrictions on which unconfirmed transactions are considered // trusted. -CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const +CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth) const { LOCK2(cs_main, cs_wallet); @@ -2199,7 +2108,7 @@ CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth, cons for (const auto& entry : mapWallet) { const CWalletTx& wtx = entry.second; const int depth = wtx.GetDepthInMainChain(); - if (depth < 0 || !CheckFinalTx(*wtx.tx) || wtx.GetBlocksToMaturity() > 0) { + if (depth < 0 || !CheckFinalTx(*wtx.tx) || wtx.IsImmatureCoinBase()) { continue; } @@ -2210,21 +2119,17 @@ CAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth, cons for (const CTxOut& out : wtx.tx->vout) { if (outgoing && IsChange(out)) { debit -= out.nValue; - } else if (IsMine(out) & filter && depth >= minDepth && (!account || *account == GetLabelName(out.scriptPubKey))) { + } else if (IsMine(out) & filter && depth >= minDepth) { balance += out.nValue; } } // For outgoing txs, subtract amount debited. - if (outgoing && (!account || *account == wtx.strFromAccount)) { + if (outgoing) { balance -= debit; } } - if (account) { - balance += WalletBatch(*database).GetAccountCreditDebit(*account); - } - return balance; } @@ -2259,7 +2164,7 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const if (!CheckFinalTx(*pcoin->tx)) continue; - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + if (pcoin->IsImmatureCoinBase()) continue; int nDepth = pcoin->GetDepthInMainChain(); @@ -2354,23 +2259,15 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const { - // TODO: Add AssertLockHeld(cs_wallet) here. - // - // Because the return value from this function contains pointers to - // CWalletTx objects, callers to this function really should acquire the - // cs_wallet lock before calling it. However, the current caller doesn't - // acquire this lock yet. There was an attempt to add the missing lock in - // https://github.com/bitcoin/bitcoin/pull/10340, but that change has been - // postponed until after https://github.com/bitcoin/bitcoin/pull/10244 to - // avoid adding some extra complexity to the Qt code. + AssertLockHeld(cs_main); + AssertLockHeld(cs_wallet); std::map<CTxDestination, std::vector<COutput>> result; std::vector<COutput> availableCoins; - LOCK2(cs_main, cs_wallet); AvailableCoins(availableCoins); - for (auto& coin : availableCoins) { + for (const COutput& coin : availableCoins) { CTxDestination address; if (coin.fSpendable && ExtractDestination(FindNonChangeParentOutput(*coin.tx->tx, coin.i).scriptPubKey, address)) { @@ -2380,7 +2277,7 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins() const std::vector<COutPoint> lockedCoins; ListLockedCoins(lockedCoins); - for (const auto& output : lockedCoins) { + for (const COutPoint& output : lockedCoins) { auto it = mapWallet.find(output.hash); if (it != mapWallet.end()) { int depth = it->second.GetDepthInMainChain(); @@ -2765,7 +2662,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac scriptChange = GetScriptForDestination(GetDestinationForKey(vchPubKey, change_type)); } CTxOut change_prototype_txout(0, scriptChange); - coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout, SER_DISK, 0); + coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout); CFeeRate discard_rate = GetDiscardRate(*this, ::feeEstimator); @@ -2809,7 +2706,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac } } // Include the fee cost for outputs. Note this is only used for BnB right now - coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, SER_NETWORK, PROTOCOL_VERSION); + coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION); if (IsDust(txout, ::dustRelayFee)) { @@ -2846,6 +2743,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac return false; } } + } else { + bnb_used = false; } const CAmount nChange = nValueIn - nValueToSelect; @@ -3056,7 +2955,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac /** * Call after CreateTransaction unless you want to abort */ -bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, std::string fromAccount, CReserveKey& reservekey, CConnman* connman, CValidationState& state) +bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CConnman* connman, CValidationState& state) { { LOCK2(cs_main, cs_wallet); @@ -3064,7 +2963,6 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve CWalletTx wtxNew(this, std::move(tx)); wtxNew.mapValue = std::move(mapValue); wtxNew.vOrderForm = std::move(orderForm); - wtxNew.strFromAccount = std::move(fromAccount); wtxNew.fTimeReceivedIsTxTime = true; wtxNew.fFromMe = true; @@ -3104,31 +3002,6 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve return true; } -void CWallet::ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries) { - WalletBatch batch(*database); - return batch.ListAccountCreditDebit(strAccount, entries); -} - -bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry) -{ - WalletBatch batch(*database); - - return AddAccountingEntry(acentry, &batch); -} - -bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, WalletBatch *batch) -{ - if (!batch->WriteAccountingEntry(++nAccountingEntryNumber, acentry)) { - return false; - } - - laccentries.push_back(acentry); - CAccountingEntry & entry = laccentries.back(); - wtxOrdered.insert(std::make_pair(entry.nOrderPos, TxPair(nullptr, &entry))); - - return true; -} - DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { LOCK2(cs_main, cs_wallet); @@ -3282,17 +3155,17 @@ bool CWallet::NewKeyPool() LOCK(cs_wallet); WalletBatch batch(*database); - for (int64_t nIndex : setInternalKeyPool) { + for (const int64_t nIndex : setInternalKeyPool) { batch.ErasePool(nIndex); } setInternalKeyPool.clear(); - for (int64_t nIndex : setExternalKeyPool) { + for (const int64_t nIndex : setExternalKeyPool) { batch.ErasePool(nIndex); } setExternalKeyPool.clear(); - for (int64_t nIndex : set_pre_split_keypool) { + for (const int64_t nIndex : set_pre_split_keypool) { batch.ErasePool(nIndex); } set_pre_split_keypool.clear(); @@ -3528,7 +3401,7 @@ std::map<CTxDestination, CAmount> CWallet::GetAddressBalances() if (!pcoin->IsTrusted()) continue; - if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + if (pcoin->IsImmatureCoinBase()) continue; int nDepth = pcoin->GetDepthInMainChain(); @@ -3569,7 +3442,7 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() { bool any_mine = false; // group all input addresses with each other - for (CTxIn txin : pcoin->tx->vin) + for (const CTxIn& txin : pcoin->tx->vin) { CTxDestination address; if(!IsMine(txin)) /* If this input isn't mine, ignore it */ @@ -3583,7 +3456,7 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() // group change with input addresses if (any_mine) { - for (CTxOut txout : pcoin->tx->vout) + for (const CTxOut& txout : pcoin->tx->vout) if (IsChange(txout)) { CTxDestination txoutAddr; @@ -3619,7 +3492,7 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() // make a set of all the groups hit by this new group std::set< std::set<CTxDestination>* > hits; std::map< CTxDestination, std::set<CTxDestination>* >::iterator it; - for (CTxDestination address : _grouping) + for (const CTxDestination& address : _grouping) if ((it = setmap.find(address)) != setmap.end()) hits.insert((*it).second); @@ -3634,12 +3507,12 @@ std::set< std::set<CTxDestination> > CWallet::GetAddressGroupings() uniqueGroupings.insert(merged); // update setmap - for (CTxDestination element : *merged) + for (const CTxDestination& element : *merged) setmap[element] = merged; } std::set< std::set<CTxDestination> > ret; - for (std::set<CTxDestination>* uniqueGrouping : uniqueGroupings) + for (const std::set<CTxDestination>* uniqueGrouping : uniqueGroupings) { ret.insert(*uniqueGrouping); delete uniqueGrouping; @@ -3662,12 +3535,6 @@ std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) co return result; } -void CWallet::DeleteLabel(const std::string& label) -{ - WalletBatch batch(*database); - batch.EraseAccount(label); -} - bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal) { if (nIndex == -1) @@ -3858,19 +3725,14 @@ unsigned int CWallet::ComputeTimeSmart(const CWalletTx& wtx) const int64_t latestTolerated = latestNow + 300; const TxItems& txOrdered = wtxOrdered; for (auto it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { - CWalletTx* const pwtx = it->second.first; + CWalletTx* const pwtx = it->second; if (pwtx == &wtx) { continue; } - CAccountingEntry* const pacentry = it->second.second; int64_t nSmartTime; - if (pwtx) { - nSmartTime = pwtx->nTimeSmart; - if (!nSmartTime) { - nSmartTime = pwtx->nTimeReceived; - } - } else { - nSmartTime = pacentry->nTime; + nSmartTime = pwtx->nTimeSmart; + if (!nSmartTime) { + nSmartTime = pwtx->nTimeReceived; } if (nSmartTime <= latestTolerated) { latestEntry = nSmartTime; @@ -3994,7 +3856,7 @@ bool CWallet::Verify(std::string wallet_file, bool salvage_wallet, std::string& return false; } } catch (const fs::filesystem_error& e) { - error_string = strprintf("Error loading wallet %s. %s", wallet_file, e.what()); + error_string = strprintf("Error loading wallet %s. %s", wallet_file, fsbridge::get_filesystem_error_message(e)); return false; } @@ -4127,10 +3989,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, if (fFirstRun) { // ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key - if (!gArgs.GetBoolArg("-usehd", true)) { - InitError(strprintf(_("Error creating %s: You can't create non-HD wallets with this version."), walletFile)); - return nullptr; - } walletInstance->SetMinVersion(FEATURE_LATEST); if ((wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { @@ -4158,16 +4016,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, if (!walletInstance->mapKeys.empty() || !walletInstance->mapCryptedKeys.empty()) { InitWarning(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile)); } - } else if (gArgs.IsArgSet("-usehd")) { - bool useHD = gArgs.GetBoolArg("-usehd", true); - if (walletInstance->IsHDEnabled() && !useHD) { - InitError(strprintf(_("Error loading %s: You can't disable HD on an already existing HD wallet"), walletFile)); - return nullptr; - } - if (!walletInstance->IsHDEnabled() && useHD) { - InitError(strprintf(_("Error loading %s: You can't enable HD on an already existing non-HD wallet"), walletFile)); - return nullptr; - } } if (!gArgs.GetArg("-addresstype", "").empty() && !ParseOutputType(gArgs.GetArg("-addresstype", ""), walletInstance->m_default_address_type)) { @@ -4315,7 +4163,6 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, copyTo->nTimeReceived = copyFrom->nTimeReceived; copyTo->nTimeSmart = copyFrom->nTimeSmart; copyTo->fFromMe = copyFrom->fFromMe; - copyTo->strFromAccount = copyFrom->strFromAccount; copyTo->nOrderPos = copyFrom->nOrderPos; batch.WriteTx(*copyTo); } @@ -4406,6 +4253,11 @@ int CMerkleTx::GetBlocksToMaturity() const return std::max(0, (COINBASE_MATURITY+1) - chain_depth); } +bool CMerkleTx::IsImmatureCoinBase() const +{ + // note GetBlocksToMaturity is 0 for non-coinbase tx + return GetBlocksToMaturity() > 0; +} bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) { @@ -4466,3 +4318,29 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu } return groups; } + +bool CWallet::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const +{ + CKeyMetadata meta; + { + LOCK(cs_wallet); + auto it = mapKeyMetadata.find(keyID); + if (it != mapKeyMetadata.end()) { + meta = it->second; + } + } + if (!meta.hdKeypath.empty()) { + if (!ParseHDKeypath(meta.hdKeypath, info.path)) return false; + // Get the proper master key id + CKey key; + GetKey(meta.hd_seed_id, key); + CExtKey masterKey; + masterKey.SetSeed(key.begin(), key.size()); + // Compute identifier + CKeyID masterid = masterKey.key.GetPubKey().GetID(); + std::copy(masterid.begin(), masterid.begin() + 4, info.fingerprint); + } else { // Single pubkeys get the master fingerprint of themselves + std::copy(keyID.begin(), keyID.begin() + 4, info.fingerprint); + } + return true; +} diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 57b22c0e49..da326517c0 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -264,15 +264,22 @@ public: * 0 : in memory pool, waiting to be included in a block * >=1 : this many blocks deep in the main chain */ - int GetDepthInMainChain() const; - bool IsInMainChain() const { return GetDepthInMainChain() > 0; } - int GetBlocksToMaturity() const; + int GetDepthInMainChain() const EXCLUSIVE_LOCKS_REQUIRED(cs_main); + bool IsInMainChain() const EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return GetDepthInMainChain() > 0; } + + /** + * @return number of blocks to maturity for this transaction: + * 0 : is not a coinbase transaction, or is a mature coinbase transaction + * >0 : is a coinbase transaction which matures in this many blocks + */ + int GetBlocksToMaturity() const EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool hashUnset() const { return (hashBlock.IsNull() || hashBlock == ABANDON_HASH); } bool isAbandoned() const { return (hashBlock == ABANDON_HASH); } void setAbandoned() { hashBlock = ABANDON_HASH; } const uint256& GetHash() const { return tx->GetHash(); } bool IsCoinBase() const { return tx->IsCoinBase(); } + bool IsImmatureCoinBase() const EXCLUSIVE_LOCKS_REQUIRED(cs_main); }; //Get the marginal bytes of spending the specified output @@ -295,7 +302,7 @@ public: * serialized in the wallet database: * * "comment", "to" - comment strings provided to sendtoaddress, - * sendfrom, sendmany wallet RPCs + * and sendmany wallet RPCs * "replaces_txid" - txid (as HexStr) of transaction replaced by * bumpfee on transaction created by bumpfee * "replaced_by_txid" - txid (as HexStr) of transaction created by @@ -333,9 +340,8 @@ public: * externally and came in through the network or sendrawtransaction RPC. */ char fFromMe; - std::string strFromAccount; int64_t nOrderPos; //!< position in ordered transaction list - std::multimap<int64_t, std::pair<CWalletTx*, CAccountingEntry*>>::const_iterator m_it_wtxOrdered; + std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered; // memory only mutable bool fDebitCached; @@ -372,7 +378,6 @@ public: nTimeReceived = 0; nTimeSmart = 0; fFromMe = false; - strFromAccount.clear(); fDebitCached = false; fCreditCached = false; fImmatureCreditCached = false; @@ -401,7 +406,7 @@ public: char fSpent = false; mapValue_t mapValueCopy = mapValue; - mapValueCopy["fromaccount"] = strFromAccount; + mapValueCopy["fromaccount"] = ""; WriteOrderPos(nOrderPos, mapValueCopy); if (nTimeSmart) { mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart); @@ -422,7 +427,6 @@ public: std::vector<CMerkleTx> vUnused; //!< Used to be vtxPrev s >> vUnused >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> fSpent; - strFromAccount = std::move(mapValue["fromaccount"]); ReadOrderPos(nOrderPos, mapValue); nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0; @@ -454,10 +458,10 @@ public: //! filter decides which addresses will count towards the debit CAmount GetDebit(const isminefilter& filter) const; - CAmount GetCredit(const isminefilter& filter) const; - CAmount GetImmatureCredit(bool fUseCache=true) const; - CAmount GetAvailableCredit(bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const; - CAmount GetImmatureWatchOnlyCredit(const bool fUseCache=true) const; + CAmount GetCredit(const isminefilter& filter) const EXCLUSIVE_LOCKS_REQUIRED(cs_main); + CAmount GetImmatureCredit(bool fUseCache=true) const EXCLUSIVE_LOCKS_REQUIRED(cs_main); + CAmount GetAvailableCredit(bool fUseCache=true, const isminefilter& filter=ISMINE_SPENDABLE) const EXCLUSIVE_LOCKS_REQUIRED(cs_main); + CAmount GetImmatureWatchOnlyCredit(const bool fUseCache=true) const EXCLUSIVE_LOCKS_REQUIRED(cs_main); CAmount GetChange() const; // Get the marginal bytes if spending the specified output from this transaction @@ -467,7 +471,7 @@ public: } void GetAmounts(std::list<COutputEntry>& listReceived, - std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const; + std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter) const; bool IsFromMe(const isminefilter& filter) const { @@ -478,15 +482,15 @@ public: bool IsEquivalentTo(const CWalletTx& tx) const; bool InMempool() const; - bool IsTrusted() const; + bool IsTrusted() const EXCLUSIVE_LOCKS_REQUIRED(cs_main); int64_t GetTxTime() const; // RelayWalletTransaction may only be called if fBroadcastTransactions! - bool RelayWalletTransaction(CConnman* connman); + bool RelayWalletTransaction(CConnman* connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */ - bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state); + bool AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); std::set<uint256> GetConflicts() const; }; @@ -562,89 +566,6 @@ public: } }; -/** - * DEPRECATED Internal transfers. - * Database key is acentry<account><counter>. - */ -class CAccountingEntry -{ -public: - std::string strAccount; - CAmount nCreditDebit; - int64_t nTime; - std::string strOtherAccount; - std::string strComment; - mapValue_t mapValue; - int64_t nOrderPos; //!< position in ordered transaction list - uint64_t nEntryNo; - - CAccountingEntry() - { - SetNull(); - } - - void SetNull() - { - nCreditDebit = 0; - nTime = 0; - strAccount.clear(); - strOtherAccount.clear(); - strComment.clear(); - nOrderPos = -1; - nEntryNo = 0; - } - - template <typename Stream> - void Serialize(Stream& s) const { - int nVersion = s.GetVersion(); - if (!(s.GetType() & SER_GETHASH)) { - s << nVersion; - } - //! Note: strAccount is serialized as part of the key, not here. - s << nCreditDebit << nTime << strOtherAccount; - - mapValue_t mapValueCopy = mapValue; - WriteOrderPos(nOrderPos, mapValueCopy); - - std::string strCommentCopy = strComment; - if (!mapValueCopy.empty() || !_ssExtra.empty()) { - CDataStream ss(s.GetType(), s.GetVersion()); - ss.insert(ss.begin(), '\0'); - ss << mapValueCopy; - ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end()); - strCommentCopy.append(ss.str()); - } - s << strCommentCopy; - } - - template <typename Stream> - void Unserialize(Stream& s) { - int nVersion = s.GetVersion(); - if (!(s.GetType() & SER_GETHASH)) { - s >> nVersion; - } - //! Note: strAccount is serialized as part of the key, not here. - s >> nCreditDebit >> nTime >> LIMITED_STRING(strOtherAccount, 65536) >> LIMITED_STRING(strComment, 65536); - - size_t nSepPos = strComment.find("\0", 0, 1); - mapValue.clear(); - if (std::string::npos != nSepPos) { - CDataStream ss(std::vector<char>(strComment.begin() + nSepPos + 1, strComment.end()), s.GetType(), s.GetVersion()); - ss >> mapValue; - _ssExtra = std::vector<char>(ss.begin(), ss.end()); - } - ReadOrderPos(nOrderPos, mapValue); - if (std::string::npos != nSepPos) { - strComment.erase(nSepPos); - } - - mapValue.erase("n"); - } - -private: - std::vector<char> _ssExtra; -}; - struct CoinSelectionParams { bool use_bnb = true; @@ -819,10 +740,8 @@ public: } std::map<uint256, CWalletTx> mapWallet; - std::list<CAccountingEntry> laccentries; - typedef std::pair<CWalletTx*, CAccountingEntry*> TxPair; - typedef std::multimap<int64_t, TxPair > TxItems; + typedef std::multimap<int64_t, CWalletTx*> TxItems; TxItems wtxOrdered; int64_t nOrderPosNext = 0; @@ -840,12 +759,12 @@ public: /** * populate vCoins with vector of available COutputs. */ - void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999) const EXCLUSIVE_LOCKS_REQUIRED(cs_main, cs_wallet); /** * Return list of available coins and locked coins grouped by non-change output address. */ - std::map<CTxDestination, std::vector<COutput>> ListCoins() const; + std::map<CTxDestination, std::vector<COutput>> ListCoins() const EXCLUSIVE_LOCKS_REQUIRED(cs_main, cs_wallet); /** * Find non-change parent output. @@ -861,7 +780,7 @@ public: bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<OutputGroup> groups, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const; - bool IsSpent(const uint256& hash, unsigned int n) const; + bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_main); std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const; bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); @@ -934,8 +853,6 @@ public: */ int64_t IncOrderPosNext(WalletBatch *batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); DBErrors ReorderTransactions(); - bool AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment = "") EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - bool GetLabelDestination(CTxDestination &dest, const std::string& label, bool bForceNew = false); void MarkDirty(); bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true); @@ -947,15 +864,15 @@ public: CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver& reserver, bool fUpdate = false); void TransactionRemovedFromMempool(const CTransactionRef &ptx) override; void ReacceptWalletTransactions(); - void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override; + void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override EXCLUSIVE_LOCKS_REQUIRED(cs_main); // ResendWalletTransactionsBefore may only be called if fBroadcastTransactions! - std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman); + std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman) EXCLUSIVE_LOCKS_REQUIRED(cs_main); CAmount GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const; CAmount GetUnconfirmedBalance() const; CAmount GetImmatureBalance() const; CAmount GetUnconfirmedWatchOnlyBalance() const; CAmount GetImmatureWatchOnlyBalance() const; - CAmount GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account) const; + CAmount GetLegacyBalance(const isminefilter& filter, int minDepth) const; CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const; OutputType TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend); @@ -974,11 +891,8 @@ public: */ bool CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign = true); - bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, std::string fromAccount, CReserveKey& reservekey, CConnman* connman, CValidationState& state); + bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, CReserveKey& reservekey, CConnman* connman, CValidationState& state); - void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries); - bool AddAccountingEntry(const CAccountingEntry&); - bool AddAccountingEntry(const CAccountingEntry&, WalletBatch *batch); bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const { std::vector<CTxOut> v_txouts(txouts.size()); @@ -992,7 +906,7 @@ public: unsigned int m_confirm_target{DEFAULT_TX_CONFIRM_TARGET}; bool m_spend_zero_conf_change{DEFAULT_SPEND_ZEROCONF_CHANGE}; bool m_signal_rbf{DEFAULT_WALLET_RBF}; - bool m_allow_fallback_fee{true}; //<! will be defined via chainparams + bool m_allow_fallback_fee{true}; //!< will be defined via chainparams CFeeRate m_min_fee{DEFAULT_TRANSACTION_MINFEE}; //!< Override with -mintxfee /** * If fee estimation does not have enough data to provide estimates, use this fee instead. @@ -1034,10 +948,9 @@ public: const std::map<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; } std::set<std::set<CTxDestination>> GetAddressGroupings() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - std::map<CTxDestination, CAmount> GetAddressBalances(); + std::map<CTxDestination, CAmount> GetAddressBalances() EXCLUSIVE_LOCKS_REQUIRED(cs_main); std::set<CTxDestination> GetLabelAddresses(const std::string& label) const; - void DeleteLabel(const std::string& label); isminetype IsMine(const CTxIn& txin) const; /** @@ -1173,7 +1086,7 @@ public: * Obviously holding cs_main/cs_wallet when going into this call may cause * deadlock */ - void BlockUntilSyncedToCurrentChain() LOCKS_EXCLUDED(cs_wallet); + void BlockUntilSyncedToCurrentChain() LOCKS_EXCLUDED(cs_main, cs_wallet); /** * Explicitly make the wallet learn the related scripts for outputs to the @@ -1211,6 +1124,8 @@ public: LogPrintf(("%s " + fmt).c_str(), GetDisplayName(), parameters...); }; + /** Implement lookup of key origin information through wallet key metadata. */ + bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override; }; /** A key allocated from the key pool. */ @@ -1244,37 +1159,6 @@ public: void KeepScript() override { KeepKey(); } }; - -/** - * DEPRECATED Account information. - * Stored in wallet with key "acc"+string account name. - */ -class CAccount -{ -public: - CPubKey vchPubKey; - - CAccount() - { - SetNull(); - } - - void SetNull() - { - vchPubKey = CPubKey(); - } - - ADD_SERIALIZE_METHODS; - - template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action) { - int nVersion = s.GetVersion(); - if (!(s.GetType() & SER_GETHASH)) - READWRITE(nVersion); - READWRITE(vchPubKey); - } -}; - /** RAII object to check and reserve a wallet rescan */ class WalletRescanReserver { diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 1b787be198..5e85151358 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -17,6 +17,7 @@ #include <wallet/wallet.h> #include <atomic> +#include <string> #include <boost/thread.hpp> @@ -150,82 +151,6 @@ bool WalletBatch::WriteMinVersion(int nVersion) return WriteIC(std::string("minversion"), nVersion); } -bool WalletBatch::ReadAccount(const std::string& strAccount, CAccount& account) -{ - account.SetNull(); - return m_batch.Read(std::make_pair(std::string("acc"), strAccount), account); -} - -bool WalletBatch::WriteAccount(const std::string& strAccount, const CAccount& account) -{ - return WriteIC(std::make_pair(std::string("acc"), strAccount), account); -} - -bool WalletBatch::EraseAccount(const std::string& strAccount) -{ - return EraseIC(std::make_pair(std::string("acc"), strAccount)); -} - -bool WalletBatch::WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry) -{ - return WriteIC(std::make_pair(std::string("acentry"), std::make_pair(acentry.strAccount, nAccEntryNum)), acentry); -} - -CAmount WalletBatch::GetAccountCreditDebit(const std::string& strAccount) -{ - std::list<CAccountingEntry> entries; - ListAccountCreditDebit(strAccount, entries); - - CAmount nCreditDebit = 0; - for (const CAccountingEntry& entry : entries) - nCreditDebit += entry.nCreditDebit; - - return nCreditDebit; -} - -void WalletBatch::ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries) -{ - bool fAllAccounts = (strAccount == "*"); - - Dbc* pcursor = m_batch.GetCursor(); - if (!pcursor) - throw std::runtime_error(std::string(__func__) + ": cannot create DB cursor"); - bool setRange = true; - while (true) - { - // Read next record - CDataStream ssKey(SER_DISK, CLIENT_VERSION); - if (setRange) - ssKey << std::make_pair(std::string("acentry"), std::make_pair((fAllAccounts ? std::string("") : strAccount), uint64_t(0))); - CDataStream ssValue(SER_DISK, CLIENT_VERSION); - int ret = m_batch.ReadAtCursor(pcursor, ssKey, ssValue, setRange); - setRange = false; - if (ret == DB_NOTFOUND) - break; - else if (ret != 0) - { - pcursor->close(); - throw std::runtime_error(std::string(__func__) + ": error scanning DB"); - } - - // Unserialize - std::string strType; - ssKey >> strType; - if (strType != "acentry") - break; - CAccountingEntry acentry; - ssKey >> acentry.strAccount; - if (!fAllAccounts && acentry.strAccount != strAccount) - break; - - ssValue >> acentry; - ssKey >> acentry.nEntryNo; - entries.push_back(acentry); - } - - pcursor->close(); -} - class CWalletScanState { public: unsigned int nKeys; @@ -284,9 +209,10 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { char fTmp; char fUnused; - ssValue >> fTmp >> fUnused >> wtx.strFromAccount; - strErr = strprintf("LoadWallet() upgrading tx ver=%d %d '%s' %s", - wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount, hash.ToString()); + std::string unused_string; + ssValue >> fTmp >> fUnused >> unused_string; + strErr = strprintf("LoadWallet() upgrading tx ver=%d %d %s", + wtx.fTimeReceivedIsTxTime, fTmp, hash.ToString()); wtx.fTimeReceivedIsTxTime = fTmp; } else @@ -302,24 +228,6 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, pwallet->LoadToWallet(wtx); } - else if (strType == "acentry") - { - std::string strAccount; - ssKey >> strAccount; - uint64_t nNumber; - ssKey >> nNumber; - if (nNumber > pwallet->nAccountingEntryNumber) { - pwallet->nAccountingEntryNumber = nNumber; - } - - if (!wss.fAnyUnordered) - { - CAccountingEntry acentry; - ssValue >> acentry; - if (acentry.nOrderPos == -1) - wss.fAnyUnordered = true; - } - } else if (strType == "watchs") { wss.nWatchKeys++; @@ -510,7 +418,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, return false; } } else if (strType != "bestblock" && strType != "bestblock_nomerkle" && - strType != "minversion") { + strType != "minversion" && strType != "acentry") { wss.m_unknown_records++; } } catch (...) @@ -612,7 +520,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) if ((wss.nKeys + wss.nCKeys + wss.nWatchKeys) != wss.nKeyMeta) pwallet->UpdateTimeFirstKey(1); - for (uint256 hash : wss.vWalletUpgrade) + for (const uint256& hash : wss.vWalletUpgrade) WriteTx(pwallet->mapWallet.at(hash)); // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: @@ -625,12 +533,6 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) if (wss.fAnyUnordered) result = pwallet->ReorderTransactions(); - pwallet->laccentries.clear(); - ListAccountCreditDebit("*", pwallet->laccentries); - for (CAccountingEntry& entry : pwallet->laccentries) { - pwallet->wtxOrdered.insert(make_pair(entry.nOrderPos, CWallet::TxPair(nullptr, &entry))); - } - return result; } @@ -709,7 +611,7 @@ DBErrors WalletBatch::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<u // erase each matching wallet TX bool delerror = false; std::vector<uint256>::iterator it = vTxHashIn.begin(); - for (uint256 hash : vTxHash) { + for (const uint256& hash : vTxHash) { while (it < vTxHashIn.end() && (*it) < hash) { it++; } @@ -740,7 +642,7 @@ DBErrors WalletBatch::ZapWalletTx(std::vector<CWalletTx>& vWtx) return err; // erase each wallet TX - for (uint256& hash : vTxHash) { + for (const uint256& hash : vTxHash) { if (!EraseTx(hash)) return DBErrors::CORRUPT; } diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 0e6cb1bba4..5584407a56 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -31,8 +31,6 @@ static const bool DEFAULT_FLUSHWALLET = true; -class CAccount; -class CAccountingEntry; struct CBlockLocator; class CKeyPool; class CMasterKey; @@ -199,21 +197,11 @@ public: bool WriteMinVersion(int nVersion); - /// This writes directly to the database, and will not update the CWallet's cached accounting entries! - /// Use wallet.AddAccountingEntry instead, to write *and* update its caches. - bool WriteAccountingEntry(const uint64_t nAccEntryNum, const CAccountingEntry& acentry); - bool ReadAccount(const std::string& strAccount, CAccount& account); - bool WriteAccount(const std::string& strAccount, const CAccount& account); - bool EraseAccount(const std::string& strAccount); - /// Write destination data key,value tuple to database bool WriteDestData(const std::string &address, const std::string &key, const std::string &value); /// Erase destination data tuple from wallet database bool EraseDestData(const std::string &address, const std::string &key); - CAmount GetAccountCreditDebit(const std::string& strAccount); - void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& acentries); - DBErrors LoadWallet(CWallet* pwallet); DBErrors FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx); DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx); diff --git a/src/walletinitinterface.h b/src/walletinitinterface.h index e955816162..6f12551273 100644 --- a/src/walletinitinterface.h +++ b/src/walletinitinterface.h @@ -12,6 +12,8 @@ class CRPCTable; class WalletInitInterface { public: + /** Is the wallet component enabled */ + virtual bool HasWalletSupport() const = 0; /** Get wallet help string */ virtual void AddWalletOptions() const = 0; /** Check wallet parameter interaction */ @@ -34,4 +36,6 @@ public: virtual ~WalletInitInterface() {} }; +extern const WalletInitInterface& g_wallet_init_interface; + #endif // BITCOIN_WALLETINITINTERFACE_H diff --git a/src/zmq/zmqnotificationinterface.cpp b/src/zmq/zmqnotificationinterface.cpp index 8cbc969972..1d9f86d450 100644 --- a/src/zmq/zmqnotificationinterface.cpp +++ b/src/zmq/zmqnotificationinterface.cpp @@ -81,10 +81,14 @@ CZMQNotificationInterface* CZMQNotificationInterface::Create() // Called at startup to conditionally set up ZMQ socket(s) bool CZMQNotificationInterface::Initialize() { + int major = 0, minor = 0, patch = 0; + zmq_version(&major, &minor, &patch); + LogPrint(BCLog::ZMQ, "zmq: version %d.%d.%d\n", major, minor, patch); + LogPrint(BCLog::ZMQ, "zmq: Initialize notification interface\n"); assert(!pcontext); - pcontext = zmq_init(1); + pcontext = zmq_ctx_new(); if (!pcontext) { @@ -127,7 +131,7 @@ void CZMQNotificationInterface::Shutdown() LogPrint(BCLog::ZMQ, " Shutdown notifier %s at %s\n", notifier->GetType(), notifier->GetAddress()); notifier->Shutdown(); } - zmq_ctx_destroy(pcontext); + zmq_ctx_term(pcontext); pcontext = nullptr; } diff --git a/test/config.ini.in b/test/config.ini.in index a1119dc739..28abee2a3d 100644 --- a/test/config.ini.in +++ b/test/config.ini.in @@ -14,6 +14,6 @@ RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py [components] # Which components are enabled. These are commented out by `configure` if they were disabled when running config. @ENABLE_WALLET_TRUE@ENABLE_WALLET=true -@BUILD_BITCOIN_UTILS_TRUE@ENABLE_UTILS=true +@BUILD_BITCOIN_CLI_TRUE@ENABLE_CLI=true @BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=true @ENABLE_ZMQ_TRUE@ENABLE_ZMQ=true diff --git a/test/functional/README.md b/test/functional/README.md index 6929ab5991..d40052ac93 100644 --- a/test/functional/README.md +++ b/test/functional/README.md @@ -60,6 +60,11 @@ don't have test cases for. - When calling RPCs with lots of arguments, consider using named keyword arguments instead of positional arguments to make the intent of the call clear to readers. +- Many of the core test framework classes such as `CBlock` and `CTransaction` + don't allow new attributes to be added to their objects at runtime like + typical Python objects allow. This helps prevent unpredictable side effects + from typographical errors or usage of the objects outside of their intended + purpose. #### RPC and P2P definitions @@ -72,7 +77,7 @@ P2P messages. These can be found in the following source files: #### Using the P2P interface -- `mininode.py` contains all the definitions for objects that pass +- `messages.py` contains all the definitions for objects that pass over the network (`CBlock`, `CTransaction`, etc, along with the network-level wrappers for them, `msg_block`, `msg_tx`, etc). diff --git a/test/functional/combine_logs.py b/test/functional/combine_logs.py index 91b6415a7c..3230d5cb6b 100755 --- a/test/functional/combine_logs.py +++ b/test/functional/combine_logs.py @@ -13,7 +13,7 @@ import re import sys # Matches on the date format at the start of the log event -TIMESTAMP_PATTERN = re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{6}Z") +TIMESTAMP_PATTERN = re.compile(r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{6})?Z") LogEvent = namedtuple('LogEvent', ['timestamp', 'source', 'event']) @@ -25,10 +25,6 @@ def main(): parser.add_argument('--html', dest='html', action='store_true', help='outputs the combined log as html. Requires jinja2. pip install jinja2') args, unknown_args = parser.parse_known_args() - if args.color and os.name != 'posix': - print("Color output requires posix terminal colors.") - sys.exit(1) - if args.html and args.color: print("Only one out of --color or --html should be specified") sys.exit(1) @@ -75,11 +71,17 @@ def get_log_events(source, logfile): if time_match: if event: yield LogEvent(timestamp=timestamp, source=source, event=event.rstrip()) - event = line timestamp = time_match.group() + if time_match.group(1) is None: + # timestamp does not have microseconds. Add zeroes. + timestamp_micro = timestamp.replace("Z", ".000000Z") + line = line.replace(timestamp, timestamp_micro) + timestamp = timestamp_micro + event = line # if it doesn't have a timestamp, it's a continuation line of the previous log. else: - event += "\n" + line + # Add the line. Prefix with space equivalent to the source + timestamp so log lines are aligned + event += " " + line # Flush the final event yield LogEvent(timestamp=timestamp, source=source, event=event.rstrip()) except FileNotFoundError: @@ -98,7 +100,11 @@ def print_logs(log_events, color=False, html=False): colors["reset"] = "\033[0m" # Reset font color for event in log_events: - print("{0} {1: <5} {2} {3}".format(colors[event.source.rstrip()], event.source, event.event, colors["reset"])) + lines = event.event.splitlines() + print("{0} {1: <5} {2} {3}".format(colors[event.source.rstrip()], event.source, lines[0], colors["reset"])) + if len(lines) > 1: + for line in lines[1:]: + print("{0}{1}{2}".format(colors[event.source.rstrip()], line, colors["reset"])) else: try: diff --git a/test/functional/data/rpc_psbt.json b/test/functional/data/rpc_psbt.json index 9f970b4961..dd40a096de 100644 --- a/test/functional/data/rpc_psbt.json +++ b/test/functional/data/rpc_psbt.json @@ -69,14 +69,6 @@ }, { "privkeys" : [ - "cT7J9YpCwY3AVRFSjN6ukeEeWY6mhpbJPxRaDaP5QTdygQRxP9Au", - "cNBc3SWUip9PPm1GjRoLEJT6T41iNzCYtD7qro84FMnM5zEqeJsE" - ], - "psbt" : "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAQMEAQAAAAABASAAwusLAAAAABepFLf1+vQOPUClpFmx2zU18rcvqSHohwEEIgAgjCNTFzdDtZXftKB7crqOQuN5fadOh/59nXSX47ICiQMBBUdSIQMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3CECOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNSriIGAjrdkE89bc9Z3bkGsN7iNSm3/7ntUOXoYVGSaGAiHw5zENkMak8AAACAAAAAgAMAAIAiBgMIncEMesbbVPkTKa9hczPbOIzq0MIx9yM3nRuZAwsC3BDZDGpPAAAAgAAAAIACAACAAQMEAQAAAAAiAgOppMN/WZbTqiXbrGtXCvBlA5RJKUJGCzVHU+2e7KWHcRDZDGpPAAAAgAAAAIAEAACAACICAn9jmXV9Lv9VoTatAsaEsYOLZVbl8bazQoKpS2tQBRCWENkMak8AAACAAAAAgAUAAIAA", - "result" : "cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWABTYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFgAUAK6pouXw+HaliN9VRuh0LR2HAI8AAAAAAAEAuwIAAAABqtc5MQGL0l+ErkALaISL4J23BurCrBgpi6vucatlb4sAAAAASEcwRAIgWPb8fGoz4bMVSNSByCbAFb0wE1qtQs1neQ2rZtKtJDsCIEoc7SYExnNbY5PltBaR3XiwDwxZQvufdRhW+qk4FX26Af7///8CgPD6AgAAAAAXqRQPuUY0IWlrgsgzryQceMF9295JNIfQ8gonAQAAABepFCnKdPigj4GZlCgYXJe12FLkBj9hh2UAAAAiAgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU210cwRAIgYxqYn+c4qSrQGYYCMxLBkhT+KAKznly8GsNniAbGksMCIDnbbDh70mdxbf2z1NjaULjoXSEzJrp8faqkwM5B65IjAQEDBAEAAAABBEdSIQKVg785rgpgl0etGZrd1jT6YQhVnWxc05tMIYPxq5bgfyEC2rYf9JoU22p9ArDNH7t4/EsYMStbTlTa5Nui+/71NtdSriIGApWDvzmuCmCXR60Zmt3WNPphCFWdbFzTm0whg/GrluB/ENkMak8AAACAAAAAgAAAAIAiBgLath/0mhTban0CsM0fu3j8SxgxK1tOVNrk26L7/vU21xDZDGpPAAAAgAAAAIABAACAAAEBIADC6wsAAAAAF6kUt/X69A49QKWkWbHbNTXyty+pIeiHIgICOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnNHMEQCIGX0W6WZi1mif/4ae+0BavHx+Q1Us6qPdFCqX1aiUQO9AiB/ckcDrR7blmgLKEtW1P/LiPf7dZ6rvgiqMPKbhROD0gEBAwQBAAAAAQQiACCMI1MXN0O1ld+0oHtyuo5C43l9p06H/n2ddJfjsgKJAwEFR1IhAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcIQI63ZBPPW3PWd25BrDe4jUpt/+57VDl6GFRkmhgIh8Oc1KuIgYCOt2QTz1tz1nduQaw3uI1Kbf/ue1Q5ehhUZJoYCIfDnMQ2QxqTwAAAIAAAACAAwAAgCIGAwidwQx6xttU+RMpr2FzM9s4jOrQwjH3IzedG5kDCwLcENkMak8AAACAAAAAgAIAAIAAIgIDqaTDf1mW06ol26xrVwrwZQOUSSlCRgs1R1Ptnuylh3EQ2QxqTwAAAIAAAACABAAAgAAiAgJ/Y5l1fS7/VaE2rQLGhLGDi2VW5fG2s0KCqUtrUAUQlhDZDGpPAAAAgAAAAIAFAACAAA==" - }, - { - "privkeys" : [ "cNBc3SWUip9PPm1GjRoLEJT6T41iNzCYtD7qro84FMnM5zEqeJsE" ], "psbt" : "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEBItPf9QUAAAAAGXapFNSO0xELlAFMsRS9Mtb00GbcdCVriKwAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIACICAurVlmh8qAYEPtw94RbN8p1eklfBls0FXPaYyNAr8k6ZELSmumcAAACAAAAAgAIAAIAAIgIDlPYr6d8ZlSxVh3aK63aYBhrSxKJciU9H2MFitNchPQUQtKa6ZwAAAIABAACAAgAAgAA=", diff --git a/test/functional/example_test.py b/test/functional/example_test.py index a8c1474876..3f15367a75 100755 --- a/test/functional/example_test.py +++ b/test/functional/example_test.py @@ -67,16 +67,17 @@ def custom_function(): # self.log.info("running custom_function") # Oops! Can't run self.log outside the BitcoinTestFramework pass + class ExampleTest(BitcoinTestFramework): # Each functional test is a subclass of the BitcoinTestFramework class. - # Override the set_test_params(), add_options(), setup_chain(), setup_network() + # Override the set_test_params(), skip_test_if_missing_module(), add_options(), setup_chain(), setup_network() # and setup_nodes() methods to customize the test setup as required. def set_test_params(self): """Override test parameters for your individual test. - This method must be overridden and num_nodes must be exlicitly set.""" + This method must be overridden and num_nodes must be explicitly set.""" self.setup_clean_chain = True self.num_nodes = 3 # Use self.extra_args to change command-line arguments for the nodes @@ -84,6 +85,11 @@ class ExampleTest(BitcoinTestFramework): # self.log.info("I've finished set_test_params") # Oops! Can't run self.log before run_test() + # Use skip_test_if_missing_module() to skip the test if your test requires certain modules to be present. + # This test uses generate which requires wallet to be compiled + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + # Use add_options() to add specific command-line options for your test. # In practice this is not used very much, since the tests are mostly written # to be run in automated environments without command-line options. @@ -111,7 +117,7 @@ class ExampleTest(BitcoinTestFramework): # sync_all() should not include node2, since we're not expecting it to # sync. connect_nodes(self.nodes[0], 1) - self.sync_all([self.nodes[0:1]]) + self.sync_all([self.nodes[0:2]]) # Use setup_nodes() to customize the node start behaviour (for example if # you don't want to start all nodes at the start of the test). @@ -135,7 +141,7 @@ class ExampleTest(BitcoinTestFramework): # Generating a block on one of the nodes will get us out of IBD blocks = [int(self.nodes[0].generate(nblocks=1)[0], 16)] - self.sync_all([self.nodes[0:1]]) + self.sync_all([self.nodes[0:2]]) # Notice above how we called an RPC by calling a method with the same # name on the node object. Notice also how we used a keyword argument diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py index 4a9445f503..8466f851ca 100755 --- a/test/functional/feature_bip68_sequence.py +++ b/test/functional/feature_bip68_sequence.py @@ -25,6 +25,9 @@ class BIP68Test(BitcoinTestFramework): self.num_nodes = 2 self.extra_args = [[], ["-acceptnonstdtxn=0"]] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"] diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index 79ed902871..628cefb76d 100755 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -169,7 +169,7 @@ class FullBlockTest(BitcoinTestFramework): self.log.info("Reject a block where the miner creates too much coinbase reward") self.move_tip(6) b9 = self.next_block(9, spend=out[4], additional_coinbase_value=1) - self.sync_blocks([b9], False, 16, b'bad-cb-amount', reconnect=True) + self.sync_blocks([b9], success=False, reject_reason='bad-cb-amount', reconnect=True) # Create a fork that ends in a block with too much fee (the one that causes the reorg) # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -181,7 +181,7 @@ class FullBlockTest(BitcoinTestFramework): self.sync_blocks([b10], False) b11 = self.next_block(11, spend=out[4], additional_coinbase_value=1) - self.sync_blocks([b11], False, 16, b'bad-cb-amount', reconnect=True) + self.sync_blocks([b11], success=False, reject_reason='bad-cb-amount', reconnect=True) # Try again, but with a valid fork first # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -194,7 +194,7 @@ class FullBlockTest(BitcoinTestFramework): b13 = self.next_block(13, spend=out[4]) self.save_spendable_output() b14 = self.next_block(14, spend=out[5], additional_coinbase_value=1) - self.sync_blocks([b12, b13, b14], False, 16, b'bad-cb-amount', reconnect=True) + self.sync_blocks([b12, b13, b14], success=False, reject_reason='bad-cb-amount', reconnect=True) # New tip should be b13. assert_equal(node.getbestblockhash(), b13.hash) @@ -213,7 +213,7 @@ class FullBlockTest(BitcoinTestFramework): self.log.info("Reject a block with too many checksigs") too_many_checksigs = CScript([OP_CHECKSIG] * (MAX_BLOCK_SIGOPS)) b16 = self.next_block(16, spend=out[6], script=too_many_checksigs) - self.sync_blocks([b16], False, 16, b'bad-blk-sigops', reconnect=True) + self.sync_blocks([b16], success=False, reject_reason='bad-blk-sigops', reconnect=True) # Attempt to spend a transaction created on a different fork # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -222,7 +222,7 @@ class FullBlockTest(BitcoinTestFramework): self.log.info("Reject a block with a spend from a re-org'ed out tx") self.move_tip(15) b17 = self.next_block(17, spend=txout_b3) - self.sync_blocks([b17], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) + self.sync_blocks([b17], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) # Attempt to spend a transaction created on a different fork (on a fork this time) # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -235,7 +235,7 @@ class FullBlockTest(BitcoinTestFramework): self.sync_blocks([b18], False) b19 = self.next_block(19, spend=out[6]) - self.sync_blocks([b19], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) + self.sync_blocks([b19], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) # Attempt to spend a coinbase at depth too low # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -244,7 +244,7 @@ class FullBlockTest(BitcoinTestFramework): self.log.info("Reject a block spending an immature coinbase.") self.move_tip(15) b20 = self.next_block(20, spend=out[7]) - self.sync_blocks([b20], False, 16, b'bad-txns-premature-spend-of-coinbase') + self.sync_blocks([b20], success=False, reject_reason='bad-txns-premature-spend-of-coinbase') # Attempt to spend a coinbase at depth too low (on a fork this time) # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -257,7 +257,7 @@ class FullBlockTest(BitcoinTestFramework): self.sync_blocks([b21], False) b22 = self.next_block(22, spend=out[5]) - self.sync_blocks([b22], False, 16, b'bad-txns-premature-spend-of-coinbase') + self.sync_blocks([b22], success=False, reject_reason='bad-txns-premature-spend-of-coinbase') # Create a block on either side of MAX_BLOCK_BASE_SIZE and make sure its accepted/rejected # genesis -> b1 (0) -> b2 (1) -> b5 (2) -> b6 (3) @@ -286,7 +286,7 @@ class FullBlockTest(BitcoinTestFramework): tx.vout = [CTxOut(0, script_output)] b24 = self.update_block(24, [tx]) assert_equal(len(b24.serialize()), MAX_BLOCK_BASE_SIZE + 1) - self.sync_blocks([b24], False, 16, b'bad-blk-length', reconnect=True) + self.sync_blocks([b24], success=False, reject_reason='bad-blk-length', reconnect=True) b25 = self.next_block(25, spend=out[7]) self.sync_blocks([b25], False) @@ -304,7 +304,7 @@ class FullBlockTest(BitcoinTestFramework): # update_block causes the merkle root to get updated, even with no new # transactions, and updates the required state. b26 = self.update_block(26, []) - self.sync_blocks([b26], False, 16, b'bad-cb-length', reconnect=True) + self.sync_blocks([b26], success=False, reject_reason='bad-cb-length', reconnect=True) # Extend the b26 chain to make sure bitcoind isn't accepting b26 b27 = self.next_block(27, spend=out[7]) @@ -316,7 +316,7 @@ class FullBlockTest(BitcoinTestFramework): b28.vtx[0].vin[0].scriptSig = b'\x00' * 101 b28.vtx[0].rehash() b28 = self.update_block(28, []) - self.sync_blocks([b28], False, 16, b'bad-cb-length', reconnect=True) + self.sync_blocks([b28], success=False, reject_reason='bad-cb-length', reconnect=True) # Extend the b28 chain to make sure bitcoind isn't accepting b28 b29 = self.next_block(29, spend=out[7]) @@ -352,7 +352,7 @@ class FullBlockTest(BitcoinTestFramework): too_many_multisigs = CScript([OP_CHECKMULTISIG] * (MAX_BLOCK_SIGOPS // 20)) b32 = self.next_block(32, spend=out[9], script=too_many_multisigs) assert_equal(get_legacy_sigopcount_block(b32), MAX_BLOCK_SIGOPS + 1) - self.sync_blocks([b32], False, 16, b'bad-blk-sigops', reconnect=True) + self.sync_blocks([b32], success=False, reject_reason='bad-blk-sigops', reconnect=True) # CHECKMULTISIGVERIFY self.log.info("Accept a block with the max number of OP_CHECKMULTISIGVERIFY sigops") @@ -365,7 +365,7 @@ class FullBlockTest(BitcoinTestFramework): self.log.info("Reject a block with too many OP_CHECKMULTISIGVERIFY sigops") too_many_multisigs = CScript([OP_CHECKMULTISIGVERIFY] * (MAX_BLOCK_SIGOPS // 20)) b34 = self.next_block(34, spend=out[10], script=too_many_multisigs) - self.sync_blocks([b34], False, 16, b'bad-blk-sigops', reconnect=True) + self.sync_blocks([b34], success=False, reject_reason='bad-blk-sigops', reconnect=True) # CHECKSIGVERIFY self.log.info("Accept a block with the max number of OP_CHECKSIGVERIFY sigops") @@ -378,7 +378,7 @@ class FullBlockTest(BitcoinTestFramework): self.log.info("Reject a block with too many OP_CHECKSIGVERIFY sigops") too_many_checksigs = CScript([OP_CHECKSIGVERIFY] * (MAX_BLOCK_SIGOPS)) b36 = self.next_block(36, spend=out[11], script=too_many_checksigs) - self.sync_blocks([b36], False, 16, b'bad-blk-sigops', reconnect=True) + self.sync_blocks([b36], success=False, reject_reason='bad-blk-sigops', reconnect=True) # Check spending of a transaction in a block which failed to connect # @@ -395,12 +395,12 @@ class FullBlockTest(BitcoinTestFramework): txout_b37 = b37.vtx[1] tx = self.create_and_sign_transaction(out[11], 0) b37 = self.update_block(37, [tx]) - self.sync_blocks([b37], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) + self.sync_blocks([b37], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) # attempt to spend b37's first non-coinbase tx, at which point b37 was still considered valid self.move_tip(35) b38 = self.next_block(38, spend=txout_b37) - self.sync_blocks([b38], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) + self.sync_blocks([b38], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) # Check P2SH SigOp counting # @@ -492,7 +492,7 @@ class FullBlockTest(BitcoinTestFramework): tx.rehash() new_txs.append(tx) self.update_block(40, new_txs) - self.sync_blocks([b40], False, 16, b'bad-blk-sigops', reconnect=True) + self.sync_blocks([b40], success=False, reject_reason='bad-blk-sigops', reconnect=True) # same as b40, but one less sigop self.log.info("Accept a block with the max number of P2SH sigops") @@ -555,7 +555,7 @@ class FullBlockTest(BitcoinTestFramework): self.block_heights[b45.sha256] = self.block_heights[self.tip.sha256] + 1 self.tip = b45 self.blocks[45] = b45 - self.sync_blocks([b45], False, 16, b'bad-cb-missing', reconnect=True) + self.sync_blocks([b45], success=False, reject_reason='bad-cb-missing', reconnect=True) self.log.info("Reject a block with no transactions") self.move_tip(44) @@ -570,7 +570,7 @@ class FullBlockTest(BitcoinTestFramework): self.tip = b46 assert 46 not in self.blocks self.blocks[46] = b46 - self.sync_blocks([b46], False, 16, b'bad-blk-length', reconnect=True) + self.sync_blocks([b46], success=False, reject_reason='bad-blk-length', reconnect=True) self.log.info("Reject a block with invalid work") self.move_tip(44) @@ -593,7 +593,7 @@ class FullBlockTest(BitcoinTestFramework): b49 = self.next_block(49) b49.hashMerkleRoot += 1 b49.solve() - self.sync_blocks([b49], False, 16, b'bad-txnmrklroot', reconnect=True) + self.sync_blocks([b49], success=False, reject_reason='bad-txnmrklroot', reconnect=True) self.log.info("Reject a block with incorrect POW limit") self.move_tip(44) @@ -607,7 +607,7 @@ class FullBlockTest(BitcoinTestFramework): b51 = self.next_block(51) cb2 = create_coinbase(51, self.coinbase_pubkey) b51 = self.update_block(51, [cb2]) - self.sync_blocks([b51], False, 16, b'bad-cb-multiple', reconnect=True) + self.sync_blocks([b51], success=False, reject_reason='bad-cb-multiple', reconnect=True) self.log.info("Reject a block with duplicate transactions") # Note: txns have to be in the right position in the merkle tree to trigger this error @@ -615,7 +615,7 @@ class FullBlockTest(BitcoinTestFramework): b52 = self.next_block(52, spend=out[15]) tx = self.create_tx(b52.vtx[1], 0, 1) b52 = self.update_block(52, [tx, tx]) - self.sync_blocks([b52], False, 16, b'bad-txns-duplicate', reconnect=True) + self.sync_blocks([b52], success=False, reject_reason='bad-txns-duplicate', reconnect=True) # Test block timestamps # -> b31 (8) -> b33 (9) -> b35 (10) -> b39 (11) -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) @@ -682,7 +682,7 @@ class FullBlockTest(BitcoinTestFramework): assert_equal(len(b56.vtx), 3) b56 = self.update_block(56, [tx1]) assert_equal(b56.hash, b57.hash) - self.sync_blocks([b56], False, 16, b'bad-txns-duplicate', reconnect=True) + self.sync_blocks([b56], success=False, reject_reason='bad-txns-duplicate', reconnect=True) # b57p2 - a good block with 6 tx'es, don't submit until end self.move_tip(55) @@ -702,7 +702,7 @@ class FullBlockTest(BitcoinTestFramework): assert_equal(b56p2.hash, b57p2.hash) assert_equal(len(b56p2.vtx), 6) b56p2 = self.update_block("b56p2", [tx3, tx4]) - self.sync_blocks([b56p2], False, 16, b'bad-txns-duplicate', reconnect=True) + self.sync_blocks([b56p2], success=False, reject_reason='bad-txns-duplicate', reconnect=True) self.move_tip("57p2") self.sync_blocks([b57p2], True) @@ -727,7 +727,7 @@ class FullBlockTest(BitcoinTestFramework): tx.vout.append(CTxOut(0, b"")) tx.calc_sha256() b58 = self.update_block(58, [tx]) - self.sync_blocks([b58], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) + self.sync_blocks([b58], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) # tx with output value > input value self.log.info("Reject a block with a transaction with outputs > inputs") @@ -735,7 +735,7 @@ class FullBlockTest(BitcoinTestFramework): b59 = self.next_block(59) tx = self.create_and_sign_transaction(out[17], 51 * COIN) b59 = self.update_block(59, [tx]) - self.sync_blocks([b59], False, 16, b'bad-txns-in-belowout', reconnect=True) + self.sync_blocks([b59], success=False, reject_reason='bad-txns-in-belowout', reconnect=True) # reset to good chain self.move_tip(57) @@ -759,7 +759,7 @@ class FullBlockTest(BitcoinTestFramework): b61.vtx[0].rehash() b61 = self.update_block(61, []) assert_equal(b60.vtx[0].serialize(), b61.vtx[0].serialize()) - self.sync_blocks([b61], False, 16, b'bad-txns-BIP30', reconnect=True) + self.sync_blocks([b61], success=False, reject_reason='bad-txns-BIP30', reconnect=True) # Test tx.isFinal is properly rejected (not an exhaustive tx.isFinal test, that should be in data-driven transaction tests) # @@ -776,7 +776,7 @@ class FullBlockTest(BitcoinTestFramework): assert(tx.vin[0].nSequence < 0xffffffff) tx.calc_sha256() b62 = self.update_block(62, [tx]) - self.sync_blocks([b62], False, 16, b'bad-txns-nonfinal') + self.sync_blocks([b62], success=False, reject_reason='bad-txns-nonfinal') # Test a non-final coinbase is also rejected # @@ -790,7 +790,7 @@ class FullBlockTest(BitcoinTestFramework): b63.vtx[0].vin[0].nSequence = 0xDEADBEEF b63.vtx[0].rehash() b63 = self.update_block(63, []) - self.sync_blocks([b63], False, 16, b'bad-txns-nonfinal') + self.sync_blocks([b63], success=False, reject_reason='bad-txns-nonfinal') # This checks that a block with a bloated VARINT between the block_header and the array of tx such that # the block is > MAX_BLOCK_BASE_SIZE with the bloated varint, but <= MAX_BLOCK_BASE_SIZE without the bloated varint, @@ -824,7 +824,7 @@ class FullBlockTest(BitcoinTestFramework): tx.vin.append(CTxIn(COutPoint(b64a.vtx[1].sha256, 0))) b64a = self.update_block("64a", [tx]) assert_equal(len(b64a.serialize()), MAX_BLOCK_BASE_SIZE + 8) - self.sync_blocks([b64a], False, 1, b'error parsing message') + self.sync_blocks([b64a], success=False, reject_reason='non-canonical ReadCompactSize():') # bitcoind doesn't disconnect us for sending a bloated block, but if we subsequently # resend the header message, it won't send us the getdata message again. Just @@ -866,7 +866,7 @@ class FullBlockTest(BitcoinTestFramework): tx1 = self.create_and_sign_transaction(out[20], out[20].vout[0].nValue) tx2 = self.create_and_sign_transaction(tx1, 1) b66 = self.update_block(66, [tx2, tx1]) - self.sync_blocks([b66], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) + self.sync_blocks([b66], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) # Attempt to double-spend a transaction created in a block # @@ -881,7 +881,7 @@ class FullBlockTest(BitcoinTestFramework): tx2 = self.create_and_sign_transaction(tx1, 1) tx3 = self.create_and_sign_transaction(tx1, 2) b67 = self.update_block(67, [tx1, tx2, tx3]) - self.sync_blocks([b67], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) + self.sync_blocks([b67], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) # More tests of block subsidy # @@ -900,7 +900,7 @@ class FullBlockTest(BitcoinTestFramework): b68 = self.next_block(68, additional_coinbase_value=10) tx = self.create_and_sign_transaction(out[20], out[20].vout[0].nValue - 9) b68 = self.update_block(68, [tx]) - self.sync_blocks([b68], False, 16, b'bad-cb-amount', reconnect=True) + self.sync_blocks([b68], success=False, reject_reason='bad-cb-amount', reconnect=True) self.log.info("Accept a block claiming the correct subsidy in the coinbase transaction") self.move_tip(65) @@ -924,7 +924,7 @@ class FullBlockTest(BitcoinTestFramework): tx.vin.append(CTxIn(COutPoint(bogus_tx.sha256, 0), b"", 0xffffffff)) tx.vout.append(CTxOut(1, b"")) b70 = self.update_block(70, [tx]) - self.sync_blocks([b70], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) + self.sync_blocks([b70], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) # Test accepting an invalid block which has the same hash as a valid one (via merkle tree tricks) # @@ -949,7 +949,7 @@ class FullBlockTest(BitcoinTestFramework): assert_equal(b72.sha256, b71.sha256) self.move_tip(71) - self.sync_blocks([b71], False, 16, b'bad-txns-duplicate', reconnect=True) + self.sync_blocks([b71], success=False, reject_reason='bad-txns-duplicate', reconnect=True) self.move_tip(72) self.sync_blocks([b72], True) @@ -987,9 +987,9 @@ class FullBlockTest(BitcoinTestFramework): tx = self.create_and_sign_transaction(out[22], 1, CScript(a)) b73 = self.update_block(73, [tx]) assert_equal(get_legacy_sigopcount_block(b73), MAX_BLOCK_SIGOPS + 1) - self.sync_blocks([b73], False, 16, b'bad-blk-sigops', reconnect=True) + self.sync_blocks([b73], success=False, reject_reason='bad-blk-sigops', reconnect=True) - # b74/75 - if we push an invalid script element, all prevous sigops are counted, + # b74/75 - if we push an invalid script element, all previous sigops are counted, # but sigops after the element are not counted. # # The invalid script element is that the push_data indicates that @@ -1011,7 +1011,7 @@ class FullBlockTest(BitcoinTestFramework): a[MAX_BLOCK_SIGOPS + 4] = 0xff tx = self.create_and_sign_transaction(out[22], 1, CScript(a)) b74 = self.update_block(74, [tx]) - self.sync_blocks([b74], False, 16, b'bad-blk-sigops', reconnect=True) + self.sync_blocks([b74], success=False, reject_reason='bad-blk-sigops', reconnect=True) self.move_tip(72) b75 = self.next_block(75) @@ -1160,7 +1160,7 @@ class FullBlockTest(BitcoinTestFramework): b89a = self.next_block("89a", spend=out[32]) tx = self.create_tx(tx1, 0, 0, CScript([OP_TRUE])) b89a = self.update_block("89a", [tx]) - self.sync_blocks([b89a], False, 16, b'bad-txns-inputs-missingorspent', reconnect=True) + self.sync_blocks([b89a], success=False, reject_reason='bad-txns-inputs-missingorspent', reconnect=True) self.log.info("Test a re-org of one week's worth of blocks (1088 blocks)") @@ -1309,11 +1309,11 @@ class FullBlockTest(BitcoinTestFramework): self.nodes[0].disconnect_p2ps() self.bootstrap_p2p() - def sync_blocks(self, blocks, success=True, reject_code=None, reject_reason=None, request_block=True, reconnect=False, timeout=60): + def sync_blocks(self, blocks, success=True, reject_reason=None, request_block=True, reconnect=False, timeout=60): """Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block. Call with success = False if the tip shouldn't advance to the most recent block.""" - self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_code=reject_code, reject_reason=reject_reason, request_block=request_block, timeout=timeout) + self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_reason=reject_reason, request_block=request_block, timeout=timeout, expect_disconnect=reconnect) if reconnect: self.reconnect_p2p() diff --git a/test/functional/feature_blocksdir.py b/test/functional/feature_blocksdir.py index 784dc2865f..c170f510c8 100755 --- a/test/functional/feature_blocksdir.py +++ b/test/functional/feature_blocksdir.py @@ -27,7 +27,7 @@ class BlocksdirTest(BitcoinTestFramework): self.log.info("Starting with existing blocksdir ...") self.start_node(0, ["-blocksdir=" + blocksdir_path]) self.log.info("mining blocks..") - self.nodes[0].generate(10) + self.nodes[0].generatetoaddress(10, self.nodes[0].get_deterministic_priv_key().address) assert os.path.isfile(os.path.join(blocksdir_path, "regtest", "blocks", "blk00000.dat")) assert os.path.isdir(os.path.join(self.nodes[0].datadir, "regtest", "blocks", "index")) diff --git a/test/functional/feature_cltv.py b/test/functional/feature_cltv.py index 8460291831..f84b08a199 100755 --- a/test/functional/feature_cltv.py +++ b/test/functional/feature_cltv.py @@ -10,10 +10,14 @@ Test that the CHECKLOCKTIMEVERIFY soft-fork activates at (regtest) block height from test_framework.blocktools import create_coinbase, create_block, create_transaction from test_framework.messages import CTransaction, msg_block, ToHex -from test_framework.mininode import mininode_lock, P2PInterface +from test_framework.mininode import P2PInterface from test_framework.script import CScript, OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP, CScriptNum from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, bytes_to_hex_str, hex_str_to_bytes, wait_until +from test_framework.util import ( + assert_equal, + bytes_to_hex_str, + hex_str_to_bytes, +) from io import BytesIO @@ -51,12 +55,16 @@ def cltv_validate(node, tx, height): list(CScript(new_tx.vin[0].scriptSig))) return new_tx + class BIP65Test(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 - self.extra_args = [['-whitelist=127.0.0.1']] + self.extra_args = [['-whitelist=127.0.0.1', '-par=1']] # Use only one script thread to get the exact reject reason for testing self.setup_clean_chain = True + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): self.nodes[0].add_p2p_connection(P2PInterface()) @@ -88,15 +96,11 @@ class BIP65Test(BitcoinTestFramework): block = create_block(tip, create_coinbase(CLTV_HEIGHT), block_time) block.nVersion = 3 block.solve() - self.nodes[0].p2p.send_and_ping(msg_block(block)) - assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) - wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) - with mininode_lock: - assert_equal(self.nodes[0].p2p.last_message["reject"].code, REJECT_OBSOLETE) - assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'bad-version(0x00000003)') - assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256) - del self.nodes[0].p2p.last_message["reject"] + with self.nodes[0].assert_debug_log(expected_msgs=['{}, bad-version(0x00000003)'.format(block.hash)]): + self.nodes[0].p2p.send_and_ping(msg_block(block)) + assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) + self.nodes[0].p2p.sync_with_ping() self.log.info("Test that invalid-according-to-cltv transactions cannot appear in a block") block.nVersion = 4 @@ -118,18 +122,10 @@ class BIP65Test(BitcoinTestFramework): block.hashMerkleRoot = block.calc_merkle_root() block.solve() - self.nodes[0].p2p.send_and_ping(msg_block(block)) - assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) - - wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) - with mininode_lock: - assert self.nodes[0].p2p.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD] - assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256) - if self.nodes[0].p2p.last_message["reject"].code == REJECT_INVALID: - # Generic rejection when a block is invalid - assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'block-validation-failed') - else: - assert b'Negative locktime' in self.nodes[0].p2p.last_message["reject"].reason + with self.nodes[0].assert_debug_log(expected_msgs=['CheckInputs on {} failed with non-mandatory-script-verify-flag (Negative locktime)'.format(block.vtx[-1].hash)]): + self.nodes[0].p2p.send_and_ping(msg_block(block)) + assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) + self.nodes[0].p2p.sync_with_ping() self.log.info("Test that a version 4 block with a valid-according-to-CLTV transaction is accepted") spendtx = cltv_validate(self.nodes[0], spendtx, CLTV_HEIGHT - 1) diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py index 62091048f9..1124119e2b 100755 --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -14,8 +14,32 @@ class ConfArgsTest(BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 1 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + + def test_config_file_parser(self): + # Assume node is stopped + + inc_conf_file_path = os.path.join(self.nodes[0].datadir, 'include.conf') + with open(os.path.join(self.nodes[0].datadir, 'bitcoin.conf'), 'a', encoding='utf-8') as conf: + conf.write('includeconf={}\n'.format(inc_conf_file_path)) + + with open(inc_conf_file_path, 'w', encoding='utf-8') as conf: + conf.write('-dash=1\n') + self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 1: -dash=1, options in configuration file must be specified without leading -') + + with open(inc_conf_file_path, 'w', encoding='utf-8') as conf: + conf.write('nono\n') + self.nodes[0].assert_start_raises_init_error(expected_msg='Error reading configuration file: parse error on line 1: nono, if you intended to specify a negated option, use nono=1 instead') + + with open(inc_conf_file_path, 'w', encoding='utf-8') as conf: + conf.write('') # clear + def run_test(self): self.stop_node(0) + + self.test_config_file_parser() + # Remove the -datadir argument so it doesn't override the config file self.nodes[0].args = [arg for arg in self.nodes[0].args if not arg.startswith("-datadir")] diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py index af14feb471..df79c4312c 100755 --- a/test/functional/feature_csv_activation.py +++ b/test/functional/feature_csv_activation.py @@ -145,6 +145,9 @@ class BIP68_112_113Test(BitcoinTestFramework): self.setup_clean_chain = True self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=4', '-addresstype=legacy']] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def generate_blocks(self, number, version, test_blocks=None): if test_blocks is None: test_blocks = [] @@ -165,11 +168,11 @@ class BIP68_112_113Test(BitcoinTestFramework): block.solve() return block - def sync_blocks(self, blocks, success=True, reject_code=None, reject_reason=None, request_block=True): + def sync_blocks(self, blocks, success=True): """Sends blocks to test node. Syncs and verifies that tip has advanced to most recent block. Call with success = False if the tip shouldn't advance to the most recent block.""" - self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success, reject_code=reject_code, reject_reason=reject_reason, request_block=request_block) + self.nodes[0].p2p.send_blocks_and_test(blocks, self.nodes[0], success=success) def run_test(self): self.nodes[0].add_p2p_connection(P2PDataStore()) diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py index d612dbe2bc..ae1eacf2d7 100755 --- a/test/functional/feature_dbcrash.py +++ b/test/functional/feature_dbcrash.py @@ -63,6 +63,9 @@ class ChainstateWriteCrashTest(BitcoinTestFramework): self.node3_args = ["-blockmaxweight=4000000"] self.extra_args = [self.node0_args, self.node1_args, self.node2_args, self.node3_args] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def setup_network(self): self.add_nodes(self.num_nodes, extra_args=self.extra_args) self.start_nodes() diff --git a/test/functional/feature_dersig.py b/test/functional/feature_dersig.py index 53e94c436a..16272877e7 100755 --- a/test/functional/feature_dersig.py +++ b/test/functional/feature_dersig.py @@ -12,7 +12,11 @@ from test_framework.messages import msg_block from test_framework.mininode import mininode_lock, P2PInterface from test_framework.script import CScript from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, bytes_to_hex_str, wait_until +from test_framework.util import ( + assert_equal, + bytes_to_hex_str, + wait_until, +) DERSIG_HEIGHT = 1251 @@ -42,9 +46,12 @@ def unDERify(tx): class BIP66Test(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 - self.extra_args = [['-whitelist=127.0.0.1']] + self.extra_args = [['-whitelist=127.0.0.1', '-par=1', '-enablebip61']] # Use only one script thread to get the exact reject reason for testing self.setup_clean_chain = True + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): self.nodes[0].add_p2p_connection(P2PInterface()) @@ -78,15 +85,11 @@ class BIP66Test(BitcoinTestFramework): block.nVersion = 2 block.rehash() block.solve() - self.nodes[0].p2p.send_and_ping(msg_block(block)) - assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) - wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) - with mininode_lock: - assert_equal(self.nodes[0].p2p.last_message["reject"].code, REJECT_OBSOLETE) - assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'bad-version(0x00000002)') - assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256) - del self.nodes[0].p2p.last_message["reject"] + with self.nodes[0].assert_debug_log(expected_msgs=['{}, bad-version(0x00000002)'.format(block.hash)]): + self.nodes[0].p2p.send_and_ping(msg_block(block)) + assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) + self.nodes[0].p2p.sync_with_ping() self.log.info("Test that transactions with non-DER signatures cannot appear in a block") block.nVersion = 3 @@ -109,23 +112,16 @@ class BIP66Test(BitcoinTestFramework): block.rehash() block.solve() - self.nodes[0].p2p.send_and_ping(msg_block(block)) - assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) + with self.nodes[0].assert_debug_log(expected_msgs=['CheckInputs on {} failed with non-mandatory-script-verify-flag (Non-canonical DER signature)'.format(block.vtx[-1].hash)]): + self.nodes[0].p2p.send_and_ping(msg_block(block)) + assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) + self.nodes[0].p2p.sync_with_ping() wait_until(lambda: "reject" in self.nodes[0].p2p.last_message.keys(), lock=mininode_lock) with mininode_lock: - # We can receive different reject messages depending on whether - # bitcoind is running with multiple script check threads. If script - # check threads are not in use, then transaction script validation - # happens sequentially, and bitcoind produces more specific reject - # reasons. assert self.nodes[0].p2p.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD] assert_equal(self.nodes[0].p2p.last_message["reject"].data, block.sha256) - if self.nodes[0].p2p.last_message["reject"].code == REJECT_INVALID: - # Generic rejection when a block is invalid - assert_equal(self.nodes[0].p2p.last_message["reject"].reason, b'block-validation-failed') - else: - assert b'Non-canonical DER signature' in self.nodes[0].p2p.last_message["reject"].reason + assert b'Non-canonical DER signature' in self.nodes[0].p2p.last_message["reject"].reason self.log.info("Test that a version 3 block with a DERSIG-compliant transaction is accepted") block.vtx[1] = create_transaction(self.nodes[0], self.coinbase_txids[1], self.nodeaddress, amount=1.0) diff --git a/test/functional/feature_fee_estimation.py b/test/functional/feature_fee_estimation.py index 709910f2e4..aaab4279b5 100755 --- a/test/functional/feature_fee_estimation.py +++ b/test/functional/feature_fee_estimation.py @@ -126,6 +126,9 @@ class EstimateFeeTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 3 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def setup_network(self): """ We'll setup the network to have 3 nodes that all mine with different parameters. @@ -168,6 +171,11 @@ class EstimateFeeTest(BitcoinTestFramework): newmem.append(utx) self.memutxo = newmem + def import_deterministic_coinbase_privkeys(self): + self.start_nodes() + super().import_deterministic_coinbase_privkeys() + self.stop_nodes() + def run_test(self): self.log.info("This test is time consuming, please be patient") self.log.info("Splitting inputs so we can generate tx's") diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py index a81ebb4220..87c318de9a 100755 --- a/test/functional/feature_maxuploadtarget.py +++ b/test/functional/feature_maxuploadtarget.py @@ -40,6 +40,9 @@ class MaxUploadTest(BitcoinTestFramework): # Cache for utxos, as the listunspent may take a long time later in the test self.utxo_cache = [] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): # Before we connect anything, we first set the time on the node # to be in the past, otherwise things break because the CNode diff --git a/test/functional/feature_minchainwork.py b/test/functional/feature_minchainwork.py index a1cced0bc7..dbff6f15f2 100755 --- a/test/functional/feature_minchainwork.py +++ b/test/functional/feature_minchainwork.py @@ -51,7 +51,8 @@ class MinimumChainWorkTest(BitcoinTestFramework): num_blocks_to_generate = int((self.node_min_work[1] - starting_chain_work) / REGTEST_WORK_PER_BLOCK) self.log.info("Generating %d blocks on node0", num_blocks_to_generate) - hashes = self.nodes[0].generate(num_blocks_to_generate) + hashes = self.nodes[0].generatetoaddress(num_blocks_to_generate, + self.nodes[0].get_deterministic_priv_key().address) self.log.info("Node0 current chain work: %s", self.nodes[0].getblockheader(hashes[-1])['chainwork']) @@ -72,7 +73,7 @@ class MinimumChainWorkTest(BitcoinTestFramework): assert_equal(self.nodes[2].getblockcount(), starting_blockcount) self.log.info("Generating one more block") - self.nodes[0].generate(1) + self.nodes[0].generatetoaddress(1, self.nodes[0].get_deterministic_priv_key().address) self.log.info("Verifying nodes are all synced") diff --git a/test/functional/feature_notifications.py b/test/functional/feature_notifications.py index 71e5e493aa..a93443f2db 100755 --- a/test/functional/feature_notifications.py +++ b/test/functional/feature_notifications.py @@ -13,18 +13,24 @@ class NotificationsTest(BitcoinTestFramework): self.num_nodes = 2 self.setup_clean_chain = True + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def setup_network(self): - self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt") - self.block_filename = os.path.join(self.options.tmpdir, "blocks.txt") - self.tx_filename = os.path.join(self.options.tmpdir, "transactions.txt") + self.alertnotify_dir = os.path.join(self.options.tmpdir, "alertnotify") + self.blocknotify_dir = os.path.join(self.options.tmpdir, "blocknotify") + self.walletnotify_dir = os.path.join(self.options.tmpdir, "walletnotify") + os.mkdir(self.alertnotify_dir) + os.mkdir(self.blocknotify_dir) + os.mkdir(self.walletnotify_dir) # -alertnotify and -blocknotify on node0, walletnotify on node1 self.extra_args = [["-blockversion=2", - "-alertnotify=echo %%s >> %s" % self.alert_filename, - "-blocknotify=echo %%s >> %s" % self.block_filename], + "-alertnotify=echo > {}".format(os.path.join(self.alertnotify_dir, '%s')), + "-blocknotify=echo > {}".format(os.path.join(self.blocknotify_dir, '%s'))], ["-blockversion=211", "-rescan", - "-walletnotify=echo %%s >> %s" % self.tx_filename]] + "-walletnotify=echo > {}".format(os.path.join(self.walletnotify_dir, '%s'))]] super().setup_network() def run_test(self): @@ -32,34 +38,32 @@ class NotificationsTest(BitcoinTestFramework): block_count = 10 blocks = self.nodes[1].generate(block_count) - # wait at most 10 seconds for expected file size before reading the content - wait_until(lambda: os.path.isfile(self.block_filename) and os.stat(self.block_filename).st_size >= (block_count * 65), timeout=10) + # wait at most 10 seconds for expected number of files before reading the content + wait_until(lambda: len(os.listdir(self.blocknotify_dir)) == block_count, timeout=10) - # file content should equal the generated blocks hashes - with open(self.block_filename, 'r', encoding="utf-8") as f: - assert_equal(sorted(blocks), sorted(l.strip() for l in f.read().splitlines())) + # directory content should equal the generated blocks hashes + assert_equal(sorted(blocks), sorted(os.listdir(self.blocknotify_dir))) self.log.info("test -walletnotify") - # wait at most 10 seconds for expected file size before reading the content - wait_until(lambda: os.path.isfile(self.tx_filename) and os.stat(self.tx_filename).st_size >= (block_count * 65), timeout=10) + # wait at most 10 seconds for expected number of files before reading the content + wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10) - # file content should equal the generated transaction hashes + # directory content should equal the generated transaction hashes txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count))) - with open(self.tx_filename, 'r', encoding="ascii") as f: - assert_equal(sorted(txids_rpc), sorted(l.strip() for l in f.read().splitlines())) - os.remove(self.tx_filename) + assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir))) + for tx_file in os.listdir(self.walletnotify_dir): + os.remove(os.path.join(self.walletnotify_dir, tx_file)) self.log.info("test -walletnotify after rescan") # restart node to rescan to force wallet notifications self.restart_node(1) connect_nodes_bi(self.nodes, 0, 1) - wait_until(lambda: os.path.isfile(self.tx_filename) and os.stat(self.tx_filename).st_size >= (block_count * 65), timeout=10) + wait_until(lambda: len(os.listdir(self.walletnotify_dir)) == block_count, timeout=10) - # file content should equal the generated transaction hashes + # directory content should equal the generated transaction hashes txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count))) - with open(self.tx_filename, 'r', encoding="ascii") as f: - assert_equal(sorted(txids_rpc), sorted(l.strip() for l in f.read().splitlines())) + assert_equal(sorted(txids_rpc), sorted(os.listdir(self.walletnotify_dir))) # Mine another 41 up-version blocks. -alertnotify should trigger on the 51st. self.log.info("test -alertnotify") @@ -67,20 +71,17 @@ class NotificationsTest(BitcoinTestFramework): self.sync_all() # Give bitcoind 10 seconds to write the alert notification - wait_until(lambda: os.path.isfile(self.alert_filename) and os.path.getsize(self.alert_filename), timeout=10) + wait_until(lambda: len(os.listdir(self.alertnotify_dir)), timeout=10) - with open(self.alert_filename, 'r', encoding='utf8') as f: - alert_text = f.read() + for notify_file in os.listdir(self.alertnotify_dir): + os.remove(os.path.join(self.alertnotify_dir, notify_file)) # Mine more up-version blocks, should not get more alerts: self.nodes[1].generate(2) self.sync_all() - with open(self.alert_filename, 'r', encoding='utf8') as f: - alert_text2 = f.read() - self.log.info("-alertnotify should not continue notifying for more unknown version blocks") - assert_equal(alert_text, alert_text2) + assert_equal(len(os.listdir(self.alertnotify_dir)), 0) if __name__ == '__main__': NotificationsTest().main() diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py index 9d1805ec2d..a79cc3d34b 100755 --- a/test/functional/feature_nulldummy.py +++ b/test/functional/feature_nulldummy.py @@ -44,9 +44,12 @@ class NULLDUMMYTest(BitcoinTestFramework): # normal segwit activation here (and don't use the default always-on behaviour). self.extra_args = [['-whitelist=127.0.0.1', '-vbparams=segwit:0:999999999999', '-addresstype=legacy', "-deprecatedrpc=addwitnessaddress"]] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): self.address = self.nodes[0].getnewaddress() - self.ms_address = self.nodes[0].addmultisigaddress(1,[self.address])['address'] + self.ms_address = self.nodes[0].addmultisigaddress(1, [self.address])['address'] self.wit_address = self.nodes[0].addwitnessaddress(self.address) self.wit_ms_address = self.nodes[0].addmultisigaddress(1, [self.address], '', 'p2sh-segwit')['address'] diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py index c8f4b7f8b5..772151dc4b 100755 --- a/test/functional/feature_pruning.py +++ b/test/functional/feature_pruning.py @@ -33,15 +33,20 @@ class PruneTest(BitcoinTestFramework): # Create nodes 0 and 1 to mine. # Create node 2 to test pruning. - self.full_node_default_args = ["-maxreceivebuffer=20000", "-checkblocks=5", "-limitdescendantcount=100", "-limitdescendantsize=5000", "-limitancestorcount=100", "-limitancestorsize=5000" ] + self.full_node_default_args = ["-maxreceivebuffer=20000", "-checkblocks=5", "-limitdescendantcount=100", "-limitdescendantsize=5000", "-limitancestorcount=100", "-limitancestorsize=5000"] # Create nodes 3 and 4 to test manual pruning (they will be re-started with manual pruning later) # Create nodes 5 to test wallet in prune mode, but do not connect - self.extra_args = [self.full_node_default_args, - self.full_node_default_args, - ["-maxreceivebuffer=20000", "-prune=550"], - ["-maxreceivebuffer=20000"], - ["-maxreceivebuffer=20000"], - ["-prune=550"]] + self.extra_args = [ + self.full_node_default_args, + self.full_node_default_args, + ["-maxreceivebuffer=20000", "-prune=550"], + ["-maxreceivebuffer=20000"], + ["-maxreceivebuffer=20000"], + ["-prune=550"], + ] + + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() def setup_network(self): self.setup_nodes() diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py index 6105da810c..ff7c2b23bf 100755 --- a/test/functional/feature_rbf.py +++ b/test/functional/feature_rbf.py @@ -61,17 +61,26 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])): return COutPoint(int(txid, 16), 0) -class ReplaceByFeeTest(BitcoinTestFramework): +class ReplaceByFeeTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 - self.extra_args= [["-maxorphantx=1000", - "-whitelist=127.0.0.1", - "-limitancestorcount=50", - "-limitancestorsize=101", - "-limitdescendantcount=200", - "-limitdescendantsize=101"], - ["-mempoolreplacement=0"]] + self.extra_args = [ + [ + "-maxorphantx=1000", + "-whitelist=127.0.0.1", + "-limitancestorcount=50", + "-limitancestorsize=101", + "-limitdescendantcount=200", + "-limitdescendantsize=101", + ], + [ + "-mempoolreplacement=0", + ], + ] + + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() def run_test(self): # Leave IBD @@ -429,6 +438,9 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) + # This transaction isn't shown as replaceable + assert_equal(self.nodes[0].getmempoolentry(tx1a_txid)['bip125-replaceable'], False) + # Shouldn't be able to double-spend tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] @@ -469,7 +481,10 @@ class ReplaceByFeeTest(BitcoinTestFramework): 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) + tx3a_txid = self.nodes[0].sendrawtransaction(tx3a_hex, True) + + # This transaction is shown as replaceable + assert_equal(self.nodes[0].getmempoolentry(tx3a_txid)['bip125-replaceable'], True) tx3b = CTransaction() tx3b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)] diff --git a/test/functional/feature_reindex.py b/test/functional/feature_reindex.py index f30e1191e3..940b403f9c 100755 --- a/test/functional/feature_reindex.py +++ b/test/functional/feature_reindex.py @@ -19,7 +19,7 @@ class ReindexTest(BitcoinTestFramework): self.num_nodes = 1 def reindex(self, justchainstate=False): - self.nodes[0].generate(3) + self.nodes[0].generatetoaddress(3, self.nodes[0].get_deterministic_priv_key().address) blockcount = self.nodes[0].getblockcount() self.stop_nodes() extra_args = [["-reindex-chainstate" if justchainstate else "-reindex"]] diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py index 13d7758e13..2cbfc26e89 100755 --- a/test/functional/feature_segwit.py +++ b/test/functional/feature_segwit.py @@ -46,9 +46,30 @@ class SegWitTest(BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 3 # This test tests SegWit both pre and post-activation, so use the normal BIP9 activation. - self.extra_args = [["-rpcserialversion=0", "-vbparams=segwit:0:999999999999", "-addresstype=legacy", "-deprecatedrpc=addwitnessaddress"], - ["-blockversion=4", "-rpcserialversion=1", "-vbparams=segwit:0:999999999999", "-addresstype=legacy", "-deprecatedrpc=addwitnessaddress"], - ["-blockversion=536870915", "-vbparams=segwit:0:999999999999", "-addresstype=legacy", "-deprecatedrpc=addwitnessaddress"]] + self.extra_args = [ + [ + "-rpcserialversion=0", + "-vbparams=segwit:0:999999999999", + "-addresstype=legacy", + "-deprecatedrpc=addwitnessaddress", + ], + [ + "-blockversion=4", + "-rpcserialversion=1", + "-vbparams=segwit:0:999999999999", + "-addresstype=legacy", + "-deprecatedrpc=addwitnessaddress", + ], + [ + "-blockversion=536870915", + "-vbparams=segwit:0:999999999999", + "-addresstype=legacy", + "-deprecatedrpc=addwitnessaddress", + ], + ] + + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() def setup_network(self): super().setup_network() diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py index 896c36fa53..88df61cabc 100755 --- a/test/functional/feature_versionbits_warning.py +++ b/test/functional/feature_versionbits_warning.py @@ -65,13 +65,14 @@ class VersionBitsWarningTest(BitcoinTestFramework): node = self.nodes[0] node.add_p2p_connection(P2PInterface()) + node_deterministic_address = node.get_deterministic_priv_key().address # Mine one period worth of blocks - node.generate(VB_PERIOD) + node.generatetoaddress(VB_PERIOD, node_deterministic_address) self.log.info("Check that there is no warning if previous VB_BLOCKS have <VB_THRESHOLD blocks with unknown versionbits version.") # Build one period of blocks with < VB_THRESHOLD blocks signaling some unknown bit self.send_blocks_with_version(node.p2p, VB_THRESHOLD - 1, VB_UNKNOWN_VERSION) - node.generate(VB_PERIOD - VB_THRESHOLD + 1) + node.generatetoaddress(VB_PERIOD - VB_THRESHOLD + 1, node_deterministic_address) # Check that we're not getting any versionbit-related errors in get*info() assert(not VB_PATTERN.match(node.getmininginfo()["warnings"])) @@ -80,7 +81,7 @@ class VersionBitsWarningTest(BitcoinTestFramework): self.log.info("Check that there is a warning if >50 blocks in the last 100 were an unknown version") # Build one period of blocks with VB_THRESHOLD blocks signaling some unknown bit self.send_blocks_with_version(node.p2p, VB_THRESHOLD, VB_UNKNOWN_VERSION) - node.generate(VB_PERIOD - VB_THRESHOLD) + node.generatetoaddress(VB_PERIOD - VB_THRESHOLD, node_deterministic_address) # Check that get*info() shows the 51/100 unknown block version error. assert(WARN_UNKNOWN_RULES_MINED in node.getmininginfo()["warnings"]) @@ -89,16 +90,16 @@ class VersionBitsWarningTest(BitcoinTestFramework): self.log.info("Check that there is a warning if previous VB_BLOCKS have >=VB_THRESHOLD blocks with unknown versionbits version.") # Mine a period worth of expected blocks so the generic block-version warning # is cleared. This will move the versionbit state to ACTIVE. - node.generate(VB_PERIOD) + node.generatetoaddress(VB_PERIOD, node_deterministic_address) # Stop-start the node. This is required because bitcoind will only warn once about unknown versions or unknown rules activating. self.restart_node(0) # Generating one block guarantees that we'll get out of IBD - node.generate(1) + node.generatetoaddress(1, node_deterministic_address) wait_until(lambda: not node.getblockchaininfo()['initialblockdownload'], timeout=10, lock=mininode_lock) # Generating one more block will be enough to generate an error. - node.generate(1) + node.generatetoaddress(1, node_deterministic_address) # Check that get*info() shows the versionbits unknown rules warning assert(WARN_UNKNOWN_RULES_ACTIVE in node.getmininginfo()["warnings"]) assert(WARN_UNKNOWN_RULES_ACTIVE in node.getnetworkinfo()["warnings"]) diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py index d3b85ce948..f311858bee 100755 --- a/test/functional/interface_bitcoin_cli.py +++ b/test/functional/interface_bitcoin_cli.py @@ -12,6 +12,9 @@ class TestBitcoinCli(BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 1 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): """Main test logic""" diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py index 41a1b4a35d..afa9de580f 100755 --- a/test/functional/interface_rest.py +++ b/test/functional/interface_rest.py @@ -43,6 +43,9 @@ class RESTTest (BitcoinTestFramework): self.num_nodes = 2 self.extra_args = [["-rest"], []] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def test_rest_request(self, uri, http_method='GET', req_type=ReqType.JSON, body='', status=200, ret_type=RetType.JSON): rest_uri = '/rest' + uri if req_type == ReqType.JSON: diff --git a/test/functional/interface_zmq.py b/test/functional/interface_zmq.py index 72de696259..1c518eab75 100755 --- a/test/functional/interface_zmq.py +++ b/test/functional/interface_zmq.py @@ -5,15 +5,16 @@ """Test the ZMQ notification interface.""" import struct -from test_framework.test_framework import ( - BitcoinTestFramework, skip_if_no_bitcoind_zmq, skip_if_no_py3_zmq) +from test_framework.test_framework import BitcoinTestFramework from test_framework.messages import CTransaction -from test_framework.util import (assert_equal, - bytes_to_hex_str, - hash256, - ) +from test_framework.util import ( + assert_equal, + bytes_to_hex_str, + hash256, +) from io import BytesIO + class ZMQSubscriber: def __init__(self, socket, topic): self.sequence = 0 @@ -37,9 +38,12 @@ class ZMQTest (BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 + def skip_test_if_missing_module(self): + self.skip_if_no_py3_zmq() + self.skip_if_no_bitcoind_zmq() + self.skip_if_no_wallet() + def setup_nodes(self): - skip_if_no_py3_zmq() - skip_if_no_bitcoind_zmq(self) import zmq # Initialize ZMQ context and socket. @@ -59,7 +63,10 @@ class ZMQTest (BitcoinTestFramework): self.rawblock = ZMQSubscriber(socket, b"rawblock") self.rawtx = ZMQSubscriber(socket, b"rawtx") - self.extra_args = [["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [self.hashblock, self.hashtx, self.rawblock, self.rawtx]], []] + self.extra_args = [ + ["-zmqpub%s=%s" % (sub.topic.decode(), address) for sub in [self.hashblock, self.hashtx, self.rawblock, self.rawtx]], + [], + ] self.add_nodes(self.num_nodes, self.extra_args) self.start_nodes() diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py index 44426a0ff7..8847777ba7 100755 --- a/test/functional/mempool_accept.py +++ b/test/functional/mempool_accept.py @@ -40,6 +40,9 @@ class MempoolAcceptanceTest(BitcoinTestFramework): '-acceptnonstdtxn=0', # Try to mimic main-net ]] * self.num_nodes + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def check_mempool_result(self, result_expected, *args, **kwargs): """Wrapper to check result of testmempoolaccept on node_0's mempool""" result_test = self.nodes[0].testmempoolaccept(*args, **kwargs) diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py index 55422b67df..c0918893cd 100755 --- a/test/functional/mempool_limit.py +++ b/test/functional/mempool_limit.py @@ -15,6 +15,9 @@ class MempoolLimitTest(BitcoinTestFramework): self.num_nodes = 1 self.extra_args = [["-maxmempool=5", "-spendzeroconfchange=0"]] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): txouts = gen_return_txouts() relayfee = self.nodes[0].getnetworkinfo()['relayfee'] diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py index da254181fe..9336547a6b 100755 --- a/test/functional/mempool_packages.py +++ b/test/functional/mempool_packages.py @@ -18,6 +18,9 @@ class MempoolPackagesTest(BitcoinTestFramework): self.num_nodes = 2 self.extra_args = [["-maxorphantx=1000"], ["-maxorphantx=1000", "-limitancestorcount=5"]] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + # Build a transaction that spends parent_txid:vout # Return amount sent def chain_transaction(self, node, parent_txid, vout, value, fee, num_outputs): @@ -34,7 +37,7 @@ class MempoolPackagesTest(BitcoinTestFramework): return (txid, send_value) def run_test(self): - ''' Mine some blocks and have them mature. ''' + # Mine some blocks and have them mature. self.nodes[0].generate(101) utxo = self.nodes[0].listunspent(10) txid = utxo[0]['txid'] diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py index 6e5f89efb8..b4e9d967fd 100755 --- a/test/functional/mempool_persist.py +++ b/test/functional/mempool_persist.py @@ -47,6 +47,9 @@ class MempoolPersistTest(BitcoinTestFramework): self.num_nodes = 3 self.extra_args = [[], ["-persistmempool=0"], []] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): chain_height = self.nodes[0].getblockcount() assert_equal(chain_height, 200) diff --git a/test/functional/mempool_reorg.py b/test/functional/mempool_reorg.py index faa00d56cf..123f0b4c28 100755 --- a/test/functional/mempool_reorg.py +++ b/test/functional/mempool_reorg.py @@ -17,6 +17,9 @@ class MempoolCoinbaseTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + alert_filename = None # Set by setup_network def run_test(self): diff --git a/test/functional/mempool_resurrect.py b/test/functional/mempool_resurrect.py index 23939f0be5..d035ca907a 100755 --- a/test/functional/mempool_resurrect.py +++ b/test/functional/mempool_resurrect.py @@ -13,6 +13,9 @@ class MempoolCoinbaseTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): node0_address = self.nodes[0].getnewaddress() # Spend block 1/2/3's coinbase transactions diff --git a/test/functional/mempool_spend_coinbase.py b/test/functional/mempool_spend_coinbase.py index ce3bc3b7e0..854d506f0d 100755 --- a/test/functional/mempool_spend_coinbase.py +++ b/test/functional/mempool_spend_coinbase.py @@ -21,6 +21,9 @@ class MempoolSpendCoinbaseTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): chain_height = self.nodes[0].getblockcount() assert_equal(chain_height, 200) diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py index 15b2d7f757..ff55ea5528 100755 --- a/test/functional/mining_basic.py +++ b/test/functional/mining_basic.py @@ -41,6 +41,12 @@ class MiningTest(BitcoinTestFramework): def run_test(self): node = self.nodes[0] + def assert_submitblock(block, result_str_1, result_str_2=None): + block.solve() + result_str_2 = result_str_2 or 'duplicate-invalid' + assert_equal(result_str_1, node.submitblock(hexdata=b2x(block.serialize()))) + assert_equal(result_str_2, node.submitblock(hexdata=b2x(block.serialize()))) + self.log.info('getmininginfo') mining_info = node.getmininginfo() assert_equal(mining_info['blocks'], 200) @@ -52,7 +58,7 @@ class MiningTest(BitcoinTestFramework): assert_equal(mining_info['pooledtx'], 0) # Mine a block to leave initial block download - node.generate(1) + node.generatetoaddress(1, node.get_deterministic_priv_key().address) tmpl = node.getblocktemplate() self.log.info("getblocktemplate: Test capability advertised") assert 'proposal' in tmpl['capabilities'] @@ -93,6 +99,7 @@ class MiningTest(BitcoinTestFramework): bad_block = copy.deepcopy(block) bad_block.vtx.append(bad_block.vtx[0]) assert_template(node, bad_block, 'bad-txns-duplicate') + assert_submitblock(bad_block, 'bad-txns-duplicate', 'bad-txns-duplicate') self.log.info("getblocktemplate: Test invalid transaction") bad_block = copy.deepcopy(block) @@ -101,12 +108,14 @@ class MiningTest(BitcoinTestFramework): bad_tx.rehash() bad_block.vtx.append(bad_tx) assert_template(node, bad_block, 'bad-txns-inputs-missingorspent') + assert_submitblock(bad_block, 'bad-txns-inputs-missingorspent') self.log.info("getblocktemplate: Test nonfinal transaction") bad_block = copy.deepcopy(block) bad_block.vtx[0].nLockTime = 2 ** 32 - 1 bad_block.vtx[0].rehash() assert_template(node, bad_block, 'bad-txns-nonfinal') + assert_submitblock(bad_block, 'bad-txns-nonfinal') self.log.info("getblocktemplate: Test bad tx count") # The tx count is immediately after the block header @@ -125,24 +134,29 @@ class MiningTest(BitcoinTestFramework): bad_block = copy.deepcopy(block) bad_block.hashMerkleRoot += 1 assert_template(node, bad_block, 'bad-txnmrklroot', False) + assert_submitblock(bad_block, 'bad-txnmrklroot', 'bad-txnmrklroot') self.log.info("getblocktemplate: Test bad timestamps") bad_block = copy.deepcopy(block) bad_block.nTime = 2 ** 31 - 1 assert_template(node, bad_block, 'time-too-new') + assert_submitblock(bad_block, 'time-too-new', 'time-too-new') bad_block.nTime = 0 assert_template(node, bad_block, 'time-too-old') + assert_submitblock(bad_block, 'time-too-old', 'time-too-old') self.log.info("getblocktemplate: Test not best block") bad_block = copy.deepcopy(block) bad_block.hashPrevBlock = 123 assert_template(node, bad_block, 'inconclusive-not-best-prevblk') + assert_submitblock(bad_block, 'prev-blk-not-found', 'prev-blk-not-found') self.log.info('submitheader tests') assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='xx' * 80)) assert_raises_rpc_error(-22, 'Block header decode failed', lambda: node.submitheader(hexdata='ff' * 78)) assert_raises_rpc_error(-25, 'Must submit previous header', lambda: node.submitheader(hexdata='ff' * 80)) + block.nTime += 1 block.solve() def chain_tip(b_hash, *, status='headers-only', branchlen=1): @@ -161,7 +175,8 @@ class MiningTest(BitcoinTestFramework): node.submitheader(hexdata=b2x(CBlockHeader(bad_block_root).serialize())) assert chain_tip(bad_block_root.hash) in node.getchaintips() # Should still reject invalid blocks, even if we have the header: - assert_equal(node.submitblock(hexdata=b2x(bad_block_root.serialize())), 'invalid') + assert_equal(node.submitblock(hexdata=b2x(bad_block_root.serialize())), 'bad-txnmrklroot') + assert_equal(node.submitblock(hexdata=b2x(bad_block_root.serialize())), 'bad-txnmrklroot') assert chain_tip(bad_block_root.hash) in node.getchaintips() # We know the header for this invalid block, so should just return early without error: node.submitheader(hexdata=b2x(CBlockHeader(bad_block_root).serialize())) @@ -172,7 +187,8 @@ class MiningTest(BitcoinTestFramework): bad_block_lock.vtx[0].rehash() bad_block_lock.hashMerkleRoot = bad_block_lock.calc_merkle_root() bad_block_lock.solve() - assert_equal(node.submitblock(hexdata=b2x(bad_block_lock.serialize())), 'invalid') + assert_equal(node.submitblock(hexdata=b2x(bad_block_lock.serialize())), 'bad-txns-nonfinal') + assert_equal(node.submitblock(hexdata=b2x(bad_block_lock.serialize())), 'duplicate-invalid') # Build a "good" block on top of the submitted bad block bad_block2 = copy.deepcopy(block) bad_block2.hashPrevBlock = bad_block_lock.sha256 @@ -188,16 +204,17 @@ class MiningTest(BitcoinTestFramework): # Should ask for the block from a p2p node, if they announce the header as well: node.add_p2p_connection(P2PDataStore()) node.p2p.wait_for_getheaders(timeout=5) # Drop the first getheaders - node.p2p.send_blocks_and_test(blocks=[block], rpc=node) + node.p2p.send_blocks_and_test(blocks=[block], node=node) # Must be active now: assert chain_tip(block.hash, status='active', branchlen=0) in node.getchaintips() # Building a few blocks should give the same results - node.generate(10) + node.generatetoaddress(10, node.get_deterministic_priv_key().address) assert_raises_rpc_error(-25, 'time-too-old', lambda: node.submitheader(hexdata=b2x(CBlockHeader(bad_block_time).serialize()))) assert_raises_rpc_error(-25, 'bad-prevblk', lambda: node.submitheader(hexdata=b2x(CBlockHeader(bad_block2).serialize()))) node.submitheader(hexdata=b2x(CBlockHeader(block).serialize())) node.submitheader(hexdata=b2x(CBlockHeader(bad_block_root).serialize())) + assert_equal(node.submitblock(hexdata=b2x(block.serialize())), 'duplicate') # valid if __name__ == '__main__': diff --git a/test/functional/mining_getblocktemplate_longpoll.py b/test/functional/mining_getblocktemplate_longpoll.py index 2bcbe8db7b..9a3c15a4a7 100755 --- a/test/functional/mining_getblocktemplate_longpoll.py +++ b/test/functional/mining_getblocktemplate_longpoll.py @@ -15,8 +15,8 @@ class LongpollThread(threading.Thread): def __init__(self, node): threading.Thread.__init__(self) # query current longpollid - templat = node.getblocktemplate() - self.longpollid = templat['longpollid'] + template = node.getblocktemplate() + self.longpollid = template['longpollid'] # create a new connection to the node, we can't use the same # connection from two threads self.node = get_rpc_proxy(node.url, 1, timeout=600, coveragedir=node.coverage_dir) @@ -28,14 +28,17 @@ class GetBlockTemplateLPTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): self.log.info("Warning: this test will take about 70 seconds in the best case. Be patient.") self.nodes[0].generate(10) - templat = self.nodes[0].getblocktemplate() - longpollid = templat['longpollid'] + template = self.nodes[0].getblocktemplate() + longpollid = template['longpollid'] # longpollid should not change between successive invocations if nothing else happens - templat2 = self.nodes[0].getblocktemplate() - assert(templat2['longpollid'] == longpollid) + template2 = self.nodes[0].getblocktemplate() + assert(template2['longpollid'] == longpollid) # Test 1: test that the longpolling wait if we do nothing thr = LongpollThread(self.nodes[0]) @@ -70,4 +73,3 @@ class GetBlockTemplateLPTest(BitcoinTestFramework): if __name__ == '__main__': GetBlockTemplateLPTest().main() - diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py index bb0cc082ab..c5ddee56f1 100755 --- a/test/functional/mining_prioritisetransaction.py +++ b/test/functional/mining_prioritisetransaction.py @@ -16,6 +16,9 @@ class PrioritiseTransactionTest(BitcoinTestFramework): self.num_nodes = 2 self.extra_args = [["-printpriority=1"], ["-printpriority=1"]] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): # Test `prioritisetransaction` required parameters assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction) @@ -26,7 +29,8 @@ class PrioritiseTransactionTest(BitcoinTestFramework): assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '', 0, 0, 0) # Test `prioritisetransaction` invalid `txid` - assert_raises_rpc_error(-1, "txid must be hexadecimal string", self.nodes[0].prioritisetransaction, txid='foo', fee_delta=0) + assert_raises_rpc_error(-8, "txid must be of length 64 (not 3, for 'foo')", self.nodes[0].prioritisetransaction, txid='foo', fee_delta=0) + assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'Zd1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000')", self.nodes[0].prioritisetransaction, txid='Zd1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000', fee_delta=0) # Test `prioritisetransaction` invalid `dummy` txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000' diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py index 5cec5dc6f0..fafc27d16a 100755 --- a/test/functional/p2p_compactblocks.py +++ b/test/functional/p2p_compactblocks.py @@ -102,6 +102,9 @@ class CompactBlocksTest(BitcoinTestFramework): self.extra_args = [["-vbparams=segwit:0:0"], ["-vbparams=segwit:0:999999999999", "-txindex", "-deprecatedrpc=addwitnessaddress"]] self.utxos = [] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def build_block_on_tip(self, node, segwit=False): height = node.getblockcount() tip = node.getbestblockhash() @@ -290,7 +293,7 @@ class CompactBlocksTest(BitcoinTestFramework): block_hash = int(node.generate(1)[0], 16) # Store the raw block in our internal format. - block = FromHex(CBlock(), node.getblock("%02x" % block_hash, False)) + block = FromHex(CBlock(), node.getblock("%064x" % block_hash, False)) for tx in block.vtx: tx.calc_sha256() block.rehash() diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py index 3bc7734e75..d589519e45 100755 --- a/test/functional/p2p_feefilter.py +++ b/test/functional/p2p_feefilter.py @@ -42,6 +42,9 @@ class FeeFilterTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): node1 = self.nodes[1] node0 = self.nodes[0] diff --git a/test/functional/p2p_fingerprint.py b/test/functional/p2p_fingerprint.py index 4a6ffced93..fab0887197 100755 --- a/test/functional/p2p_fingerprint.py +++ b/test/functional/p2p_fingerprint.py @@ -80,7 +80,7 @@ class P2PFingerprintTest(BitcoinTestFramework): self.nodes[0].setmocktime(int(time.time()) - 60 * 24 * 60 * 60) # Generating a chain of 10 blocks - block_hashes = self.nodes[0].generate(nblocks=10) + block_hashes = self.nodes[0].generatetoaddress(10, self.nodes[0].get_deterministic_priv_key().address) # Create longer chain starting 2 blocks before current tip height = len(block_hashes) - 2 @@ -111,7 +111,7 @@ class P2PFingerprintTest(BitcoinTestFramework): # Longest chain is extended so stale is much older than chain tip self.nodes[0].setmocktime(0) - tip = self.nodes[0].generate(nblocks=1)[0] + tip = self.nodes[0].generatetoaddress(1, self.nodes[0].get_deterministic_priv_key().address)[0] assert_equal(self.nodes[0].getblockcount(), 14) # Send getdata & getheaders to refresh last received getheader message diff --git a/test/functional/p2p_invalid_block.py b/test/functional/p2p_invalid_block.py index e910bedd09..0678b1a651 100755 --- a/test/functional/p2p_invalid_block.py +++ b/test/functional/p2p_invalid_block.py @@ -42,10 +42,10 @@ class InvalidBlockRequestTest(BitcoinTestFramework): # Save the coinbase for later block1 = block tip = block.sha256 - node.p2p.send_blocks_and_test([block1], node, True) + node.p2p.send_blocks_and_test([block1], node, success=True) self.log.info("Mature the block.") - node.generate(100) + node.generatetoaddress(100, node.get_deterministic_priv_key().address) best_block = node.getblock(node.getbestblockhash()) tip = int(node.getbestblockhash(), 16) @@ -79,7 +79,17 @@ class InvalidBlockRequestTest(BitcoinTestFramework): assert_equal(orig_hash, block2.rehash()) assert(block2_orig.vtx != block2.vtx) - node.p2p.send_blocks_and_test([block2], node, False, False, 16, b'bad-txns-duplicate') + node.p2p.send_blocks_and_test([block2], node, success=False, request_block=False, reject_reason='bad-txns-duplicate') + + # Check transactions for duplicate inputs + self.log.info("Test duplicate input block.") + + block2_orig.vtx[2].vin.append(block2_orig.vtx[2].vin[0]) + block2_orig.vtx[2].rehash() + block2_orig.hashMerkleRoot = block2_orig.calc_merkle_root() + block2_orig.rehash() + block2_orig.solve() + node.p2p.send_blocks_and_test([block2_orig], node, success=False, request_block=False, reject_reason='bad-txns-inputs-duplicate') self.log.info("Test very broken block.") @@ -92,7 +102,7 @@ class InvalidBlockRequestTest(BitcoinTestFramework): block3.rehash() block3.solve() - node.p2p.send_blocks_and_test([block3], node, False, False, 16, b'bad-cb-amount') + node.p2p.send_blocks_and_test([block3], node, success=False, request_block=False, reject_reason='bad-cb-amount') if __name__ == '__main__': InvalidBlockRequestTest().main() diff --git a/test/functional/p2p_invalid_locator.py b/test/functional/p2p_invalid_locator.py index 3b1654f920..c8c752d1f7 100755 --- a/test/functional/p2p_invalid_locator.py +++ b/test/functional/p2p_invalid_locator.py @@ -17,7 +17,7 @@ class InvalidLocatorTest(BitcoinTestFramework): def run_test(self): node = self.nodes[0] # convenience reference to the node - node.generate(1) # Get node out of IBD + node.generatetoaddress(1, node.get_deterministic_priv_key().address) # Get node out of IBD self.log.info('Test max locator size') block_count = node.getblockcount() diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py index 0aa5e21103..58e129b57d 100755 --- a/test/functional/p2p_invalid_tx.py +++ b/test/functional/p2p_invalid_tx.py @@ -61,7 +61,7 @@ class InvalidTxRequestTest(BitcoinTestFramework): node.p2p.send_blocks_and_test([block], node, success=True) self.log.info("Mature the block.") - self.nodes[0].generate(100) + self.nodes[0].generatetoaddress(100, self.nodes[0].get_deterministic_priv_key().address) # b'\x64' is OP_NOTIF # Transaction will be rejected with code 16 (REJECT_INVALID) @@ -76,7 +76,7 @@ class InvalidTxRequestTest(BitcoinTestFramework): self.reconnect_p2p(num_connections=2) self.log.info('Test orphan transaction handling ... ') - # Create a root transaction that we withhold until all dependend transactions + # Create a root transaction that we withhold until all dependent transactions # are sent out and in the orphan cache SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51' tx_withhold = CTransaction() @@ -116,7 +116,8 @@ class InvalidTxRequestTest(BitcoinTestFramework): assert_equal(2, len(node.getpeerinfo())) # p2ps[1] is still connected self.log.info('Send the withhold tx ... ') - node.p2p.send_txs_and_test([tx_withhold], node, success=True) + with node.assert_debug_log(expected_msgs=["bad-txns-in-belowout"]): + node.p2p.send_txs_and_test([tx_withhold], node, success=True) # Transactions that should end up in the mempool expected_mempool = { @@ -134,13 +135,6 @@ class InvalidTxRequestTest(BitcoinTestFramework): wait_until(lambda: 1 == len(node.getpeerinfo()), timeout=12) # p2ps[1] is no longer connected assert_equal(expected_mempool, set(node.getrawmempool())) - # restart node with sending BIP61 messages disabled, check that it disconnects without sending the reject message - self.log.info('Test a transaction that is rejected, with BIP61 disabled') - self.restart_node(0, ['-enablebip61=0','-persistmempool=0']) - self.reconnect_p2p(num_connections=1) - node.p2p.send_txs_and_test([tx1], node, success=False, expect_disconnect=True) - # send_txs_and_test will have waited for disconnect, so we can safely check that no reject has been received - assert_equal(node.p2p.reject_code_received, None) if __name__ == '__main__': InvalidTxRequestTest().main() diff --git a/test/functional/p2p_leak.py b/test/functional/p2p_leak.py index dcbf833e7c..336d34a81d 100755 --- a/test/functional/p2p_leak.py +++ b/test/functional/p2p_leak.py @@ -103,7 +103,7 @@ class P2PLeakTest(BitcoinTestFramework): wait_until(lambda: no_verack_idlenode.version_received, timeout=10, lock=mininode_lock) # Mine a block and make sure that it's not sent to the connected nodes - self.nodes[0].generate(1) + self.nodes[0].generatetoaddress(1, self.nodes[0].get_deterministic_priv_key().address) #Give the node enough time to possibly leak out a message time.sleep(5) diff --git a/test/functional/p2p_node_network_limited.py b/test/functional/p2p_node_network_limited.py index c987bf4b05..ec3d336dc1 100755 --- a/test/functional/p2p_node_network_limited.py +++ b/test/functional/p2p_node_network_limited.py @@ -59,7 +59,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework): self.log.info("Mine enough blocks to reach the NODE_NETWORK_LIMITED range.") connect_nodes_bi(self.nodes, 0, 1) - blocks = self.nodes[1].generate(292) + blocks = self.nodes[1].generatetoaddress(292, self.nodes[1].get_deterministic_priv_key().address) sync_blocks([self.nodes[0], self.nodes[1]]) self.log.info("Make sure we can max retrieve block at tip-288.") @@ -89,7 +89,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework): sync_blocks([self.nodes[0], self.nodes[2]], timeout=5) except: pass - # node2 must remain at heigh 0 + # node2 must remain at height 0 assert_equal(self.nodes[2].getblockheader(self.nodes[2].getbestblockhash())['height'], 0) # now connect also to node 1 (non pruned) @@ -102,7 +102,7 @@ class NodeNetworkLimitedTest(BitcoinTestFramework): self.disconnect_all() # mine 10 blocks on node 0 (pruned node) - self.nodes[0].generate(10) + self.nodes[0].generatetoaddress(10, self.nodes[0].get_deterministic_priv_key().address) # connect node1 (non pruned) with node0 (pruned) and check if the can sync connect_nodes_bi(self.nodes, 0, 1) diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index be79a13237..afbbfa8992 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -41,7 +41,6 @@ from test_framework.messages import ( from test_framework.mininode import ( P2PInterface, mininode_lock, - wait_until, ) from test_framework.script import ( CScript, @@ -119,37 +118,28 @@ def get_virtual_size(witness_block): vsize = int((3 * base_size + total_size + 3) / 4) return vsize -def test_transaction_acceptance(rpc, p2p, tx, with_witness, accepted, reason=None): +def test_transaction_acceptance(node, p2p, tx, with_witness, accepted, reason=None): """Send a transaction to the node and check that it's accepted to the mempool - Submit the transaction over the p2p interface - use the getrawmempool rpc to check for acceptance.""" - tx_message = msg_tx(tx) - if with_witness: - tx_message = msg_witness_tx(tx) - p2p.send_message(tx_message) - p2p.sync_with_ping() - assert_equal(tx.hash in rpc.getrawmempool(), accepted) - if (reason is not None and not accepted): - # Check the rejection reason as well. - with mininode_lock: - assert_equal(p2p.last_message["reject"].reason, reason) + reason = [reason] if reason else [] + with node.assert_debug_log(expected_msgs=reason): + p2p.send_message(msg_witness_tx(tx) if with_witness else msg_tx(tx)) + p2p.sync_with_ping() + assert_equal(tx.hash in node.getrawmempool(), accepted) -def test_witness_block(rpc, p2p, block, accepted, with_witness=True, reason=None): +def test_witness_block(node, p2p, block, accepted, with_witness=True, reason=None): """Send a block to the node and check that it's accepted - Submit the block over the p2p interface - use the getbestblockhash rpc to check for acceptance.""" - if with_witness: - p2p.send_message(msg_witness_block(block)) - else: - p2p.send_message(msg_block(block)) - p2p.sync_with_ping() - assert_equal(rpc.getbestblockhash() == block.hash, accepted) - if (reason is not None and not accepted): - # Check the rejection reason as well. - with mininode_lock: - assert_equal(p2p.last_message["reject"].reason, reason) + reason = [reason] if reason else [] + with node.assert_debug_log(expected_msgs=reason): + p2p.send_message(msg_witness_block(block) if with_witness else msg_block(block)) + p2p.sync_with_ping() + assert_equal(node.getbestblockhash() == block.hash, accepted) + class TestP2PConn(P2PInterface): def __init__(self): @@ -198,6 +188,9 @@ class SegWitTest(BitcoinTestFramework): # This test tests SegWit both pre and post-activation, so use the normal BIP9 activation. self.extra_args = [["-whitelist=127.0.0.1", "-vbparams=segwit:0:999999999999"], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0", "-vbparams=segwit:0:999999999999"], ["-whitelist=127.0.0.1", "-vbparams=segwit:0:0"]] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def setup_network(self): self.setup_nodes() connect_nodes(self.nodes[0], 1) @@ -212,7 +205,7 @@ class SegWitTest(BitcoinTestFramework): height = self.nodes[0].getblockcount() + 1 block_time = self.nodes[0].getblockheader(tip)["mediantime"] + 1 block = create_block(int(tip, 16), create_coinbase(height), block_time) - block.version = version + block.nVersion = version block.rehash() return block @@ -349,8 +342,7 @@ class SegWitTest(BitcoinTestFramework): self.update_witness_block_with_transactions(block, [tx]) # Sending witness data before activation is not allowed (anti-spam # rule). - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) - wait_until(lambda: 'reject' in self.test_node.last_message and self.test_node.last_message["reject"].reason == b"unexpected-witness") + test_witness_block(self.nodes[0], self.test_node, block, accepted=False, reason='unexpected-witness') # But it should not be permanently marked bad... # Resend without witness information. @@ -380,20 +372,20 @@ class SegWitTest(BitcoinTestFramework): self.test_node.announce_block_and_wait_for_getdata(block1, use_header=False) assert(self.test_node.last_message["getdata"].inv[0].type == blocktype) - test_witness_block(self.nodes[0].rpc, self.test_node, block1, True) + test_witness_block(self.nodes[0], self.test_node, block1, True) block2 = self.build_next_block(version=4) block2.solve() self.test_node.announce_block_and_wait_for_getdata(block2, use_header=True) assert(self.test_node.last_message["getdata"].inv[0].type == blocktype) - test_witness_block(self.nodes[0].rpc, self.test_node, block2, True) + test_witness_block(self.nodes[0], self.test_node, block2, True) block3 = self.build_next_block(version=(VB_TOP_BITS | (1 << 15))) block3.solve() self.test_node.announce_block_and_wait_for_getdata(block3, use_header=True) assert(self.test_node.last_message["getdata"].inv[0].type == blocktype) - test_witness_block(self.nodes[0].rpc, self.test_node, block3, True) + test_witness_block(self.nodes[0], self.test_node, block3, True) # Check that we can getdata for witness blocks or regular blocks, # and the right thing happens. @@ -423,7 +415,7 @@ class SegWitTest(BitcoinTestFramework): # This gives us a witness commitment. assert(len(block.vtx[0].wit.vtxinwit) == 1) assert(len(block.vtx[0].wit.vtxinwit[0].scriptWitness.stack) == 1) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) # Now try to retrieve it... rpc_block = self.nodes[0].getblock(block.hash, False) non_wit_block = self.test_node.request_block(block.sha256, 2) @@ -471,7 +463,7 @@ class SegWitTest(BitcoinTestFramework): blocks are permitted to contain witnesses).""" # node2 doesn't need to be connected for this test. - # (If it's connected, node0 may propogate an invalid block to it over + # (If it's connected, node0 may propagate an invalid block to it over # compact blocks and the nodes would have inconsistent tips.) disconnect_nodes(self.nodes[0], 2) @@ -497,7 +489,7 @@ class SegWitTest(BitcoinTestFramework): self.update_witness_block_with_transactions(block, [tx]) # Verify that segwit isn't activated. A block serialized with witness # should be rejected prior to activation. - test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=True, reason=b'unexpected-witness') + test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=True, reason='unexpected-witness') # Now send the block without witness. It should be accepted test_witness_block(self.nodes[0], self.test_node, block, accepted=True, with_witness=False) @@ -523,7 +515,7 @@ class SegWitTest(BitcoinTestFramework): # When the block is serialized with a witness, the block will be rejected because witness # data isn't allowed in blocks that don't commit to witness data. - test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=True, reason=b'unexpected-witness') + test_witness_block(self.nodes[0], self.test_node, block, accepted=False, with_witness=True, reason='unexpected-witness') # When the block is serialized without witness, validation fails because the transaction is # invalid (transactions are always validated with SCRIPT_VERIFY_WITNESS so a segwit v0 transaction @@ -640,11 +632,11 @@ class SegWitTest(BitcoinTestFramework): # its from) assert_equal(len(self.nodes[0].getrawmempool()), 0) assert_equal(len(self.nodes[1].getrawmempool()), 0) - test_transaction_acceptance(self.nodes[0].rpc, self.old_node, tx, with_witness=True, accepted=False) - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=False) + test_transaction_acceptance(self.nodes[0], self.old_node, tx, with_witness=True, accepted=False) + test_transaction_acceptance(self.nodes[0], self.test_node, tx, with_witness=True, accepted=False) # But eliminating the witness should fix it - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True) + test_transaction_acceptance(self.nodes[0], self.test_node, tx, with_witness=False, accepted=True) # Cleanup: mine the first transaction and update utxo self.nodes[0].generate(1) @@ -674,7 +666,7 @@ class SegWitTest(BitcoinTestFramework): p2sh_tx.rehash() # Mine it on test_node to create the confirmed output. - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_tx, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[0], self.test_node, p2sh_tx, with_witness=True, accepted=True) self.nodes[0].generate(1) sync_blocks(self.nodes) @@ -689,7 +681,7 @@ class SegWitTest(BitcoinTestFramework): # This is always accepted, since the mempool policy is to consider segwit as always active # and thus allow segwit outputs - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[1], self.std_node, tx, with_witness=True, accepted=True) # Now create something that looks like a P2PKH output. This won't be spendable. script_pubkey = CScript([OP_0, hash160(witness_hash)]) @@ -701,7 +693,7 @@ class SegWitTest(BitcoinTestFramework): tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_program] tx2.rehash() - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[1], self.std_node, tx2, with_witness=True, accepted=True) # Now update self.utxo for later tests. tx3 = CTransaction() @@ -723,7 +715,7 @@ class SegWitTest(BitcoinTestFramework): tx3.vout = [tx3_out] tx3.rehash() assert_equal(self.nodes[0].testmempoolaccept([bytes_to_hex_str(tx3.serialize_with_witness())]), [{'txid': tx3.hash, 'allowed': True}]) - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=True, accepted=True) self.nodes[0].generate(1) sync_blocks(self.nodes) @@ -760,10 +752,10 @@ class SegWitTest(BitcoinTestFramework): tx.rehash() # Verify mempool acceptance and block validity - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True) + test_transaction_acceptance(self.nodes[0], self.test_node, tx, with_witness=False, accepted=True) block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True, with_witness=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True, with_witness=True) sync_blocks(self.nodes) # Now test attempts to spend the output. @@ -777,12 +769,16 @@ class SegWitTest(BitcoinTestFramework): # will require a witness to spend a witness program regardless of # segwit activation. Note that older bitcoind's that are not # segwit-aware would also reject this for failing CLEANSTACK. - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=False, accepted=False) + with self.nodes[0].assert_debug_log( + expected_msgs=(spend_tx.hash, 'was not accepted: non-mandatory-script-verify-flag (Witness program was passed an empty witness)')): + test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False) - # Try to put the witness script in the script_sig, should also fail. - spend_tx.vin[0].script_sig = CScript([p2wsh_pubkey, b'a']) + # Try to put the witness script in the scriptSig, should also fail. + spend_tx.vin[0].scriptSig = CScript([p2wsh_pubkey, b'a']) spend_tx.rehash() - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=False, accepted=False) + with self.nodes[0].assert_debug_log( + expected_msgs=('Not relaying invalid transaction {}'.format(spend_tx.hash), 'was not accepted: mandatory-script-verify-flag-failed (Script evaluated without error but finished with a false/empty top stack element)')): + test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=False, accepted=False) # Now put the witness script in the witness, should succeed after # segwit activates. @@ -792,7 +788,7 @@ class SegWitTest(BitcoinTestFramework): spend_tx.wit.vtxinwit[0].scriptWitness.stack = [b'a', witness_program] # Verify mempool acceptance - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[0], self.test_node, spend_tx, with_witness=True, accepted=True) block = self.build_next_block() self.update_witness_block_with_transactions(block, [spend_tx]) @@ -800,7 +796,7 @@ class SegWitTest(BitcoinTestFramework): # This no longer works before activation, because SCRIPT_VERIFY_WITNESS # is always set. # TODO: rewrite this test to make clear that it only works after activation. - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) # Update self.utxo self.utxo.pop(0) @@ -821,7 +817,7 @@ class SegWitTest(BitcoinTestFramework): assert(msg_witness_block(block).serialize() != msg_block(block).serialize()) # This empty block should be valid. - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) # Try to tweak the nonce block_2 = self.build_next_block() @@ -832,7 +828,7 @@ class SegWitTest(BitcoinTestFramework): assert(block_2.vtx[0].vout[-1] != block.vtx[0].vout[-1]) # This should also be valid. - test_witness_block(self.nodes[0].rpc, self.test_node, block_2, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block_2, accepted=True) # Now test commitments with actual transactions tx = CTransaction() @@ -864,7 +860,7 @@ class SegWitTest(BitcoinTestFramework): block_3.rehash() block_3.solve() - test_witness_block(self.nodes[0].rpc, self.test_node, block_3, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block_3, accepted=False) # Add a different commitment with different nonce, but in the # right location, and with some funds burned(!). @@ -878,7 +874,7 @@ class SegWitTest(BitcoinTestFramework): block_3.rehash() assert(len(block_3.vtx[0].vout) == 4) # 3 OP_returns block_3.solve() - test_witness_block(self.nodes[0].rpc, self.test_node, block_3, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block_3, accepted=True) # Finally test that a block with no witness transactions can # omit the commitment. @@ -890,7 +886,7 @@ class SegWitTest(BitcoinTestFramework): block_4.vtx.append(tx3) block_4.hashMerkleRoot = block_4.calc_merkle_root() block_4.solve() - test_witness_block(self.nodes[0].rpc, self.test_node, block_4, with_witness=False, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block_4, with_witness=False, accepted=True) # Update available utxo's for use in later test. self.utxo.pop(0) @@ -930,11 +926,11 @@ class SegWitTest(BitcoinTestFramework): # Change the nonce -- should not cause the block to be permanently # failed block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(1)] - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block, accepted=False) # Changing the witness reserved value doesn't change the block hash block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)] - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) @subtest def test_witness_block_size(self): @@ -998,7 +994,7 @@ class SegWitTest(BitcoinTestFramework): # limit assert(len(block.serialize(True)) > 2 * 1024 * 1024) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block, accepted=False) # Now resize the second transaction to make the block fit. cur_length = len(block.vtx[-1].wit.vtxinwit[0].scriptWitness.stack[0]) @@ -1008,7 +1004,7 @@ class SegWitTest(BitcoinTestFramework): block.solve() assert(get_virtual_size(block) == MAX_BLOCK_BASE_SIZE) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) # Update available utxo's self.utxo.pop(0) @@ -1071,7 +1067,7 @@ class SegWitTest(BitcoinTestFramework): self.update_witness_block_with_transactions(block, [tx]) # Extra witness data should not be allowed. - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block, accepted=False) # Try extra signature data. Ok if we're not spending a witness output. block.vtx[1].wit.vtxinwit = [] @@ -1080,7 +1076,7 @@ class SegWitTest(BitcoinTestFramework): add_witness_commitment(block) block.solve() - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) # Now try extra witness/signature data on an input that DOES require a # witness @@ -1096,7 +1092,7 @@ class SegWitTest(BitcoinTestFramework): self.update_witness_block_with_transactions(block, [tx2]) # This has extra witness data, so it should fail. - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block, accepted=False) # Now get rid of the extra witness, but add extra scriptSig data tx2.vin[0].scriptSig = CScript([OP_TRUE]) @@ -1108,7 +1104,7 @@ class SegWitTest(BitcoinTestFramework): block.solve() # This has extra signature data for a witness input, so it should fail. - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block, accepted=False) # Now get rid of the extra scriptsig on the witness input, and verify # success (even with extra scriptsig data in the non-witness input) @@ -1117,7 +1113,7 @@ class SegWitTest(BitcoinTestFramework): add_witness_commitment(block) block.solve() - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) # Update utxo for later tests self.utxo.pop(0) @@ -1147,14 +1143,14 @@ class SegWitTest(BitcoinTestFramework): tx2.rehash() self.update_witness_block_with_transactions(block, [tx, tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block, accepted=False) # Now reduce the length of the stack element tx2.wit.vtxinwit[0].scriptWitness.stack[0] = b'a' * (MAX_SCRIPT_ELEMENT_SIZE) add_witness_commitment(block) block.solve() - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) # Update the utxo for later tests self.utxo.pop() @@ -1188,7 +1184,7 @@ class SegWitTest(BitcoinTestFramework): self.update_witness_block_with_transactions(block, [tx, tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block, accepted=False) # Try again with one less byte in the witness program witness_program = CScript([b'a' * 520] * 19 + [OP_DROP] * 62 + [OP_TRUE]) @@ -1203,7 +1199,7 @@ class SegWitTest(BitcoinTestFramework): tx2.rehash() block.vtx = [block.vtx[0]] self.update_witness_block_with_transactions(block, [tx, tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) self.utxo.pop() self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue)) @@ -1227,7 +1223,7 @@ class SegWitTest(BitcoinTestFramework): block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) # Try various ways to spend tx that should all break. # This "broken" transaction serializer will not normalize @@ -1262,7 +1258,7 @@ class SegWitTest(BitcoinTestFramework): block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block, accepted=False) # Now try using a too short vtxinwit tx2.wit.vtxinwit.pop() @@ -1270,7 +1266,7 @@ class SegWitTest(BitcoinTestFramework): block.vtx = [block.vtx[0]] self.update_witness_block_with_transactions(block, [tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block, accepted=False) # Now make one of the intermediate witnesses be incorrect tx2.wit.vtxinwit.append(CTxInWitness()) @@ -1279,13 +1275,13 @@ class SegWitTest(BitcoinTestFramework): block.vtx = [block.vtx[0]] self.update_witness_block_with_transactions(block, [tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block, accepted=False) # Fix the broken witness and the block should be accepted. tx2.wit.vtxinwit[5].scriptWitness.stack = [b'a', witness_program] block.vtx = [block.vtx[0]] self.update_witness_block_with_transactions(block, [tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) self.utxo.pop() self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue)) @@ -1314,11 +1310,11 @@ class SegWitTest(BitcoinTestFramework): # Verify that unnecessary witnesses are rejected. self.test_node.announce_tx_and_wait_for_getdata(tx) assert_equal(len(self.nodes[0].getrawmempool()), 0) - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=False) + test_transaction_acceptance(self.nodes[0], self.test_node, tx, with_witness=True, accepted=False) # Verify that removing the witness succeeds. self.test_node.announce_tx_and_wait_for_getdata(tx) - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True) + test_transaction_acceptance(self.nodes[0], self.test_node, tx, with_witness=False, accepted=True) # Now try to add extra witness data to a valid witness tx. witness_program = CScript([OP_TRUE]) @@ -1343,24 +1339,24 @@ class SegWitTest(BitcoinTestFramework): # Node will not be blinded to the transaction self.std_node.announce_tx_and_wait_for_getdata(tx3) - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, True, False, b'tx-size') + test_transaction_acceptance(self.nodes[1], self.std_node, tx3, True, False, 'tx-size') self.std_node.announce_tx_and_wait_for_getdata(tx3) - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, True, False, b'tx-size') + test_transaction_acceptance(self.nodes[1], self.std_node, tx3, True, False, 'tx-size') # Remove witness stuffing, instead add extra witness push on stack tx3.vout[0] = CTxOut(tx2.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])) tx3.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)]), witness_program] tx3.rehash() - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, with_witness=True, accepted=True) - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=False) + test_transaction_acceptance(self.nodes[0], self.test_node, tx2, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=True, accepted=False) # Get rid of the extra witness, and verify acceptance. tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program] # Also check that old_node gets a tx announcement, even though this is # a witness transaction. self.old_node.wait_for_inv([CInv(1, tx2.sha256)]) # wait until tx2 was inv'ed - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=True, accepted=True) self.old_node.wait_for_inv([CInv(1, tx3.sha256)]) # Test that getrawtransaction returns correct witness information @@ -1400,7 +1396,7 @@ class SegWitTest(BitcoinTestFramework): tx.rehash() block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) self.utxo.pop(0) for i in range(NUM_SEGWIT_VERSIONS): self.utxo.append(UTXO(tx.sha256, i, split_value)) @@ -1417,8 +1413,8 @@ class SegWitTest(BitcoinTestFramework): tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")] tx.vout = [CTxOut(self.utxo[0].nValue - 1000, script_pubkey)] tx.rehash() - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx, with_witness=True, accepted=False) - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[1], self.std_node, tx, with_witness=True, accepted=False) + test_transaction_acceptance(self.nodes[0], self.test_node, tx, with_witness=True, accepted=True) self.utxo.pop(0) temp_utxo.append(UTXO(tx.sha256, 0, tx.vout[0].nValue)) @@ -1437,8 +1433,8 @@ class SegWitTest(BitcoinTestFramework): tx2.rehash() # Gets accepted to test_node, because standardness of outputs isn't # checked with fRequireStandard - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, with_witness=True, accepted=True) - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, with_witness=True, accepted=False) + test_transaction_acceptance(self.nodes[0], self.test_node, tx2, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[1], self.std_node, tx2, with_witness=True, accepted=False) temp_utxo.pop() # last entry in temp_utxo was the output we just spent temp_utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue)) @@ -1454,15 +1450,12 @@ class SegWitTest(BitcoinTestFramework): tx3.rehash() # Spending a higher version witness output is not allowed by policy, # even with fRequireStandard=false. - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=False) - self.test_node.sync_with_ping() - with mininode_lock: - assert(b"reserved for soft-fork upgrades" in self.test_node.last_message["reject"].reason) + test_transaction_acceptance(self.nodes[0], self.test_node, tx3, with_witness=True, accepted=False, reason="reserved for soft-fork upgrades") # Building a block with the transaction must be valid, however. block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx2, tx3]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) sync_blocks(self.nodes) # Add utxo to our list @@ -1480,7 +1473,7 @@ class SegWitTest(BitcoinTestFramework): # This next line will rehash the coinbase and update the merkle # root, and solve. self.update_witness_block_with_transactions(block, []) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) spend_tx = CTransaction() spend_tx.vin = [CTxIn(COutPoint(block.vtx[0].sha256, 0), b"")] @@ -1494,13 +1487,13 @@ class SegWitTest(BitcoinTestFramework): sync_blocks(self.nodes) block2 = self.build_next_block() self.update_witness_block_with_transactions(block2, [spend_tx]) - test_witness_block(self.nodes[0].rpc, self.test_node, block2, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block2, accepted=False) # Advancing one more block should allow the spend. self.nodes[0].generate(1) block2 = self.build_next_block() self.update_witness_block_with_transactions(block2, [spend_tx]) - test_witness_block(self.nodes[0].rpc, self.test_node, block2, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block2, accepted=True) sync_blocks(self.nodes) @subtest @@ -1532,7 +1525,7 @@ class SegWitTest(BitcoinTestFramework): # Confirm it in a block. block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) # Now try to spend it. Send it to a P2WSH output, which we'll # use in the next test. @@ -1551,11 +1544,11 @@ class SegWitTest(BitcoinTestFramework): tx2.rehash() # Should fail policy test. - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') + test_transaction_acceptance(self.nodes[0], self.test_node, tx2, True, False, 'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') # But passes consensus. block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) # Test 2: P2WSH # Try to spend the P2WSH output created in last test. @@ -1571,11 +1564,11 @@ class SegWitTest(BitcoinTestFramework): sign_p2pk_witness_input(witness_program, tx3, 0, SIGHASH_ALL, tx2.vout[0].nValue, key) # Should fail policy test. - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') + test_transaction_acceptance(self.nodes[0], self.test_node, tx3, True, False, 'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') # But passes consensus. block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx3]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) # Test 3: P2SH(P2WSH) # Try to spend the P2SH output created in the last test. @@ -1588,10 +1581,10 @@ class SegWitTest(BitcoinTestFramework): sign_p2pk_witness_input(witness_program, tx4, 0, SIGHASH_ALL, tx3.vout[0].nValue, key) # Should fail policy test. - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx4, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') + test_transaction_acceptance(self.nodes[0], self.test_node, tx4, True, False, 'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx4]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) # Test 4: Uncompressed pubkeys should still be valid in non-segwit # transactions. @@ -1603,10 +1596,10 @@ class SegWitTest(BitcoinTestFramework): tx5.vin[0].scriptSig = CScript([signature, pubkey]) tx5.rehash() # Should pass policy and consensus. - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx5, True, True) + test_transaction_acceptance(self.nodes[0], self.test_node, tx5, True, True) block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx5]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) self.utxo.append(UTXO(tx5.sha256, 0, tx5.vout[0].nValue)) @subtest @@ -1626,11 +1619,11 @@ class SegWitTest(BitcoinTestFramework): tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, script_pubkey)) tx.rehash() - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[0], self.test_node, tx, with_witness=True, accepted=True) # Mine this transaction in preparation for following tests. block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) sync_blocks(self.nodes) self.utxo.pop(0) @@ -1647,19 +1640,19 @@ class SegWitTest(BitcoinTestFramework): # Too-large input value sign_p2pk_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue + 1, key) self.update_witness_block_with_transactions(block, [tx]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block, accepted=False) # Too-small input value sign_p2pk_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue - 1, key) block.vtx.pop() # remove last tx self.update_witness_block_with_transactions(block, [tx]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block, accepted=False) # Now try correct value sign_p2pk_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue, key) block.vtx.pop() self.update_witness_block_with_transactions(block, [tx]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) prev_utxo = UTXO(tx.sha256, 0, tx.vout[0].nValue) @@ -1683,7 +1676,7 @@ class SegWitTest(BitcoinTestFramework): block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) block = self.build_next_block() used_sighash_single_out_of_bounds = False @@ -1725,7 +1718,7 @@ class SegWitTest(BitcoinTestFramework): # Test the block periodically, if we're close to maxblocksize if (get_virtual_size(block) > MAX_BLOCK_BASE_SIZE - 1000): self.update_witness_block_with_transactions(block, []) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) block = self.build_next_block() if (not used_sighash_single_out_of_bounds): @@ -1733,7 +1726,7 @@ class SegWitTest(BitcoinTestFramework): # Test the transactions we've added to the block if (len(block.vtx) > 1): self.update_witness_block_with_transactions(block, []) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) # Now test witness version 0 P2PKH transactions pubkeyhash = hash160(pubkey) @@ -1755,7 +1748,7 @@ class SegWitTest(BitcoinTestFramework): tx2.vin[0].scriptSig = CScript([signature, pubkey]) block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx, tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block, accepted=False) # Move the signature to the witness. block.vtx.pop() @@ -1765,7 +1758,7 @@ class SegWitTest(BitcoinTestFramework): tx2.rehash() self.update_witness_block_with_transactions(block, [tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) temp_utxos.pop(0) @@ -1786,7 +1779,7 @@ class SegWitTest(BitcoinTestFramework): index += 1 block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx]) - test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block, accepted=True) for i in range(len(tx.vout)): self.utxo.append(UTXO(tx.sha256, i, tx.vout[i].nValue)) @@ -1808,7 +1801,7 @@ class SegWitTest(BitcoinTestFramework): tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, script_pubkey)) tx.rehash() - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, False, True) + test_transaction_acceptance(self.nodes[0], self.test_node, tx, False, True) self.nodes[0].generate(1) sync_blocks(self.nodes) @@ -1825,18 +1818,18 @@ class SegWitTest(BitcoinTestFramework): tx2.rehash() # This will be rejected due to a policy check: # No witness is allowed, since it is not a witness program but a p2sh program - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, True, False, b'bad-witness-nonstandard') + test_transaction_acceptance(self.nodes[1], self.std_node, tx2, True, False, 'bad-witness-nonstandard') # If we send without witness, it should be accepted. - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, False, True) + test_transaction_acceptance(self.nodes[1], self.std_node, tx2, False, True) # Now create a new anyone-can-spend utxo for the next test. tx3 = CTransaction() tx3.vin.append(CTxIn(COutPoint(tx2.sha256, 0), CScript([p2sh_program]))) tx3.vout.append(CTxOut(tx2.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx3.rehash() - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, False, True) - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, False, True) + test_transaction_acceptance(self.nodes[0], self.test_node, tx2, False, True) + test_transaction_acceptance(self.nodes[0], self.test_node, tx3, False, True) self.nodes[0].generate(1) sync_blocks(self.nodes) @@ -1872,7 +1865,7 @@ class SegWitTest(BitcoinTestFramework): tx.vout.append(CTxOut(outputvalue, CScript([OP_HASH160, p2sh, OP_EQUAL]))) tx.rehash() txid = tx.sha256 - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True) + test_transaction_acceptance(self.nodes[0], self.test_node, tx, with_witness=False, accepted=True) self.nodes[0].generate(1) sync_blocks(self.nodes) @@ -1897,45 +1890,45 @@ class SegWitTest(BitcoinTestFramework): # Testing native P2WSH # Witness stack size, excluding witnessScript, over 100 is non-standard p2wsh_txs[0].wit.vtxinwit[0].scriptWitness.stack = [pad] * 101 + [scripts[0]] - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2wsh_txs[0], True, False, b'bad-witness-nonstandard') + test_transaction_acceptance(self.nodes[1], self.std_node, p2wsh_txs[0], True, False, 'bad-witness-nonstandard') # Non-standard nodes should accept - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2wsh_txs[0], True, True) + test_transaction_acceptance(self.nodes[0], self.test_node, p2wsh_txs[0], True, True) # Stack element size over 80 bytes is non-standard p2wsh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 81] * 100 + [scripts[1]] - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2wsh_txs[1], True, False, b'bad-witness-nonstandard') + test_transaction_acceptance(self.nodes[1], self.std_node, p2wsh_txs[1], True, False, 'bad-witness-nonstandard') # Non-standard nodes should accept - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2wsh_txs[1], True, True) + test_transaction_acceptance(self.nodes[0], self.test_node, p2wsh_txs[1], True, True) # Standard nodes should accept if element size is not over 80 bytes p2wsh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 80] * 100 + [scripts[1]] - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2wsh_txs[1], True, True) + test_transaction_acceptance(self.nodes[1], self.std_node, p2wsh_txs[1], True, True) # witnessScript size at 3600 bytes is standard p2wsh_txs[2].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, scripts[2]] - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2wsh_txs[2], True, True) - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2wsh_txs[2], True, True) + test_transaction_acceptance(self.nodes[0], self.test_node, p2wsh_txs[2], True, True) + test_transaction_acceptance(self.nodes[1], self.std_node, p2wsh_txs[2], True, True) # witnessScript size at 3601 bytes is non-standard p2wsh_txs[3].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, pad, scripts[3]] - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2wsh_txs[3], True, False, b'bad-witness-nonstandard') + test_transaction_acceptance(self.nodes[1], self.std_node, p2wsh_txs[3], True, False, 'bad-witness-nonstandard') # Non-standard nodes should accept - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2wsh_txs[3], True, True) + test_transaction_acceptance(self.nodes[0], self.test_node, p2wsh_txs[3], True, True) # Repeating the same tests with P2SH-P2WSH p2sh_txs[0].wit.vtxinwit[0].scriptWitness.stack = [pad] * 101 + [scripts[0]] - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[0], True, False, b'bad-witness-nonstandard') - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_txs[0], True, True) + test_transaction_acceptance(self.nodes[1], self.std_node, p2sh_txs[0], True, False, 'bad-witness-nonstandard') + test_transaction_acceptance(self.nodes[0], self.test_node, p2sh_txs[0], True, True) p2sh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 81] * 100 + [scripts[1]] - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[1], True, False, b'bad-witness-nonstandard') - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_txs[1], True, True) + test_transaction_acceptance(self.nodes[1], self.std_node, p2sh_txs[1], True, False, 'bad-witness-nonstandard') + test_transaction_acceptance(self.nodes[0], self.test_node, p2sh_txs[1], True, True) p2sh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 80] * 100 + [scripts[1]] - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[1], True, True) + test_transaction_acceptance(self.nodes[1], self.std_node, p2sh_txs[1], True, True) p2sh_txs[2].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, scripts[2]] - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_txs[2], True, True) - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[2], True, True) + test_transaction_acceptance(self.nodes[0], self.test_node, p2sh_txs[2], True, True) + test_transaction_acceptance(self.nodes[1], self.std_node, p2sh_txs[2], True, True) p2sh_txs[3].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, pad, scripts[3]] - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[3], True, False, b'bad-witness-nonstandard') - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_txs[3], True, True) + test_transaction_acceptance(self.nodes[1], self.std_node, p2sh_txs[3], True, False, 'bad-witness-nonstandard') + test_transaction_acceptance(self.nodes[0], self.test_node, p2sh_txs[3], True, True) self.nodes[0].generate(1) # Mine and clean up the mempool of non-standard node # Valid but non-standard transactions in a block should be accepted by standard node @@ -2011,7 +2004,7 @@ class SegWitTest(BitcoinTestFramework): block_1 = self.build_next_block() self.update_witness_block_with_transactions(block_1, [tx]) - test_witness_block(self.nodes[0].rpc, self.test_node, block_1, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block_1, accepted=True) tx2 = CTransaction() # If we try to spend the first n-1 outputs from tx, that should be @@ -2028,7 +2021,7 @@ class SegWitTest(BitcoinTestFramework): block_2 = self.build_next_block() self.update_witness_block_with_transactions(block_2, [tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block_2, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block_2, accepted=False) # Try dropping the last input in tx2, and add an output that has # too many sigops (contributing to legacy sigop count). @@ -2041,14 +2034,14 @@ class SegWitTest(BitcoinTestFramework): tx2.rehash() block_3 = self.build_next_block() self.update_witness_block_with_transactions(block_3, [tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block_3, accepted=False) + test_witness_block(self.nodes[0], self.test_node, block_3, accepted=False) # If we drop the last checksig in this output, the tx should succeed. block_4 = self.build_next_block() tx2.vout[-1].scriptPubKey = CScript([OP_CHECKSIG] * (checksig_count - 1)) tx2.rehash() self.update_witness_block_with_transactions(block_4, [tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block_4, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block_4, accepted=True) # Reset the tip back down for the next test sync_blocks(self.nodes) @@ -2064,7 +2057,7 @@ class SegWitTest(BitcoinTestFramework): tx2.wit.vtxinwit[-1].scriptWitness.stack = [witness_program_justright] tx2.rehash() self.update_witness_block_with_transactions(block_5, [tx2]) - test_witness_block(self.nodes[0].rpc, self.test_node, block_5, accepted=True) + test_witness_block(self.nodes[0], self.test_node, block_5, accepted=True) # TODO: test p2sh sigop counting diff --git a/test/functional/p2p_sendheaders.py b/test/functional/p2p_sendheaders.py index 9cc496d510..c7ae57de86 100755 --- a/test/functional/p2p_sendheaders.py +++ b/test/functional/p2p_sendheaders.py @@ -213,7 +213,7 @@ class SendHeadersTest(BitcoinTestFramework): # Clear out block announcements from each p2p listener [x.clear_block_announcements() for x in self.nodes[0].p2ps] - self.nodes[0].generate(count) + self.nodes[0].generatetoaddress(count, self.nodes[0].get_deterministic_priv_key().address) return int(self.nodes[0].getbestblockhash(), 16) def mine_reorg(self, length): @@ -223,7 +223,8 @@ class SendHeadersTest(BitcoinTestFramework): to-be-reorged-out blocks are mined, so that we don't break later tests. return the list of block hashes newly mined.""" - self.nodes[0].generate(length) # make sure all invalidated blocks are node0's + # make sure all invalidated blocks are node0's + self.nodes[0].generatetoaddress(length, self.nodes[0].get_deterministic_priv_key().address) sync_blocks(self.nodes, wait=0.1) for x in self.nodes[0].p2ps: x.wait_for_block_announcement(int(self.nodes[0].getbestblockhash(), 16)) @@ -232,7 +233,7 @@ class SendHeadersTest(BitcoinTestFramework): tip_height = self.nodes[1].getblockcount() hash_to_invalidate = self.nodes[1].getblockhash(tip_height - (length - 1)) self.nodes[1].invalidateblock(hash_to_invalidate) - all_hashes = self.nodes[1].generate(length + 1) # Must be longer than the orig chain + all_hashes = self.nodes[1].generatetoaddress(length + 1, self.nodes[1].get_deterministic_priv_key().address) # Must be longer than the orig chain sync_blocks(self.nodes, wait=0.1) return [int(x, 16) for x in all_hashes] @@ -251,7 +252,7 @@ class SendHeadersTest(BitcoinTestFramework): self.test_nonnull_locators(test_node, inv_node) def test_null_locators(self, test_node, inv_node): - tip = self.nodes[0].getblockheader(self.nodes[0].generate(1)[0]) + tip = self.nodes[0].getblockheader(self.nodes[0].generatetoaddress(1, self.nodes[0].get_deterministic_priv_key().address)[0]) tip_hash = int(tip["hash"], 16) inv_node.check_last_inv_announcement(inv=[tip_hash]) @@ -401,7 +402,7 @@ class SendHeadersTest(BitcoinTestFramework): block_time += 9 - fork_point = self.nodes[0].getblock("%02x" % new_block_hashes[0])["previousblockhash"] + fork_point = self.nodes[0].getblock("%064x" % new_block_hashes[0])["previousblockhash"] fork_point = int(fork_point, 16) # Use getblocks/getdata diff --git a/test/functional/p2p_unrequested_blocks.py b/test/functional/p2p_unrequested_blocks.py index 2e86954aba..11299cbc00 100755 --- a/test/functional/p2p_unrequested_blocks.py +++ b/test/functional/p2p_unrequested_blocks.py @@ -82,8 +82,8 @@ class AcceptBlockTest(BitcoinTestFramework): min_work_node = self.nodes[1].add_p2p_connection(P2PInterface()) # 1. Have nodes mine a block (leave IBD) - [ n.generate(1) for n in self.nodes ] - tips = [ int("0x" + n.getbestblockhash(), 0) for n in self.nodes ] + [n.generatetoaddress(1, n.get_deterministic_priv_key().address) for n in self.nodes] + tips = [int("0x" + n.getbestblockhash(), 0) for n in self.nodes] # 2. Send one block that builds on each tip. # This should be accepted by node0 diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index d681cdc8ab..92b690176d 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -47,9 +47,10 @@ from test_framework.mininode import ( class BlockchainTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 - self.extra_args = [['-stopatheight=207', '-prune=1']] def run_test(self): + self.restart_node(0, extra_args=['-stopatheight=207', '-prune=1']) # Set extra args with pruning after rescan is complete + self._test_getblockchaininfo() self._test_getchaintxstats() self._test_gettxoutsetinfo() @@ -124,7 +125,9 @@ class BlockchainTest(BitcoinTestFramework): # Test `getchaintxstats` invalid `blockhash` assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].getchaintxstats, blockhash=0) - assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getchaintxstats, blockhash='0') + assert_raises_rpc_error(-8, "blockhash must be of length 64 (not 1, for '0')", self.nodes[0].getchaintxstats, blockhash='0') + assert_raises_rpc_error(-8, "blockhash must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].getchaintxstats, blockhash='ZZZ0000000000000000000000000000000000000000000000000000000000000') + assert_raises_rpc_error(-5, "Block not found", self.nodes[0].getchaintxstats, blockhash='0000000000000000000000000000000000000000000000000000000000000000') blockhash = self.nodes[0].getblockhash(200) self.nodes[0].invalidateblock(blockhash) assert_raises_rpc_error(-8, "Block is not in main chain", self.nodes[0].getchaintxstats, blockhash=blockhash) @@ -169,7 +172,7 @@ class BlockchainTest(BitcoinTestFramework): assert_equal(res['transactions'], 200) assert_equal(res['height'], 200) assert_equal(res['txouts'], 200) - assert_equal(res['bogosize'], 17000), + assert_equal(res['bogosize'], 15000), assert_equal(res['bestblock'], node.getblockhash(200)) size = res['disk_size'] assert size > 6400 @@ -202,7 +205,9 @@ class BlockchainTest(BitcoinTestFramework): def _test_getblockheader(self): node = self.nodes[0] - assert_raises_rpc_error(-5, "Block not found", node.getblockheader, "nonsense") + assert_raises_rpc_error(-8, "hash must be of length 64 (not 8, for 'nonsense')", node.getblockheader, "nonsense") + assert_raises_rpc_error(-8, "hash must be hexadecimal string (not 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844')", node.getblockheader, "ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844") + assert_raises_rpc_error(-5, "Block not found", node.getblockheader, "0cf7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844") besthash = node.getbestblockhash() secondbesthash = node.getblockhash(199) @@ -238,12 +243,12 @@ class BlockchainTest(BitcoinTestFramework): def _test_stopatheight(self): assert_equal(self.nodes[0].getblockcount(), 200) - self.nodes[0].generate(6) + self.nodes[0].generatetoaddress(6, self.nodes[0].get_deterministic_priv_key().address) assert_equal(self.nodes[0].getblockcount(), 206) self.log.debug('Node should not stop at this height') assert_raises(subprocess.TimeoutExpired, lambda: self.nodes[0].process.wait(timeout=3)) try: - self.nodes[0].generate(1) + self.nodes[0].generatetoaddress(1, self.nodes[0].get_deterministic_priv_key().address) except (ConnectionError, http.client.BadStatusLine): pass # The node already shut down before response self.log.debug('Node should stop at this height...') diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py index f346e1ebfc..3cc35a7b9a 100755 --- a/test/functional/rpc_createmultisig.py +++ b/test/functional/rpc_createmultisig.py @@ -12,8 +12,11 @@ class RpcCreateMultiSigTest(BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 3 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def get_keys(self): - node0,node1,node2 = self.nodes + node0, node1, node2 = self.nodes self.add = [node1.getnewaddress() for _ in range(self.nkeys)] self.pub = [node1.getaddressinfo(a)["pubkey"] for a in self.add] self.priv = [node1.dumpprivkey(a) for a in self.add] diff --git a/test/functional/rpc_deprecated.py b/test/functional/rpc_deprecated.py index deffe4e6bd..58074803cc 100755 --- a/test/functional/rpc_deprecated.py +++ b/test/functional/rpc_deprecated.py @@ -4,13 +4,12 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test deprecation of RPC calls.""" from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_raises_rpc_error class DeprecatedRpcTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True - self.extra_args = [[], ["-deprecatedrpc=validateaddress", "-deprecatedrpc=accounts"]] + self.extra_args = [[], ["-deprecatedrpc=validateaddress"]] def run_test(self): # This test should be used to verify correct behaviour of deprecated @@ -19,90 +18,7 @@ class DeprecatedRpcTest(BitcoinTestFramework): # self.log.info("Make sure that -deprecatedrpc=createmultisig allows it to take addresses") # assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 1, [self.nodes[0].getnewaddress()]) # self.nodes[1].createmultisig(1, [self.nodes[1].getnewaddress()]) - - self.log.info("Test validateaddress deprecation") - SOME_ADDRESS = "mnvGjUy3NMj67yJ6gkK5o9e5RS33Z2Vqcu" # This is just some random address to pass as a parameter to validateaddress - dep_validate_address = self.nodes[0].validateaddress(SOME_ADDRESS) - assert "ismine" not in dep_validate_address - not_dep_val = self.nodes[1].validateaddress(SOME_ADDRESS) - assert "ismine" in not_dep_val - - self.log.info("Test accounts deprecation") - # The following account RPC methods are deprecated: - # - getaccount - # - getaccountaddress - # - getaddressesbyaccount - # - getreceivedbyaccount - # - listaccouts - # - listreceivedbyaccount - # - move - # - setaccount - # - # The following 'label' RPC methods are usable both with and without the - # -deprecatedrpc=accounts switch enabled. - # - getaddressesbylabel - # - getreceivedbylabel - # - listlabels - # - listreceivedbylabel - # - setlabel - # - address0 = self.nodes[0].getnewaddress() - self.nodes[0].generatetoaddress(101, address0) - self.sync_all() - address1 = self.nodes[1].getnewaddress() - self.nodes[1].generatetoaddress(101, address1) - - self.log.info("- getaccount") - assert_raises_rpc_error(-32, "getaccount is deprecated", self.nodes[0].getaccount, address0) - self.nodes[1].getaccount(address1) - - self.log.info("- setaccount") - assert_raises_rpc_error(-32, "setaccount is deprecated", self.nodes[0].setaccount, address0, "label0") - self.nodes[1].setaccount(address1, "label1") - - self.log.info("- setlabel") - self.nodes[0].setlabel(address0, "label0") - self.nodes[1].setlabel(address1, "label1") - - self.log.info("- getaccountaddress") - assert_raises_rpc_error(-32, "getaccountaddress is deprecated", self.nodes[0].getaccountaddress, "label0") - self.nodes[1].getaccountaddress("label1") - - self.log.info("- getaddressesbyaccount") - assert_raises_rpc_error(-32, "getaddressesbyaccount is deprecated", self.nodes[0].getaddressesbyaccount, "label0") - self.nodes[1].getaddressesbyaccount("label1") - - self.log.info("- getaddressesbylabel") - self.nodes[0].getaddressesbylabel("label0") - self.nodes[1].getaddressesbylabel("label1") - - self.log.info("- getreceivedbyaccount") - assert_raises_rpc_error(-32, "getreceivedbyaccount is deprecated", self.nodes[0].getreceivedbyaccount, "label0") - self.nodes[1].getreceivedbyaccount("label1") - - self.log.info("- getreceivedbylabel") - self.nodes[0].getreceivedbylabel("label0") - self.nodes[1].getreceivedbylabel("label1") - - self.log.info("- listaccounts") - assert_raises_rpc_error(-32, "listaccounts is deprecated", self.nodes[0].listaccounts) - self.nodes[1].listaccounts() - - self.log.info("- listlabels") - self.nodes[0].listlabels() - self.nodes[1].listlabels() - - self.log.info("- listreceivedbyaccount") - assert_raises_rpc_error(-32, "listreceivedbyaccount is deprecated", self.nodes[0].listreceivedbyaccount) - self.nodes[1].listreceivedbyaccount() - - self.log.info("- listreceivedbylabel") - self.nodes[0].listreceivedbylabel() - self.nodes[1].listreceivedbylabel() - - self.log.info("- move") - assert_raises_rpc_error(-32, "move is deprecated", self.nodes[0].move, "label0", "label0b", 10) - self.nodes[1].move("label1", "label1b", 10) + pass if __name__ == '__main__': DeprecatedRpcTest().main() diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py index a806de43b4..0c61e9ab62 100755 --- a/test/functional/rpc_fundrawtransaction.py +++ b/test/functional/rpc_fundrawtransaction.py @@ -29,6 +29,9 @@ class RawTransactionsTest(BitcoinTestFramework): self.num_nodes = 4 self.setup_clean_chain = True + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def setup_network(self, split=False): self.setup_nodes() @@ -475,10 +478,8 @@ class RawTransactionsTest(BitcoinTestFramework): ############################################################ # locked wallet test - self.stop_node(0) - self.nodes[1].node_encrypt_wallet("test") - self.stop_node(2) - self.stop_node(3) + self.nodes[1].encryptwallet("test") + self.stop_nodes() self.start_nodes() # This test is not meant to test fee estimation and we'd like diff --git a/test/functional/rpc_getchaintips.py b/test/functional/rpc_getchaintips.py index 07929ca0a1..c869c7262f 100755 --- a/test/functional/rpc_getchaintips.py +++ b/test/functional/rpc_getchaintips.py @@ -17,17 +17,17 @@ class GetChainTipsTest (BitcoinTestFramework): def set_test_params(self): self.num_nodes = 4 - def run_test (self): - tips = self.nodes[0].getchaintips () - assert_equal (len (tips), 1) - assert_equal (tips[0]['branchlen'], 0) - assert_equal (tips[0]['height'], 200) - assert_equal (tips[0]['status'], 'active') + def run_test(self): + tips = self.nodes[0].getchaintips() + assert_equal(len(tips), 1) + assert_equal(tips[0]['branchlen'], 0) + assert_equal(tips[0]['height'], 200) + assert_equal(tips[0]['status'], 'active') # Split the network and build two chains of different lengths. - self.split_network () - self.nodes[0].generate(10) - self.nodes[2].generate(20) + self.split_network() + self.nodes[0].generatetoaddress(10, self.nodes[0].get_deterministic_priv_key().address) + self.nodes[2].generatetoaddress(20, self.nodes[2].get_deterministic_priv_key().address) self.sync_all([self.nodes[:2], self.nodes[2:]]) tips = self.nodes[1].getchaintips () diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py new file mode 100755 index 0000000000..be096af892 --- /dev/null +++ b/test/functional/rpc_help.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# Copyright (c) 2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test RPC help output.""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal, assert_raises_rpc_error + + +class HelpRpcTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + + def run_test(self): + node = self.nodes[0] + + # wrong argument count + assert_raises_rpc_error(-1, 'help', node.help, 'foo', 'bar') + + # invalid argument + assert_raises_rpc_error(-1, 'JSON value is not a string as expected', node.help, 0) + + # help of unknown command + assert_equal(node.help('foo'), 'help: unknown command: foo') + + # command titles + titles = [line[3:-3] for line in node.help().splitlines() if line.startswith('==')] + + components = ['Blockchain', 'Control', 'Generating', 'Mining', 'Network', 'Rawtransactions', 'Util'] + + if self.is_wallet_compiled(): + components.append('Wallet') + + if self.is_zmq_compiled(): + components.append('Zmq') + + assert_equal(titles, components) + + +if __name__ == '__main__': + HelpRpcTest().main() diff --git a/test/functional/rpc_invalidateblock.py b/test/functional/rpc_invalidateblock.py index f40710ef74..d8ecdd573a 100755 --- a/test/functional/rpc_invalidateblock.py +++ b/test/functional/rpc_invalidateblock.py @@ -20,12 +20,12 @@ class InvalidateTest(BitcoinTestFramework): def run_test(self): self.log.info("Make sure we repopulate setBlockIndexCandidates after InvalidateBlock:") self.log.info("Mine 4 blocks on Node 0") - self.nodes[0].generate(4) + self.nodes[0].generatetoaddress(4, self.nodes[0].get_deterministic_priv_key().address) assert(self.nodes[0].getblockcount() == 4) besthash = self.nodes[0].getbestblockhash() self.log.info("Mine competing 6 blocks on Node 1") - self.nodes[1].generate(6) + self.nodes[1].generatetoaddress(6, self.nodes[1].get_deterministic_priv_key().address) assert(self.nodes[1].getblockcount() == 6) self.log.info("Connect nodes to force a reorg") @@ -53,7 +53,7 @@ class InvalidateTest(BitcoinTestFramework): self.nodes[2].invalidateblock(self.nodes[2].getblockhash(3)) assert(self.nodes[2].getblockcount() == 2) self.log.info("..and then mine a block") - self.nodes[2].generate(1) + self.nodes[2].generatetoaddress(1, self.nodes[2].get_deterministic_priv_key().address) self.log.info("Verify all nodes are at the right height") time.sleep(5) assert_equal(self.nodes[2].getblockcount(), 3) diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py index 02114eca76..1e525214fa 100755 --- a/test/functional/rpc_net.py +++ b/test/functional/rpc_net.py @@ -7,20 +7,26 @@ Tests correspond to code in rpc/net.cpp. """ +from decimal import Decimal + from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_greater_than_or_equal, + assert_greater_than, assert_raises_rpc_error, connect_nodes_bi, p2p_port, wait_until, ) +from test_framework.mininode import P2PInterface +from test_framework.messages import CAddress, msg_addr, NODE_NETWORK, NODE_WITNESS class NetTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 + self.extra_args = [["-minrelaytxfee=0.00001000"],["-minrelaytxfee=0.00000500"]] def run_test(self): self._test_connection_count() @@ -28,6 +34,7 @@ class NetTest(BitcoinTestFramework): self._test_getnetworkinginfo() self._test_getaddednodeinfo() self._test_getpeerinfo() + self._test_getnodeaddresses() def _test_connection_count(self): # connect_nodes_bi connects each node to the other @@ -95,6 +102,43 @@ class NetTest(BitcoinTestFramework): # the address bound to on one side will be the source address for the other node assert_equal(peer_info[0][0]['addrbind'], peer_info[1][0]['addr']) assert_equal(peer_info[1][0]['addrbind'], peer_info[0][0]['addr']) + assert_equal(peer_info[0][0]['minfeefilter'], Decimal("0.00000500")) + assert_equal(peer_info[1][0]['minfeefilter'], Decimal("0.00001000")) + + def _test_getnodeaddresses(self): + self.nodes[0].add_p2p_connection(P2PInterface()) + + # send some addresses to the node via the p2p message addr + msg = msg_addr() + imported_addrs = [] + for i in range(256): + a = "123.123.123.{}".format(i) + imported_addrs.append(a) + addr = CAddress() + addr.time = 100000000 + addr.nServices = NODE_NETWORK | NODE_WITNESS + addr.ip = a + addr.port = 8333 + msg.addrs.append(addr) + self.nodes[0].p2p.send_and_ping(msg) + + # obtain addresses via rpc call and check they were ones sent in before + REQUEST_COUNT = 10 + node_addresses = self.nodes[0].getnodeaddresses(REQUEST_COUNT) + assert_equal(len(node_addresses), REQUEST_COUNT) + for a in node_addresses: + assert_greater_than(a["time"], 1527811200) # 1st June 2018 + assert_equal(a["services"], NODE_NETWORK | NODE_WITNESS) + assert a["address"] in imported_addrs + assert_equal(a["port"], 8333) + + assert_raises_rpc_error(-8, "Address count out of range", self.nodes[0].getnodeaddresses, -1) + + # addrman's size cannot be known reliably after insertion, as hash collisions may occur + # so only test that requesting a large number of addresses returns less than that + LARGE_REQUEST_COUNT = 10000 + node_addresses = self.nodes[0].getnodeaddresses(LARGE_REQUEST_COUNT) + assert_greater_than(LARGE_REQUEST_COUNT, len(node_addresses)) if __name__ == '__main__': NetTest().main() diff --git a/test/functional/rpc_preciousblock.py b/test/functional/rpc_preciousblock.py index 1e20d1a046..72e6e6329f 100755 --- a/test/functional/rpc_preciousblock.py +++ b/test/functional/rpc_preciousblock.py @@ -43,18 +43,19 @@ class PreciousTest(BitcoinTestFramework): def run_test(self): self.log.info("Ensure submitblock can in principle reorg to a competing chain") - self.nodes[0].generate(1) + gen_address = lambda i: self.nodes[i].get_deterministic_priv_key().address # A non-wallet address to mine to + self.nodes[0].generatetoaddress(1, gen_address(0)) assert_equal(self.nodes[0].getblockcount(), 1) - hashZ = self.nodes[1].generate(2)[-1] + hashZ = self.nodes[1].generatetoaddress(2, gen_address(1))[-1] assert_equal(self.nodes[1].getblockcount(), 2) node_sync_via_rpc(self.nodes[0:3]) assert_equal(self.nodes[0].getbestblockhash(), hashZ) self.log.info("Mine blocks A-B-C on Node 0") - hashC = self.nodes[0].generate(3)[-1] + hashC = self.nodes[0].generatetoaddress(3, gen_address(0))[-1] assert_equal(self.nodes[0].getblockcount(), 5) self.log.info("Mine competing blocks E-F-G on Node 1") - hashG = self.nodes[1].generate(3)[-1] + hashG = self.nodes[1].generatetoaddress(3, gen_address(1))[-1] assert_equal(self.nodes[1].getblockcount(), 5) assert(hashC != hashG) self.log.info("Connect nodes and check no reorg occurs") @@ -83,7 +84,7 @@ class PreciousTest(BitcoinTestFramework): self.nodes[1].preciousblock(hashC) assert_equal(self.nodes[1].getbestblockhash(), hashC) self.log.info("Mine another block (E-F-G-)H on Node 0 and reorg Node 1") - self.nodes[0].generate(1) + self.nodes[0].generatetoaddress(1, gen_address(0)) assert_equal(self.nodes[0].getblockcount(), 6) sync_blocks(self.nodes[0:2]) hashH = self.nodes[0].getbestblockhash() @@ -92,7 +93,7 @@ class PreciousTest(BitcoinTestFramework): self.nodes[1].preciousblock(hashC) assert_equal(self.nodes[1].getbestblockhash(), hashH) self.log.info("Mine competing blocks I-J-K-L on Node 2") - self.nodes[2].generate(4) + self.nodes[2].generatetoaddress(4, gen_address(2)) assert_equal(self.nodes[2].getblockcount(), 6) hashL = self.nodes[2].getbestblockhash() self.log.info("Connect nodes and check no reorg occurs") diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index f847a01e59..54dc871448 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -20,6 +20,9 @@ class PSBTTest(BitcoinTestFramework): self.setup_clean_chain = False self.num_nodes = 3 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): # Create and fund a raw tx for sending 10 BTC psbtx1 = self.nodes[0].walletcreatefundedpsbt([], {self.nodes[2].getnewaddress():10})['psbt'] @@ -105,7 +108,7 @@ class PSBTTest(BitcoinTestFramework): signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx['hex']) assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].converttopsbt, signedtx['hex']) - # Explicilty allow converting non-empty txs + # Explicitly allow converting non-empty txs new_psbt = self.nodes[0].converttopsbt(rawtx['hex']) self.nodes[0].decodepsbt(new_psbt) @@ -143,17 +146,19 @@ class PSBTTest(BitcoinTestFramework): # replaceable arg block_height = self.nodes[0].getblockcount() unspent = self.nodes[0].listunspent()[0] - psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"replaceable":True}) + psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"replaceable":True}, False) decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"]) - for tx_in in decoded_psbt["tx"]["vin"]: + for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]): assert_equal(tx_in["sequence"], MAX_BIP125_RBF_SEQUENCE) + assert "bip32_derivs" not in psbt_in assert_equal(decoded_psbt["tx"]["locktime"], block_height+2) # Same construction with only locktime set - psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height) + psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height, {}, True) decoded_psbt = self.nodes[0].decodepsbt(psbtx_info["psbt"]) - for tx_in in decoded_psbt["tx"]["vin"]: + for tx_in, psbt_in in zip(decoded_psbt["tx"]["vin"], decoded_psbt["inputs"]): assert tx_in["sequence"] > MAX_BIP125_RBF_SEQUENCE + assert "bip32_derivs" in psbt_in assert_equal(decoded_psbt["tx"]["locktime"], block_height) # Same construction without optional arguments diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py index 1e3ada26f8..8ed490f552 100755 --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -44,6 +44,9 @@ class RawTransactionsTest(BitcoinTestFramework): self.num_nodes = 3 self.extra_args = [["-addresstype=legacy"], ["-addresstype=legacy"], ["-addresstype=legacy"]] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def setup_network(self, split=False): super().setup_network() connect_nodes_bi(self.nodes, 0, 2) @@ -77,8 +80,9 @@ class RawTransactionsTest(BitcoinTestFramework): txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000' assert_raises_rpc_error(-3, "Expected type array", self.nodes[0].createrawtransaction, 'foo', {}) assert_raises_rpc_error(-1, "JSON value is not an object as expected", self.nodes[0].createrawtransaction, ['foo'], {}) - assert_raises_rpc_error(-8, "txid must be hexadecimal string", self.nodes[0].createrawtransaction, [{}], {}) - assert_raises_rpc_error(-8, "txid must be hexadecimal string", self.nodes[0].createrawtransaction, [{'txid': 'foo'}], {}) + assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].createrawtransaction, [{}], {}) + assert_raises_rpc_error(-8, "txid must be of length 64 (not 3, for 'foo')", self.nodes[0].createrawtransaction, [{'txid': 'foo'}], {}) + assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844')", self.nodes[0].createrawtransaction, [{'txid': 'ZZZ7bb8b1697ea987f3b223ba7819250cae33efacb068d23dc24859824a77844'}], {}) assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid}], {}) assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 'foo'}], {}) assert_raises_rpc_error(-8, "Invalid parameter, vout must be positive", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': -1}], {}) @@ -221,9 +225,10 @@ class RawTransactionsTest(BitcoinTestFramework): # We should not get the tx if we provide an unrelated block assert_raises_rpc_error(-5, "No such transaction found", self.nodes[0].getrawtransaction, tx, True, block2) # An invalid block hash should raise the correct errors - assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", self.nodes[0].getrawtransaction, tx, True, True) - assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", self.nodes[0].getrawtransaction, tx, True, "foobar") - assert_raises_rpc_error(-8, "parameter 3 must be of length 64", self.nodes[0].getrawtransaction, tx, True, "abcd1234") + assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[0].getrawtransaction, tx, True, True) + assert_raises_rpc_error(-8, "parameter 3 must be of length 64 (not 6, for 'foobar')", self.nodes[0].getrawtransaction, tx, True, "foobar") + assert_raises_rpc_error(-8, "parameter 3 must be of length 64 (not 8, for 'abcd1234')", self.nodes[0].getrawtransaction, tx, True, "abcd1234") + assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].getrawtransaction, tx, True, "ZZZ0000000000000000000000000000000000000000000000000000000000000") assert_raises_rpc_error(-5, "Block hash not found", self.nodes[0].getrawtransaction, tx, True, "0000000000000000000000000000000000000000000000000000000000000000") # Undo the blocks and check in_active_chain self.nodes[0].invalidateblock(block1) @@ -358,7 +363,7 @@ class RawTransactionsTest(BitcoinTestFramework): # decoderawtransaction tests # witness transaction - encrawtx = "010000000001010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f50500000000000000000000" + encrawtx = "010000000001010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f50500000000000102616100000000" decrawtx = self.nodes[0].decoderawtransaction(encrawtx, True) # decode as witness transaction assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000')) assert_raises_rpc_error(-22, 'TX decode failed', self.nodes[0].decoderawtransaction, encrawtx, False) # force decode as non-witness transaction diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py index ceb38e969a..96f9ccdbdb 100755 --- a/test/functional/rpc_scantxoutset.py +++ b/test/functional/rpc_scantxoutset.py @@ -14,6 +14,10 @@ class ScantxoutsetTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True + + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): self.log.info("Mining blocks...") self.nodes[0].generate(110) diff --git a/test/functional/rpc_signmessage.py b/test/functional/rpc_signmessage.py index 449c4075bd..ad0e29b451 100755 --- a/test/functional/rpc_signmessage.py +++ b/test/functional/rpc_signmessage.py @@ -13,6 +13,9 @@ class SignMessagesTest(BitcoinTestFramework): self.num_nodes = 1 self.extra_args = [["-addresstype=legacy"]] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): message = 'This is just a test message' diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py index 32b099294f..291538df64 100755 --- a/test/functional/rpc_signrawtransaction.py +++ b/test/functional/rpc_signrawtransaction.py @@ -14,6 +14,9 @@ class SignRawTransactionsTest(BitcoinTestFramework): self.num_nodes = 1 self.extra_args = [["-deprecatedrpc=signrawtransaction"]] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def successful_signing_test(self): """Create and sign a valid raw transaction with one input. @@ -42,9 +45,13 @@ class SignRawTransactionsTest(BitcoinTestFramework): # 2) No script verification error occurred assert 'errors' not in rawTxSigned - # Perform the same test on signrawtransaction - rawTxSigned2 = self.nodes[0].signrawtransaction(rawTx, inputs, privKeys) - assert_equal(rawTxSigned, rawTxSigned2) + def test_with_lock_outputs(self): + """Test correct error reporting when trying to sign a locked output""" + self.nodes[0].encryptwallet("password") + + rawTx = '020000000156b958f78e3f24e0b2f4e4db1255426b0902027cb37e3ddadb52e37c3557dddb0000000000ffffffff01c0a6b929010000001600149a2ee8c77140a053f36018ac8124a6ececc1668a00000000' + + assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].signrawtransactionwithwallet, rawTx) def script_verification_error_test(self): """Create and sign a raw transaction with valid (vin 0), invalid (vin 1) and one missing (vin 2) input script. @@ -112,10 +119,6 @@ class SignRawTransactionsTest(BitcoinTestFramework): assert_equal(rawTxSigned['errors'][1]['vout'], inputs[2]['vout']) assert not rawTxSigned['errors'][0]['witness'] - # Perform same test with signrawtransaction - rawTxSigned2 = self.nodes[0].signrawtransaction(rawTx, scripts, privKeys) - assert_equal(rawTxSigned, rawTxSigned2) - # Now test signing failure for transaction with input witnesses p2wpkh_raw_tx = "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000494830450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000" @@ -140,13 +143,10 @@ class SignRawTransactionsTest(BitcoinTestFramework): assert_equal(rawTxSigned['errors'][1]['witness'], ["304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee01", "025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357"]) assert not rawTxSigned['errors'][0]['witness'] - # Perform same test with signrawtransaction - rawTxSigned2 = self.nodes[0].signrawtransaction(p2wpkh_raw_tx) - assert_equal(rawTxSigned, rawTxSigned2) - def run_test(self): self.successful_signing_test() self.script_verification_error_test() + self.test_with_lock_outputs() if __name__ == '__main__': diff --git a/test/functional/rpc_txoutproof.py b/test/functional/rpc_txoutproof.py index f9f8574646..8913b8698d 100755 --- a/test/functional/rpc_txoutproof.py +++ b/test/functional/rpc_txoutproof.py @@ -15,6 +15,9 @@ class MerkleBlockTest(BitcoinTestFramework): # Nodes 0/1 are "wallet" nodes, Nodes 2/3 are used for testing self.extra_args = [[], [], [], ["-txindex"]] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def setup_network(self): self.setup_nodes() connect_nodes(self.nodes[0], 1) @@ -63,12 +66,18 @@ class MerkleBlockTest(BitcoinTestFramework): txid_spent = txin_spent["txid"] txid_unspent = txid1 if txin_spent["txid"] != txid1 else txid2 + # Invalid txids + assert_raises_rpc_error(-8, "txid must be of length 64 (not 32, for '00000000000000000000000000000000')", self.nodes[2].gettxoutproof, ["00000000000000000000000000000000"], blockhash) + assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[2].gettxoutproof, ["ZZZ0000000000000000000000000000000000000000000000000000000000000"], blockhash) + # Invalid blockhashes + assert_raises_rpc_error(-8, "blockhash must be of length 64 (not 32, for '00000000000000000000000000000000')", self.nodes[2].gettxoutproof, [txid_spent], "00000000000000000000000000000000") + assert_raises_rpc_error(-8, "blockhash must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", self.nodes[2].gettxoutproof, [txid_spent], "ZZZ0000000000000000000000000000000000000000000000000000000000000") # We can't find the block from a fully-spent tx assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[2].gettxoutproof, [txid_spent]) # We can get the proof if we specify the block assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_spent], blockhash)), [txid_spent]) # We can't get the proof if we specify a non-existent block - assert_raises_rpc_error(-5, "Block not found", self.nodes[2].gettxoutproof, [txid_spent], "00000000000000000000000000000000") + assert_raises_rpc_error(-5, "Block not found", self.nodes[2].gettxoutproof, [txid_spent], "0000000000000000000000000000000000000000000000000000000000000000") # We can get the proof if the transaction is unspent assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_unspent])), [txid_unspent]) # We can get the proof if we provide a list of transactions and one of them is unspent. The ordering of the list should not matter. diff --git a/test/functional/rpc_zmq.py b/test/functional/rpc_zmq.py index 6dbc726d5e..bfa6b06f67 100755 --- a/test/functional/rpc_zmq.py +++ b/test/functional/rpc_zmq.py @@ -4,8 +4,7 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test for the ZMQ RPC methods.""" -from test_framework.test_framework import ( - BitcoinTestFramework, skip_if_no_py3_zmq, skip_if_no_bitcoind_zmq) +from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal @@ -17,9 +16,11 @@ class RPCZMQTest(BitcoinTestFramework): self.num_nodes = 1 self.setup_clean_chain = True + def skip_test_if_missing_module(self): + self.skip_if_no_py3_zmq() + self.skip_if_no_bitcoind_zmq() + def run_test(self): - skip_if_no_py3_zmq() - skip_if_no_bitcoind_zmq(self) self._test_getzmqnotifications() def _test_getzmqnotifications(self): diff --git a/test/functional/test_framework/authproxy.py b/test/functional/test_framework/authproxy.py index 900090bb66..1140fe9b3e 100644 --- a/test/functional/test_framework/authproxy.py +++ b/test/functional/test_framework/authproxy.py @@ -38,6 +38,7 @@ import decimal import http.client import json import logging +import os import socket import time import urllib.parse @@ -71,19 +72,12 @@ class AuthServiceProxy(): self._service_name = service_name self.ensure_ascii = ensure_ascii # can be toggled on the fly by tests self.__url = urllib.parse.urlparse(service_url) - port = 80 if self.__url.port is None else self.__url.port user = None if self.__url.username is None else self.__url.username.encode('utf8') passwd = None if self.__url.password is None else self.__url.password.encode('utf8') authpair = user + b':' + passwd self.__auth_header = b'Basic ' + base64.b64encode(authpair) - - if connection: - # Callables re-use the connection of the original proxy - self.__conn = connection - elif self.__url.scheme == 'https': - self.__conn = http.client.HTTPSConnection(self.__url.hostname, port, timeout=timeout) - else: - self.__conn = http.client.HTTPConnection(self.__url.hostname, port, timeout=timeout) + self.timeout = timeout + self._set_conn(connection) def __getattr__(self, name): if name.startswith('__') and name.endswith('__'): @@ -102,6 +96,10 @@ class AuthServiceProxy(): 'User-Agent': USER_AGENT, 'Authorization': self.__auth_header, 'Content-type': 'application/json'} + if os.name == 'nt': + # Windows somehow does not like to re-use connections + # TODO: Find out why the connection would disconnect occasionally and make it reusable on Windows + self._set_conn() try: self.__conn.request(method, path, postdata, headers) return self._get_response() @@ -178,3 +176,13 @@ class AuthServiceProxy(): def __truediv__(self, relative_uri): return AuthServiceProxy("{}/{}".format(self.__service_url, relative_uri), self._service_name, connection=self.__conn) + + def _set_conn(self, connection=None): + port = 80 if self.__url.port is None else self.__url.port + if connection: + self.__conn = connection + self.timeout = connection.timeout + elif self.__url.scheme == 'https': + self.__conn = http.client.HTTPSConnection(self.__url.hostname, port, timeout=self.timeout) + else: + self.__conn = http.client.HTTPConnection(self.__url.hostname, port, timeout=self.timeout) diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py index 987ade4044..35004fb588 100644 --- a/test/functional/test_framework/blocktools.py +++ b/test/functional/test_framework/blocktools.py @@ -122,7 +122,7 @@ def create_tx_with_script(prevtx, n, script_sig=b"", *, amount, script_pub_key=C """Return one-input, one-output transaction object spending the prevtx's n-th output with the given amount. - Can optionally pass scriptPubKey and scriptSig, default is anyone-can-spend ouput. + Can optionally pass scriptPubKey and scriptSig, default is anyone-can-spend output. """ tx = CTransaction() assert(n < len(prevtx.vout)) diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index 7276f6b450..8e9372767d 100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -13,7 +13,11 @@ CBlock, CTransaction, CBlockHeader, CTxIn, CTxOut, etc....: msg_block, msg_tx, msg_headers, etc.: data structures that represent network messages -ser_*, deser_*: functions that handle serialization/deserialization.""" +ser_*, deser_*: functions that handle serialization/deserialization. + +Classes use __slots__ to ensure extraneous attributes aren't accidentally added +by tests, compromising their intended effect. +""" from codecs import encode import copy import hashlib @@ -185,7 +189,10 @@ def ToHex(obj): # Objects that map to bitcoind objects, which can be serialized/deserialized -class CAddress(): + +class CAddress: + __slots__ = ("ip", "nServices", "pchReserved", "port", "time") + def __init__(self): self.time = 0 self.nServices = 1 @@ -215,7 +222,10 @@ class CAddress(): return "CAddress(nServices=%i ip=%s port=%i)" % (self.nServices, self.ip, self.port) -class CInv(): + +class CInv: + __slots__ = ("hash", "type") + typemap = { 0: "Error", 1: "TX", @@ -244,7 +254,9 @@ class CInv(): % (self.typemap[self.type], self.hash) -class CBlockLocator(): +class CBlockLocator: + __slots__ = ("nVersion", "vHave") + def __init__(self): self.nVersion = MY_VERSION self.vHave = [] @@ -264,7 +276,9 @@ class CBlockLocator(): % (self.nVersion, repr(self.vHave)) -class COutPoint(): +class COutPoint: + __slots__ = ("hash", "n") + def __init__(self, hash=0, n=0): self.hash = hash self.n = n @@ -283,7 +297,9 @@ class COutPoint(): return "COutPoint(hash=%064x n=%i)" % (self.hash, self.n) -class CTxIn(): +class CTxIn: + __slots__ = ("nSequence", "prevout", "scriptSig") + def __init__(self, outpoint=None, scriptSig=b"", nSequence=0): if outpoint is None: self.prevout = COutPoint() @@ -311,7 +327,9 @@ class CTxIn(): self.nSequence) -class CTxOut(): +class CTxOut: + __slots__ = ("nValue", "scriptPubKey") + def __init__(self, nValue=0, scriptPubKey=b""): self.nValue = nValue self.scriptPubKey = scriptPubKey @@ -332,7 +350,9 @@ class CTxOut(): bytes_to_hex_str(self.scriptPubKey)) -class CScriptWitness(): +class CScriptWitness: + __slots__ = ("stack",) + def __init__(self): # stack is a vector of strings self.stack = [] @@ -347,7 +367,9 @@ class CScriptWitness(): return True -class CTxInWitness(): +class CTxInWitness: + __slots__ = ("scriptWitness",) + def __init__(self): self.scriptWitness = CScriptWitness() @@ -364,7 +386,9 @@ class CTxInWitness(): return self.scriptWitness.is_null() -class CTxWitness(): +class CTxWitness: + __slots__ = ("vtxinwit",) + def __init__(self): self.vtxinwit = [] @@ -392,7 +416,10 @@ class CTxWitness(): return True -class CTransaction(): +class CTransaction: + __slots__ = ("hash", "nLockTime", "nVersion", "sha256", "vin", "vout", + "wit") + def __init__(self, tx=None): if tx is None: self.nVersion = 1 @@ -496,7 +523,10 @@ class CTransaction(): % (self.nVersion, repr(self.vin), repr(self.vout), repr(self.wit), self.nLockTime) -class CBlockHeader(): +class CBlockHeader: + __slots__ = ("hash", "hashMerkleRoot", "hashPrevBlock", "nBits", "nNonce", + "nTime", "nVersion", "sha256") + def __init__(self, header=None): if header is None: self.set_null() @@ -565,6 +595,8 @@ class CBlockHeader(): class CBlock(CBlockHeader): + __slots__ = ("vtx",) + def __init__(self, header=None): super(CBlock, self).__init__(header) self.vtx = [] @@ -636,7 +668,9 @@ class CBlock(CBlockHeader): time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.vtx)) -class PrefilledTransaction(): +class PrefilledTransaction: + __slots__ = ("index", "tx") + def __init__(self, index=0, tx = None): self.index = index self.tx = tx @@ -664,8 +698,12 @@ class PrefilledTransaction(): def __repr__(self): return "PrefilledTransaction(index=%d, tx=%s)" % (self.index, repr(self.tx)) + # This is what we send on the wire, in a cmpctblock message. -class P2PHeaderAndShortIDs(): +class P2PHeaderAndShortIDs: + __slots__ = ("header", "nonce", "prefilled_txn", "prefilled_txn_length", + "shortids", "shortids_length") + def __init__(self): self.header = CBlockHeader() self.nonce = 0 @@ -703,9 +741,11 @@ class P2PHeaderAndShortIDs(): def __repr__(self): return "P2PHeaderAndShortIDs(header=%s, nonce=%d, shortids_length=%d, shortids=%s, prefilled_txn_length=%d, prefilledtxn=%s" % (repr(self.header), self.nonce, self.shortids_length, repr(self.shortids), self.prefilled_txn_length, repr(self.prefilled_txn)) + # P2P version of the above that will use witness serialization (for compact # block version 2) class P2PHeaderAndShortWitnessIDs(P2PHeaderAndShortIDs): + __slots__ = () def serialize(self): return super(P2PHeaderAndShortWitnessIDs, self).serialize(with_witness=True) @@ -715,9 +755,12 @@ def calculate_shortid(k0, k1, tx_hash): expected_shortid &= 0x0000ffffffffffff return expected_shortid + # This version gets rid of the array lengths, and reinterprets the differential # encoding into indices that can be used for lookup. -class HeaderAndShortIDs(): +class HeaderAndShortIDs: + __slots__ = ("header", "nonce", "prefilled_txn", "shortids", "use_witness") + def __init__(self, p2pheaders_and_shortids = None): self.header = CBlockHeader() self.nonce = 0 @@ -778,7 +821,8 @@ class HeaderAndShortIDs(): return "HeaderAndShortIDs(header=%s, nonce=%d, shortids=%s, prefilledtxn=%s" % (repr(self.header), self.nonce, repr(self.shortids), repr(self.prefilled_txn)) -class BlockTransactionsRequest(): +class BlockTransactionsRequest: + __slots__ = ("blockhash", "indexes") def __init__(self, blockhash=0, indexes = None): self.blockhash = blockhash @@ -818,7 +862,8 @@ class BlockTransactionsRequest(): return "BlockTransactionsRequest(hash=%064x indexes=%s)" % (self.blockhash, repr(self.indexes)) -class BlockTransactions(): +class BlockTransactions: + __slots__ = ("blockhash", "transactions") def __init__(self, blockhash=0, transactions = None): self.blockhash = blockhash @@ -840,7 +885,10 @@ class BlockTransactions(): def __repr__(self): return "BlockTransactions(hash=%064x transactions=%s)" % (self.blockhash, repr(self.transactions)) -class CPartialMerkleTree(): + +class CPartialMerkleTree: + __slots__ = ("fBad", "nTransactions", "vBits", "vHash") + def __init__(self): self.nTransactions = 0 self.vHash = [] @@ -868,7 +916,10 @@ class CPartialMerkleTree(): def __repr__(self): return "CPartialMerkleTree(nTransactions=%d, vHash=%s, vBits=%s)" % (self.nTransactions, repr(self.vHash), repr(self.vBits)) -class CMerkleBlock(): + +class CMerkleBlock: + __slots__ = ("header", "txn") + def __init__(self): self.header = CBlockHeader() self.txn = CPartialMerkleTree() @@ -888,7 +939,9 @@ class CMerkleBlock(): # Objects that correspond to messages on the wire -class msg_version(): +class msg_version: + __slots__ = ("addrFrom", "addrTo", "nNonce", "nRelay", "nServices", + "nStartingHeight", "nTime", "nVersion", "strSubVer") command = b"version" def __init__(self): @@ -904,28 +957,17 @@ class msg_version(): def deserialize(self, f): self.nVersion = struct.unpack("<i", f.read(4))[0] - if self.nVersion == 10300: - self.nVersion = 300 self.nServices = struct.unpack("<Q", f.read(8))[0] self.nTime = struct.unpack("<q", f.read(8))[0] self.addrTo = CAddress() self.addrTo.deserialize(f, False) - if self.nVersion >= 106: - self.addrFrom = CAddress() - self.addrFrom.deserialize(f, False) - self.nNonce = struct.unpack("<Q", f.read(8))[0] - self.strSubVer = deser_string(f) - else: - self.addrFrom = None - self.nNonce = None - self.strSubVer = None - self.nStartingHeight = None + self.addrFrom = CAddress() + self.addrFrom.deserialize(f, False) + self.nNonce = struct.unpack("<Q", f.read(8))[0] + self.strSubVer = deser_string(f) - if self.nVersion >= 209: - self.nStartingHeight = struct.unpack("<i", f.read(4))[0] - else: - self.nStartingHeight = None + self.nStartingHeight = struct.unpack("<i", f.read(4))[0] if self.nVersion >= 70001: # Relay field is optional for version 70001 onwards @@ -956,7 +998,8 @@ class msg_version(): self.strSubVer, self.nStartingHeight, self.nRelay) -class msg_verack(): +class msg_verack: + __slots__ = () command = b"verack" def __init__(self): @@ -972,7 +1015,8 @@ class msg_verack(): return "msg_verack()" -class msg_addr(): +class msg_addr: + __slots__ = ("addrs",) command = b"addr" def __init__(self): @@ -988,7 +1032,8 @@ class msg_addr(): return "msg_addr(addrs=%s)" % (repr(self.addrs)) -class msg_inv(): +class msg_inv: + __slots__ = ("inv",) command = b"inv" def __init__(self, inv=None): @@ -1007,7 +1052,8 @@ class msg_inv(): return "msg_inv(inv=%s)" % (repr(self.inv)) -class msg_getdata(): +class msg_getdata: + __slots__ = ("inv",) command = b"getdata" def __init__(self, inv=None): @@ -1023,7 +1069,8 @@ class msg_getdata(): return "msg_getdata(inv=%s)" % (repr(self.inv)) -class msg_getblocks(): +class msg_getblocks: + __slots__ = ("locator", "hashstop") command = b"getblocks" def __init__(self): @@ -1046,7 +1093,8 @@ class msg_getblocks(): % (repr(self.locator), self.hashstop) -class msg_tx(): +class msg_tx: + __slots__ = ("tx",) command = b"tx" def __init__(self, tx=CTransaction()): @@ -1061,13 +1109,16 @@ class msg_tx(): def __repr__(self): return "msg_tx(tx=%s)" % (repr(self.tx)) + class msg_witness_tx(msg_tx): + __slots__ = () def serialize(self): return self.tx.serialize_with_witness() -class msg_block(): +class msg_block: + __slots__ = ("block",) command = b"block" def __init__(self, block=None): @@ -1085,9 +1136,12 @@ class msg_block(): def __repr__(self): return "msg_block(block=%s)" % (repr(self.block)) + # for cases where a user needs tighter control over what is sent over the wire # note that the user must supply the name of the command, and the data -class msg_generic(): +class msg_generic: + __slots__ = ("command", "data") + def __init__(self, command, data=None): self.command = command self.data = data @@ -1098,13 +1152,16 @@ class msg_generic(): def __repr__(self): return "msg_generic()" -class msg_witness_block(msg_block): +class msg_witness_block(msg_block): + __slots__ = () def serialize(self): r = self.block.serialize(with_witness=True) return r -class msg_getaddr(): + +class msg_getaddr: + __slots__ = () command = b"getaddr" def __init__(self): @@ -1120,7 +1177,8 @@ class msg_getaddr(): return "msg_getaddr()" -class msg_ping(): +class msg_ping: + __slots__ = ("nonce",) command = b"ping" def __init__(self, nonce=0): @@ -1138,7 +1196,8 @@ class msg_ping(): return "msg_ping(nonce=%08x)" % self.nonce -class msg_pong(): +class msg_pong: + __slots__ = ("nonce",) command = b"pong" def __init__(self, nonce=0): @@ -1156,7 +1215,8 @@ class msg_pong(): return "msg_pong(nonce=%08x)" % self.nonce -class msg_mempool(): +class msg_mempool: + __slots__ = () command = b"mempool" def __init__(self): @@ -1171,7 +1231,9 @@ class msg_mempool(): def __repr__(self): return "msg_mempool()" -class msg_sendheaders(): + +class msg_sendheaders: + __slots__ = () command = b"sendheaders" def __init__(self): @@ -1191,7 +1253,8 @@ class msg_sendheaders(): # number of entries # vector of hashes # hash_stop (hash of last desired block header, 0 to get as many as possible) -class msg_getheaders(): +class msg_getheaders: + __slots__ = ("hashstop", "locator",) command = b"getheaders" def __init__(self): @@ -1216,7 +1279,8 @@ class msg_getheaders(): # headers message has # <count> <vector of block headers> -class msg_headers(): +class msg_headers: + __slots__ = ("headers",) command = b"headers" def __init__(self, headers=None): @@ -1236,7 +1300,8 @@ class msg_headers(): return "msg_headers(headers=%s)" % repr(self.headers) -class msg_reject(): +class msg_reject: + __slots__ = ("code", "data", "message", "reason") command = b"reject" REJECT_MALFORMED = 1 @@ -1267,7 +1332,9 @@ class msg_reject(): return "msg_reject: %s %d %s [%064x]" \ % (self.message, self.code, self.reason, self.data) -class msg_feefilter(): + +class msg_feefilter: + __slots__ = ("feerate",) command = b"feefilter" def __init__(self, feerate=0): @@ -1284,7 +1351,9 @@ class msg_feefilter(): def __repr__(self): return "msg_feefilter(feerate=%08x)" % self.feerate -class msg_sendcmpct(): + +class msg_sendcmpct: + __slots__ = ("announce", "version") command = b"sendcmpct" def __init__(self): @@ -1304,7 +1373,9 @@ class msg_sendcmpct(): def __repr__(self): return "msg_sendcmpct(announce=%s, version=%lu)" % (self.announce, self.version) -class msg_cmpctblock(): + +class msg_cmpctblock: + __slots__ = ("header_and_shortids",) command = b"cmpctblock" def __init__(self, header_and_shortids = None): @@ -1322,7 +1393,9 @@ class msg_cmpctblock(): def __repr__(self): return "msg_cmpctblock(HeaderAndShortIDs=%s)" % repr(self.header_and_shortids) -class msg_getblocktxn(): + +class msg_getblocktxn: + __slots__ = ("block_txn_request",) command = b"getblocktxn" def __init__(self): @@ -1340,7 +1413,9 @@ class msg_getblocktxn(): def __repr__(self): return "msg_getblocktxn(block_txn_request=%s)" % (repr(self.block_txn_request)) -class msg_blocktxn(): + +class msg_blocktxn: + __slots__ = ("block_transactions",) command = b"blocktxn" def __init__(self): @@ -1357,7 +1432,10 @@ class msg_blocktxn(): def __repr__(self): return "msg_blocktxn(block_transactions=%s)" % (repr(self.block_transactions)) + class msg_witness_blocktxn(msg_blocktxn): + __slots__ = () + def serialize(self): r = b"" r += self.block_transactions.serialize(with_witness=True) diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py index d1ddbbe8ee..f8caa57250 100755 --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -425,8 +425,6 @@ class P2PDataStore(P2PInterface): def __init__(self): super().__init__() - self.reject_code_received = None - self.reject_reason_received = None # store of blocks. key is block hash, value is a CBlock object self.block_store = {} self.last_block_hash = '' @@ -477,12 +475,7 @@ class P2PDataStore(P2PInterface): if response is not None: self.send_message(response) - def on_reject(self, message): - """Store reject reason and code for testing.""" - self.reject_code_received = message.code - self.reject_reason_received = message.reason - - def send_blocks_and_test(self, blocks, rpc, success=True, request_block=True, reject_code=None, reject_reason=None, timeout=60): + def send_blocks_and_test(self, blocks, node, *, success=True, request_block=True, reject_reason=None, expect_disconnect=False, timeout=60): """Send blocks to test node and test whether the tip advances. - add all blocks to our block_store @@ -492,66 +485,59 @@ class P2PDataStore(P2PInterface): ensure that any getdata messages are responded to - if success is True: assert that the node's tip advances to the most recent block - if success is False: assert that the node's tip doesn't advance - - if reject_code and reject_reason are set: assert that the correct reject message is received""" + - if reject_reason is set: assert that the correct reject message is logged""" with mininode_lock: - self.reject_code_received = None - self.reject_reason_received = None - for block in blocks: self.block_store[block.sha256] = block self.last_block_hash = block.sha256 - self.send_message(msg_headers([CBlockHeader(blocks[-1])])) + reject_reason = [reject_reason] if reject_reason else [] + with node.assert_debug_log(expected_msgs=reject_reason): + self.send_message(msg_headers([CBlockHeader(blocks[-1])])) - if request_block: - wait_until(lambda: blocks[-1].sha256 in self.getdata_requests, timeout=timeout, lock=mininode_lock) + if request_block: + wait_until(lambda: blocks[-1].sha256 in self.getdata_requests, timeout=timeout, lock=mininode_lock) - if success: - wait_until(lambda: rpc.getbestblockhash() == blocks[-1].hash, timeout=timeout) - else: - assert rpc.getbestblockhash() != blocks[-1].hash + if expect_disconnect: + self.wait_for_disconnect() + else: + self.sync_with_ping() - if reject_code is not None: - wait_until(lambda: self.reject_code_received == reject_code, lock=mininode_lock) - if reject_reason is not None: - wait_until(lambda: self.reject_reason_received == reject_reason, lock=mininode_lock) + if success: + wait_until(lambda: node.getbestblockhash() == blocks[-1].hash, timeout=timeout) + else: + assert node.getbestblockhash() != blocks[-1].hash - def send_txs_and_test(self, txs, rpc, success=True, expect_disconnect=False, reject_code=None, reject_reason=None): + def send_txs_and_test(self, txs, node, *, success=True, expect_disconnect=False, reject_reason=None): """Send txs to test node and test whether they're accepted to the mempool. - add all txs to our tx_store - send tx messages for all txs - if success is True/False: assert that the txs are/are not accepted to the mempool - if expect_disconnect is True: Skip the sync with ping - - if reject_code and reject_reason are set: assert that the correct reject message is received.""" + - if reject_reason is set: assert that the correct reject message is logged.""" with mininode_lock: - self.reject_code_received = None - self.reject_reason_received = None - for tx in txs: self.tx_store[tx.sha256] = tx - for tx in txs: - self.send_message(msg_tx(tx)) - - if expect_disconnect: - self.wait_for_disconnect() - else: - self.sync_with_ping() - - raw_mempool = rpc.getrawmempool() - if success: - # Check that all txs are now in the mempool - for tx in txs: - assert tx.hash in raw_mempool, "{} not found in mempool".format(tx.hash) - else: - # Check that none of the txs are now in the mempool + reject_reason = [reject_reason] if reject_reason else [] + with node.assert_debug_log(expected_msgs=reject_reason): for tx in txs: - assert tx.hash not in raw_mempool, "{} tx found in mempool".format(tx.hash) + self.send_message(msg_tx(tx)) - if reject_code is not None: - wait_until(lambda: self.reject_code_received == reject_code, lock=mininode_lock) - if reject_reason is not None: - wait_until(lambda: self.reject_reason_received == reject_reason, lock=mininode_lock) + if expect_disconnect: + self.wait_for_disconnect() + else: + self.sync_with_ping() + + raw_mempool = node.getrawmempool() + if success: + # Check that all txs are now in the mempool + for tx in txs: + assert tx.hash in raw_mempool, "{} not found in mempool".format(tx.hash) + else: + # Check that none of the txs are now in the mempool + for tx in txs: + assert tx.hash not in raw_mempool, "{} tx found in mempool".format(tx.hash) diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 375d6334f7..2fe44010ba 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -26,7 +26,7 @@ def hash160(s): _opcode_instances = [] class CScriptOp(int): """A single script opcode""" - __slots__ = [] + __slots__ = () @staticmethod def encode_op_pushdata(d): @@ -361,8 +361,11 @@ class CScriptTruncatedPushDataError(CScriptInvalidError): self.data = data super(CScriptTruncatedPushDataError, self).__init__(msg) + # This is used, eg, for blockchain heights in coinbase scripts (bip34) -class CScriptNum(): +class CScriptNum: + __slots__ = ("value",) + def __init__(self, d=0): self.value = d @@ -393,6 +396,8 @@ class CScript(bytes): iter(script) however does iterate by opcode. """ + __slots__ = () + @classmethod def __coerce_instance(cls, other): # Coerce other into bytes diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index b876d9bd76..97710bd6cd 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -44,6 +44,13 @@ TEST_EXIT_FAILED = 1 TEST_EXIT_SKIPPED = 77 +class SkipTest(Exception): + """This exception is raised to skip a test""" + + def __init__(self, message): + self.message = message + + class BitcoinTestMetaClass(type): """Metaclass for BitcoinTestFramework. @@ -156,8 +163,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): try: if self.options.usecli and not self.supports_cli: raise SkipTest("--usecli specified but test does not support using CLI") + self.skip_test_if_missing_module() self.setup_chain() self.setup_network() + self.import_deterministic_coinbase_privkeys() self.run_test() success = TestStatus.PASSED except JSONRPCException as e: @@ -220,6 +229,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): """Override this method to add command-line options to the test""" pass + def skip_test_if_missing_module(self): + """Override this method to skip a test if a module is not compiled""" + pass + def setup_chain(self): """Override this method to customize blockchain setup""" self.log.info("Initializing test directory " + self.options.tmpdir) @@ -247,6 +260,19 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): self.add_nodes(self.num_nodes, extra_args) self.start_nodes() + def import_deterministic_coinbase_privkeys(self): + if self.setup_clean_chain: + return + + for n in self.nodes: + try: + n.getwalletinfo() + except JSONRPCException as e: + assert str(e).startswith('Method not found') + continue + + n.importprivkey(n.get_deterministic_priv_key().key) + def run_test(self): """Tests must override this method to define test logic""" raise NotImplementedError @@ -415,7 +441,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): # Create cache directories, run bitcoinds: for i in range(MAX_NODES): datadir = initialize_datadir(self.options.cachedir, i) - args = [self.options.bitcoind, "-datadir=" + datadir] + args = [self.options.bitcoind, "-datadir=" + datadir, '-disablewallet'] if i > 0: args.append("-connect=127.0.0.1:" + str(p2p_port(0))) self.nodes.append(TestNode(i, get_datadir_path(self.options.cachedir, i), extra_conf=["bind=127.0.0.1"], extra_args=[], rpchost=None, timewait=self.rpc_timewait, bitcoind=self.options.bitcoind, bitcoin_cli=self.options.bitcoincli, mocktime=self.mocktime, coverage_dir=None)) @@ -439,7 +465,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): for peer in range(4): for j in range(25): set_node_times(self.nodes, block_time) - self.nodes[peer].generate(1) + self.nodes[peer].generatetoaddress(1, self.nodes[peer].get_deterministic_priv_key().address) block_time += 10 * 60 # Must sync before next peer starts generating blocks sync_blocks(self.nodes) @@ -453,8 +479,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): return os.path.join(get_datadir_path(self.options.cachedir, n), "regtest", *paths) for i in range(MAX_NODES): + os.rmdir(cache_path(i, 'wallets')) # Remove empty wallets dir for entry in os.listdir(cache_path(i)): - if entry not in ['wallets', 'chainstate', 'blocks']: + if entry not in ['chainstate', 'blocks']: os.remove(cache_path(i, entry)) for i in range(self.num_nodes): @@ -471,25 +498,45 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): for i in range(self.num_nodes): initialize_datadir(self.options.tmpdir, i) + def skip_if_no_py3_zmq(self): + """Attempt to import the zmq package and skip the test if the import fails.""" + try: + import zmq # noqa + except ImportError: + raise SkipTest("python3-zmq module not available.") + + def skip_if_no_bitcoind_zmq(self): + """Skip the running test if bitcoind has not been compiled with zmq support.""" + if not self.is_zmq_compiled(): + raise SkipTest("bitcoind has not been built with zmq enabled.") + + def skip_if_no_wallet(self): + """Skip the running test if wallet has not been compiled.""" + if not self.is_wallet_compiled(): + raise SkipTest("wallet has not been compiled.") + + def skip_if_no_cli(self): + """Skip the running test if bitcoin-cli has not been compiled.""" + if not self.is_cli_compiled(): + raise SkipTest("bitcoin-cli has not been compiled.") + + def is_cli_compiled(self): + """Checks whether bitcoin-cli was compiled.""" + config = configparser.ConfigParser() + config.read_file(open(self.options.configfile)) -class SkipTest(Exception): - """This exception is raised to skip a test""" - def __init__(self, message): - self.message = message - + return config["components"].getboolean("ENABLE_CLI") -def skip_if_no_py3_zmq(): - """Attempt to import the zmq package and skip the test if the import fails.""" - try: - import zmq # noqa - except ImportError: - raise SkipTest("python3-zmq module not available.") + def is_wallet_compiled(self): + """Checks whether the wallet module was compiled.""" + config = configparser.ConfigParser() + config.read_file(open(self.options.configfile)) + return config["components"].getboolean("ENABLE_WALLET") -def skip_if_no_bitcoind_zmq(test_instance): - """Skip the running test if bitcoind has not been compiled with zmq support.""" - config = configparser.ConfigParser() - config.read_file(open(test_instance.options.configfile)) + def is_zmq_compiled(self): + """Checks whether the zmq module was compiled.""" + config = configparser.ConfigParser() + config.read_file(open(self.options.configfile)) - if not config["components"].getboolean("ENABLE_ZMQ"): - raise SkipTest("bitcoind has not been built with zmq enabled.") + return config["components"].getboolean("ENABLE_ZMQ") diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 0d00cc2082..7ab7fcfcb4 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -4,6 +4,7 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Class for bitcoind node under test""" +import contextlib import decimal import errno from enum import Enum @@ -16,6 +17,7 @@ import subprocess import tempfile import time import urllib.parse +import collections from .authproxy import JSONRPCException from .util import ( @@ -96,6 +98,23 @@ class TestNode(): self.p2ps = [] + def get_deterministic_priv_key(self): + """Return a deterministic priv key in base58, that only depends on the node's index""" + AddressKeyPair = collections.namedtuple('AddressKeyPair', ['address', 'key']) + PRIV_KEYS = [ + # address , privkey + AddressKeyPair('mjTkW3DjgyZck4KbiRusZsqTgaYTxdSz6z', 'cVpF924EspNh8KjYsfhgY96mmxvT6DgdWiTYMtMjuM74hJaU5psW'), + AddressKeyPair('msX6jQXvxiNhx3Q62PKeLPrhrqZQdSimTg', 'cUxsWyKyZ9MAQTaAhUQWJmBbSvHMwSmuv59KgxQV7oZQU3PXN3KE'), + AddressKeyPair('mnonCMyH9TmAsSj3M59DsbH8H63U3RKoFP', 'cTrh7dkEAeJd6b3MRX9bZK8eRmNqVCMH3LSUkE3dSFDyzjU38QxK'), + AddressKeyPair('mqJupas8Dt2uestQDvV2NH3RU8uZh2dqQR', 'cVuKKa7gbehEQvVq717hYcbE9Dqmq7KEBKqWgWrYBa2CKKrhtRim'), + AddressKeyPair('msYac7Rvd5ywm6pEmkjyxhbCDKqWsVeYws', 'cQDCBuKcjanpXDpCqacNSjYfxeQj8G6CAtH1Dsk3cXyqLNC4RPuh'), + AddressKeyPair('n2rnuUnwLgXqf9kk2kjvVm8R5BZK1yxQBi', 'cQakmfPSLSqKHyMFGwAqKHgWUiofJCagVGhiB4KCainaeCSxeyYq'), + AddressKeyPair('myzuPxRwsf3vvGzEuzPfK9Nf2RfwauwYe6', 'cQMpDLJwA8DBe9NcQbdoSb1BhmFxVjWD5gRyrLZCtpuF9Zi3a9RK'), + AddressKeyPair('mumwTaMtbxEPUswmLBBN3vM9oGRtGBrys8', 'cSXmRKXVcoouhNNVpcNKFfxsTsToY5pvB9DVsFksF1ENunTzRKsy'), + AddressKeyPair('mpV7aGShMkJCZgbW7F6iZgrvuPHjZjH9qg', 'cSoXt6tm3pqy43UMabY6eUTmR3eSUYFtB2iNQDGgb3VUnRsQys2k'), + ] + return PRIV_KEYS[self.index] + def _node_msg(self, msg: str) -> str: """Return a modified msg that identifies this node by its index as a debugging aid.""" return "[node %d] %s" % (self.index, msg) @@ -229,6 +248,23 @@ class TestNode(): def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT): wait_until(self.is_node_stopped, timeout=timeout) + @contextlib.contextmanager + def assert_debug_log(self, expected_msgs): + debug_log = os.path.join(self.datadir, 'regtest', 'debug.log') + with open(debug_log, encoding='utf-8') as dl: + dl.seek(0, 2) + prev_size = dl.tell() + try: + yield + finally: + with open(debug_log, encoding='utf-8') as dl: + dl.seek(prev_size) + log = dl.read() + print_log = " - " + "\n - ".join(log.splitlines()) + for expected_msg in expected_msgs: + if re.search(re.escape(expected_msg), log, flags=re.MULTILINE) is None: + self._raise_assertion_error('Expected message "{}" does not partially match log:\n\n{}\n\n'.format(expected_msg, print_log)) + def assert_start_raises_init_error(self, extra_args=None, expected_msg=None, match=ErrorMatch.FULL_TEXT, *args, **kwargs): """Attempt to start the node and expect it to raise an error. @@ -271,14 +307,6 @@ class TestNode(): assert_msg = "bitcoind should have exited with expected error " + expected_msg self._raise_assertion_error(assert_msg) - def node_encrypt_wallet(self, passphrase): - """"Encrypts the wallet. - - This causes bitcoind to shutdown, so this method takes - care of cleaning up resources.""" - self.encryptwallet(passphrase) - self.wait_until_stopped() - def add_p2p_connection(self, p2p_conn, *, wait_for_verack=True, **kwargs): """Add a p2p connection to the node. diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index feea2a327a..28437f8925 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -29,7 +29,7 @@ import re import logging # Formatting. Default colors to empty strings. -BOLD, BLUE, RED, GREY = ("", ""), ("", ""), ("", ""), ("", "") +BOLD, GREEN, RED, GREY = ("", ""), ("", ""), ("", ""), ("", "") try: # Make sure python thinks it can write unicode to its stdout "\u2713".encode("utf_8").decode(sys.stdout.encoding) @@ -41,11 +41,27 @@ except UnicodeDecodeError: CROSS = "x " CIRCLE = "o " -if os.name == 'posix': +if os.name != 'nt' or sys.getwindowsversion() >= (10, 0, 14393): + if os.name == 'nt': + import ctypes + kernel32 = ctypes.windll.kernel32 + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4 + STD_OUTPUT_HANDLE = -11 + STD_ERROR_HANDLE = -12 + # Enable ascii color control to stdout + stdout = kernel32.GetStdHandle(STD_OUTPUT_HANDLE) + stdout_mode = ctypes.c_int32() + kernel32.GetConsoleMode(stdout, ctypes.byref(stdout_mode)) + kernel32.SetConsoleMode(stdout, stdout_mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING) + # Enable ascii color control to stderr + stderr = kernel32.GetStdHandle(STD_ERROR_HANDLE) + stderr_mode = ctypes.c_int32() + kernel32.GetConsoleMode(stderr, ctypes.byref(stderr_mode)) + kernel32.SetConsoleMode(stderr, stderr_mode.value | ENABLE_VIRTUAL_TERMINAL_PROCESSING) # primitive formatting on supported # terminal via ANSI escape sequences: BOLD = ('\033[0m', '\033[1m') - BLUE = ('\033[0m', '\033[0;34m') + GREEN = ('\033[0m', '\033[0;32m') RED = ('\033[0m', '\033[0;31m') GREY = ('\033[0m', '\033[1;30m') @@ -58,9 +74,12 @@ TRAVIS_TIMEOUT_DURATION = 20 * 60 BASE_SCRIPTS = [ # Scripts that are run by the travis build process. # Longest test should go first, to favor running tests in parallel + 'feature_fee_estimation.py', 'wallet_hd.py', 'wallet_backup.py', # vv Tests less than 5m vv + 'mining_getblocktemplate_longpoll.py', + 'feature_maxuploadtarget.py', 'feature_block.py', 'rpc_fundrawtransaction.py', 'p2p_compactblocks.py', @@ -69,6 +88,7 @@ BASE_SCRIPTS = [ 'wallet_basic.py', 'wallet_labels.py', 'p2p_segwit.py', + 'p2p_timeouts.py', 'wallet_dump.py', 'wallet_listtransactions.py', # vv Tests less than 60s vv @@ -82,6 +102,8 @@ BASE_SCRIPTS = [ 'feature_csv_activation.py', 'rpc_rawtransaction.py', 'wallet_address_types.py', + 'feature_bip68_sequence.py', + 'p2p_feefilter.py', 'feature_reindex.py', # vv Tests less than 30s vv 'wallet_keypool_topup.py', @@ -118,6 +140,14 @@ BASE_SCRIPTS = [ 'p2p_invalid_locator.py', 'p2p_invalid_block.py', 'p2p_invalid_tx.py', + 'feature_assumevalid.py', + 'example_test.py', + 'wallet_txn_doublespend.py', + 'wallet_txn_clone.py --mineblock', + 'feature_notifications.py', + 'rpc_invalidateblock.py', + 'feature_rbf.py', + 'mempool_packages.py', 'rpc_createmultisig.py', 'feature_versionbits_warning.py', 'rpc_preciousblock.py', @@ -152,6 +182,7 @@ BASE_SCRIPTS = [ 'p2p_node_network_limited.py', 'feature_blocksdir.py', 'feature_config_args.py', + 'rpc_help.py', 'feature_help.py', # Don't append tests at the end to avoid merge conflicts # Put them in a random line within the section that fits their approximate run-time @@ -161,26 +192,7 @@ EXTENDED_SCRIPTS = [ # These tests are not run by the travis build process. # Longest test should go first, to favor running tests in parallel 'feature_pruning.py', - # vv Tests less than 20m vv - 'feature_fee_estimation.py', - # vv Tests less than 5m vv - 'feature_maxuploadtarget.py', - 'mempool_packages.py', 'feature_dbcrash.py', - # vv Tests less than 2m vv - 'feature_bip68_sequence.py', - 'mining_getblocktemplate_longpoll.py', - 'p2p_timeouts.py', - # vv Tests less than 60s vv - 'p2p_feefilter.py', - # vv Tests less than 30s vv - 'feature_assumevalid.py', - 'example_test.py', - 'wallet_txn_doublespend.py', - 'wallet_txn_clone.py --mineblock', - 'feature_notifications.py', - 'rpc_invalidateblock.py', - 'feature_rbf.py', ] # Place EXTENDED_SCRIPTS first since it has the 3 longest running tests @@ -231,12 +243,15 @@ def main(): # Create base test directory tmpdir = "%s/test_runner_₿_🏃_%s" % (args.tmpdirprefix, datetime.datetime.now().strftime("%Y%m%d_%H%M%S")) + + # If we fixed the command-line and filename encoding issue on Windows, these two lines could be removed + if config["environment"]["EXEEXT"] == ".exe": + tmpdir = "%s/test_runner_%s" % (args.tmpdirprefix, datetime.datetime.now().strftime("%Y%m%d_%H%M%S")) + os.makedirs(tmpdir) logging.debug("Temporary test directory at %s" % tmpdir) - enable_wallet = config["components"].getboolean("ENABLE_WALLET") - enable_utils = config["components"].getboolean("ENABLE_UTILS") enable_bitcoind = config["components"].getboolean("ENABLE_BITCOIND") if config["environment"]["EXEEXT"] == ".exe" and not args.force: @@ -245,9 +260,9 @@ def main(): print("Tests currently disabled on Windows by default. Use --force option to enable") sys.exit(0) - if not (enable_wallet and enable_utils and enable_bitcoind): - print("No functional tests to run. Wallet, utils, and bitcoind must all be enabled") - print("Rerun `configure` with -enable-wallet, -with-utils and -with-daemon and rerun make") + if not enable_bitcoind: + print("No functional tests to run.") + print("Rerun ./configure with --with-daemon and then make") sys.exit(0) # Build list of tests @@ -270,7 +285,7 @@ def main(): # Remove the test cases that the user has explicitly asked to exclude. if args.exclude: - exclude_tests = [re.sub("\.py$", "", test) + ".py" for test in args.exclude.split(',')] + exclude_tests = [re.sub("\.py$", "", test) + (".py" if ".py" not in test else "") for test in args.exclude.split(',')] for exclude_test in exclude_tests: if exclude_test in test_list: test_list.remove(exclude_test) @@ -365,7 +380,10 @@ def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=Fal print('\n============') print('{}Combined log for {}:{}'.format(BOLD[1], testdir, BOLD[0])) print('============\n') - combined_logs, _ = subprocess.Popen([sys.executable, os.path.join(tests_dir, 'combine_logs.py'), '-c', testdir], universal_newlines=True, stdout=subprocess.PIPE).communicate() + combined_logs_args = [sys.executable, os.path.join(tests_dir, 'combine_logs.py'), testdir] + if BOLD[0]: + combined_logs_args += ['--color'] + combined_logs, _ = subprocess.Popen(combined_logs_args, universal_newlines=True, stdout=subprocess.PIPE).communicate() print("\n".join(deque(combined_logs.splitlines(), combined_logs_len))) if failfast: @@ -504,7 +522,7 @@ class TestResult(): def __repr__(self): if self.status == "Passed": - color = BLUE + color = GREEN glyph = TICK elif self.status == "Failed": color = RED diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py index 8a57b55ca9..e5ac2c8bd4 100755 --- a/test/functional/wallet_abandonconflict.py +++ b/test/functional/wallet_abandonconflict.py @@ -20,6 +20,9 @@ class AbandonConflictTest(BitcoinTestFramework): self.num_nodes = 2 self.extra_args = [["-minrelaytxfee=0.00001"], []] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): self.nodes[1].generate(100) sync_blocks(self.nodes) diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py index 01c7b6366a..0f75045c9d 100755 --- a/test/functional/wallet_address_types.py +++ b/test/functional/wallet_address_types.py @@ -63,6 +63,7 @@ from test_framework.util import ( sync_mempools, ) + class AddressTypeTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 6 @@ -72,9 +73,12 @@ class AddressTypeTest(BitcoinTestFramework): ["-addresstype=p2sh-segwit", "-changetype=bech32"], ["-addresstype=bech32"], ["-changetype=p2sh-segwit"], - [] + [], ] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def setup_network(self): self.setup_nodes() @@ -143,7 +147,7 @@ class AddressTypeTest(BitcoinTestFramework): assert(False) def test_change_output_type(self, node_sender, destinations, expected_type): - txid = self.nodes[node_sender].sendmany(fromaccount="", amounts=dict.fromkeys(destinations, 0.001)) + txid = self.nodes[node_sender].sendmany(dummy="", amounts=dict.fromkeys(destinations, 0.001)) raw_tx = self.nodes[node_sender].getrawtransaction(txid) tx = self.nodes[node_sender].decoderawtransaction(raw_tx) diff --git a/test/functional/wallet_backup.py b/test/functional/wallet_backup.py index 4ef8f4776b..32ec385fa1 100755 --- a/test/functional/wallet_backup.py +++ b/test/functional/wallet_backup.py @@ -45,6 +45,9 @@ class WalletBackupTest(BitcoinTestFramework): # nodes 1, 2,3 are spenders, let's give them a keypool=100 self.extra_args = [["-keypool=100"], ["-keypool=100"], ["-keypool=100"], []] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def setup_network(self, split=False): self.setup_nodes() connect_nodes(self.nodes[0], 3) diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py index ea0346543a..8bbff7f7ef 100755 --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -23,6 +23,9 @@ class WalletTest(BitcoinTestFramework): self.num_nodes = 4 self.setup_clean_chain = True + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def setup_network(self): self.add_nodes(4) self.start_node(0) @@ -131,9 +134,15 @@ class WalletTest(BitcoinTestFramework): assert_equal([unspent_0], self.nodes[2].listlockunspent()) self.nodes[2].lockunspent(True, [unspent_0]) assert_equal(len(self.nodes[2].listlockunspent()), 0) - assert_raises_rpc_error(-8, "Invalid parameter, unknown transaction", + assert_raises_rpc_error(-8, "txid must be of length 64 (not 34, for '0000000000000000000000000000000000')", self.nodes[2].lockunspent, False, [{"txid": "0000000000000000000000000000000000", "vout": 0}]) + assert_raises_rpc_error(-8, "txid must be hexadecimal string (not 'ZZZ0000000000000000000000000000000000000000000000000000000000000')", + self.nodes[2].lockunspent, False, + [{"txid": "ZZZ0000000000000000000000000000000000000000000000000000000000000", "vout": 0}]) + assert_raises_rpc_error(-8, "Invalid parameter, unknown transaction", + self.nodes[2].lockunspent, False, + [{"txid": "0000000000000000000000000000000000000000000000000000000000000000", "vout": 0}]) assert_raises_rpc_error(-8, "Invalid parameter, vout index out of bounds", self.nodes[2].lockunspent, False, [{"txid": unspent_0["txid"], "vout": 999}]) diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py index a49590df19..67ee00871d 100755 --- a/test/functional/wallet_bumpfee.py +++ b/test/functional/wallet_bumpfee.py @@ -31,13 +31,18 @@ class BumpFeeTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True - self.extra_args = [["-deprecatedrpc=addwitnessaddress", "-walletrbf={}".format(i), "-mintxfee=0.00002"] - for i in range(self.num_nodes)] + self.extra_args = [[ + "-deprecatedrpc=addwitnessaddress", + "-walletrbf={}".format(i), + "-mintxfee=0.00002", + ] for i in range(self.num_nodes)] + + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() def run_test(self): # Encrypt wallet for test_locked_wallet_fails test - self.nodes[1].node_encrypt_wallet(WALLET_PASSPHRASE) - self.start_node(1) + self.nodes[1].encryptwallet(WALLET_PASSPHRASE) self.nodes[1].walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) connect_nodes_bi(self.nodes, 0, 1) diff --git a/test/functional/wallet_disableprivatekeys.py b/test/functional/wallet_disableprivatekeys.py index 0ba2cfe9be..34ff525255 100755 --- a/test/functional/wallet_disableprivatekeys.py +++ b/test/functional/wallet_disableprivatekeys.py @@ -17,6 +17,9 @@ class DisablePrivateKeysTest(BitcoinTestFramework): self.num_nodes = 1 self.supports_cli = True + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): node = self.nodes[0] self.log.info("Test disableprivatekeys creation.") diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py index 63316d9644..b1db1e4ab9 100755 --- a/test/functional/wallet_dump.py +++ b/test/functional/wallet_dump.py @@ -29,50 +29,58 @@ def read_dump(file_name, addrs, script_addrs, hd_master_addr_old): # only read non comment lines if line[0] != "#" and len(line) > 10: # split out some data - key_label, comment = line.split("#") - # key = key_label.split(" ")[0] - keytype = key_label.split(" ")[2] - if len(comment) > 1: - addr_keypath = comment.split(" addr=")[1] - addr = addr_keypath.split(" ")[0] + key_date_label, comment = line.split("#") + key_date_label = key_date_label.split(" ") + # key = key_date_label[0] + date = key_date_label[1] + keytype = key_date_label[2] + + imported_key = date == '1970-01-01T00:00:01Z' + if imported_key: + # Imported keys have multiple addresses, no label (keypath) and timestamp + # Skip them + continue + + addr_keypath = comment.split(" addr=")[1] + addr = addr_keypath.split(" ")[0] + keypath = None + if keytype == "inactivehdseed=1": + # ensure the old master is still available + assert (hd_master_addr_old == addr) + elif keytype == "hdseed=1": + # ensure we have generated a new hd master key + assert (hd_master_addr_old != addr) + hd_master_addr_ret = addr + elif keytype == "script=1": + # scripts don't have keypaths keypath = None - if keytype == "inactivehdseed=1": - # ensure the old master is still available - assert(hd_master_addr_old == addr) - elif keytype == "hdseed=1": - # ensure we have generated a new hd master key - assert(hd_master_addr_old != addr) - hd_master_addr_ret = addr - elif keytype == "script=1": - # scripts don't have keypaths - keypath = None - else: - keypath = addr_keypath.rstrip().split("hdkeypath=")[1] - - # count key types - for addrObj in addrs: - if addrObj['address'] == addr.split(",")[0] and addrObj['hdkeypath'] == keypath and keytype == "label=": - # a labeled entry in the wallet should contain both a native address - # and the p2sh-p2wpkh address that was added at wallet setup - if len(addr.split(",")) == 2: - addr_list = addr.split(",") - # the entry should be of the first key in the wallet - assert_equal(addrs[0]['address'], addr_list[0]) - witness_addr_ret = addr_list[1] - found_addr += 1 - break - elif keytype == "change=1": - found_addr_chg += 1 - break - elif keytype == "reserve=1": - found_addr_rsv += 1 - break - - # count scripts - for script_addr in script_addrs: - if script_addr == addr.rstrip() and keytype == "script=1": - found_script_addr += 1 - break + else: + keypath = addr_keypath.rstrip().split("hdkeypath=")[1] + + # count key types + for addrObj in addrs: + if addrObj['address'] == addr.split(",")[0] and addrObj['hdkeypath'] == keypath and keytype == "label=": + # a labeled entry in the wallet should contain both a native address + # and the p2sh-p2wpkh address that was added at wallet setup + if len(addr.split(",")) == 2: + addr_list = addr.split(",") + # the entry should be of the first key in the wallet + assert_equal(addrs[0]['address'], addr_list[0]) + witness_addr_ret = addr_list[1] + found_addr += 1 + break + elif keytype == "change=1": + found_addr_chg += 1 + break + elif keytype == "reserve=1": + found_addr_rsv += 1 + break + + # count scripts + for script_addr in script_addrs: + if script_addr == addr.rstrip() and keytype == "script=1": + found_script_addr += 1 + break return found_addr, found_script_addr, found_addr_chg, found_addr_rsv, hd_master_addr_ret, witness_addr_ret @@ -83,6 +91,9 @@ class WalletDumpTest(BitcoinTestFramework): self.extra_args = [["-keypool=90", "-addresstype=legacy", "-deprecatedrpc=addwitnessaddress"]] self.rpc_timeout = 120 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def setup_network(self, split=False): self.add_nodes(self.num_nodes, extra_args=self.extra_args) self.start_nodes() @@ -116,13 +127,12 @@ class WalletDumpTest(BitcoinTestFramework): read_dump(wallet_unenc_dump, addrs, script_addrs, None) assert_equal(found_addr, test_addr_count) # all keys must be in the dump assert_equal(found_script_addr, 2) # all scripts must be in the dump - assert_equal(found_addr_chg, 50) # 50 blocks where mined - assert_equal(found_addr_rsv, 90*2) # 90 keys plus 100% internal keys - assert_equal(witness_addr_ret, witness_addr) # p2sh-p2wsh address added to the first key + assert_equal(found_addr_chg, 0) # 0 blocks where mined + assert_equal(found_addr_rsv, 90 * 2) # 90 keys plus 100% internal keys + assert_equal(witness_addr_ret, witness_addr) # p2sh-p2wsh address added to the first key #encrypt wallet, restart, unlock and dump - self.nodes[0].node_encrypt_wallet('test') - self.start_node(0) + self.nodes[0].encryptwallet('test') self.nodes[0].walletpassphrase('test', 10) # Should be a no-op: self.nodes[0].keypoolrefill() @@ -132,8 +142,8 @@ class WalletDumpTest(BitcoinTestFramework): read_dump(wallet_enc_dump, addrs, script_addrs, hd_master_addr_unenc) assert_equal(found_addr, test_addr_count) assert_equal(found_script_addr, 2) - assert_equal(found_addr_chg, 90*2 + 50) # old reserve keys are marked as change now - assert_equal(found_addr_rsv, 90*2) + assert_equal(found_addr_chg, 90 * 2) # old reserve keys are marked as change now + assert_equal(found_addr_rsv, 90 * 2) assert_equal(witness_addr_ret, witness_addr) # Overwriting should fail diff --git a/test/functional/wallet_encryption.py b/test/functional/wallet_encryption.py index cec9660259..ab9ebed8d4 100755 --- a/test/functional/wallet_encryption.py +++ b/test/functional/wallet_encryption.py @@ -19,6 +19,9 @@ class WalletEncryptionTest(BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 1 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): passphrase = "WalletPassphrase" passphrase2 = "SecondWalletPassphrase" @@ -30,8 +33,7 @@ class WalletEncryptionTest(BitcoinTestFramework): assert_equal(len(privkey), 52) # Encrypt the wallet - self.nodes[0].node_encrypt_wallet(passphrase) - self.start_node(0) + self.nodes[0].encryptwallet(passphrase) # Test that the wallet is encrypted assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address) diff --git a/test/functional/wallet_fallbackfee.py b/test/functional/wallet_fallbackfee.py index 91dbae8939..0c67982bbe 100755 --- a/test/functional/wallet_fallbackfee.py +++ b/test/functional/wallet_fallbackfee.py @@ -11,6 +11,9 @@ class WalletRBFTest(BitcoinTestFramework): self.num_nodes = 1 self.setup_clean_chain = True + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): self.nodes[0].generate(101) diff --git a/test/functional/wallet_groups.py b/test/functional/wallet_groups.py index 12dac145bd..9d61483868 100755 --- a/test/functional/wallet_groups.py +++ b/test/functional/wallet_groups.py @@ -23,7 +23,10 @@ class WalletGroupTest(BitcoinTestFramework): self.extra_args = [[], [], ['-avoidpartialspends']] self.rpc_timewait = 120 - def run_test (self): + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + + def run_test(self): # Mine some coins self.nodes[0].generate(110) diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py index 2a5f1934b7..eb42531693 100755 --- a/test/functional/wallet_hd.py +++ b/test/functional/wallet_hd.py @@ -21,13 +21,10 @@ class WalletHDTest(BitcoinTestFramework): self.num_nodes = 2 self.extra_args = [[], ['-keypool=0']] - def run_test(self): - # Make sure can't switch off usehd after wallet creation - self.stop_node(1) - self.nodes[1].assert_start_raises_init_error(['-usehd=0'], "Error: Error loading : You can't disable HD on an already existing HD wallet") - self.start_node(1) - connect_nodes_bi(self.nodes, 0, 1) + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): # Make sure we use hd, keep masterkeyid masterkeyid = self.nodes[1].getwalletinfo()['hdseedid'] assert_equal(masterkeyid, self.nodes[1].getwalletinfo()['hdmasterkeyid']) diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py index 0c6359976a..f043a544fc 100755 --- a/test/functional/wallet_import_rescan.py +++ b/test/functional/wallet_import_rescan.py @@ -26,7 +26,7 @@ import collections import enum import itertools -Call = enum.Enum("Call", "single multi") +Call = enum.Enum("Call", "single multiaddress multiscript") Data = enum.Enum("Data", "address pub priv") Rescan = enum.Enum("Rescan", "no yes late_timestamp") @@ -53,11 +53,11 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")): response = self.try_rpc(self.node.importprivkey, privkey=self.key, rescan=rescan) assert_equal(response, None) - elif self.call == Call.multi: + elif self.call in (Call.multiaddress, Call.multiscript): response = self.node.importmulti([{ "scriptPubKey": { "address": self.address["address"] - }, + } if self.call == Call.multiaddress else self.address["scriptPubKey"], "timestamp": timestamp + TIMESTAMP_WINDOW + (1 if self.rescan == Rescan.late_timestamp else 0), "pubkeys": [self.address["pubkey"]] if self.data == Data.pub else [], "keys": [self.key] if self.data == Data.priv else [], @@ -109,6 +109,9 @@ class ImportRescanTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 + len(IMPORT_NODES) + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def setup_network(self): extra_args = [["-addresstype=legacy"] for _ in range(self.num_nodes)] for i, import_node in enumerate(IMPORT_NODES, 2): @@ -116,17 +119,26 @@ class ImportRescanTest(BitcoinTestFramework): extra_args[i] += ["-prune=1"] self.add_nodes(self.num_nodes, extra_args=extra_args) + + # Import keys with pruning disabled + self.start_nodes(extra_args=[[]] * self.num_nodes) + super().import_deterministic_coinbase_privkeys() + self.stop_nodes() + self.start_nodes() for i in range(1, self.num_nodes): connect_nodes(self.nodes[i], 0) + def import_deterministic_coinbase_privkeys(self): + pass + def run_test(self): # Create one transaction on node 0 with a unique amount for # each possible type of wallet import RPC. for i, variant in enumerate(IMPORT_VARIANTS): variant.address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress()) variant.key = self.nodes[1].dumpprivkey(variant.address["address"]) - variant.initial_amount = 10 - (i + 1) / 4.0 + variant.initial_amount = 1 - (i + 1) / 64 variant.initial_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.initial_amount) # Generate a block containing the initial transactions, then another @@ -156,7 +168,7 @@ class ImportRescanTest(BitcoinTestFramework): # Create new transactions sending to each address. for i, variant in enumerate(IMPORT_VARIANTS): - variant.sent_amount = 10 - (2 * i + 1) / 8.0 + variant.sent_amount = 1 - (2 * i + 1) / 128 variant.sent_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.sent_amount) # Generate a block containing the new transactions. diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py index 5f19e1e2c6..f78ff19791 100755 --- a/test/functional/wallet_importmulti.py +++ b/test/functional/wallet_importmulti.py @@ -3,15 +3,26 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the importmulti RPC.""" + +from test_framework import script from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, assert_greater_than, assert_raises_rpc_error +from test_framework.util import ( + assert_equal, + assert_greater_than, + assert_raises_rpc_error, + bytes_to_hex_str, +) + -class ImportMultiTest (BitcoinTestFramework): +class ImportMultiTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 self.extra_args = [["-addresstype=legacy"], ["-addresstype=legacy"]] self.setup_clean_chain = True + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def setup_network(self): self.setup_nodes() @@ -79,16 +90,17 @@ class ImportMultiTest (BitcoinTestFramework): assert_equal(address_assert['ismine'], False) assert_equal(address_assert['timestamp'], timestamp) - # ScriptPubKey + !internal - self.log.info("Should not import a scriptPubKey without internal flag") + # Nonstandard scriptPubKey + !internal + self.log.info("Should not import a nonstandard scriptPubKey without internal flag") + nonstandardScriptPubKey = address['scriptPubKey'] + bytes_to_hex_str(script.CScript([script.OP_NOP])) address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ - "scriptPubKey": address['scriptPubKey'], + "scriptPubKey": nonstandardScriptPubKey, "timestamp": "now", }]) assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -8) - assert_equal(result[0]['error']['message'], 'Internal must be set for hex scriptPubKey') + assert_equal(result[0]['error']['message'], 'Internal must be set to true for nonstandard scriptPubKey imports.') address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) @@ -128,18 +140,18 @@ class ImportMultiTest (BitcoinTestFramework): assert_equal(address_assert['ismine'], False) assert_equal(address_assert['timestamp'], timestamp) - # ScriptPubKey + Public key + !internal - self.log.info("Should not import a scriptPubKey without internal and with public key") + # Nonstandard scriptPubKey + Public key + !internal + self.log.info("Should not import a nonstandard scriptPubKey without internal and with public key") address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) request = [{ - "scriptPubKey": address['scriptPubKey'], + "scriptPubKey": nonstandardScriptPubKey, "timestamp": "now", "pubkeys": [ address['pubkey'] ] }] result = self.nodes[1].importmulti(request) assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -8) - assert_equal(result[0]['error']['message'], 'Internal must be set for hex scriptPubKey') + assert_equal(result[0]['error']['message'], 'Internal must be set to true for nonstandard scriptPubKey imports.') address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) @@ -207,17 +219,17 @@ class ImportMultiTest (BitcoinTestFramework): assert_equal(address_assert['ismine'], True) assert_equal(address_assert['timestamp'], timestamp) - # ScriptPubKey + Private key + !internal - self.log.info("Should not import a scriptPubKey without internal and with private key") + # Nonstandard scriptPubKey + Private key + !internal + self.log.info("Should not import a nonstandard scriptPubKey without internal and with private key") address = self.nodes[0].getaddressinfo(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ - "scriptPubKey": address['scriptPubKey'], + "scriptPubKey": nonstandardScriptPubKey, "timestamp": "now", "keys": [ self.nodes[0].dumpprivkey(address['address']) ] }]) assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -8) - assert_equal(result[0]['error']['message'], 'Internal must be set for hex scriptPubKey') + assert_equal(result[0]['error']['message'], 'Internal must be set to true for nonstandard scriptPubKey imports.') address_assert = self.nodes[1].getaddressinfo(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py index 57fc754d35..26b181db33 100755 --- a/test/functional/wallet_importprunedfunds.py +++ b/test/functional/wallet_importprunedfunds.py @@ -16,6 +16,9 @@ class ImportPrunedFundsTest(BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 2 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): self.log.info("Mining blocks...") self.nodes[0].generate(101) diff --git a/test/functional/wallet_keypool.py b/test/functional/wallet_keypool.py index f52dce04dd..51afa0cb1a 100755 --- a/test/functional/wallet_keypool.py +++ b/test/functional/wallet_keypool.py @@ -13,6 +13,9 @@ class KeyPoolTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): nodes = self.nodes addr_before_encrypting = nodes[0].getnewaddress() @@ -22,9 +25,7 @@ class KeyPoolTest(BitcoinTestFramework): assert(addr_before_encrypting_data['hdseedid'] == wallet_info_old['hdseedid']) # Encrypt wallet and wait to terminate - nodes[0].node_encrypt_wallet('test') - # Restart node 0 - self.start_node(0) + nodes[0].encryptwallet('test') # Keep creating keys addr = nodes[0].getnewaddress() addr_data = nodes[0].getaddressinfo(addr) diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py index f6b88e6225..f1a441c399 100755 --- a/test/functional/wallet_keypool_topup.py +++ b/test/functional/wallet_keypool_topup.py @@ -27,6 +27,9 @@ class KeypoolRestoreTest(BitcoinTestFramework): self.num_nodes = 2 self.extra_args = [[], ['-keypool=100']] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): wallet_path = os.path.join(self.nodes[1].datadir, "regtest", "wallets", "wallet.dat") wallet_backup_path = os.path.join(self.nodes[1].datadir, "wallet.bak") diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py index 0817054149..8d7c77bb96 100755 --- a/test/functional/wallet_labels.py +++ b/test/functional/wallet_labels.py @@ -5,15 +5,9 @@ """Test label RPCs. RPCs tested are: - - getaccountaddress - - getaddressesbyaccount/getaddressesbylabel + - getaddressesbylabel - listaddressgroupings - setlabel - - sendfrom (with account arguments) - - move (with account arguments) - -Run the test twice - once using the accounts API and once using the labels API. -The accounts API test can be removed in V0.18. """ from collections import defaultdict @@ -23,22 +17,14 @@ from test_framework.util import assert_equal, assert_raises_rpc_error class WalletLabelsTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True - self.num_nodes = 2 - self.extra_args = [['-deprecatedrpc=accounts'], []] + self.num_nodes = 1 - def setup_network(self): - """Don't connect nodes.""" - self.setup_nodes() + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() def run_test(self): - """Run the test twice - once using the accounts API and once using the labels API.""" - self.log.info("Test accounts API") - self._run_subtest(True, self.nodes[0]) - self.log.info("Test labels API") - self._run_subtest(False, self.nodes[1]) - - def _run_subtest(self, accounts_api, node): - # Check that there's no UTXO on any of the nodes + # Check that there's no UTXO on the node + node = self.nodes[0] assert_equal(len(node.listunspent()), 0) # Note each time we call generate, all generated coins go into @@ -61,17 +47,12 @@ class WalletLabelsTest(BitcoinTestFramework): linked_addresses.add(address_group[0][0]) # send 50 from each address to a third address not in this wallet - # There's some fee that will come back to us when the miner reward - # matures. common_address = "msf4WtN1YQKXvNtvdFYt9JBnUD2FB41kjr" - txid = node.sendmany( - fromaccount="", + node.sendmany( amounts={common_address: 100}, subtractfeefrom=[common_address], minconf=1, ) - tx_details = node.gettransaction(txid) - fee = -tx_details['details'][0]['fee'] # there should be 1 address group, with the previously # unlinked addresses now linked (they both have 0 balance) address_groups = node.listaddressgroupings() @@ -85,32 +66,22 @@ class WalletLabelsTest(BitcoinTestFramework): # we want to reset so that the "" label has what's expected. # otherwise we're off by exactly the fee amount as that's mined # and matures in the next 100 blocks - if accounts_api: - node.sendfrom("", common_address, fee) amount_to_send = 1.0 # Create labels and make sure subsequent label API calls # recognize the label/address associations. - labels = [Label(name, accounts_api) for name in ("a", "b", "c", "d", "e")] + labels = [Label(name) for name in ("a", "b", "c", "d", "e")] for label in labels: - if accounts_api: - address = node.getaccountaddress(label.name) - else: - address = node.getnewaddress(label.name) + address = node.getnewaddress(label.name) label.add_receive_address(address) label.verify(node) # Check all labels are returned by listlabels. assert_equal(node.listlabels(), [label.name for label in labels]) - # Send a transaction to each label, and make sure this forces - # getaccountaddress to generate a new receiving address. + # Send a transaction to each label. for label in labels: - if accounts_api: - node.sendtoaddress(label.receive_address, amount_to_send) - label.add_receive_address(node.getaccountaddress(label.name)) - else: - node.sendtoaddress(label.addresses[0], amount_to_send) + node.sendtoaddress(label.addresses[0], amount_to_send) label.verify(node) # Check the amounts received. @@ -120,32 +91,17 @@ class WalletLabelsTest(BitcoinTestFramework): node.getreceivedbyaddress(label.addresses[0]), amount_to_send) assert_equal(node.getreceivedbylabel(label.name), amount_to_send) - # Check that sendfrom label reduces listaccounts balances. for i, label in enumerate(labels): to_label = labels[(i + 1) % len(labels)] - if accounts_api: - node.sendfrom(label.name, to_label.receive_address, amount_to_send) - else: - node.sendtoaddress(to_label.addresses[0], amount_to_send) + node.sendtoaddress(to_label.addresses[0], amount_to_send) node.generate(1) for label in labels: - if accounts_api: - address = node.getaccountaddress(label.name) - else: - address = node.getnewaddress(label.name) + address = node.getnewaddress(label.name) label.add_receive_address(address) label.verify(node) assert_equal(node.getreceivedbylabel(label.name), 2) - if accounts_api: - node.move(label.name, "", node.getbalance(label.name)) label.verify(node) node.generate(101) - expected_account_balances = {"": 5200} - for label in labels: - expected_account_balances[label.name] = 0 - if accounts_api: - assert_equal(node.listaccounts(), expected_account_balances) - assert_equal(node.getbalance(""), 5200) # Check that setlabel can assign a label to a new unused address. for label in labels: @@ -153,10 +109,7 @@ class WalletLabelsTest(BitcoinTestFramework): node.setlabel(address, label.name) label.add_address(address) label.verify(node) - if accounts_api: - assert address not in node.getaddressesbyaccount("") - else: - assert_raises_rpc_error(-11, "No addresses with label", node.getaddressesbylabel, "") + assert_raises_rpc_error(-11, "No addresses with label", node.getaddressesbylabel, "") # Check that addmultisigaddress can assign labels. for label in labels: @@ -167,35 +120,20 @@ class WalletLabelsTest(BitcoinTestFramework): label.add_address(multisig_address) label.purpose[multisig_address] = "send" label.verify(node) - if accounts_api: - node.sendfrom("", multisig_address, 50) node.generate(101) - if accounts_api: - for label in labels: - assert_equal(node.getbalance(label.name), 50) # Check that setlabel can change the label of an address from a # different label. - change_label(node, labels[0].addresses[0], labels[0], labels[1], accounts_api) + change_label(node, labels[0].addresses[0], labels[0], labels[1]) # Check that setlabel can set the label of an address already # in the label. This is a no-op. - change_label(node, labels[2].addresses[0], labels[2], labels[2], accounts_api) - - if accounts_api: - # Check that setaccount can change the label of an address which - # is the receiving address of a different label. - change_label(node, labels[0].receive_address, labels[0], labels[1], accounts_api) - - # Check that setaccount can set the label of an address which is - # already the receiving address of the label. This is a no-op. - change_label(node, labels[2].receive_address, labels[2], labels[2], accounts_api) + change_label(node, labels[2].addresses[0], labels[2], labels[2]) class Label: - def __init__(self, name, accounts_api): + def __init__(self, name): # Label name self.name = name - self.accounts_api = accounts_api # Current receiving address associated with this label. self.receive_address = None # List of all addresses assigned with this label @@ -209,56 +147,31 @@ class Label: def add_receive_address(self, address): self.add_address(address) - if self.accounts_api: - self.receive_address = address def verify(self, node): if self.receive_address is not None: assert self.receive_address in self.addresses - if self.accounts_api: - assert_equal(node.getaccountaddress(self.name), self.receive_address) for address in self.addresses: assert_equal( node.getaddressinfo(address)['labels'][0], {"name": self.name, "purpose": self.purpose[address]}) - if self.accounts_api: - assert_equal(node.getaccount(address), self.name) - else: - assert_equal(node.getaddressinfo(address)['label'], self.name) + assert_equal(node.getaddressinfo(address)['label'], self.name) assert_equal( node.getaddressesbylabel(self.name), {address: {"purpose": self.purpose[address]} for address in self.addresses}) - if self.accounts_api: - assert_equal(set(node.getaddressesbyaccount(self.name)), set(self.addresses)) - -def change_label(node, address, old_label, new_label, accounts_api): +def change_label(node, address, old_label, new_label): assert_equal(address in old_label.addresses, True) - if accounts_api: - node.setaccount(address, new_label.name) - else: - node.setlabel(address, new_label.name) + node.setlabel(address, new_label.name) old_label.addresses.remove(address) new_label.add_address(address) - # Calling setaccount on an address which was previously the receiving - # address of a different account should reset the receiving address of - # the old account, causing getaccountaddress to return a brand new - # address. - if accounts_api: - if old_label.name != new_label.name and address == old_label.receive_address: - new_address = node.getaccountaddress(old_label.name) - assert_equal(new_address not in old_label.addresses, True) - assert_equal(new_address not in new_label.addresses, True) - old_label.add_receive_address(new_address) - old_label.verify(node) new_label.verify(node) - if __name__ == '__main__': WalletLabelsTest().main() diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py index 09a7c72347..3485c4470f 100755 --- a/test/functional/wallet_listreceivedby.py +++ b/test/functional/wallet_listreceivedby.py @@ -18,6 +18,14 @@ class ReceivedByTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 + def import_deterministic_coinbase_privkeys(self): + assert_equal(0, len(self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True))) + super().import_deterministic_coinbase_privkeys() + self.num_cb_reward_addresses = len(self.nodes[1].listreceivedbyaddress(minconf=0, include_empty=True, include_watchonly=True)) + + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): # Generate block to get out of IBD self.nodes[0].generate(1) @@ -64,7 +72,7 @@ class ReceivedByTest(BitcoinTestFramework): assert_raises_rpc_error(-4, "address_filter parameter was invalid", self.nodes[1].listreceivedbyaddress, minconf=0, include_empty=True, include_watchonly=True, address_filter="bamboozling") # Another address receive money res = self.nodes[1].listreceivedbyaddress(0, True, True) - assert_equal(len(res), 2) # Right now 2 entries + assert_equal(len(res), 2 + self.num_cb_reward_addresses) # Right now 2 entries other_addr = self.nodes[1].getnewaddress() txid2 = self.nodes[0].sendtoaddress(other_addr, 0.1) self.nodes[0].generate(1) @@ -81,7 +89,7 @@ class ReceivedByTest(BitcoinTestFramework): assert_equal(len(res), 1) # Should be two entries though without filter res = self.nodes[1].listreceivedbyaddress(0, True, True) - assert_equal(len(res), 3) # Became 3 entries + assert_equal(len(res), 3 + self.num_cb_reward_addresses) # Became 3 entries # Not on random addr other_addr = self.nodes[0].getnewaddress() # note on node[0]! just a random addr diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py index 847b48aa89..25ab222375 100755 --- a/test/functional/wallet_listsinceblock.py +++ b/test/functional/wallet_listsinceblock.py @@ -12,6 +12,9 @@ class ListSinceBlockTest (BitcoinTestFramework): self.num_nodes = 4 self.setup_clean_chain = True + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): self.nodes[2].generate(101) self.sync_all() @@ -50,8 +53,10 @@ class ListSinceBlockTest (BitcoinTestFramework): "42759cde25462784395a337460bde75f58e73d3f08bd31fdc3507cbac856a2c4") assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock, "0000000000000000000000000000000000000000000000000000000000000000") - assert_raises_rpc_error(-5, "Block not found", self.nodes[0].listsinceblock, + assert_raises_rpc_error(-8, "blockhash must be of length 64 (not 11, for 'invalid-hex')", self.nodes[0].listsinceblock, "invalid-hex") + assert_raises_rpc_error(-8, "blockhash must be hexadecimal string (not 'Z000000000000000000000000000000000000000000000000000000000000000')", self.nodes[0].listsinceblock, + "Z000000000000000000000000000000000000000000000000000000000000000") def test_reorg(self): ''' diff --git a/test/functional/wallet_listtransactions.py b/test/functional/wallet_listtransactions.py index 4dc3ff4b26..5a17395abd 100755 --- a/test/functional/wallet_listtransactions.py +++ b/test/functional/wallet_listtransactions.py @@ -27,6 +27,9 @@ class ListTransactionsTest(BitcoinTestFramework): self.num_nodes = 2 self.enable_mocktime() + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): # Simple send, 0 to 1: txid = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 0.1) diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index 0690dcf92d..189bc2d50e 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -23,6 +23,9 @@ class MultiWalletTest(BitcoinTestFramework): self.num_nodes = 2 self.supports_cli = True + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): node = self.nodes[0] @@ -41,8 +44,9 @@ class MultiWalletTest(BitcoinTestFramework): # create symlink to verify wallet directory path can be referenced # through symlink - os.mkdir(wallet_dir('w7')) - os.symlink('w7', wallet_dir('w7_symlink')) + if os.name != 'nt': + os.mkdir(wallet_dir('w7')) + os.symlink('w7', wallet_dir('w7_symlink')) # rename wallet.dat to make sure plain wallet file paths (as opposed to # directory paths) can be loaded @@ -63,6 +67,8 @@ class MultiWalletTest(BitcoinTestFramework): # w8 - to verify existing wallet file is loaded correctly # '' - to verify default wallet file is created correctly wallet_names = ['w1', 'w2', 'w3', 'w', 'sub/w5', os.path.join(self.options.tmpdir, 'extern/w6'), 'w7_symlink', 'w8', ''] + if os.name == 'nt': + wallet_names.remove('w7_symlink') extra_args = ['-wallet={}'.format(n) for n in wallet_names] self.start_node(0, extra_args) assert_equal(set(node.listwallets()), set(wallet_names)) @@ -73,7 +79,7 @@ class MultiWalletTest(BitcoinTestFramework): assert_equal(os.path.isfile(wallet_file(wallet_name)), True) # should not initialize if wallet path can't be created - exp_stderr = "boost::filesystem::create_directory: (The system cannot find the path specified|Not a directory):" + exp_stderr = "boost::filesystem::create_directory:" self.nodes[0].assert_start_raises_init_error(['-wallet=wallet.dat/bad'], exp_stderr, match=ErrorMatch.PARTIAL_REGEX) self.nodes[0].assert_start_raises_init_error(['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" does not exist') @@ -89,8 +95,9 @@ class MultiWalletTest(BitcoinTestFramework): self.nodes[0].assert_start_raises_init_error(['-wallet=w8', '-wallet=w8_copy'], exp_stderr, match=ErrorMatch.PARTIAL_REGEX) # should not initialize if wallet file is a symlink - os.symlink('w8', wallet_dir('w8_symlink')) - self.nodes[0].assert_start_raises_init_error(['-wallet=w8_symlink'], 'Error: Invalid -wallet path \'w8_symlink\'\. .*', match=ErrorMatch.FULL_REGEX) + if os.name != 'nt': + os.symlink('w8', wallet_dir('w8_symlink')) + self.nodes[0].assert_start_raises_init_error(['-wallet=w8_symlink'], 'Error: Invalid -wallet path \'w8_symlink\'\. .*', match=ErrorMatch.FULL_REGEX) # should not initialize if the specified walletdir does not exist self.nodes[0].assert_start_raises_init_error(['-walletdir=bad'], 'Error: Specified -walletdir "bad" does not exist') @@ -217,7 +224,8 @@ class MultiWalletTest(BitcoinTestFramework): assert_raises_rpc_error(-1, "BerkeleyBatch: Can't open database w8_copy (duplicates fileid", self.nodes[0].loadwallet, 'w8_copy') # Fail to load if wallet file is a symlink - assert_raises_rpc_error(-4, "Wallet file verification failed: Invalid -wallet path 'w8_symlink'", self.nodes[0].loadwallet, 'w8_symlink') + if os.name != 'nt': + assert_raises_rpc_error(-4, "Wallet file verification failed: Invalid -wallet path 'w8_symlink'", self.nodes[0].loadwallet, 'w8_symlink') # Fail to load if a directory is specified that doesn't contain a wallet os.mkdir(wallet_dir('empty_wallet_dir')) diff --git a/test/functional/wallet_resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py index 8995aa743f..00bf58d709 100755 --- a/test/functional/wallet_resendwallettransactions.py +++ b/test/functional/wallet_resendwallettransactions.py @@ -12,6 +12,9 @@ class ResendWalletTransactionsTest(BitcoinTestFramework): self.num_nodes = 1 self.extra_args = [['--walletbroadcast=false']] + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): # Should raise RPC_WALLET_ERROR (-4) if walletbroadcast is disabled. assert_raises_rpc_error(-4, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast", self.nodes[0].resendwallettransactions) diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py index 4ca4ee14e9..d78c105c17 100755 --- a/test/functional/wallet_txn_clone.py +++ b/test/functional/wallet_txn_clone.py @@ -16,6 +16,9 @@ class TxnMallTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 4 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def add_options(self, parser): parser.add_argument("--mineblock", dest="mine_block", default=False, action="store_true", help="Test double-spend of 1-confirmed transaction") diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py index 6811f6ab73..f114d5ab68 100755 --- a/test/functional/wallet_txn_doublespend.py +++ b/test/functional/wallet_txn_doublespend.py @@ -18,6 +18,9 @@ class TxnMallTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 4 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def add_options(self, parser): parser.add_argument("--mineblock", dest="mine_block", default=False, action="store_true", help="Test double-spend of 1-confirmed transaction") diff --git a/test/functional/wallet_zapwallettxes.py b/test/functional/wallet_zapwallettxes.py index cbd142d1ee..adebff360a 100755 --- a/test/functional/wallet_zapwallettxes.py +++ b/test/functional/wallet_zapwallettxes.py @@ -26,6 +26,9 @@ class ZapWalletTXesTest (BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 2 + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + def run_test(self): self.log.info("Mining blocks...") self.nodes[0].generate(1) diff --git a/test/lint/check-doc.py b/test/lint/check-doc.py index ab8e0e57d1..b0d9f87958 100755 --- a/test/lint/check-doc.py +++ b/test/lint/check-doc.py @@ -22,12 +22,12 @@ CMD_ROOT_DIR = '`git rev-parse --show-toplevel`/{}'.format(FOLDER_GREP) CMD_GREP_ARGS = r"git grep --perl-regexp '{}' -- {} ':(exclude){}'".format(REGEX_ARG, CMD_ROOT_DIR, FOLDER_TEST) CMD_GREP_DOCS = r"git grep --perl-regexp '{}' {}".format(REGEX_DOC, CMD_ROOT_DIR) # list unsupported, deprecated and duplicate args as they need no documentation -SET_DOC_OPTIONAL = set(['-rpcssl', '-benchmark', '-h', '-help', '-socks', '-tor', '-debugnet', '-whitelistalwaysrelay', '-promiscuousmempoolflags', '-blockminsize', '-dbcrashratio', '-forcecompactdb', '-usehd']) +SET_DOC_OPTIONAL = set(['-h', '-help', '-dbcrashratio', '-forcecompactdb']) def main(): - used = check_output(CMD_GREP_ARGS, shell=True, universal_newlines=True) - docd = check_output(CMD_GREP_DOCS, shell=True, universal_newlines=True) + used = check_output(CMD_GREP_ARGS, shell=True, universal_newlines=True, encoding='utf8') + docd = check_output(CMD_GREP_DOCS, shell=True, universal_newlines=True, encoding='utf8') args_used = set(re.findall(re.compile(REGEX_ARG), used)) args_docd = set(re.findall(re.compile(REGEX_DOC), docd)).union(SET_DOC_OPTIONAL) diff --git a/test/lint/check-rpc-mappings.py b/test/lint/check-rpc-mappings.py index 33e49bac13..137cc82b5d 100755 --- a/test/lint/check-rpc-mappings.py +++ b/test/lint/check-rpc-mappings.py @@ -90,6 +90,10 @@ def process_mapping(fname): return cmds def main(): + if len(sys.argv) != 2: + print('Usage: {} ROOT-DIR'.format(sys.argv[0]), file=sys.stderr) + sys.exit(1) + root = sys.argv[1] # Get all commands from dispatch tables diff --git a/test/lint/lint-assertions.sh b/test/lint/lint-assertions.sh new file mode 100755 index 0000000000..5bbcae79eb --- /dev/null +++ b/test/lint/lint-assertions.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# Check for assertions with obvious side effects. + +export LC_ALL=C + +EXIT_CODE=0 + +# PRE31-C (SEI CERT C Coding Standard): +# "Assertions should not contain assignments, increment, or decrement operators." +OUTPUT=$(git grep -E '[^_]assert\(.*(\+\+|\-\-|[^=!<>]=[^=!<>]).*\);' -- "*.cpp" "*.h") +if [[ ${OUTPUT} != "" ]]; then + echo "Assertions should not have side effects:" + echo + echo "${OUTPUT}" + EXIT_CODE=1 +fi + +exit ${EXIT_CODE} diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh index b8d105b49b..3972baed1d 100755 --- a/test/lint/lint-circular-dependencies.sh +++ b/test/lint/lint-circular-dependencies.sh @@ -25,7 +25,6 @@ EXPECTED_CIRCULAR_DEPENDENCIES=( "qt/sendcoinsdialog -> qt/walletmodel -> qt/sendcoinsdialog" "qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel" "qt/walletmodel -> qt/walletmodeltransaction -> qt/walletmodel" - "rpc/rawtransaction -> wallet/rpcwallet -> rpc/rawtransaction" "txmempool -> validation -> txmempool" "validation -> validationinterface -> validation" "wallet/coincontrol -> wallet/wallet -> wallet/coincontrol" diff --git a/test/lint/lint-format-strings.py b/test/lint/lint-format-strings.py index 60389176c9..2fb35fd8ca 100755 --- a/test/lint/lint-format-strings.py +++ b/test/lint/lint-format-strings.py @@ -20,6 +20,7 @@ FALSE_POSITIVES = [ ("src/util.cpp", "strprintf(COPYRIGHT_HOLDERS, COPYRIGHT_HOLDERS_SUBSTITUTION)"), ("src/wallet/wallet.h", "WalletLogPrintf(std::string fmt, Params... parameters)"), ("src/wallet/wallet.h", "LogPrintf((\"%s \" + fmt).c_str(), GetDisplayName(), parameters...)"), + ("src/logging.h", "LogPrintf(const char* fmt, const Args&... args)"), ] diff --git a/test/lint/lint-format-strings.sh b/test/lint/lint-format-strings.sh index 17f846d29b..2c443abf6b 100755 --- a/test/lint/lint-format-strings.sh +++ b/test/lint/lint-format-strings.sh @@ -33,7 +33,9 @@ if ! python3 -m doctest test/lint/lint-format-strings.py; then fi for S in "${FUNCTION_NAMES_AND_NUMBER_OF_LEADING_ARGUMENTS[@]}"; do IFS="," read -r FUNCTION_NAME SKIP_ARGUMENTS <<< "${S}" - mapfile -t MATCHING_FILES < <(git grep --full-name -l "${FUNCTION_NAME}" -- "*.c" "*.cpp" "*.h" | sort | grep -vE "^src/(leveldb|secp256k1|tinyformat|univalue)") + for MATCHING_FILE in $(git grep --full-name -l "${FUNCTION_NAME}" -- "*.c" "*.cpp" "*.h" | sort | grep -vE "^src/(leveldb|secp256k1|tinyformat|univalue)"); do + MATCHING_FILES+=("${MATCHING_FILE}") + done if ! test/lint/lint-format-strings.py --skip-arguments "${SKIP_ARGUMENTS}" "${FUNCTION_NAME}" "${MATCHING_FILES[@]}"; then EXIT_CODE=1 fi diff --git a/test/lint/lint-includes.sh b/test/lint/lint-includes.sh index 8f7a1fd76b..f6d0fd382b 100755 --- a/test/lint/lint-includes.sh +++ b/test/lint/lint-includes.sh @@ -47,7 +47,6 @@ fi EXPECTED_BOOST_INCLUDES=( boost/algorithm/string.hpp - boost/algorithm/string/case_conv.hpp boost/algorithm/string/classification.hpp boost/algorithm/string/replace.hpp boost/algorithm/string/split.hpp @@ -55,9 +54,7 @@ EXPECTED_BOOST_INCLUDES=( boost/chrono/chrono.hpp boost/date_time/posix_time/posix_time.hpp boost/filesystem.hpp - boost/filesystem/detail/utf8_codecvt_facet.hpp boost/filesystem/fstream.hpp - boost/interprocess/sync/file_lock.hpp boost/multi_index/hashed_index.hpp boost/multi_index/ordered_index.hpp boost/multi_index/sequenced_index.hpp @@ -65,7 +62,6 @@ EXPECTED_BOOST_INCLUDES=( boost/optional.hpp boost/preprocessor/cat.hpp boost/preprocessor/stringize.hpp - boost/scoped_array.hpp boost/signals2/connection.hpp boost/signals2/last_value.hpp boost/signals2/signal.hpp diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh index cbe1143bd0..cbee437c91 100755 --- a/test/lint/lint-locale-dependence.sh +++ b/test/lint/lint-locale-dependence.sh @@ -11,11 +11,9 @@ KNOWN_VIOLATIONS=( "src/dbwrapper.cpp:.*vsnprintf" "src/httprpc.cpp.*trim" "src/init.cpp:.*atoi" - "src/netbase.cpp.*to_lower" "src/qt/rpcconsole.cpp:.*atoi" "src/qt/rpcconsole.cpp:.*isdigit" "src/rest.cpp:.*strtol" - "src/rpc/server.cpp.*to_upper" "src/test/dbwrapper_tests.cpp:.*snprintf" "src/test/getarg_tests.cpp.*split" "src/torcontrol.cpp:.*atoi" @@ -199,11 +197,11 @@ REGEXP_IGNORE_KNOWN_VIOLATIONS=$(join_array "|" "${KNOWN_VIOLATIONS[@]}") # Invoke "git grep" only once in order to minimize run-time REGEXP_LOCALE_DEPENDENT_FUNCTIONS=$(join_array "|" "${LOCALE_DEPENDENT_FUNCTIONS[@]}") -GIT_GREP_OUTPUT=$(git grep -E "[^a-zA-Z0-9_\`'\"<>](${REGEXP_LOCALE_DEPENDENT_FUNCTIONS}(|_r|_s))[^a-zA-Z0-9_\`'\"<>]" -- "*.cpp" "*.h") +GIT_GREP_OUTPUT=$(git grep -E "[^a-zA-Z0-9_\`'\"<>](${REGEXP_LOCALE_DEPENDENT_FUNCTIONS}(_r|_s)?)[^a-zA-Z0-9_\`'\"<>]" -- "*.cpp" "*.h") EXIT_CODE=0 for LOCALE_DEPENDENT_FUNCTION in "${LOCALE_DEPENDENT_FUNCTIONS[@]}"; do - MATCHES=$(grep -E "[^a-zA-Z0-9_\`'\"<>]${LOCALE_DEPENDENT_FUNCTION}(|_r|_s)[^a-zA-Z0-9_\`'\"<>]" <<< "${GIT_GREP_OUTPUT}" | \ + MATCHES=$(grep -E "[^a-zA-Z0-9_\`'\"<>]${LOCALE_DEPENDENT_FUNCTION}(_r|_s)?[^a-zA-Z0-9_\`'\"<>]" <<< "${GIT_GREP_OUTPUT}" | \ grep -vE "\.(c|cpp|h):\s*(//|\*|/\*|\").*${LOCALE_DEPENDENT_FUNCTION}" | \ grep -vE 'fprintf\(.*(stdout|stderr)') if [[ ${REGEXP_IGNORE_EXTERNAL_DEPENDENCIES} != "" ]]; then diff --git a/test/lint/lint-python-utf8-encoding.sh b/test/lint/lint-python-utf8-encoding.sh index 14183a5ccf..d03c20205d 100755 --- a/test/lint/lint-python-utf8-encoding.sh +++ b/test/lint/lint-python-utf8-encoding.sh @@ -17,4 +17,12 @@ if [[ ${OUTPUT} != "" ]]; then echo "${OUTPUT}" EXIT_CODE=1 fi +OUTPUT=$(git grep "check_output(" -- "*.py" | grep "universal_newlines=True" | grep -vE "encoding=.(ascii|utf8|utf-8).") +if [[ ${OUTPUT} != "" ]]; then + echo "Python's check_output(...) seems to be used to get program outputs without explicitly" + echo "specifying encoding=\"utf8\":" + echo + echo "${OUTPUT}" + EXIT_CODE=1 +fi exit ${EXIT_CODE} diff --git a/test/lint/lint-python.sh b/test/lint/lint-python.sh index 7e73790517..4f4542ff0c 100755 --- a/test/lint/lint-python.sh +++ b/test/lint/lint-python.sh @@ -79,4 +79,12 @@ export LC_ALL=C # W605 invalid escape sequence "x" # W606 'async' and 'await' are reserved keywords starting with Python 3.7 -flake8 --ignore=B,C,E,F,I,N,W --select=E101,E112,E113,E115,E116,E125,E129,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,E901,E902,F401,F402,F403,F404,F405,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W191,W291,W292,W293,W504,W601,W602,W603,W604,W605,W606 . +if ! command -v flake8 > /dev/null; then + echo "Skipping Python linting since flake8 is not installed. Install by running \"pip3 install flake8\"" + exit 0 +elif PYTHONWARNINGS="ignore" flake8 --version | grep -q "Python 2"; then + echo "Skipping Python linting since flake8 is running under Python 2. Install the Python 3 version of flake8 by running \"pip3 install flake8\"" + exit 0 +fi + +PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=E101,E112,E113,E115,E116,E125,E129,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,E901,E902,F401,F402,F403,F404,F405,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W191,W291,W292,W293,W504,W601,W602,W603,W604,W605,W606 . diff --git a/test/lint/lint-shell-locale.sh b/test/lint/lint-shell-locale.sh index 242b27c763..084dc93f76 100755 --- a/test/lint/lint-shell-locale.sh +++ b/test/lint/lint-shell-locale.sh @@ -5,7 +5,8 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. # # Make sure all shell scripts: -# a.) explicitly opt out of locale dependence using "export LC_ALL=C", or +# a.) explicitly opt out of locale dependence using +# "export LC_ALL=C" or "export LC_ALL=C.UTF-8", or # b.) explicitly opt in to locale dependence using the annotation below. export LC_ALL=C @@ -15,8 +16,8 @@ for SHELL_SCRIPT in $(git ls-files -- "*.sh" | grep -vE "src/(secp256k1|univalue if grep -q "# This script is intentionally locale dependent by not setting \"export LC_ALL=C\"" "${SHELL_SCRIPT}"; then continue fi - FIRST_NON_COMMENT_LINE=$(grep -vE '^(#.*|)$' "${SHELL_SCRIPT}" | head -1) - if [[ ${FIRST_NON_COMMENT_LINE} != "export LC_ALL=C" ]]; then + FIRST_NON_COMMENT_LINE=$(grep -vE '^(#.*)?$' "${SHELL_SCRIPT}" | head -1) + if [[ ${FIRST_NON_COMMENT_LINE} != "export LC_ALL=C" && ${FIRST_NON_COMMENT_LINE} != "export LC_ALL=C.UTF-8" ]]; then echo "Missing \"export LC_ALL=C\" (to avoid locale dependence) as first non-comment non-empty line in ${SHELL_SCRIPT}" EXIT_CODE=1 fi diff --git a/test/lint/lint-shell.sh b/test/lint/lint-shell.sh index 5e1e136e7d..9af3c10ed6 100755 --- a/test/lint/lint-shell.sh +++ b/test/lint/lint-shell.sh @@ -16,7 +16,14 @@ if [ "$TRAVIS" = "true" ]; then unset LC_ALL fi +if ! command -v shellcheck > /dev/null; then + echo "Skipping shell linting since shellcheck is not installed." + exit 0 +fi + # Disabled warnings: +# SC1087: Use braces when expanding arrays, e.g. ${array[idx]} (or ${var}[.. to quiet). +# SC1117: Backslash is literal in "\.". Prefer explicit escaping: "\\.". # SC2001: See if you can use ${variable//search/replace} instead. # SC2004: $/${} is unnecessary on arithmetic variables. # SC2005: Useless echo? Instead of 'echo $(cmd)', just use 'cmd'. @@ -33,5 +40,8 @@ fi # SC2166: Prefer [ p ] && [ q ] as [ p -a q ] is not well defined. # SC2166: Prefer [ p ] || [ q ] as [ p -o q ] is not well defined. # SC2181: Check exit code directly with e.g. 'if mycmd;', not indirectly with $?. -shellcheck -e SC2001,SC2004,SC2005,SC2006,SC2016,SC2028,SC2046,SC2048,SC2066,SC2086,SC2116,SC2148,SC2162,SC2166,SC2181 \ +# SC2206: Quote to prevent word splitting, or split robustly with mapfile or read -a. +# SC2207: Prefer mapfile or read -a to split command output (or quote to avoid splitting). +# SC2230: which is non-standard. Use builtin 'command -v' instead. +shellcheck -e SC1087,SC1117,SC2001,SC2004,SC2005,SC2006,SC2016,SC2028,SC2046,SC2048,SC2066,SC2086,SC2116,SC2148,SC2162,SC2166,SC2181,SC2206,SC2207,SC2230 \ $(git ls-files -- "*.sh" | grep -vE 'src/(secp256k1|univalue)/') diff --git a/test/lint/lint-spelling.ignore-words.txt b/test/lint/lint-spelling.ignore-words.txt new file mode 100644 index 0000000000..9a49f32271 --- /dev/null +++ b/test/lint/lint-spelling.ignore-words.txt @@ -0,0 +1,6 @@ +cas +hights +mor +objext +unselect +useable diff --git a/test/lint/lint-spelling.sh b/test/lint/lint-spelling.sh new file mode 100755 index 0000000000..ebeafd7d58 --- /dev/null +++ b/test/lint/lint-spelling.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# Warn in case of spelling errors. +# Note: Will exit successfully regardless of spelling errors. + +export LC_ALL=C + +EXIT_CODE=0 +IGNORE_WORDS_FILE=test/lint/lint-spelling.ignore-words.txt +if ! codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=${IGNORE_WORDS_FILE} $(git ls-files -- ":(exclude)build-aux/m4/" ":(exclude)contrib/seeds/*.txt" ":(exclude)depends/" ":(exclude)doc/release-notes/" ":(exclude)src/leveldb/" ":(exclude)src/qt/locale/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/"); then + echo "^ Warning: codespell identified likely spelling errors. Any false positives? Add them to the list of ignored words in ${IGNORE_WORDS_FILE}" + EXIT_CODE=1 +fi +exit ${EXIT_CODE} |