diff options
310 files changed, 5696 insertions, 3283 deletions
diff --git a/.appveyor.yml b/.appveyor.yml index b4850d49c8..dacfba658b 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -9,8 +9,8 @@ environment: CLCACHE_SERVER: 1 PATH: 'C:\Python37-x64;C:\Python37-x64\Scripts;%PATH%' PYTHONUTF8: 1 - QT_DOWNLOAD_URL: 'https://github.com/sipsorcery/qt_win_binary/releases/download/v1.4/Qt5.9.8_x64_static_vs2019.zip' - QT_DOWNLOAD_HASH: 'f285cbb02bec3b3f3cc2621e3fa7d5edf0d6a66fa30c57859e583acda954ea80' + QT_DOWNLOAD_URL: 'https://github.com/sipsorcery/qt_win_binary/releases/download/v1.6/Qt5.9.8_x64_static_vs2019.zip' + QT_DOWNLOAD_HASH: '9a8c6eb20967873785057fdcd329a657c7f922b0af08c5fde105cc597dd37e21' QT_LOCAL_PATH: 'C:\Qt5.9.8_x64_static_vs2019' VCPKG_INSTALL_PATH: 'C:\tools\vcpkg\installed' cache: diff --git a/.cirrus.yml b/.cirrus.yml index 517cd93585..f4a3878ed8 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -16,7 +16,7 @@ task: ccache_cache: folder: "/tmp/ccache_dir" install_script: - - pkg install -y autoconf automake boost-libs git gmake libevent libtool openssl pkgconf python3 ccache + - pkg install -y autoconf automake boost-libs git gmake libevent libtool pkgconf python3 ccache - ./contrib/install_db4.sh $(pwd) - ccache --max-size=${CCACHE_SIZE} configure_script: diff --git a/.github/ISSUE_TEMPLATE/good_first_issue.md b/.github/ISSUE_TEMPLATE/good_first_issue.md index 6782218616..c441f7a307 100644 --- a/.github/ISSUE_TEMPLATE/good_first_issue.md +++ b/.github/ISSUE_TEMPLATE/good_first_issue.md @@ -7,13 +7,14 @@ assignees: '' --- -The purpose of the `good first issue` label is to highlight which issues are suitable for a new contributor without a deep understanding of the codebase. -Useful skills: +#### Useful skills: + +<!-- (For example, “C++11 std::thread”, “Qt5 GUI and async GUI design” or “basic understanding of Bitcoin mining and the Bitcoin Core RPC interface”.) --> -(For example, “C++11 std::thread”, “Qt5 GUI and async GUI design” or “basic understanding of Bitcoin mining and the Bitcoin Core RPC interface”.) +#### Want to work on this issue? -Want to work on this issue? +The purpose of the `good first issue` label is to highlight which issues are suitable for a new contributor without a deep understanding of the codebase. You do not need to request permission to start working on this. You are encouraged to comment on the issue if you are planning to work on it. This will help other contributors monitor which issues are actively being addressed and is also an effective way to request assistance if and when you need it. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..dd1003d8e3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,77 @@ +name: bitcoin-core-ci + +on: + push: +jobs: + build: + runs-on: windows-latest + env: + PYTHONUTF8: 1 + QT_DOWNLOAD_URL: 'https://github.com/sipsorcery/qt_win_binary/releases/download/v1.4/Qt5.9.8_x64_static_vs2019.zip' + QT_DOWNLOAD_HASH: 'f285cbb02bec3b3f3cc2621e3fa7d5edf0d6a66fa30c57859e583acda954ea80' + QT_LOCAL_PATH: 'C:\Qt5.9.8_x64_static_vs2019' + VCPKG_INSTALL_PATH: "$env:VCPKG_INSTALLATION_ROOT/installed" + PLATFORM: x64 + steps: + - uses: actions/checkout@v1 + + - uses: actions/setup-python@v1 + with: + python-version: '3.7' # Needed for PEP 540 + + - uses: actions/cache@v1 + id: vcpkgcache + with: + path: C:/vcpkg/installed + key: ${{ runner.os }}-vcpkg + + - name: Update vcpkg and install packages + if: steps.vcpkgcache.outputs.cache-hit != 'true' + run: | + $env:PACKAGES = Get-Content -Path "$env:GITHUB_WORKSPACE/build_msvc/vcpkg-packages.txt" + Write-Host "vcpkg list: $env:PACKAGES" + cd $env:VCPKG_INSTALLATION_ROOT + git pull origin master + .\bootstrap-vcpkg.bat + .\vcpkg install --triplet $env:PLATFORM-windows-static $env:PACKAGES.split() > $null + - name: Install prebuilt Qt libraries + run: | + if(!(Test-Path -Path ($env:QT_LOCAL_PATH))) { + Write-Host "Downloading Qt binaries."; + Invoke-WebRequest -Uri $env:QT_DOWNLOAD_URL -Out qtdownload.zip; + Write-Host "Qt binaries successfully downloaded, checking hash against $env:QT_DOWNLOAD_HASH..."; + if((Get-FileHash qtdownload.zip).Hash -eq $env:QT_DOWNLOAD_HASH) { + Expand-Archive qtdownload.zip -DestinationPath $env:QT_LOCAL_PATH; + Write-Host "Qt binary download matched the expected hash."; + } + else { + Write-Host "ERROR: Qt binary download did not match the expected hash."; + exit 1 + } + } + else { + Write-Host "Qt binaries already present."; + } + - name: Generate project files + run: python build_msvc\msvc-autogen.py + - name: Setup MSBuild.exe + uses: warrenbuckley/Setup-MSBuild@v1 + - name: vcpkg integration + run: C:/vcpkg/vcpkg.exe integrate install + - name: Build + run: msbuild build_msvc\bitcoin.sln /m /v:n /p:Configuration=Release + - name: Run test_bitcoin + shell: cmd + run: src\test_bitcoin.exe -k stdout -e stdout 2> NUL + - name: Run bench_bitcoin + shell: cmd + run: src\bench_bitcoin.exe -evals=1 -scaling=0 > NUL + - name: bitcoin-util-test + run: python test\util\bitcoin-util-test.py + - name: rpcauth-test + shell: cmd + run: python test\util\rpcauth-test.py + - name: test_runner + shell: cmd + run: | + python test\functional\test_runner.py --ansi --ci --quiet --combinedlogslen=4000 --failfast --exclude feature_fee_estimation diff --git a/.travis.yml b/.travis.yml index 73e62cd2b0..38ad79af29 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,12 +43,11 @@ before_cache: stages: - lint - test - - extended-lint env: global: - CI_RETRY_EXE="travis_retry" - CI_WAIT="while sleep 500; do echo .; done" - - CACHE_ERR_MSG="Error! Initial build successful, but not enough time remains to run later build stages and tests. Please manually re-run this job by using the travis restart button or asking a bitcoin maintainer to restart. The next run should not time out because the build cache has been saved." + - CACHE_ERR_MSG="Error! Initial build successful, but not enough time remains to run later build stages and tests. See https://docs.travis-ci.com/user/customizing-the-build#build-timeouts . Please manually re-run this job by using the travis restart button. The next run should not time out because the build cache has been saved." before_install: - set -o errexit; source ./ci/test/00_setup_env.sh - set -o errexit; source ./ci/test/03_before_install.sh @@ -82,27 +81,21 @@ jobs: script: - set -o errexit; source ./ci/lint/06_script.sh - - stage: extended-lint - name: 'extended lint [runtime >= 60 seconds]' - env: - cache: false - language: python - python: '3.5' - install: - - set -o errexit; source ./ci/extended_lint/04_install.sh - before_script: - - set -o errexit; source ./ci/lint/05_before_script.sh - script: - - set -o errexit; source ./ci/extended_lint/06_script.sh - - stage: test - name: 'ARM [GOAL: install] [unit tests, functional tests]' + name: 'ARM [GOAL: install] [bionic] [unit tests, functional tests]' arch: arm64 env: >- FILE_ENV="./ci/test/00_setup_env_arm.sh" QEMU_USER_CMD="" # Can run the tests natively without qemu - stage: test + name: 'S390x [GOAL: install] [bionic] [unit tests, functional tests]' + arch: s390x + env: >- + FILE_ENV="./ci/test/00_setup_env_s390x.sh" + QEMU_USER_CMD="" # Can run the tests natively without qemu + + - stage: test name: 'Win64 [GOAL: deploy] [unit tests, no gui, no functional tests]' env: >- FILE_ENV="./ci/test/00_setup_env_win64.sh" @@ -113,14 +106,14 @@ jobs: FILE_ENV="./ci/test/00_setup_env_i686.sh" - stage: test - name: 'x86_64 Linux [GOAL: install] [bionic] [uses qt5 dev package instead of depends Qt to speed up build and avoid timeout] [unsigned char]' + name: 'x86_64 Linux [GOAL: install] [CentOS 7] [no depends, only system libs]' env: >- - FILE_ENV="./ci/test/00_setup_env_native_qt5.sh" + FILE_ENV="./ci/test/00_setup_env_native_centos.sh" - stage: test - name: 'x86_64 Linux [GOAL: install] [trusty] [no functional tests, no depends, only system libs]' + name: 'x86_64 Linux [GOAL: install] [bionic] [uses qt5 dev package and some depends packages] [unsigned char]' env: >- - FILE_ENV="./ci/test/00_setup_env_native_trusty.sh" + FILE_ENV="./ci/test/00_setup_env_native_qt5.sh" - stage: test name: 'x86_64 Linux [GOAL: install] [xenial] [no depends, only system libs, sanitizers: thread (TSan), no wallet]' @@ -134,6 +127,11 @@ jobs: FILE_ENV="./ci/test/00_setup_env_native_asan.sh" - stage: test + name: 'x86_64 Linux [GOAL: install] [bionic] [no depends, only system libs, valgrind]' + env: >- + FILE_ENV="./ci/test/00_setup_env_native_valgrind.sh" + + - stage: test name: 'x86_64 Linux [GOAL: install] [bionic] [no depends, only system libs, sanitizers: fuzzer,address,undefined]' env: >- FILE_ENV="./ci/test/00_setup_env_native_fuzz.sh" @@ -144,7 +142,7 @@ jobs: FILE_ENV="./ci/test/00_setup_env_native_nowallet.sh" - stage: test - name: 'macOS 10.10 [GOAL: deploy] [no functional tests]' + name: 'macOS 10.12 [GOAL: deploy] [no functional tests]' env: >- FILE_ENV="./ci/test/00_setup_env_mac.sh" @@ -152,8 +150,8 @@ jobs: name: 'macOS 10.14 native [GOAL: install] [GUI] [no depends]' os: osx # Use the most recent version: - # Xcode 11, macOS 10.14, JDK 12.0.1 + # Xcode 11.2.1, macOS 10.14, JDK 13.0.1, SDK 10.15 # https://docs.travis-ci.com/user/reference/osx/#macos-version - osx_image: xcode11 + osx_image: xcode11.2 env: >- FILE_ENV="./ci/test/00_setup_env_mac_host.sh" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ecb5704a8f..1e34f65728 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,7 +16,7 @@ release cycle, overall merging, moderation and appointment of maintainers. If you're looking for somewhere to start contributing, check out the [good first issue](https://github.com/bitcoin/bitcoin/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) -list. +list or participate in a weekly [Bitcoin Core PR Review Club](https://bitcoincore.reviews/) meeting. Communication Channels ---------------------- diff --git a/Makefile.am b/Makefile.am index deae2c1b57..22b83e80dd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -25,7 +25,7 @@ BITCOIN_QT_BIN=$(top_builddir)/src/qt/$(BITCOIN_GUI_NAME)$(EXEEXT) BITCOIN_CLI_BIN=$(top_builddir)/src/$(BITCOIN_CLI_NAME)$(EXEEXT) BITCOIN_TX_BIN=$(top_builddir)/src/$(BITCOIN_TX_NAME)$(EXEEXT) BITCOIN_WALLET_BIN=$(top_builddir)/src/$(BITCOIN_WALLET_TOOL_NAME)$(EXEEXT) -BITCOIN_WIN_INSTALLER=$(PACKAGE)-$(PACKAGE_VERSION)-win$(WINDOWS_BITS)-setup$(EXEEXT) +BITCOIN_WIN_INSTALLER=$(PACKAGE)-$(PACKAGE_VERSION)-win64-setup$(EXEEXT) empty := space := $(empty) $(empty) @@ -99,7 +99,7 @@ $(OSX_APP)/Contents/PkgInfo: $(OSX_APP)/Contents/Resources/empty.lproj: $(MKDIR_P) $(@D) - @touch $@ + @touch $@ $(OSX_APP)/Contents/Info.plist: $(OSX_PLIST) $(MKDIR_P) $(@D) @@ -189,7 +189,16 @@ $(BITCOIN_WALLET_BIN): FORCE $(MAKE) -C src $(@F) if USE_LCOV -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" +LCOV_FILTER_PATTERN = \ + -p "/usr/include/" \ + -p "/usr/lib/" \ + -p "/usr/lib64/" \ + -p "src/leveldb/" \ + -p "src/bench/" \ + -p "src/univalue" \ + -p "src/crypto/ctaes" \ + -p "src/secp256k1" \ + -p "depends" baseline.info: $(LCOV) -c -i -d $(abs_builddir)/src -o $@ @@ -321,4 +330,3 @@ clean-local: clean-docs rm -rf coverage_percent.txt test_bitcoin.coverage/ total.coverage/ test/tmp/ cache/ $(OSX_APP) rm -rf test/functional/__pycache__ test/functional/test_framework/__pycache__ test/cache share/rpcauth/__pycache__ rm -rf osx_volname dist/ dpi36.background.tiff dpi72.background.tiff - diff --git a/build_msvc/README.md b/build_msvc/README.md index 59be5298ad..704470cac8 100644 --- a/build_msvc/README.md +++ b/build_msvc/README.md @@ -12,7 +12,7 @@ Quick Start The minimal steps required to build Bitcoin Core with the msbuild toolchain are below. More detailed instructions are contained in the following sections. ``` -vcpkg install --triplet x64-windows-static boost-filesystem boost-multi-index boost-signals2 boost-test boost-thread libevent openssl zeromq berkeleydb rapidcheck double-conversion +vcpkg install --triplet x64-windows-static boost-filesystem boost-multi-index boost-signals2 boost-test boost-thread libevent zeromq berkeleydb rapidcheck double-conversion py -3 build_msvc\msvc-autogen.py msbuild /m build_msvc\bitcoin.sln /p:Platform=x64 /p:Configuration=Release /t:build ``` @@ -33,7 +33,6 @@ The [external dependencies](https://github.com/bitcoin/bitcoin/blob/master/doc/d - Boost - DoubleConversion - libevent -- OpenSSL - Qt5 - RapidCheck - ZeroMQ diff --git a/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj.in b/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj.in index 56401d7618..128c1bd8e7 100644 --- a/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj.in +++ b/build_msvc/bench_bitcoin/bench_bitcoin.vcxproj.in @@ -42,6 +42,9 @@ <ProjectReference Include="..\libleveldb\libleveldb.vcxproj"> <Project>{18430fef-6b61-4c53-b396-718e02850f1b}</Project> </ProjectReference> + <ProjectReference Include="..\libtest_util\libtest_util.vcxproj"> + <Project>{1e065f03-3566-47d0-8fa9-daa72b084e7d}</Project> + </ProjectReference> </ItemGroup> <Target Name="RawBenchHeaderGen" BeforeTargets="PrepareForBuild"> <PropertyGroup> diff --git a/build_msvc/bitcoin.sln b/build_msvc/bitcoin.sln index d4b83b6529..9dff0e316e 100644 --- a/build_msvc/bitcoin.sln +++ b/build_msvc/bitcoin.sln @@ -44,6 +44,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libbitcoin_qt", "libbitcoin EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bitcoin-qt", "bitcoin-qt\bitcoin-qt.vcxproj", "{7E99172D-7FF2-4CB6-B736-AC9B76ED412A}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libtest_util", "libtest_util\libtest_util.vcxproj", "{868474FD-35F6-4400-8EED-30A33E7521D4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -220,11 +222,21 @@ Global {7E99172D-7FF2-4CB6-B736-AC9B76ED412A}.Release|x64.Build.0 = Release|x64 {7E99172D-7FF2-4CB6-B736-AC9B76ED412A}.Release|x86.ActiveCfg = Release|Win32 {7E99172D-7FF2-4CB6-B736-AC9B76ED412A}.Release|x86.Build.0 = Release|Win32 + {868474FD-35F6-4400-8EED-30A33E7521D4}.Debug|x64.ActiveCfg = Debug|x64 + {868474FD-35F6-4400-8EED-30A33E7521D4}.Debug|x64.Build.0 = Debug|x64 + {868474FD-35F6-4400-8EED-30A33E7521D4}.Debug|x86.ActiveCfg = Debug|Win32 + {868474FD-35F6-4400-8EED-30A33E7521D4}.Debug|x86.Build.0 = Debug|Win32 + {868474FD-35F6-4400-8EED-30A33E7521D4}.Release|x64.ActiveCfg = Release|x64 + {868474FD-35F6-4400-8EED-30A33E7521D4}.Release|x64.Build.0 = Release|x64 + {868474FD-35F6-4400-8EED-30A33E7521D4}.Release|x86.ActiveCfg = Release|Win32 + {868474FD-35F6-4400-8EED-30A33E7521D4}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {8AA72EDA-2CD4-4564-B1E4-688B760EEEE9} + SolutionGuid = {8AA72EDA-2CD4-4564-B1E4-688B760EEEE9} + SolutionGuid = {8607C0F4-F33D-41B8-8D51-18E366A0F8DF} + SolutionGuid = {58AAB032-7274-49BD-845E-5EF4DBB69B70} EndGlobalSection EndGlobal diff --git a/build_msvc/common.init.vcxproj b/build_msvc/common.init.vcxproj index a04a38ff7c..583cd9f88c 100644 --- a/build_msvc/common.init.vcxproj +++ b/build_msvc/common.init.vcxproj @@ -107,7 +107,7 @@ <WarningLevel>Level3</WarningLevel> <PrecompiledHeader>NotUsing</PrecompiledHeader> <AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions> - <DisableSpecificWarnings>4018;4221;4244;4267;4334;4715;4805;</DisableSpecificWarnings> + <DisableSpecificWarnings>4018;4221;4244;4267;4334;4715;4805;4834</DisableSpecificWarnings> <TreatWarningAsError>true</TreatWarningAsError> <PreprocessorDefinitions>ZMQ_STATIC;NOMINMAX;WIN32;HAVE_CONFIG_H;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_CONSOLE;_WIN32_WINNT=0x0601;%(PreprocessorDefinitions)</PreprocessorDefinitions> <AdditionalIncludeDirectories>..\..\src;..\..\src\univalue\include;..\..\src\secp256k1\include;..\..\src\leveldb\include;..\..\src\leveldb\helpers\memenv;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> diff --git a/build_msvc/libtest_util/libtest_util.vcxproj.in b/build_msvc/libtest_util/libtest_util.vcxproj.in new file mode 100644 index 0000000000..b5e844010e --- /dev/null +++ b/build_msvc/libtest_util/libtest_util.vcxproj.in @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="..\common.init.vcxproj" /> + <PropertyGroup Label="Globals"> + <ProjectGuid>{868474FD-35F6-4400-8EED-30A33E7521D4}</ProjectGuid> + </PropertyGroup> + <PropertyGroup Label="Configuration"> + <ConfigurationType>StaticLibrary</ConfigurationType> + </PropertyGroup> + <ItemGroup> +@SOURCE_FILES@ + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <Import Project="..\common.vcxproj" /> +</Project> diff --git a/build_msvc/msvc-autogen.py b/build_msvc/msvc-autogen.py index 5ddda3c03e..3951438408 100644 --- a/build_msvc/msvc-autogen.py +++ b/build_msvc/msvc-autogen.py @@ -18,6 +18,7 @@ libs = [ 'libbitcoin_wallet', 'libbitcoin_zmq', 'bench_bitcoin', + 'libtest_util', ] ignore_list = [ diff --git a/build_msvc/test_bitcoin/test_bitcoin.vcxproj b/build_msvc/test_bitcoin/test_bitcoin.vcxproj index 1d05ff6cba..96d20a2720 100644 --- a/build_msvc/test_bitcoin/test_bitcoin.vcxproj +++ b/build_msvc/test_bitcoin/test_bitcoin.vcxproj @@ -42,6 +42,9 @@ <ProjectReference Include="..\libbitcoin_zmq\libbitcoin_zmq.vcxproj"> <Project>{792d487f-f14c-49fc-a9de-3fc150f31c3f}</Project> </ProjectReference> + <ProjectReference Include="..\libtest_util\libtest_util.vcxproj"> + <Project>{1e065f03-3566-47d0-8fa9-daa72b084e7d}</Project> + </ProjectReference> <ProjectReference Include="..\libunivalue\libunivalue.vcxproj"> <Project>{5724ba7d-a09a-4ba8-800b-c4c1561b3d69}</Project> </ProjectReference> diff --git a/build_msvc/vcpkg-packages.txt b/build_msvc/vcpkg-packages.txt index 082a13f1cf..27e2bbdcde 100644 --- a/build_msvc/vcpkg-packages.txt +++ b/build_msvc/vcpkg-packages.txt @@ -1 +1 @@ -berkeleydb boost-filesystem boost-multi-index boost-signals2 boost-test boost-thread libevent openssl rapidcheck zeromq double-conversion
\ No newline at end of file +berkeleydb boost-filesystem boost-multi-index boost-signals2 boost-test boost-thread libevent[thread] rapidcheck zeromq double-conversion
\ No newline at end of file diff --git a/ci/README.md b/ci/README.md index fb1cd7460b..880e49b459 100644 --- a/ci/README.md +++ b/ci/README.md @@ -8,11 +8,21 @@ and numbered according to which stage and lifecycle step it belongs to. ### Running a stage locally +Be aware that the tests will be built and run in-place, so please run at your own risk. +If the repository is not a fresh git clone, you might have to clean files from previous builds or test runs first. + +The ci needs to perform various sysadmin tasks such as installing packages or writing to the user's home directory. +While most of the actions are done inside a docker container, this is not possible for all. Thus, cache directories, +such as the depends cache or ccache, are mounted as read-write into the docker container. While it should be fine to run +the ci system locally on you development box, the ci scripts can generally be assumed to have received less review and +testing compared to other parts of the codebase. If you want to keep the work tree clean, you might want to run the ci +system in a virtual machine with a Linux operating system of your choice. + To allow for a wide range of tested environments, but also ensure reproducibility to some extent, the test stage requires `docker` to be installed. To install all requirements on Ubuntu, run ``` -sudo apt install docker.io bash git +sudo apt install docker.io bash ``` To run the default test stage, @@ -26,6 +36,3 @@ To run the test stage with a specific configuration, ``` FILE_ENV="./ci/test/00_setup_env_arm.sh" ./ci/test_run_all.sh ``` - -Be aware that the tests will be build and run in-place, so please run at your own risk. -If the repository is not a fresh git clone, you might have to clean files from previous builds or test runs first. diff --git a/ci/extended_lint/04_install.sh b/ci/extended_lint/04_install.sh deleted file mode 100755 index 123d874a84..0000000000 --- a/ci/extended_lint/04_install.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2019 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 - -CPPCHECK_VERSION=1.86 -curl -s https://codeload.github.com/danmar/cppcheck/tar.gz/${CPPCHECK_VERSION} | tar -zxf - --directory /tmp/ -(cd /tmp/cppcheck-${CPPCHECK_VERSION}/ && make CFGDIR=/tmp/cppcheck-${CPPCHECK_VERSION}/cfg/ > /dev/null) -export PATH="$PATH:/tmp/cppcheck-${CPPCHECK_VERSION}/" diff --git a/ci/extended_lint/06_script.sh b/ci/extended_lint/06_script.sh deleted file mode 100755 index e8228c9c4d..0000000000 --- a/ci/extended_lint/06_script.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2019 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 - -test/lint/extended-lint-all.sh diff --git a/ci/lint/04_install.sh b/ci/lint/04_install.sh index 12c3bfce45..8b2d609504 100755 --- a/ci/lint/04_install.sh +++ b/ci/lint/04_install.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Copyright (c) 2018 The Bitcoin Core developers +# Copyright (c) 2018-2019 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -8,6 +8,7 @@ export LC_ALL=C travis_retry pip3 install codespell==1.15.0 travis_retry pip3 install flake8==3.7.8 +travis_retry pip3 install yq SHELLCHECK_VERSION=v0.6.0 curl -s "https://storage.googleapis.com/shellcheck/shellcheck-${SHELLCHECK_VERSION}.linux.x86_64.tar.xz" | tar --xz -xf - --directory /tmp/ diff --git a/ci/test/00_setup_env.sh b/ci/test/00_setup_env.sh index c65054cf9e..1f485fbec4 100755 --- a/ci/test/00_setup_env.sh +++ b/ci/test/00_setup_env.sh @@ -6,6 +6,12 @@ export LC_ALL=C.UTF-8 +# The root dir. +# The ci system copies this folder. +# This is where the build is done (depends and dist). +BASE_ROOT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../../ >/dev/null 2>&1 && pwd ) +export BASE_ROOT_DIR + echo "Setting specific values in env" if [ -n "${FILE_ENV}" ]; then set -o errexit; @@ -13,13 +19,11 @@ if [ -n "${FILE_ENV}" ]; then source "${FILE_ENV}" fi -BASE_ROOT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )"/../../ >/dev/null 2>&1 && pwd ) -export BASE_ROOT_DIR - echo "Fallback to default values in env (if not yet set)" # The number of parallel jobs to pass down to make and test_runner.py export MAKEJOBS=${MAKEJOBS:--j4} # A folder for the ci system to put temporary files (ccache, datadirs for tests, ...) +# This folder only exists on the ci host. export BASE_SCRATCH_DIR=${BASE_SCRATCH_DIR:-$BASE_ROOT_DIR/ci/scratch/} # What host to compile for. See also ./depends/README.md # Tests that need cross-compilation export the appropriate HOST. @@ -37,15 +41,18 @@ export BOOST_TEST_RANDOM=${BOOST_TEST_RANDOM:-1} export CCACHE_SIZE=${CCACHE_SIZE:-100M} export CCACHE_TEMPDIR=${CCACHE_TEMPDIR:-/tmp/.ccache-temp} export CCACHE_COMPRESS=${CCACHE_COMPRESS:-1} +# The cache dir. +# This folder exists on the ci host and ci guest. Changes are propagated back and forth. export CCACHE_DIR=${CCACHE_DIR:-$BASE_SCRATCH_DIR/.ccache} -# Folder where the build is done (depends and dist). Can not be changed and is equal to the root of the git repo -export BASE_BUILD_DIR=${BASE_BUILD_DIR:-$BASE_ROOT_DIR} -# Folder where the build is done (bin and lib). Can not be changed. -export BASE_OUTDIR=${BASE_OUTDIR:-$BASE_BUILD_DIR/out/$HOST} +# The depends dir. +# This folder exists on the ci host and ci guest. Changes are propagated back and forth. +export DEPENDS_DIR=${DEPENDS_DIR:-$BASE_ROOT_DIR/depends} +# Folder where the build is done (bin and lib). +export BASE_OUTDIR=${BASE_OUTDIR:-$BASE_SCRATCH_DIR/out/$HOST} export SDK_URL=${SDK_URL:-https://bitcoincore.org/depends-sources/sdks} export WINEDEBUG=${WINEDEBUG:-fixme-all} -export DOCKER_PACKAGES=${DOCKER_PACKAGES:-build-essential libtool autotools-dev automake pkg-config bsdmainutils curl ca-certificates ccache python3} +export DOCKER_PACKAGES=${DOCKER_PACKAGES:-build-essential libtool autotools-dev automake pkg-config bsdmainutils curl ca-certificates ccache python3 rsync git} export GOAL=${GOAL:-install} -export DIR_QA_ASSETS=${DIR_QA_ASSETS:-${BASE_BUILD_DIR}/qa-assets} +export DIR_QA_ASSETS=${DIR_QA_ASSETS:-${BASE_SCRATCH_DIR}/qa-assets} export PATH=${BASE_ROOT_DIR}/ci/retry:$PATH export CI_RETRY_EXE=${CI_RETRY_EXE:retry} diff --git a/ci/test/00_setup_env_mac.sh b/ci/test/00_setup_env_mac.sh index f384ba9263..af166b6ca7 100644 --- a/ci/test/00_setup_env_mac.sh +++ b/ci/test/00_setup_env_mac.sh @@ -6,7 +6,7 @@ export LC_ALL=C.UTF-8 -export HOST=x86_64-apple-darwin14 +export HOST=x86_64-apple-darwin16 export PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python3-dev python3-setuptools" export OSX_SDK=10.11 export RUN_UNIT_TESTS=false diff --git a/ci/test/00_setup_env_mac_host.sh b/ci/test/00_setup_env_mac_host.sh index aa68a5700e..0b437a723f 100644 --- a/ci/test/00_setup_env_mac_host.sh +++ b/ci/test/00_setup_env_mac_host.sh @@ -6,7 +6,7 @@ export LC_ALL=C.UTF-8 -export HOST=x86_64-apple-darwin14 +export HOST=x86_64-apple-darwin16 export BREW_PACKAGES="automake berkeley-db4 libtool boost miniupnpc pkg-config qt qrencode python3 ccache zeromq" export PIP_PACKAGES="zmq" export RUN_CI_ON_HOST=true diff --git a/ci/test/00_setup_env_native_asan.sh b/ci/test/00_setup_env_native_asan.sh index 293f37fedf..2ffd3c5107 100644 --- a/ci/test/00_setup_env_native_asan.sh +++ b/ci/test/00_setup_env_native_asan.sh @@ -6,7 +6,8 @@ export LC_ALL=C.UTF-8 -export PACKAGES="clang llvm 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 libqrencode-dev" +export PACKAGES="clang-8 llvm-8 python3-zmq qtbase5-dev qttools5-dev-tools 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 libqrencode-dev" +# Use clang-8 instead of default clang (which is clang-6 on Bionic) to avoid spurious segfaults when running on ppc64le export NO_DEPENDS=1 export GOAL="install" -export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER --with-sanitizers=address,integer,undefined CC=clang CXX=clang++" +export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=qt5 CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER' --with-sanitizers=address,integer,undefined CC=clang-8 CXX=clang++-8" diff --git a/ci/test/00_setup_env_native_centos.sh b/ci/test/00_setup_env_native_centos.sh new file mode 100644 index 0000000000..56b915b6c7 --- /dev/null +++ b/ci/test/00_setup_env_native_centos.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2019 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 + +export DOCKER_NAME_TAG=centos:7 +export DOCKER_PACKAGES="gcc-c++ libtool make git python3 python36-zmq" +export PACKAGES="boost-devel libevent-devel libdb4-devel libdb4-cxx-devel miniupnpc-devel zeromq-devel qt5-qtbase-devel qt5-qttools-devel qrencode-devel" +export NO_DEPENDS=1 +export GOAL="install" +export BITCOIN_CONFIG="--enable-reduce-exports" diff --git a/ci/test/00_setup_env_native_fuzz.sh b/ci/test/00_setup_env_native_fuzz.sh index 2e9f529216..b0405bb762 100644 --- a/ci/test/00_setup_env_native_fuzz.sh +++ b/ci/test/00_setup_env_native_fuzz.sh @@ -6,10 +6,11 @@ export LC_ALL=C.UTF-8 -export PACKAGES="clang llvm python3 libssl1.0-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev" +export PACKAGES="clang-8 llvm-8 python3 libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev" export NO_DEPENDS=1 export RUN_UNIT_TESTS=false export RUN_FUNCTIONAL_TESTS=false export RUN_FUZZ_TESTS=true export GOAL="install" -export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer,address,undefined CC=clang CXX=clang++" +export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer,address,undefined CC=clang-8 CXX=clang++-8" +# Use clang-8, instead of default clang on bionic, which is clang-6 and does not come with libfuzzer on aarch64 diff --git a/ci/test/00_setup_env_native_trusty.sh b/ci/test/00_setup_env_native_trusty.sh deleted file mode 100644 index fbf1488f07..0000000000 --- a/ci/test/00_setup_env_native_trusty.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -# -# Copyright (c) 2019 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 - -export DOCKER_NAME_TAG=ubuntu:14.04 -export PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools libicu-dev libpng-dev libssl-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev libdb5.1++-dev libzmq3-dev libqrencode-dev" -export NO_DEPENDS=1 -export RUN_FUNCTIONAL_TESTS=false -export GOAL="install" -export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=no" diff --git a/ci/test/00_setup_env_native_tsan.sh b/ci/test/00_setup_env_native_tsan.sh index afc082d6cf..e9b7a7bba1 100644 --- a/ci/test/00_setup_env_native_tsan.sh +++ b/ci/test/00_setup_env_native_tsan.sh @@ -7,7 +7,11 @@ export LC_ALL=C.UTF-8 export DOCKER_NAME_TAG=ubuntu:16.04 -export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools libssl-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 libqrencode-dev" +export PACKAGES="clang llvm python3-zmq qtbase5-dev qttools5-dev-tools 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 libqrencode-dev" export NO_DEPENDS=1 export GOAL="install" -export BITCOIN_CONFIG="--enable-zmq --disable-wallet --with-gui=qt5 CPPFLAGS=-DDEBUG_LOCKORDER --with-sanitizers=thread --disable-hardening --disable-asm CC=clang CXX=clang++" +export BITCOIN_CONFIG="--enable-zmq --disable-wallet --with-gui=qt5 CPPFLAGS='-DARENA_DEBUG -DDEBUG_LOCKORDER' --with-sanitizers=thread --disable-hardening --disable-asm CC=clang CXX=clang++" + +# xenial comes with old clang versions that can not parse the sanitizer suppressions files +# Remove unparseable lines as a hacky workaround +sed -i '/^implicit-/d' "${BASE_ROOT_DIR}/test/sanitizer_suppressions/ubsan" diff --git a/ci/test/00_setup_env_native_valgrind.sh b/ci/test/00_setup_env_native_valgrind.sh new file mode 100644 index 0000000000..906ffd7d79 --- /dev/null +++ b/ci/test/00_setup_env_native_valgrind.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2019 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 + +export PACKAGES="valgrind clang llvm python3-zmq 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" +export USE_VALGRIND=1 +export NO_DEPENDS=1 +export TEST_RUNNER_EXTRA="p2p_segwit.py" # Only run one test for now. TODO enable all and bump timeouts +export RUN_FUNCTIONAL_TESTS=true +export GOAL="install" +export BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --with-gui=no CC=clang CXX=clang++" # TODO enable GUI diff --git a/ci/test/00_setup_env_s390x.sh b/ci/test/00_setup_env_s390x.sh new file mode 100644 index 0000000000..637d549553 --- /dev/null +++ b/ci/test/00_setup_env_s390x.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2019 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 + +export HOST=s390x-linux-gnu +# The host arch is unknown, so we run the tests through qemu. +# If the host is s390x and wants to run the tests natively, it can set QEMU_USER_CMD to the empty string. +export QEMU_USER_CMD="${QEMU_USER_CMD:"qemu-s390x"}" +export PACKAGES="python3-zmq bsdmainutils qemu-user" +export RUN_UNIT_TESTS=true +export RUN_FUNCTIONAL_TESTS=true +export GOAL="install" +export BITCOIN_CONFIG="--enable-reduce-exports --with-incompatible-bdb" diff --git a/ci/test/04_install.sh b/ci/test/04_install.sh index 271ae82e5c..62f30535cb 100755 --- a/ci/test/04_install.sh +++ b/ci/test/04_install.sh @@ -6,6 +6,10 @@ export LC_ALL=C.UTF-8 +if [[ $DOCKER_NAME_TAG == centos* ]]; then + export LC_ALL=en_US.utf8 +fi + if [ "$TRAVIS_OS_NAME" == "osx" ]; then set +o errexit pushd /usr/local/Homebrew || exit 1 @@ -25,8 +29,6 @@ if [ "$TRAVIS_OS_NAME" == "osx" ]; then done export PATH="/usr/local/opt/ccache/libexec:$PATH" - OPENSSL_PKG_CONFIG_PATH="/usr/local/opt/openssl@1.1/lib/pkgconfig" - export PKG_CONFIG_PATH=$OPENSSL_PKG_CONFIG_PATH:$PKG_CONFIG_PATH ${CI_RETRY_EXE} pip3 install $PIP_PACKAGES @@ -35,36 +37,38 @@ fi mkdir -p "${BASE_SCRATCH_DIR}" mkdir -p "${CCACHE_DIR}" -if [ ! -d ${DIR_QA_ASSETS} ]; then - git clone https://github.com/bitcoin-core/qa-assets ${DIR_QA_ASSETS} -fi -export DIR_FUZZ_IN=${DIR_QA_ASSETS}/fuzz_seed_corpus/ - -mkdir -p "${BASE_BUILD_DIR}/sanitizer-output/" -export ASAN_OPTIONS="detect_stack_use_after_return=1" -export LSAN_OPTIONS="suppressions=${BASE_BUILD_DIR}/test/sanitizer_suppressions/lsan" -export TSAN_OPTIONS="suppressions=${BASE_BUILD_DIR}/test/sanitizer_suppressions/tsan:log_path=${BASE_BUILD_DIR}/sanitizer-output/tsan" -export UBSAN_OPTIONS="suppressions=${BASE_BUILD_DIR}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1" -env | grep -E '^(BITCOIN_CONFIG|CCACHE_|WINEDEBUG|LC_ALL|BOOST_TEST_RANDOM|CONFIG_SHELL|(ASAN|LSAN|TSAN|UBSAN)_OPTIONS)' | tee /tmp/env +export ASAN_OPTIONS="detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1" +export LSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/lsan" +export TSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/tsan:log_path=${BASE_SCRATCH_DIR}/sanitizer-output/tsan" +export UBSAN_OPTIONS="suppressions=${BASE_ROOT_DIR}/test/sanitizer_suppressions/ubsan:print_stacktrace=1:halt_on_error=1:report_error_type=1" +env | grep -E '^(BITCOIN_CONFIG|BASE_|CCACHE_|WINEDEBUG|LC_ALL|BOOST_TEST_RANDOM|CONFIG_SHELL|(ASAN|LSAN|TSAN|UBSAN)_OPTIONS)' | tee /tmp/env if [[ $HOST = *-mingw32 ]]; then DOCKER_ADMIN="--cap-add SYS_ADMIN" elif [[ $BITCOIN_CONFIG = *--with-sanitizers=*address* ]]; then # If ran with (ASan + LSan), Docker needs access to ptrace (https://github.com/google/sanitizers/issues/764) DOCKER_ADMIN="--cap-add SYS_PTRACE" fi +export P_CI_DIR="$PWD" + if [ -z "$RUN_CI_ON_HOST" ]; then echo "Creating $DOCKER_NAME_TAG container to run in" ${CI_RETRY_EXE} docker pull "$DOCKER_NAME_TAG" - DOCKER_ID=$(docker run $DOCKER_ADMIN -idt --mount type=bind,src=$BASE_BUILD_DIR,dst=$BASE_BUILD_DIR --mount type=bind,src=$CCACHE_DIR,dst=$CCACHE_DIR -w $BASE_BUILD_DIR --env-file /tmp/env $DOCKER_NAME_TAG) + DOCKER_ID=$(docker run $DOCKER_ADMIN -idt \ + --mount type=bind,src=$BASE_ROOT_DIR,dst=/ro_base,readonly \ + --mount type=bind,src=$CCACHE_DIR,dst=$CCACHE_DIR \ + --mount type=bind,src=$DEPENDS_DIR,dst=$DEPENDS_DIR \ + -w $BASE_ROOT_DIR \ + --env-file /tmp/env \ + $DOCKER_NAME_TAG) DOCKER_EXEC () { - docker exec $DOCKER_ID bash -c "export PATH=$BASE_SCRATCH_DIR/bins/:\$PATH && cd $PWD && $*" + docker exec $DOCKER_ID bash -c "export PATH=$BASE_SCRATCH_DIR/bins/:\$PATH && cd $P_CI_DIR && $*" } else echo "Running on host system without docker wrapper" DOCKER_EXEC () { - bash -c "export PATH=$BASE_SCRATCH_DIR/bins/:\$PATH && cd $PWD && $*" + bash -c "export PATH=$BASE_SCRATCH_DIR/bins/:\$PATH && cd $P_CI_DIR && $*" } fi @@ -74,17 +78,34 @@ if [ "$TRAVIS_OS_NAME" == "osx" ]; then else DOCKER_EXEC free -m -h DOCKER_EXEC echo "Number of CPUs \(nproc\):" \$\(nproc\) + DOCKER_EXEC echo "Free disk space:" + DOCKER_EXEC df -h fi if [ -n "$DPKG_ADD_ARCH" ]; then DOCKER_EXEC dpkg --add-architecture "$DPKG_ADD_ARCH" fi -if [ "$TRAVIS_OS_NAME" != "osx" ]; then +if [[ $DOCKER_NAME_TAG == centos* ]]; then + ${CI_RETRY_EXE} DOCKER_EXEC yum -y install epel-release + ${CI_RETRY_EXE} DOCKER_EXEC yum -y install $DOCKER_PACKAGES $PACKAGES +elif [ "$TRAVIS_OS_NAME" != "osx" ]; then ${CI_RETRY_EXE} DOCKER_EXEC apt-get update ${CI_RETRY_EXE} DOCKER_EXEC apt-get install --no-install-recommends --no-upgrade -y $PACKAGES $DOCKER_PACKAGES fi +if [ ! -d ${DIR_QA_ASSETS} ]; then + DOCKER_EXEC git clone https://github.com/bitcoin-core/qa-assets ${DIR_QA_ASSETS} +fi +export DIR_FUZZ_IN=${DIR_QA_ASSETS}/fuzz_seed_corpus/ + +DOCKER_EXEC mkdir -p "${BASE_SCRATCH_DIR}/sanitizer-output/" + +if [ -z "$RUN_CI_ON_HOST" ]; then + echo "Create $BASE_ROOT_DIR" + DOCKER_EXEC rsync -a /ro_base/ $BASE_ROOT_DIR +fi + if [ "$USE_BUSY_BOX" = "true" ]; then echo "Setup to use BusyBox utils" DOCKER_EXEC mkdir -p $BASE_SCRATCH_DIR/bins/ diff --git a/ci/test/05_before_script.sh b/ci/test/05_before_script.sh index a0488f0807..51a5830682 100755 --- a/ci/test/05_before_script.sh +++ b/ci/test/05_before_script.sh @@ -13,13 +13,13 @@ else DOCKER_EXEC echo \> \$HOME/.bitcoin fi -mkdir -p depends/SDKs depends/sdk-sources +DOCKER_EXEC mkdir -p ${DEPENDS_DIR}/SDKs ${DEPENDS_DIR}/sdk-sources -if [ -n "$OSX_SDK" ] && [ ! -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 +if [ -n "$OSX_SDK" ] && [ ! -f ${DEPENDS_DIR}/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then + curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o ${DEPENDS_DIR}/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz fi -if [ -n "$OSX_SDK" ] && [ -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 +if [ -n "$OSX_SDK" ] && [ -f ${DEPENDS_DIR}/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then + DOCKER_EXEC tar -C ${DEPENDS_DIR}/SDKs -xf ${DEPENDS_DIR}/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz fi if [[ $HOST = *-mingw32 ]]; then DOCKER_EXEC update-alternatives --set $HOST-g++ \$\(which $HOST-g++-posix\) diff --git a/ci/test/06_script_a.sh b/ci/test/06_script_a.sh index c4dc22bdd8..98b75d7497 100755 --- a/ci/test/06_script_a.sh +++ b/ci/test/06_script_a.sh @@ -6,7 +6,7 @@ export LC_ALL=C.UTF-8 -BITCOIN_CONFIG_ALL="--disable-dependency-tracking --prefix=$BASE_BUILD_DIR/depends/$HOST --bindir=$BASE_OUTDIR/bin --libdir=$BASE_OUTDIR/lib" +BITCOIN_CONFIG_ALL="--disable-dependency-tracking --prefix=$DEPENDS_DIR/$HOST --bindir=$BASE_OUTDIR/bin --libdir=$BASE_OUTDIR/lib" if [ -z "$NO_DEPENDS" ]; then DOCKER_EXEC ccache --max-size=$CCACHE_SIZE fi @@ -19,36 +19,28 @@ else fi END_FOLD -mkdir -p build - -# Temporarily disable errexit, because Travis macOS fails without error message -set +o errexit -cd build || (echo "could not enter build directory"; exit 1) -set -o errexit +DOCKER_EXEC mkdir -p build +export P_CI_DIR="$P_CI_DIR/build" BEGIN_FOLD configure -DOCKER_EXEC ../configure --cache-file=config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) +DOCKER_EXEC ../configure --cache-file=config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( (DOCKER_EXEC cat config.log) && false) END_FOLD BEGIN_FOLD distdir +# Create folder on host and docker, so that `cd` works +mkdir -p "bitcoin-$HOST" DOCKER_EXEC make distdir VERSION=$HOST END_FOLD -set +o errexit -cd "bitcoin-$HOST" || (echo "could not enter distdir bitcoin-$HOST"; exit 1) -set -o errexit +export P_CI_DIR="$P_CI_DIR/bitcoin-$HOST" BEGIN_FOLD configure -DOCKER_EXEC ./configure --cache-file=../config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) +DOCKER_EXEC ./configure --cache-file=../config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( (DOCKER_EXEC cat config.log) && false) END_FOLD set -o errtrace -trap 'DOCKER_EXEC "cat ${BASE_BUILD_DIR}/sanitizer-output/* 2> /dev/null"' ERR +trap 'DOCKER_EXEC "cat ${BASE_SCRATCH_DIR}/sanitizer-output/* 2> /dev/null"' ERR BEGIN_FOLD build DOCKER_EXEC make $MAKEJOBS $GOAL || ( echo "Build failure. Verbose build follows." && DOCKER_EXEC make $GOAL V=1 ; false ) END_FOLD - -set +o errexit -cd ${BASE_BUILD_DIR} || (echo "could not enter travis build dir $BASE_BUILD_DIR"; exit 1) -set -o errexit diff --git a/ci/test/06_script_b.sh b/ci/test/06_script_b.sh index 7ac4907d32..e641cef279 100755 --- a/ci/test/06_script_b.sh +++ b/ci/test/06_script_b.sh @@ -6,14 +6,8 @@ export LC_ALL=C.UTF-8 -# Temporarily disable errexit, because Travis macOS fails without error message -set +o errexit -cd "build/bitcoin-$HOST" || (echo "could not enter distdir build/bitcoin-$HOST"; exit 1) -set -o errexit - if [ -n "$QEMU_USER_CMD" ]; then BEGIN_FOLD wrap-qemu - echo "Prepare to run functional tests for HOST=$HOST" # Generate all binaries, so that they can be wrapped DOCKER_EXEC make $MAKEJOBS -C src/secp256k1 VERBOSE=1 DOCKER_EXEC make $MAKEJOBS -C src/univalue VERBOSE=1 @@ -30,10 +24,16 @@ if [ -n "$QEMU_USER_CMD" ]; then END_FOLD fi +if [ -n "$USE_VALGRIND" ]; then + BEGIN_FOLD wrap-valgrind + DOCKER_EXEC "${BASE_ROOT_DIR}/ci/test/wrap-valgrind.sh" + END_FOLD +fi + if [ "$RUN_UNIT_TESTS" = "true" ]; then BEGIN_FOLD unit-tests bash -c "${CI_WAIT}" & # Print dots in case the unit tests take a long time to run - DOCKER_EXEC LD_LIBRARY_PATH=$BASE_BUILD_DIR/depends/$HOST/lib make $MAKEJOBS check VERBOSE=1 + DOCKER_EXEC LD_LIBRARY_PATH=$DEPENDS_DIR/$HOST/lib make $MAKEJOBS check VERBOSE=1 END_FOLD fi @@ -48,7 +48,3 @@ if [ "$RUN_FUZZ_TESTS" = "true" ]; then DOCKER_EXEC test/fuzz/test_runner.py -l DEBUG ${DIR_FUZZ_IN} END_FOLD fi - -set +o errexit -cd ${BASE_BUILD_DIR} || (echo "could not enter travis build dir $BASE_BUILD_DIR"; exit 1) -set -o errexit diff --git a/ci/test/wrap-valgrind.sh b/ci/test/wrap-valgrind.sh new file mode 100755 index 0000000000..d2192061db --- /dev/null +++ b/ci/test/wrap-valgrind.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. + +export LC_ALL=C.UTF-8 + +for b_name in "${BASE_OUTDIR}/bin"/*; do + # shellcheck disable=SC2044 + for b in $(find "${BASE_ROOT_DIR}" -executable -type f -name $(basename $b_name)); do + echo "Wrap $b ..." + mv "$b" "${b}_orig" + echo '#!/usr/bin/env bash' > "$b" + echo "valgrind --gen-suppressions=all --quiet --error-exitcode=1 --suppressions=${BASE_ROOT_DIR}/contrib/valgrind.supp \"${b}_orig\" \"\$@\"" >> "$b" + chmod +x "$b" + done +done diff --git a/configure.ac b/configure.ac index 8bde6d9bc4..ad76317820 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,4 @@ -dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) -AC_PREREQ([2.60]) +AC_PREREQ([2.69]) define(_CLIENT_VERSION_MAJOR, 0) define(_CLIENT_VERSION_MINOR, 19) define(_CLIENT_VERSION_REVISION, 99) @@ -243,27 +242,27 @@ AC_ARG_ENABLE(man, enable_man=yes) AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no) -# Enable debug +dnl Enable debug AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [use compiler flags and macros suited for debugging (default is no)])], [enable_debug=$enableval], [enable_debug=no]) -# Enable different -fsanitize options +dnl Enable different -fsanitize options AC_ARG_WITH([sanitizers], [AS_HELP_STRING([--with-sanitizers], [comma separated list of extra sanitizers to build with (default is none enabled)])], [use_sanitizers=$withval]) -# Enable gprof profiling +dnl Enable gprof profiling AC_ARG_ENABLE([gprof], [AS_HELP_STRING([--enable-gprof], [use gprof profiling compiler flags (default is no)])], [enable_gprof=$enableval], [enable_gprof=no]) -# Turn warnings into errors +dnl Turn warnings into errors AC_ARG_ENABLE([werror], [AS_HELP_STRING([--enable-werror], [Treat certain compiler warnings as errors (default is no)])], @@ -274,15 +273,15 @@ AC_LANG_PUSH([C++]) AX_CHECK_COMPILE_FLAG([-Werror],[CXXFLAG_WERROR="-Werror"],[CXXFLAG_WERROR=""]) if test "x$enable_debug" = xyes; then - # Clear default -g -O2 flags + dnl Clear default -g -O2 flags if test "x$CXXFLAGS_overridden" = xno; then CXXFLAGS="" fi - # Disable all optimizations + dnl Disable all optimizations AX_CHECK_COMPILE_FLAG([-O0], [[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -O0"]],,[[$CXXFLAG_WERROR]]) - # Prefer -g3, fall back to -g if that is unavailable. + dnl Prefer -g3, fall back to -g if that is unavailable. AX_CHECK_COMPILE_FLAG( [-g3], [[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -g3"]], @@ -295,19 +294,19 @@ if test "x$enable_debug" = xyes; then fi if test x$use_sanitizers != x; then - # First check if the compiler accepts flags. If an incompatible pair like - # -fsanitize=address,thread is used here, this check will fail. This will also - # fail if a bad argument is passed, e.g. -fsanitize=undfeined + dnl First check if the compiler accepts flags. If an incompatible pair like + dnl -fsanitize=address,thread is used here, this check will fail. This will also + dnl fail if a bad argument is passed, e.g. -fsanitize=undfeined AX_CHECK_COMPILE_FLAG( [[-fsanitize=$use_sanitizers]], [[SANITIZER_CXXFLAGS=-fsanitize=$use_sanitizers]], [AC_MSG_ERROR([compiler did not accept requested flags])]) - # Some compilers (e.g. GCC) require additional libraries like libasan, - # libtsan, libubsan, etc. Make sure linking still works with the sanitize - # flag. This is a separate check so we can give a better error message when - # the sanitize flags are supported by the compiler but the actual sanitizer - # libs are missing. + dnl Some compilers (e.g. GCC) require additional libraries like libasan, + dnl libtsan, libubsan, etc. Make sure linking still works with the sanitize + dnl flag. This is a separate check so we can give a better error message when + dnl the sanitize flags are supported by the compiler but the actual sanitizer + dnl libs are missing. AX_CHECK_LINK_FLAG( [[-fsanitize=$use_sanitizers]], [[SANITIZER_LDFLAGS=-fsanitize=$use_sanitizers]], @@ -329,6 +328,7 @@ if test "x$enable_werror" = "xyes"; then AX_CHECK_COMPILE_FLAG([-Werror=vla],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=vla"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Werror=switch],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=switch"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Werror=thread-safety-analysis],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=thread-safety-analysis"],,[[$CXXFLAG_WERROR]]) + AX_CHECK_COMPILE_FLAG([-Werror=unused-variable],[ERROR_CXXFLAGS="$ERROR_CXXFLAGS -Werror=unused-variable"],,[[$CXXFLAG_WERROR]]) fi if test "x$CXXFLAGS_overridden" = "xno"; then @@ -341,10 +341,11 @@ if test "x$CXXFLAGS_overridden" = "xno"; then AX_CHECK_COMPILE_FLAG([-Wthread-safety-analysis],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wthread-safety-analysis"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Wrange-loop-analysis],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wrange-loop-analysis"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Wredundant-decls],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wredundant-decls"],,[[$CXXFLAG_WERROR]]) + AX_CHECK_COMPILE_FLAG([-Wunused-variable],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wunused-variable"],,[[$CXXFLAG_WERROR]]) - ## Some compilers (gcc) ignore unknown -Wno-* options, but warn about all - ## unknown options if any other warning is produced. Test the -Wfoo case, and - ## set the -Wno-foo case if it works. + dnl Some compilers (gcc) ignore unknown -Wno-* options, but warn about all + dnl unknown options if any other warning is produced. Test the -Wfoo case, and + dnl set the -Wno-foo case if it works. AX_CHECK_COMPILE_FLAG([-Wunused-parameter],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-unused-parameter"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Wself-assign],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-self-assign"],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-Wunused-local-typedef],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-unused-local-typedef"],,[[$CXXFLAG_WERROR]]) @@ -359,9 +360,9 @@ enable_shani=no if test "x$use_asm" = "xyes"; then -# Check for optional instruction set support. Enabling these does _not_ imply that all code will -# be compiled with them, rather that specific objects/libs may use them after checking for runtime -# compatibility. +dnl Check for optional instruction set support. Enabling these does _not_ imply that all code will +dnl be compiled with them, rather that specific objects/libs may use them after checking for runtime +dnl compatibility. AX_CHECK_COMPILE_FLAG([-msse4.2],[[SSE42_CXXFLAGS="-msse4.2"]],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-msse4.1],[[SSE41_CXXFLAGS="-msse4.1"]],,[[$CXXFLAG_WERROR]]) AX_CHECK_COMPILE_FLAG([-mavx -mavx2],[[AVX2_CXXFLAGS="-mavx -mavx2"]],,[[$CXXFLAG_WERROR]]) @@ -504,8 +505,8 @@ case $host in AC_CHECK_LIB([iphlpapi], [main],, AC_MSG_ERROR(libiphlpapi missing)) AC_CHECK_LIB([crypt32], [main],, AC_MSG_ERROR(libcrypt32 missing)) - # -static is interpreted by libtool, where it has a different meaning. - # In libtool-speak, it's -all-static. + dnl -static is interpreted by libtool, where it has a different meaning. + dnl In libtool-speak, it's -all-static. AX_CHECK_LINK_FLAG([[-static]],[LIBTOOL_APP_LDFLAGS="$LIBTOOL_APP_LDFLAGS -all-static"]) AC_PATH_PROG([MAKENSIS], [makensis], none) @@ -523,12 +524,6 @@ case $host in if test "x$CXXFLAGS_overridden" = "xno"; then CXXFLAGS="$CXXFLAGS -w" fi - case $host in - i?86-*) WINDOWS_BITS=32 ;; - x86_64-*) WINDOWS_BITS=64 ;; - *) AC_MSG_ERROR("Could not determine win32/win64 for installer") ;; - esac - AC_SUBST(WINDOWS_BITS) dnl libtool insists upon adding -nostdlib and a list of objects/libs to link against. dnl That breaks our ability to build dll's with static libgcc/libstdc++/libssp. Override @@ -553,13 +548,8 @@ case $host in dnl It's safe to add these paths even if the functionality is disabled by dnl the user (--without-wallet or --without-gui for example). - openssl_prefix=`$BREW --prefix openssl 2>/dev/null` - bdb_prefix=`$BREW --prefix berkeley-db4 2>/dev/null` - qt5_prefix=`$BREW --prefix qt5 2>/dev/null` - if test x$openssl_prefix != x; then - PKG_CONFIG_PATH="$openssl_prefix/lib/pkgconfig:$PKG_CONFIG_PATH" - export PKG_CONFIG_PATH - fi + bdb_prefix=$($BREW --prefix berkeley-db4 2>/dev/null) + qt5_prefix=$($BREW --prefix qt5 2>/dev/null) if test x$bdb_prefix != x; then CPPFLAGS="$CPPFLAGS -I$bdb_prefix/include" LIBS="$LIBS -L$bdb_prefix/lib" @@ -676,11 +666,11 @@ AC_C_BIGENDIAN dnl Check for pthread compile/link requirements AX_PTHREAD -# The following macro will add the necessary defines to bitcoin-config.h, but -# they also need to be passed down to any subprojects. Pull the results out of -# the cache and add them to CPPFLAGS. +dnl The following macro will add the necessary defines to bitcoin-config.h, but +dnl they also need to be passed down to any subprojects. Pull the results out of +dnl the cache and add them to CPPFLAGS. AC_SYS_LARGEFILE -# detect POSIX or GNU variant of strerror_r +dnl detect POSIX or GNU variant of strerror_r AC_FUNC_STRERROR_R if test x$ac_cv_sys_file_offset_bits != x && @@ -703,12 +693,12 @@ AX_GCC_FUNC_ATTRIBUTE([dllimport]) if test x$use_glibc_compat != xno; then - #glibc absorbed clock_gettime in 2.17. librt (its previous location) is safe to link - #in anyway for back-compat. + dnl glibc absorbed clock_gettime in 2.17. librt (its previous location) is safe to link + dnl in anyway for back-compat. AC_CHECK_LIB([rt],[clock_gettime],, AC_MSG_ERROR(librt missing)) - #__fdelt_chk's params and return type have changed from long unsigned int to long int. - # See which one is present here. + dnl __fdelt_chk's params and return type have changed from long unsigned int to long int. + dnl See which one is present here. AC_MSG_CHECKING(__fdelt_chk type) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#ifdef _FORTIFY_SOURCE #undef _FORTIFY_SOURCE @@ -744,22 +734,22 @@ if test "x$enable_gprof" = xyes; then fi if test x$TARGET_OS != xwindows; then - # All windows code is PIC, forcing it on just adds useless compile warnings + dnl All windows code is PIC, forcing it on just adds useless compile warnings AX_CHECK_COMPILE_FLAG([-fPIC],[PIC_FLAGS="-fPIC"]) fi -# All versions of gcc that we commonly use for building are subject to bug -# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90348. To work around that, set -# -fstack-reuse=none for all gcc builds. (Only gcc understands this flag) +dnl All versions of gcc that we commonly use for building are subject to bug +dnl https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90348. To work around that, set +dnl -fstack-reuse=none for all gcc builds. (Only gcc understands this flag) AX_CHECK_COMPILE_FLAG([-fstack-reuse=none],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-reuse=none"]) if test x$use_hardening != xno; then use_hardening=yes AX_CHECK_COMPILE_FLAG([-Wstack-protector],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -Wstack-protector"]) AX_CHECK_COMPILE_FLAG([-fstack-protector-all],[HARDENED_CXXFLAGS="$HARDENED_CXXFLAGS -fstack-protector-all"]) - # When enable_debug is yes, all optimizations are disabled. - # However, FORTIFY_SOURCE requires that there is some level of optimization, otherwise it does nothing and just creates a compiler warning. - # Since FORTIFY_SOURCE is a no-op without optimizations, do not enable it when enable_debug is yes. + dnl When enable_debug is yes, all optimizations are disabled. + dnl However, FORTIFY_SOURCE requires that there is some level of optimization, otherwise it does nothing and just creates a compiler warning. + dnl Since FORTIFY_SOURCE is a no-op without optimizations, do not enable it when enable_debug is yes. if test x$enable_debug != xyes; then AX_CHECK_PREPROC_FLAG([-D_FORTIFY_SOURCE=2],[ AX_CHECK_PREPROC_FLAG([-U_FORTIFY_SOURCE],[ @@ -786,12 +776,14 @@ fi dnl this flag screws up non-darwin gcc even when the check fails. special-case it. if test x$TARGET_OS = xdarwin; then AX_CHECK_LINK_FLAG([[-Wl,-dead_strip]], [LDFLAGS="$LDFLAGS -Wl,-dead_strip"]) + AX_CHECK_LINK_FLAG([[-Wl,-dead_strip_dylibs]], [LDFLAGS="$LDFLAGS -Wl,-dead_strip_dylibs"]) + AX_CHECK_LINK_FLAG([[-Wl,-bind_at_load]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,-bind_at_load"]) 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_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 sys/sysctl.h vm/vm_param.h sys/vmmeter.h sys/resources.h]) -# FD_ZERO may be dependent on a declaration of memcpy, e.g. in SmartOS -# check that it fails to build without memcpy, then that it builds with +dnl FD_ZERO may be dependent on a declaration of memcpy, e.g. in SmartOS +dnl check that it fails to build without memcpy, then that it builds with AC_MSG_CHECKING(FD_ZERO memcpy dependence) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <cstddef> @@ -829,7 +821,7 @@ AC_CHECK_DECLS([getifaddrs, freeifaddrs],,, ) AC_CHECK_DECLS([strnlen]) -# Check for daemon(3), unrelated to --with-daemon (although used by it) +dnl Check for daemon(3), unrelated to --with-daemon (although used by it) AC_CHECK_DECLS([daemon]) AC_CHECK_DECLS([le16toh, le32toh, le64toh, htole16, htole32, htole64, be16toh, be32toh, be64toh, htobe16, htobe32, htobe64],,, @@ -895,19 +887,19 @@ if test "x$use_thread_local" = xyes || { test "x$use_thread_local" = xauto && te [ case $host in *mingw*) - # mingw32's implementation of thread_local has also been shown to behave - # erroneously under concurrent usage; see: - # https://gist.github.com/jamesob/fe9a872051a88b2025b1aa37bfa98605 + dnl mingw32's implementation of thread_local has also been shown to behave + dnl erroneously under concurrent usage; see: + dnl https://gist.github.com/jamesob/fe9a872051a88b2025b1aa37bfa98605 AC_MSG_RESULT(no) ;; *darwin*) - # TODO enable thread_local on later versions of Darwin where it is - # supported (per https://stackoverflow.com/a/29929949) + dnl TODO enable thread_local on later versions of Darwin where it is + dnl supported (per https://stackoverflow.com/a/29929949) AC_MSG_RESULT(no) ;; *freebsd*) - # FreeBSD's implementation of thread_local is also buggy (per - # https://groups.google.com/d/msg/bsdmailinglist/22ncTZAbDp4/Dii_pII5AwAJ) + dnl FreeBSD's implementation of thread_local is also buggy (per + dnl https://groups.google.com/d/msg/bsdmailinglist/22ncTZAbDp4/Dii_pII5AwAJ) AC_MSG_RESULT(no) ;; *) @@ -923,7 +915,7 @@ if test "x$use_thread_local" = xyes || { test "x$use_thread_local" = xauto && te LDFLAGS="$TEMP_LDFLAGS" fi -# Check for different ways of gathering OS randomness +dnl Check for different ways of gathering OS randomness AC_MSG_CHECKING(for Linux getrandom syscall) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <unistd.h> #include <sys/syscall.h> @@ -948,6 +940,18 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <unistd.h> [ AC_MSG_RESULT(no)] ) +AC_MSG_CHECKING(for sysctl) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h> + #include <sys/sysctl.h>]], + [[ static const int name[2] = {CTL_KERN, KERN_VERSION}; + #ifdef __linux__ + #error "Don't use sysctl on Linux, it's deprecated even when it works" + #endif + sysctl(name, 2, nullptr, nullptr, nullptr, 0); ]])], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSCTL, 1,[Define this symbol if the BSD sysctl() is available]) ], + [ AC_MSG_RESULT(no)] +) + AC_MSG_CHECKING(for sysctl KERN_ARND) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h> #include <sys/sysctl.h>]], @@ -965,7 +969,7 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <stdint.h> [ AC_MSG_RESULT(no)] ) -# Check for reduced exports +dnl Check for reduced exports if test x$use_reduce_exports = xyes; then AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[RE_CXXFLAGS="-fvisibility=hidden"], [AC_MSG_ERROR([Cannot set default symbol visibility. Use --disable-reduce-exports.])]) @@ -977,7 +981,7 @@ AC_LINK_IFELSE( [[ #include <cstdlib> ]], [[ int nErr = std::system(""); ]] )], - [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_STD__SYSTEM, 1, Define to 1 if you have the `std::system' function.)], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_STD__SYSTEM, 1, Define to 1 if std::system is available.)], [ AC_MSG_RESULT(no) ] ) @@ -987,11 +991,10 @@ AC_LINK_IFELSE( [[ ]], [[ int nErr = ::_wsystem(""); ]] )], - [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_WSYSTEM, 1, Define to 1 if you have the `::wsystem' function.)], + [ AC_MSG_RESULT(yes); AC_DEFINE(HAVE_WSYSTEM, 1, Define to 1 if ::wsystem is available.)], [ AC_MSG_RESULT(no) ] ) -# Define to 1 if std::system or ::wsystem (Windows) is available AC_DEFINE([HAVE_SYSTEM], [HAVE_STD__SYSTEM || HAVE_WSYSTEM], [std::system or ::wsystem]) LEVELDB_CPPFLAGS= @@ -1241,7 +1244,6 @@ if test x$use_pkgconfig = xyes; then m4_ifdef( [PKG_CHECK_MODULES], [ - PKG_CHECK_MODULES([CRYPTO], [libcrypto],,[AC_MSG_ERROR(libcrypto not found.)]) if test x$use_qr != xno; then BITCOIN_QT_CHECK([PKG_CHECK_MODULES([QR], [libqrencode], [have_qrencode=yes], [have_qrencode=no])]) fi @@ -1264,8 +1266,6 @@ if test x$use_pkgconfig = xyes; then ] ) else - AC_CHECK_HEADER([openssl/crypto.h],,AC_MSG_ERROR(libcrypto headers missing)) - AC_CHECK_LIB([crypto], [main],CRYPTO_LIBS=-lcrypto, AC_MSG_ERROR(libcrypto missing)) if test x$build_bitcoin_cli$build_bitcoind$bitcoin_enable_qt$use_tests != xnononono; then AC_CHECK_HEADER([event2/event.h],, AC_MSG_ERROR(libevent headers missing),) @@ -1465,18 +1465,16 @@ if test x$bitcoin_enable_qt != xno; then AC_MSG_CHECKING([whether to build GUI with support for QR codes]) if test x$have_qrencode = xno; then if test x$use_qr = xyes; then - AC_MSG_ERROR("QR support requested but cannot be built. use --without-qrencode") + AC_MSG_ERROR([QR support requested but cannot be built. Use --without-qrencode]) fi - AC_MSG_RESULT(no) + use_qr=no else if test x$use_qr != xno; then - AC_MSG_RESULT(yes) AC_DEFINE([USE_QRCODE],[1],[Define if QR support should be compiled in]) use_qr=yes - else - AC_MSG_RESULT(no) fi fi + AC_MSG_RESULT([$use_qr]) if test x$XGETTEXT = x; then AC_MSG_WARN("xgettext is required to update qt translations") @@ -1585,7 +1583,6 @@ AC_SUBST(TESTDEFS) AC_SUBST(LEVELDB_TARGET_FLAGS) AC_SUBST(MINIUPNPC_CPPFLAGS) AC_SUBST(MINIUPNPC_LIBS) -AC_SUBST(CRYPTO_LIBS) AC_SUBST(EVENT_LIBS) AC_SUBST(EVENT_PTHREADS_LIBS) AC_SUBST(ZMQ_LIBS) diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py index d8b684026c..0c59ab6239 100755 --- a/contrib/devtools/symbol-check.py +++ b/contrib/devtools/symbol-check.py @@ -4,8 +4,8 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. ''' A script to check that the (Linux) executables produced by gitian only contain -allowed gcc, glibc and libstdc++ version symbols. This makes sure they are -still compatible with the minimum supported Linux distribution versions. +allowed gcc and glibc version symbols. This makes sure they are still compatible +with the minimum supported Linux distribution versions. Example usage: @@ -16,30 +16,30 @@ import re import sys import os -# Debian 6.0.9 (Squeeze) has: +# Debian 8 (Jessie) EOL: 2020. https://wiki.debian.org/DebianReleases#Production_Releases # -# - g++ version 4.4.5 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=g%2B%2B) -# - libc version 2.11.3 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=libc6) -# - libstdc++ version 4.4.5 (https://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=libstdc%2B%2B6) +# - g++ version 4.9.2 (https://packages.debian.org/search?suite=jessie&arch=any&searchon=names&keywords=g%2B%2B) +# - libc version 2.19 (https://packages.debian.org/search?suite=jessie&arch=any&searchon=names&keywords=libc6) # -# Ubuntu 10.04.4 (Lucid Lynx) has: +# Ubuntu 16.04 (Xenial) EOL: 2024. https://wiki.ubuntu.com/Releases # -# - g++ version 4.4.3 (http://packages.ubuntu.com/search?keywords=g%2B%2B&searchon=names&suite=lucid§ion=all) -# - libc version 2.11.1 (http://packages.ubuntu.com/search?keywords=libc6&searchon=names&suite=lucid§ion=all) -# - libstdc++ version 4.4.3 (http://packages.ubuntu.com/search?suite=lucid§ion=all&arch=any&keywords=libstdc%2B%2B&searchon=names) +# - g++ version 5.3.1 (https://packages.ubuntu.com/search?keywords=g%2B%2B&searchon=names&suite=xenial§ion=all) +# - libc version 2.23.0 (https://packages.ubuntu.com/search?keywords=libc6&searchon=names&suite=xenial§ion=all) +# +# CentOS 7 EOL: 2024. https://wiki.centos.org/FAQ/General +# +# - g++ version 4.8.5 (http://mirror.centos.org/centos/7/os/x86_64/Packages/) +# - libc version 2.17 (http://mirror.centos.org/centos/7/os/x86_64/Packages/) # # Taking the minimum of these as our target. # -# According to GNU ABI document (http://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html) this corresponds to: -# GCC 4.4.0: GCC_4.4.0 -# GCC 4.4.2: GLIBCXX_3.4.13, CXXABI_1.3.3 -# (glibc) GLIBC_2_11 +# According to GNU ABI document (https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html) this corresponds to: +# GCC 4.8.5: GCC_4.8.0 +# (glibc) GLIBC_2_17 # MAX_VERSIONS = { -'GCC': (4,4,0), -'CXXABI': (1,3,3), -'GLIBCXX': (3,4,13), -'GLIBC': (2,11), +'GCC': (4,8,0), +'GLIBC': (2,17), 'LIBATOMIC': (1,0) } # See here for a description of _IO_stdin_used: @@ -47,7 +47,8 @@ MAX_VERSIONS = { # Ignore symbols that are exported as part of every executable IGNORE_EXPORTS = { -'_edata', '_end', '__end__', '_init', '__bss_start', '__bss_start__', '_bss_end__', '__bss_end__', '_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', +'environ', '_environ', '__environ', } READELF_CMD = os.getenv('READELF', '/usr/bin/readelf') CPPFILT_CMD = os.getenv('CPPFILT', '/usr/bin/c++filt') @@ -57,7 +58,6 @@ ALLOWED_LIBRARIES = { 'libgcc_s.so.1', # GCC base support 'libc.so.6', # C library 'libpthread.so.0', # threading -'libanl.so.1', # DNS resolve 'libm.so.6', # math library 'librt.so.1', # real-time (clock) 'libatomic.so.1', diff --git a/contrib/gitian-build.py b/contrib/gitian-build.py index 570d4906cd..e38fa6fcb0 100755 --- a/contrib/gitian-build.py +++ b/contrib/gitian-build.py @@ -51,10 +51,8 @@ def build(): os.chdir('gitian-builder') os.makedirs('inputs', exist_ok=True) - subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz']) - subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch']) - subprocess.check_call(["echo 'a8c4e9cafba922f89de0df1f2152e7be286aba73f78505169bc351a7938dd911 inputs/osslsigncode-Backports-to-1.7.1.patch' | sha256sum -c"], shell=True) - subprocess.check_call(["echo 'f9a8cdb38b9c309326764ebc937cba1523a3a751a7ab05df3ecc99d18ae466c9 inputs/osslsigncode-1.7.1.tar.gz' | sha256sum -c"], shell=True) + subprocess.check_call(['wget', '-O', 'inputs/osslsigncode-2.0.tar.gz', 'https://github.com/mtrojnar/osslsigncode/archive/2.0.tar.gz']) + subprocess.check_call(["echo '5a60e0a4b3e0b4d655317b2f12a810211c50242138322b16e7e01c6fbb89d92f inputs/osslsigncode-2.0.tar.gz' | sha256sum -c"], shell=True) subprocess.check_call(['make', '-C', '../bitcoin/depends', 'download', 'SOURCES_PATH=' + os.getcwd() + '/cache/common']) if args.linux: diff --git a/contrib/gitian-descriptors/gitian-linux.yml b/contrib/gitian-descriptors/gitian-linux.yml index c5b4ac1e83..2b86602a82 100644 --- a/contrib/gitian-descriptors/gitian-linux.yml +++ b/contrib/gitian-descriptors/gitian-linux.yml @@ -46,12 +46,12 @@ script: | FAKETIME_PROGS="date ar ranlib nm" HOST_CFLAGS="-O2 -g" HOST_CXXFLAGS="-O2 -g" - HOST_LDFLAGS=-static-libstdc++ + HOST_LDFLAGS_BASE="-static-libstdc++" export QT_RCC_TEST=1 export QT_RCC_SOURCE_DATE_OVERRIDE=1 export TZ="UTC" - export BUILD_DIR=`pwd` + export BUILD_DIR="$PWD" mkdir -p ${WRAP_DIR} if test -n "$GBUILD_CACHE_ENABLED"; then export SOURCES_PATH=${GBUILD_COMMON_CACHE} @@ -108,7 +108,7 @@ script: | rm -f ${WRAP_DIR}/${prog} cat << EOF > ${WRAP_DIR}/${prog} #!/usr/bin/env bash - REAL="`which -a ${prog}-8 | grep -v ${WRAP_DIR}/${prog} | head -1`" + REAL="$(which -a ${prog}-8 | grep -v ${WRAP_DIR}/${prog} | head -1)" for var in "\$@" do if [ "\$var" = "-m32" ]; then @@ -123,7 +123,7 @@ script: | done cd bitcoin - BASEPREFIX=`pwd`/depends + BASEPREFIX="${PWD}/depends" # Build dependencies for each host for i in $HOSTS; do EXTRA_INCLUDES="$EXTRA_INCLUDES_BASE/$i" @@ -142,10 +142,11 @@ script: | # Create the release tarball using (arbitrarily) the first host ./autogen.sh - CONFIG_SITE=${BASEPREFIX}/`echo "${HOSTS}" | awk '{print $1;}'`/share/config.site ./configure --prefix=/ + CONFIG_SITE=${BASEPREFIX}/$(echo "${HOSTS}" | awk '{print $1;}')/share/config.site ./configure --prefix=/ make dist - SOURCEDIST=`echo bitcoin-*.tar.gz` - DISTNAME=`echo ${SOURCEDIST} | sed 's/.tar.*//'` + SOURCEDIST=$(echo bitcoin-*.tar.gz) + DISTNAME=${SOURCEDIST/%.tar.gz} + # Correct tar file order mkdir -p temp pushd temp @@ -160,9 +161,16 @@ script: | # Extract the release tarball into a dir for each host and build for i in ${HOSTS}; do export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} + if [ "${i}" = "riscv64-linux-gnu" ]; then + # Workaround for https://bugs.launchpad.net/ubuntu/+source/gcc-8-cross-ports/+bug/1853740 + # TODO: remove this when no longer needed + HOST_LDFLAGS="${HOST_LDFLAGS_BASE} -Wl,-z,noexecstack" + else + HOST_LDFLAGS="${HOST_LDFLAGS_BASE}" + fi mkdir -p distsrc-${i} cd distsrc-${i} - INSTALLPATH=`pwd`/installed/${DISTNAME} + INSTALLPATH="${PWD}/installed/${DISTNAME}" mkdir -p ${INSTALLPATH} tar --strip-components=1 -xf ../$SOURCEDIST diff --git a/contrib/gitian-descriptors/gitian-osx-signer.yml b/contrib/gitian-descriptors/gitian-osx-signer.yml index 9957fc0514..2d49493641 100644 --- a/contrib/gitian-descriptors/gitian-osx-signer.yml +++ b/contrib/gitian-descriptors/gitian-osx-signer.yml @@ -17,7 +17,7 @@ script: | WRAP_DIR=$HOME/wrapped mkdir -p ${WRAP_DIR} - export PATH=`pwd`:$PATH + export PATH="$PWD":$PATH FAKETIME_PROGS="dmg genisoimage" # Create global faketime wrappers diff --git a/contrib/gitian-descriptors/gitian-osx.yml b/contrib/gitian-descriptors/gitian-osx.yml index 79b1b22cdf..d3a2645c53 100644 --- a/contrib/gitian-descriptors/gitian-osx.yml +++ b/contrib/gitian-descriptors/gitian-osx.yml @@ -37,7 +37,7 @@ script: | set -e -o pipefail WRAP_DIR=$HOME/wrapped - HOSTS="x86_64-apple-darwin14" + HOSTS="x86_64-apple-darwin16" CONFIGFLAGS="--enable-reduce-exports --disable-bench --disable-gui-tests GENISOIMAGE=$WRAP_DIR/genisoimage" FAKETIME_HOST_PROGS="" FAKETIME_PROGS="ar ranlib date dmg genisoimage" @@ -45,7 +45,7 @@ script: | export QT_RCC_TEST=1 export QT_RCC_SOURCE_DATE_OVERRIDE=1 export TZ="UTC" - export BUILD_DIR=`pwd` + export BUILD_DIR="$PWD" mkdir -p ${WRAP_DIR} if test -n "$GBUILD_CACHE_ENABLED"; then export SOURCES_PATH=${GBUILD_COMMON_CACHE} @@ -87,7 +87,7 @@ script: | export PATH=${WRAP_DIR}:${PATH} cd bitcoin - BASEPREFIX=`pwd`/depends + BASEPREFIX="${PWD}/depends" mkdir -p ${BASEPREFIX}/SDKs tar -C ${BASEPREFIX}/SDKs -xf ${BUILD_DIR}/MacOSX10.11.sdk.tar.gz @@ -105,10 +105,10 @@ script: | # Create the release tarball using (arbitrarily) the first host ./autogen.sh - CONFIG_SITE=${BASEPREFIX}/`echo "${HOSTS}" | awk '{print $1;}'`/share/config.site ./configure --prefix=/ + CONFIG_SITE=${BASEPREFIX}/$(echo "${HOSTS}" | awk '{print $1;}')/share/config.site ./configure --prefix=/ make dist - SOURCEDIST=`echo bitcoin-*.tar.gz` - DISTNAME=`echo ${SOURCEDIST} | sed 's/.tar.*//'` + SOURCEDIST=$(echo bitcoin-*.tar.gz) + DISTNAME=${SOURCEDIST/%.tar.gz} # Correct tar file order mkdir -p temp @@ -126,7 +126,7 @@ script: | export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} mkdir -p distsrc-${i} cd distsrc-${i} - INSTALLPATH=`pwd`/installed/${DISTNAME} + INSTALLPATH="${PWD}/installed/${DISTNAME}" mkdir -p ${INSTALLPATH} tar --strip-components=1 -xf ../$SOURCEDIST diff --git a/contrib/gitian-descriptors/gitian-win-signer.yml b/contrib/gitian-descriptors/gitian-win-signer.yml index 8076fe13af..70b7bb111d 100644 --- a/contrib/gitian-descriptors/gitian-win-signer.yml +++ b/contrib/gitian-descriptors/gitian-win-signer.yml @@ -6,37 +6,36 @@ suites: architectures: - "linux64" packages: -# Once osslsigncode supports openssl 1.1, we can change this back to libssl-dev -- "libssl1.0-dev" +- "libssl-dev" - "autoconf" +- "libtool" +- "pkg-config" remotes: - "url": "https://github.com/bitcoin-core/bitcoin-detached-sigs.git" "dir": "signature" files: -- "osslsigncode-1.7.1.tar.gz" -- "osslsigncode-Backports-to-1.7.1.patch" +- "osslsigncode-2.0.tar.gz" - "bitcoin-win-unsigned.tar.gz" script: | set -e -o pipefail - BUILD_DIR=`pwd` + BUILD_DIR="$PWD" SIGDIR=${BUILD_DIR}/signature/win UNSIGNED_DIR=${BUILD_DIR}/unsigned - echo "f9a8cdb38b9c309326764ebc937cba1523a3a751a7ab05df3ecc99d18ae466c9 osslsigncode-1.7.1.tar.gz" | sha256sum -c - echo "a8c4e9cafba922f89de0df1f2152e7be286aba73f78505169bc351a7938dd911 osslsigncode-Backports-to-1.7.1.patch" | sha256sum -c + echo "5a60e0a4b3e0b4d655317b2f12a810211c50242138322b16e7e01c6fbb89d92f osslsigncode-2.0.tar.gz" | sha256sum -c mkdir -p ${UNSIGNED_DIR} tar -C ${UNSIGNED_DIR} -xf bitcoin-win-unsigned.tar.gz - tar xf osslsigncode-1.7.1.tar.gz - cd osslsigncode-1.7.1 - patch -p1 < ${BUILD_DIR}/osslsigncode-Backports-to-1.7.1.patch + tar xf osslsigncode-2.0.tar.gz + cd osslsigncode-2.0 + ./autogen.sh ./configure --without-gsf --without-curl --disable-dependency-tracking make find ${UNSIGNED_DIR} -name "*-unsigned.exe" | while read i; do - INFILE="`basename "${i}"`" - OUTFILE="`echo "${INFILE}" | sed s/-unsigned//`" + INFILE="$(basename "${i}")" + OUTFILE="${INFILE/-unsigned}" ./osslsigncode attach-signature -in "${i}" -out "${OUTDIR}/${OUTFILE}" -sigin "${SIGDIR}/${INFILE}.pem" done diff --git a/contrib/gitian-descriptors/gitian-win.yml b/contrib/gitian-descriptors/gitian-win.yml index 4259a10e14..b772404ae5 100644 --- a/contrib/gitian-descriptors/gitian-win.yml +++ b/contrib/gitian-descriptors/gitian-win.yml @@ -40,7 +40,7 @@ script: | export QT_RCC_TEST=1 export QT_RCC_SOURCE_DATE_OVERRIDE=1 export TZ="UTC" - export BUILD_DIR=`pwd` + export BUILD_DIR="$PWD" mkdir -p ${WRAP_DIR} if test -n "$GBUILD_CACHE_ENABLED"; then export SOURCES_PATH=${GBUILD_COMMON_CACHE} @@ -97,7 +97,7 @@ script: | export PATH=${WRAP_DIR}:${PATH} cd bitcoin - BASEPREFIX=`pwd`/depends + BASEPREFIX="${PWD}/depends" # Build dependencies for each host for i in $HOSTS; do make ${MAKEOPTS} -C ${BASEPREFIX} HOST="${i}" @@ -112,10 +112,11 @@ script: | # Create the release tarball using (arbitrarily) the first host ./autogen.sh - CONFIG_SITE=${BASEPREFIX}/`echo "${HOSTS}" | awk '{print $1;}'`/share/config.site ./configure --prefix=/ + CONFIG_SITE=${BASEPREFIX}/$(echo "${HOSTS}" | awk '{print $1;}')/share/config.site ./configure --prefix=/ make dist - SOURCEDIST=`echo bitcoin-*.tar.gz` - DISTNAME=`echo ${SOURCEDIST} | sed 's/.tar.*//'` + SOURCEDIST=$(echo bitcoin-*.tar.gz) + DISTNAME=${SOURCEDIST/%.tar.gz} + # Correct tar file order mkdir -p temp pushd temp @@ -132,7 +133,7 @@ script: | export PATH=${BASEPREFIX}/${i}/native/bin:${ORIGPATH} mkdir -p distsrc-${i} cd distsrc-${i} - INSTALLPATH=`pwd`/installed/${DISTNAME} + INSTALLPATH="${PWD}/installed/${DISTNAME}" mkdir -p ${INSTALLPATH} tar --strip-components=1 -xf ../$SOURCEDIST diff --git a/contrib/gitian-keys/keys.txt b/contrib/gitian-keys/keys.txt index 9222a40b17..ba3036a89f 100644 --- a/contrib/gitian-keys/keys.txt +++ b/contrib/gitian-keys/keys.txt @@ -27,6 +27,7 @@ D62A803E27E7F43486035ADBBCD04D8E9CCCAC2A Paul Rabahy 37EC7D7B0A217CDB4B4E007E7FAB114267E4FA04 Peter Todd D762373D24904A3E42F33B08B9A408E71DAAC974 Pieter Wuille (Location: Leuven, Belgium) 133EAC179436F14A5CF1B794860FEB804E669320 Pieter Wuille +A8FC55F3B04BA3146F3492E79303B33A305224CB Sebastian Kung (TheCharlatan) ED9BDF7AD6A55E232E84524257FF9BDBCC301009 Sjors Provoost AEC1884398647C47413C1C3FB1179EB7347DC10D Warren Togami 79D00BAC68B56D422F945A8F8E3A8F3247DBCBBF Willy Ko diff --git a/contrib/valgrind.supp b/contrib/valgrind.supp index 0f6d993fd2..f232bb62c2 100644 --- a/contrib/valgrind.supp +++ b/contrib/valgrind.supp @@ -6,7 +6,14 @@ # Example use: # $ valgrind --suppressions=contrib/valgrind.supp src/test/test_bitcoin # $ valgrind --suppressions=contrib/valgrind.supp --leak-check=full \ -# --show-leak-kinds=all src/test/test_bitcoin --log_level=test_suite +# --show-leak-kinds=all src/test/test_bitcoin +# +# To create suppressions for found issues, use the --gen-suppressions=all option: +# $ valgrind --suppressions=contrib/valgrind.supp --leak-check=full \ +# --show-leak-kinds=all --gen-suppressions=all --show-reachable=yes \ +# --error-limit=no src/test/test_bitcoin +# +# Note that suppressions may depend on OS and/or library versions. { Suppress libstdc++ warning - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65434 Memcheck:Leak @@ -23,8 +30,57 @@ Memcheck:Cond obj:*/libdb_cxx-*.so fun:__log_put +} +{ + Suppress libdb warning + Memcheck:Param + pwrite64(buf) + fun:pwrite + fun:__os_io +} +{ + Suppress libdb warning + Memcheck:Cond + fun:__log_putr.isra.1 +} +{ + Suppress libdb warning + Memcheck:Param + pwrite64(buf) + fun:pwrite + fun:__os_io + obj:*/libdb_cxx-*.so +} +{ + Suppress uninitialized bytes warning in compat code + Memcheck:Param + ioctl(TCSET{S,SW,SF}) + fun:tcsetattr +} +{ + Suppress libdb warning + Memcheck:Leak + fun:malloc + ... obj:*/libdb_cxx-*.so - fun:__log_put_record +} +{ + Suppress leaks on init + Memcheck:Leak + ... + fun:_Z11AppInitMainR11NodeContext +} +{ + Suppress leaks on shutdown + Memcheck:Leak + ... + fun:_Z8ShutdownR11NodeContext +} +{ + Ignore GUI warning + Memcheck:Leak + ... + obj:/usr/lib64/libgdk-3.so.0.2404.7 } { Suppress leveldb warning (leveldb::InitModule()) - https://github.com/google/leveldb/issues/113 @@ -41,3 +97,90 @@ ... fun:_ZN7leveldbL14InitDefaultEnvEv } +{ + Suppress leveldb leak + Memcheck:Leak + match-leak-kinds: reachable + fun:_Znwm + ... + fun:_ZN7leveldb6DBImpl14BackgroundCallEv +} +{ + Suppress leveldb leak + Memcheck:Leak + fun:_Znwm + ... + fun:GetCoin +} +{ + Suppress wcsnrtombs glibc SSE4 warning (could be related: https://stroika.atlassian.net/browse/STK-626) + Memcheck:Addr16 + fun:__wcsnlen_sse4_1 + fun:wcsnrtombs +} +{ + Suppress wcsnrtombs warning (remove after removing boost::fs) + Memcheck:Cond + ... + fun:_ZN5boost10filesystem6detail11unique_pathERKNS0_4pathEPNS_6system10error_codeE + fun:unique_path +} +{ + Suppress boost warning + Memcheck:Leak + fun:_Znwm + ... + fun:_ZN5boost9unit_test9framework5state17execute_test_treeEmjPKNS2_23random_generator_helperE + fun:_ZN5boost9unit_test9framework3runEmb + fun:_ZN5boost9unit_test14unit_test_mainEPFbvEiPPc + fun:main +} +{ + Suppress boost::filesystem warning (fixed in boost 1.70: https://github.com/boostorg/filesystem/commit/bbe9d1771e5d679b3f10c42a58fc81f7e8c024a9) + Memcheck:Cond + fun:_ZN5boost10filesystem6detail28directory_iterator_incrementERNS0_18directory_iteratorEPNS_6system10error_codeE + ... + obj:*/libboost_filesystem.so.* +} +{ + Suppress boost::filesystem warning (could be related: https://stackoverflow.com/questions/9830182/function-boostfilesystemcomplete-being-reported-as-possible-memory-leak-by-v) + Memcheck:Leak + match-leak-kinds: reachable + fun:_Znwm + ... + fun:_ZN5boost10filesystem8absoluteERKNS0_4pathES3_ +} +{ + Suppress boost still reachable memory warning + Memcheck:Leak + match-leak-kinds: reachable + fun:_Znwm + ... + fun:_M_construct_aux<char*> + fun:_M_construct<char*> + fun:basic_string + fun:path +} +{ + Suppress LogInstance still reachable memory warning + Memcheck:Leak + match-leak-kinds: reachable + fun:_Znwm + fun:_Z11LogInstancev +} +{ + Suppress secp256k1_context_create still reachable memory warning + Memcheck:Leak + match-leak-kinds: reachable + fun:malloc + ... + fun:secp256k1_context_create +} +{ + Suppress BCLog::Logger::StartLogging() still reachable memory warning + Memcheck:Leak + match-leak-kinds: reachable + fun:malloc + ... + fun:_ZN5BCLog6Logger12StartLoggingEv +} diff --git a/contrib/verify-commits/README.md b/contrib/verify-commits/README.md index 1215962a16..e95a57586f 100644 --- a/contrib/verify-commits/README.md +++ b/contrib/verify-commits/README.md @@ -40,7 +40,7 @@ Import trusted keys In order to check the commit signatures, you must add the trusted PGP keys to your machine. [GnuPG](https://gnupg.org/) may be used to import the trusted keys by running the following command: ```sh -gpg --recv-keys $(<contrib/verify-commits/trusted-keys) +gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys $(<contrib/verify-commits/trusted-keys) ``` Key expiry/revocation diff --git a/depends/.gitignore b/depends/.gitignore index 72734102c5..19c506ce54 100644 --- a/depends/.gitignore +++ b/depends/.gitignore @@ -8,5 +8,7 @@ i686* mips* arm* aarch64* +powerpc* riscv32* riscv64* +s390x* diff --git a/depends/Makefile b/depends/Makefile index cec99777ff..f81bc3001b 100644 --- a/depends/Makefile +++ b/depends/Makefile @@ -9,6 +9,7 @@ WORK_PATH = $(BASEDIR)/work BASE_CACHE ?= $(BASEDIR)/built SDK_PATH ?= $(BASEDIR)/SDKs NO_QT ?= +NO_QR ?= RAPIDCHECK ?= NO_WALLET ?= NO_ZMQ ?= @@ -100,7 +101,10 @@ $(host_arch)_$(host_os)_id_string+=$(shell $(host_CXX) --version 2>/dev/null) $(host_arch)_$(host_os)_id_string+=$(shell $(host_RANLIB) --version 2>/dev/null) $(host_arch)_$(host_os)_id_string+=$(shell $(host_STRIP) --version 2>/dev/null) -qt_packages_$(NO_QT) = $(qt_packages) $(qt_$(host_os)_packages) $(qt_$(host_arch)_$(host_os)_packages) +qrencode_packages_$(NO_QR) = $(qrencode_packages) + +qt_packages_$(NO_QT) = $(qt_packages) $(qt_$(host_os)_packages) $(qt_$(host_arch)_$(host_os)_packages) $(qrencode_packages_) + wallet_packages_$(NO_WALLET) = $(wallet_packages) upnp_packages_$(NO_UPNP) = $(upnp_packages) zmq_packages_$(NO_ZMQ) = $(zmq_packages) @@ -154,6 +158,7 @@ $(host_prefix)/share/config.site : config.site.in $(host_prefix)/.stamp_$(final_ -e 's|@LDFLAGS@|$(strip $(host_LDFLAGS) $(host_$(release_type)_LDFLAGS))|' \ -e 's|@allow_host_packages@|$(ALLOW_HOST_PACKAGES)|' \ -e 's|@no_qt@|$(NO_QT)|' \ + -e 's|@no_qr@|$(NO_QR)|' \ -e 's|@no_zmq@|$(NO_ZMQ)|' \ -e 's|@no_wallet@|$(NO_WALLET)|' \ -e 's|@no_upnp@|$(NO_UPNP)|' \ @@ -186,7 +191,7 @@ $(host_prefix)/share/config.site: check-packages check-packages: check-sources clean-all: clean - @rm -rf $(SOURCES_PATH) x86_64* i686* mips* arm* aarch64* riscv32* riscv64* + @rm -rf $(SOURCES_PATH) x86_64* i686* mips* arm* aarch64* powerpc* riscv32* riscv64* s390x* clean: @rm -rf $(WORK_PATH) $(BASE_CACHE) $(BUILD) diff --git a/depends/README.md b/depends/README.md index e5e2a8a653..93f619983f 100644 --- a/depends/README.md +++ b/depends/README.md @@ -12,24 +12,27 @@ For example: make HOST=x86_64-w64-mingw32 -j4 -**Bitcoin's configure script by default will ignore the depends output.** In +**Bitcoin Core's configure script by default will ignore the depends output.** In order for it to pick up libraries, tools, and settings from the depends build, you must point it at the appropriate `--prefix` directory generated by the build. In the above example, a prefix dir named x86_64-w64-mingw32 will be created. To use it for Bitcoin: - ./configure --prefix=`pwd`/depends/x86_64-w64-mingw32 + ./configure --prefix=$PWD/depends/x86_64-w64-mingw32 Common `host-platform-triplets` for cross compilation are: - `i686-pc-linux-gnu` for Linux 32 bit - `x86_64-pc-linux-gnu` for x86 Linux - `x86_64-w64-mingw32` for Win64 -- `x86_64-apple-darwin14` for macOS +- `x86_64-apple-darwin16` for macOS - `arm-linux-gnueabihf` for Linux ARM 32 bit - `aarch64-linux-gnu` for Linux ARM 64 bit +- `powerpc64-linux-gnu` for Linux POWER 64-bit (big endian) +- `powerpc64le-linux-gnu` for Linux POWER 64-bit (little endian) - `riscv32-linux-gnu` for Linux RISC-V 32 bit - `riscv64-linux-gnu` for Linux RISC-V 64 bit +- `s390x-linux-gnu` for Linux S390X - `armv7a-linux-android` for Android ARM 32 bit - `aarch64-linux-android` for Android ARM 64 bit - `i686-linux-android` for Android x86 32 bit @@ -61,6 +64,10 @@ For linux AARCH64 cross compilation: sudo apt-get install g++-aarch64-linux-gnu binutils-aarch64-linux-gnu +For linux POWER 64-bit cross compilation (there are no packages for 32-bit): + + sudo apt-get install g++-powerpc64-linux-gnu binutils-powerpc64-linux-gnu g++-powerpc64le-linux-gnu binutils-powerpc64le-linux-gnu + For linux RISC-V 64-bit cross compilation (there are no packages for 32-bit): sudo apt-get install g++-riscv64-linux-gnu binutils-riscv64-linux-gnu @@ -68,6 +75,10 @@ For linux RISC-V 64-bit cross compilation (there are no packages for 32-bit): RISC-V known issue: gcc-7.3.0 and gcc-7.3.1 result in a broken `test_bitcoin` executable (see https://github.com/bitcoin/bitcoin/pull/13543), this is apparently fixed in gcc-8.1.0. +For linux S390X cross compilation: + + sudo apt-get install g++-s390x-linux-gnu binutils-s390x-linux-gnu + ### Dependency Options The following can be set when running make: make FOO=bar @@ -76,6 +87,7 @@ The following can be set when running make: make FOO=bar SDK_PATH: Path where sdk's can be found (used by macOS) FALLBACK_DOWNLOAD_PATH: If a source file can't be fetched, try here before giving up NO_QT: Don't download/build/cache qt and its dependencies + NO_QR: Don't download/build/cache packages needed for enabling qrencode NO_ZMQ: Don't download/build/cache packages needed for enabling zeromq 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 diff --git a/depends/config.site.in b/depends/config.site.in index dee568bc25..c5731e5269 100644 --- a/depends/config.site.in +++ b/depends/config.site.in @@ -17,6 +17,10 @@ if test -z $with_qt_bindir && test -z "@no_qt@"; then with_qt_bindir=$depends_prefix/native/bin fi +if test -z $with_qrencode && test -n "@no_qr@"; then + with_qrencode=no +fi + if test -z $enable_wallet && test -n "@no_wallet@"; then enable_wallet=no fi diff --git a/depends/hosts/darwin.mk b/depends/hosts/darwin.mk index a1c943d60b..1f88c209cf 100644 --- a/depends/hosts/darwin.mk +++ b/depends/hosts/darwin.mk @@ -1,4 +1,4 @@ -OSX_MIN_VERSION=10.10 +OSX_MIN_VERSION=10.12 OSX_SDK_VERSION=10.11 OSX_SDK=$(SDK_PATH)/MacOSX$(OSX_SDK_VERSION).sdk LD64_VERSION=253.9 diff --git a/depends/packages/libevent.mk b/depends/packages/libevent.mk index b4333abaf0..eb45e14f6f 100644 --- a/depends/packages/libevent.mk +++ b/depends/packages/libevent.mk @@ -1,19 +1,12 @@ package=libevent -$(package)_version=2.1.8-stable +$(package)_version=2.1.11-stable $(package)_download_path=https://github.com/libevent/libevent/archive/ $(package)_file_name=release-$($(package)_version).tar.gz -$(package)_sha256_hash=316ddb401745ac5d222d7c529ef1eada12f58f6376a66c1118eee803cb70f83d -$(package)_patches=fix_android_arc4random_addrandom.patch +$(package)_sha256_hash=229393ab2bf0dc94694f21836846b424f3532585bac3468738b7bf752c03901e -ifneq (,$(findstring android,$(host))) - define $(package)_preprocess_cmds - ./autogen.sh && patch -p1 < $($(package)_patch_dir)/fix_android_arc4random_addrandom.patch - endef -else - define $(package)_preprocess_cmds - ./autogen.sh - endef -endif +define $(package)_preprocess_cmds + ./autogen.sh +endef define $(package)_set_vars $(package)_config_opts=--disable-shared --disable-openssl --disable-libevent-regress --disable-samples diff --git a/depends/packages/openssl.mk b/depends/packages/openssl.mk deleted file mode 100644 index e3b3647dd9..0000000000 --- a/depends/packages/openssl.mk +++ /dev/null @@ -1,88 +0,0 @@ -package=openssl -$(package)_version=1.0.1k -$(package)_download_path=https://www.openssl.org/source -$(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=8f9faeaebad088e772f4ef5e38252d472be4d878c6b3a2718c10a4fcebe7a41c -$(package)_patches=0001-Add-OpenSSL-termios-fix-for-musl-libc.patch - -define $(package)_set_vars -$(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" -$(package)_config_opts=--prefix=$(host_prefix) --openssldir=$(host_prefix)/etc/openssl -$(package)_config_opts+=no-camellia -$(package)_config_opts+=no-capieng -$(package)_config_opts+=no-cast -$(package)_config_opts+=no-comp -$(package)_config_opts+=no-dso -$(package)_config_opts+=no-dtls1 -$(package)_config_opts+=no-ec_nistp_64_gcc_128 -$(package)_config_opts+=no-gost -$(package)_config_opts+=no-gmp -$(package)_config_opts+=no-heartbeats -$(package)_config_opts+=no-idea -$(package)_config_opts+=no-jpake -$(package)_config_opts+=no-krb5 -$(package)_config_opts+=no-libunbound -$(package)_config_opts+=no-md2 -$(package)_config_opts+=no-mdc2 -$(package)_config_opts+=no-rc4 -$(package)_config_opts+=no-rc5 -$(package)_config_opts+=no-rdrand -$(package)_config_opts+=no-rfc3779 -$(package)_config_opts+=no-rsax -$(package)_config_opts+=no-sctp -$(package)_config_opts+=no-seed -$(package)_config_opts+=no-sha0 -$(package)_config_opts+=no-shared -$(package)_config_opts+=no-ssl-trace -$(package)_config_opts+=no-ssl2 -$(package)_config_opts+=no-ssl3 -$(package)_config_opts+=no-static_engine -$(package)_config_opts+=no-store -$(package)_config_opts+=no-unit-test -$(package)_config_opts+=no-weak-ssl-ciphers -$(package)_config_opts+=no-whirlpool -$(package)_config_opts+=no-zlib -$(package)_config_opts+=no-zlib-dynamic -$(package)_config_opts+=$($(package)_cflags) $($(package)_cppflags) -$(package)_config_opts_linux=-fPIC -Wa,--noexecstack -$(package)_config_opts_x86_64_linux=linux-x86_64 -$(package)_config_opts_i686_linux=linux-generic32 -$(package)_config_opts_arm_linux=linux-generic32 -$(package)_config_opts_armv7l_linux=linux-generic32 -$(package)_config_opts_aarch64_linux=linux-generic64 -$(package)_config_opts_mipsel_linux=linux-generic32 -$(package)_config_opts_mips_linux=linux-generic32 -$(package)_config_opts_powerpc_linux=linux-generic32 -$(package)_config_opts_riscv32_linux=linux-generic32 -$(package)_config_opts_riscv64_linux=linux-generic64 -$(package)_config_opts_x86_64_darwin=darwin64-x86_64-cc -$(package)_config_opts_x86_64_mingw32=mingw64 -$(package)_config_opts_i686_mingw32=mingw -$(package)_config_opts_android=-fPIC -$(package)_config_opts_aarch64_android=linux-generic64 -$(package)_config_opts_x86_64_android=linux-generic64 -$(package)_config_opts_armv7a_android=linux-generic32 -$(package)_config_opts_i686_android=linux-generic32 -endef - -define $(package)_preprocess_cmds - patch -p1 < $($(package)_patch_dir)/0001-Add-OpenSSL-termios-fix-for-musl-libc.patch && \ - sed -i.old "/define DATE/d" util/mkbuildinf.pl && \ - sed -i.old "s|engines apps test|engines|" Makefile.org -endef - -define $(package)_config_cmds - ./Configure $($(package)_config_opts) -endef - -define $(package)_build_cmds - $(MAKE) -j1 build_crypto libcrypto.pc libssl.pc openssl.pc -endef - -define $(package)_stage_cmds - $(MAKE) INSTALL_PREFIX=$($(package)_staging_dir) -j1 install_sw -endef - -define $(package)_postprocess_cmds - rm -rf share bin etc -endef diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index 09734c7e6f..d8ccb208cc 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -1,6 +1,8 @@ -packages:=boost openssl libevent +packages:=boost libevent -qt_packages = qrencode zlib +qt_packages = zlib + +qrencode_packages = qrencode qt_linux_packages:=qt expat libxcb xcb_proto libXau xproto freetype fontconfig qt_android_packages=qt diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index a0f7cda2c0..efa76965d5 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -25,7 +25,6 @@ $(package)_config_opts_debug = -debug $(package)_config_opts += -bindir $(build_prefix)/bin $(package)_config_opts += -c++std c++11 $(package)_config_opts += -confirm-license -$(package)_config_opts += -dbus-runtime $(package)_config_opts += -hostprefix $(build_prefix) $(package)_config_opts += -no-compile-examples $(package)_config_opts += -no-cups @@ -40,12 +39,14 @@ $(package)_config_opts += -no-iconv $(package)_config_opts += -no-kms $(package)_config_opts += -no-linuxfb $(package)_config_opts += -no-libjpeg +$(package)_config_opts += -no-libproxy $(package)_config_opts += -no-libudev $(package)_config_opts += -no-mtdev $(package)_config_opts += -no-openssl $(package)_config_opts += -no-openvg $(package)_config_opts += -no-reduce-relocations $(package)_config_opts += -no-qml-debug +$(package)_config_opts += -no-sctp $(package)_config_opts += -no-securetransport $(package)_config_opts += -no-sql-db2 $(package)_config_opts += -no-sql-ibase @@ -56,12 +57,13 @@ $(package)_config_opts += -no-sql-odbc $(package)_config_opts += -no-sql-psql $(package)_config_opts += -no-sql-sqlite $(package)_config_opts += -no-sql-sqlite2 +$(package)_config_opts += -no-system-proxies $(package)_config_opts += -no-use-gold-linker $(package)_config_opts += -no-xinput2 $(package)_config_opts += -nomake examples $(package)_config_opts += -nomake tests $(package)_config_opts += -opensource -$(package)_config_opts += -optimized-qmake +$(package)_config_opts += -optimized-tools $(package)_config_opts += -pch $(package)_config_opts += -pkg-config $(package)_config_opts += -prefix $(host_prefix) @@ -80,9 +82,12 @@ $(package)_config_opts += -no-feature-dial $(package)_config_opts += -no-feature-filesystemwatcher $(package)_config_opts += -no-feature-fontcombobox $(package)_config_opts += -no-feature-ftp +$(package)_config_opts += -no-feature-http $(package)_config_opts += -no-feature-image_heuristic_mask $(package)_config_opts += -no-feature-keysequenceedit $(package)_config_opts += -no-feature-lcdnumber +$(package)_config_opts += -no-feature-networkdiskcache +$(package)_config_opts += -no-feature-networkproxy $(package)_config_opts += -no-feature-pdf $(package)_config_opts += -no-feature-printdialog $(package)_config_opts += -no-feature-printer @@ -90,6 +95,7 @@ $(package)_config_opts += -no-feature-printpreviewdialog $(package)_config_opts += -no-feature-printpreviewwidget $(package)_config_opts += -no-feature-regularexpression $(package)_config_opts += -no-feature-sessionmanager +$(package)_config_opts += -no-feature-socks5 $(package)_config_opts += -no-feature-sql $(package)_config_opts += -no-feature-statemachine $(package)_config_opts += -no-feature-syntaxhighlighter @@ -105,8 +111,11 @@ $(package)_config_opts += -no-feature-vnc $(package)_config_opts += -no-feature-wizard $(package)_config_opts += -no-feature-xml +$(package)_config_opts_darwin = -no-dbus +$(package)_config_opts_darwin += -no-opengl + ifneq ($(build_os),darwin) -$(package)_config_opts_darwin = -xplatform macx-clang-linux +$(package)_config_opts_darwin += -xplatform macx-clang-linux $(package)_config_opts_darwin += -device-option MAC_SDK_PATH=$(OSX_SDK) $(package)_config_opts_darwin += -device-option MAC_SDK_VERSION=$(OSX_SDK_VERSION) $(package)_config_opts_darwin += -device-option CROSS_COMPILE="$(host)-" @@ -122,12 +131,20 @@ $(package)_config_opts_linux += -no-feature-xlib $(package)_config_opts_linux += -system-freetype $(package)_config_opts_linux += -fontconfig $(package)_config_opts_linux += -no-opengl +$(package)_config_opts_linux += -dbus-runtime $(package)_config_opts_arm_linux += -platform linux-g++ -xplatform bitcoin-linux-g++ $(package)_config_opts_i686_linux = -xplatform linux-g++-32 $(package)_config_opts_x86_64_linux = -xplatform linux-g++-64 $(package)_config_opts_aarch64_linux = -xplatform linux-aarch64-gnu-g++ +$(package)_config_opts_powerpc64_linux = -platform linux-g++ -xplatform bitcoin-linux-g++ +$(package)_config_opts_powerpc64le_linux = -platform linux-g++ -xplatform bitcoin-linux-g++ $(package)_config_opts_riscv64_linux = -platform linux-g++ -xplatform bitcoin-linux-g++ -$(package)_config_opts_mingw32 = -no-opengl -xplatform win32-g++ -device-option CROSS_COMPILE="$(host)-" +$(package)_config_opts_s390x_linux = -platform linux-g++ -xplatform bitcoin-linux-g++ + +$(package)_config_opts_mingw32 = -no-opengl +$(package)_config_opts_mingw32 += -no-dbus +$(package)_config_opts_mingw32 += -xplatform win32-g++ +$(package)_config_opts_mingw32 += -device-option CROSS_COMPILE="$(host)-" $(package)_config_opts_android = -xplatform android-clang $(package)_config_opts_android += -android-sdk $(ANDROID_SDK) @@ -137,6 +154,7 @@ $(package)_config_opts_android += -device-option CROSS_COMPILE="$(host)-" $(package)_config_opts_android += -egl $(package)_config_opts_android += -qpa xcb $(package)_config_opts_android += -no-eglfs +$(package)_config_opts_android += -no-dbus $(package)_config_opts_android += -opengl es2 $(package)_config_opts_android += -qt-freetype $(package)_config_opts_android += -no-fontconfig @@ -202,6 +220,8 @@ define $(package)_preprocess_cmds sed -i.old "s|QMAKE_CFLAGS += |!host_build: QMAKE_CFLAGS = $($(package)_cflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \ sed -i.old "s|QMAKE_CXXFLAGS += |!host_build: QMAKE_CXXFLAGS = $($(package)_cxxflags) $($(package)_cppflags) |" qtbase/mkspecs/win32-g++/qmake.conf && \ sed -i.old "0,/^QMAKE_LFLAGS_/s|^QMAKE_LFLAGS_|!host_build: QMAKE_LFLAGS = $($(package)_ldflags)\n&|" qtbase/mkspecs/win32-g++/qmake.conf && \ + sed -i.old "s|QMAKE_CC = clang|QMAKE_CC = $($(package)_cc)|" qtbase/mkspecs/common/clang.conf && \ + sed -i.old "s|QMAKE_CXX = clang++|QMAKE_CXX = $($(package)_cxx)|" qtbase/mkspecs/common/clang.conf && \ sed -i.old "s/LIBRARY_PATH/(CROSS_)?\0/g" qtbase/mkspecs/features/toolchain.prf endef diff --git a/depends/packages/xcb_proto.mk b/depends/packages/xcb_proto.mk index 85d01ecd2f..01203a0718 100644 --- a/depends/packages/xcb_proto.mk +++ b/depends/packages/xcb_proto.mk @@ -4,11 +4,6 @@ $(package)_download_path=https://xcb.freedesktop.org/dist $(package)_file_name=xcb-proto-$($(package)_version).tar.bz2 $(package)_sha256_hash=7ef40ddd855b750bc597d2a435da21e55e502a0fefa85b274f2c922800baaf05 -define $(package)_set_vars - $(package)_config_opts=--disable-shared --enable-option-checking - $(package)_config_opts_linux=--with-pic -endef - define $(package)_config_cmds $($(package)_autoconf) endef diff --git a/depends/patches/libevent/fix_android_arc4random_addrandom.patch b/depends/patches/libevent/fix_android_arc4random_addrandom.patch deleted file mode 100644 index 5bcc64bef6..0000000000 --- a/depends/patches/libevent/fix_android_arc4random_addrandom.patch +++ /dev/null @@ -1,68 +0,0 @@ -From cadae3ab7abf45e61ecae8aac39d97d1f3cbd336 Mon Sep 17 00:00:00 2001 -From: Lawrence Nahum <lawrence@greenaddress.it> -Date: Sun, 3 Dec 2017 22:56:09 +0100 -Subject: [PATCH] fixup - ---- - configure.ac | 1 + - evutil_rand.c | 3 +++ - include/event2/util.h | 4 ++-- - 3 files changed, 6 insertions(+), 2 deletions(-) - -diff --git a/configure.ac b/configure.ac -index 7528d37..3bb2121 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -341,6 +341,7 @@ dnl Checks for library functions. - AC_CHECK_FUNCS([ \ - accept4 \ - arc4random \ -+ arc4random_addrandom \ - arc4random_buf \ - eventfd \ - epoll_create1 \ -diff --git a/evutil_rand.c b/evutil_rand.c -index 046a14b..3f0bf2c 100644 ---- a/evutil_rand.c -+++ b/evutil_rand.c -@@ -191,6 +191,7 @@ evutil_secure_rng_get_bytes(void *buf, size_t n) - { - ev_arc4random_buf(buf, n); - } -+#ifdef HAVE_ARC4RANDOM_ADDRANDOM - - void - evutil_secure_rng_add_bytes(const char *buf, size_t n) -@@ -199,6 +200,8 @@ evutil_secure_rng_add_bytes(const char *buf, size_t n) - n>(size_t)INT_MAX ? INT_MAX : (int)n); - } - -+#endif -+ - void - evutil_free_secure_rng_globals_(void) - { -diff --git a/include/event2/util.h b/include/event2/util.h -index dd4bbb6..a9a169d 100644 ---- a/include/event2/util.h -+++ b/include/event2/util.h -@@ -841,7 +841,7 @@ int evutil_secure_rng_init(void); - */ - EVENT2_EXPORT_SYMBOL - int evutil_secure_rng_set_urandom_device_file(char *fname); -- -+#ifdef HAVE_ARC4RANDOM_ADDRANDOM - /** Seed the random number generator with extra random bytes. - - You should almost never need to call this function; it should be -@@ -858,7 +858,7 @@ int evutil_secure_rng_set_urandom_device_file(char *fname); - */ - EVENT2_EXPORT_SYMBOL - void evutil_secure_rng_add_bytes(const char *dat, size_t datlen); -- -+#endif - #ifdef __cplusplus - } - #endif --- -2.14.3 diff --git a/depends/patches/openssl/0001-Add-OpenSSL-termios-fix-for-musl-libc.patch b/depends/patches/openssl/0001-Add-OpenSSL-termios-fix-for-musl-libc.patch deleted file mode 100644 index 003099bdc2..0000000000 --- a/depends/patches/openssl/0001-Add-OpenSSL-termios-fix-for-musl-libc.patch +++ /dev/null @@ -1,17 +0,0 @@ -diff --git a/crypto/ui/ui_openssl.c b/crypto/ui/ui_openssl.c -index a38c758..d99edc2 100644 ---- a/crypto/ui/ui_openssl.c -+++ b/crypto/ui/ui_openssl.c -@@ -190,9 +190,9 @@ - # undef SGTTY - #endif - --#if defined(linux) && !defined(TERMIO) --# undef TERMIOS --# define TERMIO -+#if defined(linux) -+# define TERMIOS -+# undef TERMIO - # undef SGTTY - #endif - diff --git a/doc/README.md b/doc/README.md index d3017de2ab..4dd21c4620 100644 --- a/doc/README.md +++ b/doc/README.md @@ -54,7 +54,7 @@ The Bitcoin repo's [root README](/README.md) contains relevant information on th - [Productivity Notes](productivity.md) - [Release Notes](release-notes.md) - [Release Process](release-process.md) -- [Source Code Documentation (External Link)](https://dev.visucore.com/bitcoin/doxygen/) +- [Source Code Documentation (External Link)](https://doxygen.bitcoincore.org/) - [Translation Process](translation_process.md) - [Translation Strings Policy](translation_strings_policy.md) - [JSON-RPC Interface](JSON-RPC-interface.md) @@ -84,5 +84,3 @@ The Bitcoin repo's [root README](/README.md) contains relevant information on th License --------------------- Distributed under the [MIT software license](/COPYING). -This product includes software developed by the OpenSSL Project for use in the [OpenSSL Toolkit](https://www.openssl.org/). This product includes -cryptographic software written by Eric Young ([eay@cryptsoft.com](mailto:eay@cryptsoft.com)), and UPnP software written by Thomas Bernard. diff --git a/doc/build-freebsd.md b/doc/build-freebsd.md index d22b6e8383..4831623504 100644 --- a/doc/build-freebsd.md +++ b/doc/build-freebsd.md @@ -11,7 +11,7 @@ This guide does not contain instructions for building the GUI. You will need the following dependencies, which can be installed as root via pkg: ```shell -pkg install autoconf automake boost-libs git gmake libevent libtool openssl pkgconf +pkg install autoconf automake boost-libs git gmake libevent libtool pkgconf git clone https://github.com/bitcoin/bitcoin.git ``` diff --git a/doc/build-osx.md b/doc/build-osx.md index 9942449bf6..bf655538c7 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -19,7 +19,7 @@ Then install [Homebrew](https://brew.sh). ## Dependencies ```shell -brew install automake berkeley-db4 libtool boost miniupnpc openssl pkg-config python qt libevent qrencode +brew install automake berkeley-db4 libtool boost miniupnpc pkg-config python qt libevent qrencode ``` See [dependencies.md](dependencies.md) for a complete overview. @@ -110,7 +110,7 @@ tail -f $HOME/Library/Application\ Support/Bitcoin/debug.log ``` ## Notes -* Tested on OS X 10.10 Yosemite through macOS 10.14 Mojave on 64-bit Intel +* Tested on OS X 10.12 Sierra through macOS 10.15 Catalina on 64-bit Intel processors only. * Building with downloaded Qt binaries is not officially supported. See the notes in [#7714](https://github.com/bitcoin/bitcoin/issues/7714). diff --git a/doc/build-unix.md b/doc/build-unix.md index d048bdeff5..e799e709fa 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -33,7 +33,6 @@ These dependencies are required: Library | Purpose | Description ------------|------------------|---------------------- - libssl | Crypto | Random Number Generation, Elliptic Curve Cryptography libboost | Utility | Library for threading, data structures, etc libevent | Networking | OS independent asynchronous networking @@ -81,7 +80,7 @@ Build requirements: Now, you can either build from self-compiled [depends](/depends/README.md) or install the required dependencies: - sudo apt-get install libssl-dev libevent-dev libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev + sudo apt-get install libevent-dev libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-test-dev libboost-thread-dev BerkeleyDB is required for the wallet. @@ -127,7 +126,7 @@ built by default. Build requirements: - sudo dnf install gcc-c++ libtool make autoconf automake openssl-devel libevent-devel boost-devel libdb4-devel libdb4-cxx-devel python3 + sudo dnf install gcc-c++ libtool make autoconf automake libevent-devel boost-devel libdb4-devel libdb4-cxx-devel python3 Optional (see `--with-miniupnpc` and `--enable-upnp-default`): diff --git a/doc/dependencies.md b/doc/dependencies.md index dc88626761..b739881a7a 100644 --- a/doc/dependencies.md +++ b/doc/dependencies.md @@ -13,11 +13,10 @@ These are the dependencies currently used by Bitcoin Core. You can find instruct | FreeType | [2.7.1](https://download.savannah.gnu.org/releases/freetype) | | No | | | | GCC | | [4.8+](https://gcc.gnu.org/) (C++11 support) | | | | | HarfBuzz-NG | | | | | | -| libevent | [2.1.8-stable](https://github.com/libevent/libevent/releases) | 2.0.22 | No | | | +| libevent | [2.1.11-stable](https://github.com/libevent/libevent/releases) | 2.0.22 | No | | | | libpng | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) | | librsvg | | | | | | -| MiniUPnPc | [2.0.20180203](http://miniupnp.free.fr/files) | | No | | | -| OpenSSL | [1.0.1k](https://www.openssl.org/source) | | Yes | | | +| MiniUPnPc | [2.0.20180203](https://miniupnp.tuxfamily.org/files) | | No | | | | PCRE | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk) | | Python (tests) | | [3.5](https://www.python.org/downloads) | | | | | qrencode | [3.4.4](https://fukuchi.org/works/qrencode) | | No | | | diff --git a/doc/developer-notes.md b/doc/developer-notes.md index e7fd8102a4..b50f552e92 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -12,12 +12,13 @@ Developer Notes - [Development tips and tricks](#development-tips-and-tricks) - [Compiling for debugging](#compiling-for-debugging) - [Compiling for gprof profiling](#compiling-for-gprof-profiling) - - [debug.log](#debuglog) + - [`debug.log`](#debuglog) - [Testnet and Regtest modes](#testnet-and-regtest-modes) - [DEBUG_LOCKORDER](#debug_lockorder) - [Valgrind suppressions file](#valgrind-suppressions-file) - [Compiling for test coverage](#compiling-for-test-coverage) - [Performance profiling with perf](#performance-profiling-with-perf) + - [Sanitizers](#sanitizers) - [Locking/mutex usage notes](#lockingmutex-usage-notes) - [Threads](#threads) - [Ignoring IDE/editor files](#ignoring-ideeditor-files) @@ -35,6 +36,7 @@ Developer Notes - [GUI](#gui) - [Subtrees](#subtrees) - [Scripted diffs](#scripted-diffs) + - [Suggestions and examples](#suggestions-and-examples) - [Release notes](#release-notes) - [RPC interface guidelines](#rpc-interface-guidelines) @@ -62,7 +64,7 @@ tool to clean up patches automatically before submission. - Braces on the same line for everything else. - 4 space indentation (no tabs) for every block except namespaces. - No indentation for `public`/`protected`/`private` or for `namespace`. - - No extra spaces inside parenthesis; don't do ( this ). + - No extra spaces inside parenthesis; don't do `( this )`. - No space after function names; one space after `if`, `for` and `while`. - If an `if` only has a single-statement `then`-clause, it can appear on the same line as the `if`, without braces. In every other case, @@ -76,7 +78,7 @@ code. separate words (snake_case). - Class member variables have a `m_` prefix. - Global variables have a `g_` prefix. - - Constant names are all uppercase, and use `_` to separate words. + - Compile-time constant names are all uppercase, and use `_` to separate words. - Class names, function names, and method names are UpperCamelCase (PascalCase). Do not prefix class names with `C`. - Test suite naming convention: The Boost test suite in file @@ -207,15 +209,15 @@ produce better debugging builds. Run configure with the `--enable-gprof` option, then make. -### debug.log +### `debug.log` -If the code is behaving strangely, take a look in the debug.log file in the data directory; +If the code is behaving strangely, take a look in the `debug.log` file in the data directory; error and debugging messages are written there. The `-debug=...` command-line option controls debugging; running with just `-debug` or `-debug=1` will turn -on all categories (and give you a very large debug.log file). +on all categories (and give you a very large `debug.log` file). -The Qt code routes `qDebug()` output to debug.log under category "qt": run with `-debug=qt` +The Qt code routes `qDebug()` output to `debug.log` under category "qt": run with `-debug=qt` to see it. ### Testnet and Regtest modes @@ -233,7 +235,7 @@ Bitcoin Core is a multi-threaded application, and deadlocks or other multi-threading bugs can be very difficult to track down. The `--enable-debug` configure option adds `-DDEBUG_LOCKORDER` to the compiler flags. This inserts run-time checks to keep track of which locks are held and adds warnings to the -debug.log file if inconsistencies are detected. +`debug.log` file if inconsistencies are detected. ### Valgrind suppressions file @@ -275,8 +277,7 @@ the functional test framework. Perf can observe a running process and sample (at some frequency) where its execution is. Perf installation is contingent on which kernel version you're running; see -[this StackExchange -thread](https://askubuntu.com/questions/50145/how-to-install-perf-monitoring-tool) +[this thread](https://askubuntu.com/questions/50145/how-to-install-perf-monitoring-tool) for specific instructions. Certain kernel parameters may need to be set for perf to be able to inspect the @@ -311,7 +312,7 @@ or using a graphical tool like [Hotspot](https://github.com/KDAB/hotspot). See the functional test documentation for how to invoke perf within tests. -**Sanitizers** +### Sanitizers Bitcoin Core can be compiled with various "sanitizers" enabled, which add instrumentation for issues regarding things like memory safety, thread race @@ -372,7 +373,7 @@ Deadlocks due to inconsistent lock ordering (thread 1 locks `cs_main` and then `cs_wallet`, while thread 2 locks them in the opposite order: result, deadlock as each waits for the other to release its lock) are a problem. Compile with `-DDEBUG_LOCKORDER` (or use `--enable-debug`) to get lock order inconsistencies -reported in the debug.log file. +reported in the `debug.log` file. Re-architecting the core code so there are better-defined interfaces between the various components is a goal, with any necessary locking @@ -386,8 +387,6 @@ Threads - ThreadImport : Loads blocks from `blk*.dat` files or `-loadblock=<file>`. -- StartNode : Starts other threads. - - ThreadDNSAddressSeed : Loads addresses of peers from the DNS. - ThreadMapPort : Universal plug-and-play startup/shutdown. @@ -400,7 +399,7 @@ Threads - ThreadMessageHandler : Higher-level message handling (sending and receiving). -- DumpAddresses : Dumps IP addresses of nodes to peers.dat. +- DumpAddresses : Dumps IP addresses of nodes to `peers.dat`. - ThreadRPCServer : Remote procedure call handler, listens on port 8332 for connections and services them. @@ -466,11 +465,6 @@ Wallet - Make sure that no crashes happen with run-time option `-disablewallet`. - - *Rationale*: In RPC code that conditionally uses the wallet (such as - `validateaddress`), it is easy to forget that global pointer `pwalletMain` - can be nullptr. See `test/functional/disablewallet.py` for functional tests - exercising the API with `-disablewallet`. - - Include `db_cxx.h` (BerkeleyDB header) only when `ENABLE_WALLET` is set. - *Rationale*: Otherwise compilation of the disable-wallet build will fail in environments without BerkeleyDB. @@ -545,11 +539,10 @@ class A } ``` -- By default, declare single-argument constructors `explicit`. +- By default, declare constructors `explicit`. - - *Rationale*: This is a precaution to avoid unintended conversions that might - arise when single-argument constructors are used as implicit conversion - functions. + - *Rationale*: This is a precaution to avoid unintended + [conversions](https://en.cppreference.com/w/cpp/language/converting_constructor). - Use explicitly signed or unsigned `char`s, or even better `uint8_t` and `int8_t`. Do not use bare `char` unless it is to pass to a third-party API. @@ -827,7 +820,7 @@ Current subtrees include: - **Note**: Follow the instructions in [Upgrading LevelDB](#upgrading-leveldb) when merging upstream changes to the LevelDB subtree. -- src/libsecp256k1 +- src/secp256k1 - Upstream at https://github.com/bitcoin-core/secp256k1/ ; actively maintained by Core contributors. - src/crypto/ctaes @@ -889,7 +882,7 @@ Scripted diffs For reformatting and refactoring commits where the changes can be easily automated using a bash script, we use scripted-diff commits. The bash script is included in the commit message and our Travis CI job checks that the result of the script is identical to the commit. This aids reviewers since they can verify that the script -does exactly what it's supposed to do. It is also helpful for rebasing (since the same script can just be re-run +does exactly what it is supposed to do. It is also helpful for rebasing (since the same script can just be re-run on the new master commit). To create a scripted-diff: @@ -910,7 +903,35 @@ For development, it might be more convenient to verify all scripted-diffs in a r test/lint/commit-script-check.sh origin/master..HEAD ``` -Commit [`bb81e173`](https://github.com/bitcoin/bitcoin/commit/bb81e173) is an example of a scripted-diff. +### Suggestions and examples + +If you need to replace in multiple files, prefer `git ls-files` to `find` or globbing, and `git grep` to `grep`, to +avoid changing files that are not under version control. + +For efficient replacement scripts, reduce the selection to the files that potentially need to be modified, so for +example, instead of a blanket `git ls-files src | xargs sed -i s/apple/orange/`, use +`git grep -l apple src | xargs sed -i s/apple/orange/`. + +Also, it is good to keep the selection of files as specific as possible — for example, replace only in directories where +you expect replacements — because it reduces the risk that a rebase of your commit by re-running the script will +introduce accidental changes. + +Some good examples of scripted-diff: + +- [scripted-diff: Rename InitInterfaces to NodeContext](https://github.com/bitcoin/bitcoin/commit/301bd41a2e6765b185bd55f4c541f9e27aeea29d) +uses an elegant script to replace occurences of multiple terms in all source files. + +- [scripted-diff: Remove g_connman, g_banman globals](https://github.com/bitcoin/bitcoin/commit/301bd41a2e6765b185bd55f4c541f9e27aeea29d) +replaces specific terms in a list of specific source files. + +- [scripted-diff: Replace fprintf with tfm::format](https://github.com/bitcoin/bitcoin/commit/fac03ec43a15ad547161e37e53ea82482cc508f9) +does a global replacement but excludes certain directories. + +To find all previous uses of scripted diffs in the repository, do: + +``` +git log --grep="-BEGIN VERIFY SCRIPT-" +``` Release notes ------------- @@ -993,7 +1014,7 @@ A few guidelines for introducing and reviewing new RPC interfaces: - A RPC method must either be a wallet method or a non-wallet method. Do not introduce new methods that differ in behavior based on the presence of a wallet. - - *Rationale*: as well as complicating the implementation and interfering + - *Rationale*: As well as complicating the implementation and interfering with the introduction of multi-wallet, wallet and non-wallet code should be separated to avoid introducing circular dependencies between code units. @@ -1020,8 +1041,13 @@ A few guidelines for introducing and reviewing new RPC interfaces: - *Rationale*: RPC methods registered with the same function pointer will be considered aliases and only the first method name will show up in the - `help` rpc command list. + `help` RPC command list. - *Exception*: Using RPC method aliases may be appropriate in cases where a new RPC is replacing a deprecated RPC, to avoid both RPCs confusingly showing up in the command list. + +- Use the `UNIX_EPOCH_TIME` constant when describing UNIX epoch time or + timestamps in the documentation. + + - *Rationale*: User-facing consistency. diff --git a/doc/man/bitcoin-cli.1 b/doc/man/bitcoin-cli.1 index 95c1d24dff..129651d8e9 100644 --- a/doc/man/bitcoin-cli.1 +++ b/doc/man/bitcoin-cli.1 @@ -113,7 +113,3 @@ The source code is available from <https://github.com/bitcoin/bitcoin>. This is experimental software. Distributed under the MIT software license, see the accompanying file COPYING or <https://opensource.org/licenses/MIT> - -This product includes software developed by the OpenSSL Project for use in the -OpenSSL Toolkit <https://www.openssl.org> and cryptographic software written by -Eric Young and UPnP software written by Thomas Bernard. diff --git a/doc/man/bitcoin-qt.1 b/doc/man/bitcoin-qt.1 index 1957fb736e..f68be21e8d 100644 --- a/doc/man/bitcoin-qt.1 +++ b/doc/man/bitcoin-qt.1 @@ -608,7 +608,3 @@ The source code is available from <https://github.com/bitcoin/bitcoin>. This is experimental software. Distributed under the MIT software license, see the accompanying file COPYING or <https://opensource.org/licenses/MIT> - -This product includes software developed by the OpenSSL Project for use in the -OpenSSL Toolkit <https://www.openssl.org> and cryptographic software written by -Eric Young and UPnP software written by Thomas Bernard. diff --git a/doc/man/bitcoin-tx.1 b/doc/man/bitcoin-tx.1 index 6b6071d9b7..b4c7698896 100644 --- a/doc/man/bitcoin-tx.1 +++ b/doc/man/bitcoin-tx.1 @@ -114,7 +114,3 @@ The source code is available from <https://github.com/bitcoin/bitcoin>. This is experimental software. Distributed under the MIT software license, see the accompanying file COPYING or <https://opensource.org/licenses/MIT> - -This product includes software developed by the OpenSSL Project for use in the -OpenSSL Toolkit <https://www.openssl.org> and cryptographic software written by -Eric Young and UPnP software written by Thomas Bernard. diff --git a/doc/man/bitcoin-wallet.1 b/doc/man/bitcoin-wallet.1 index 1cb8cdebcd..aadea09a2b 100644 --- a/doc/man/bitcoin-wallet.1 +++ b/doc/man/bitcoin-wallet.1 @@ -61,7 +61,3 @@ The source code is available from <https://github.com/bitcoin/bitcoin>. This is experimental software. Distributed under the MIT software license, see the accompanying file COPYING or <https://opensource.org/licenses/MIT> - -This product includes software developed by the OpenSSL Project for use in the -OpenSSL Toolkit <https://www.openssl.org> and cryptographic software written by -Eric Young and UPnP software written by Thomas Bernard. diff --git a/doc/man/bitcoind.1 b/doc/man/bitcoind.1 index b0aff99ca2..211ba10285 100644 --- a/doc/man/bitcoind.1 +++ b/doc/man/bitcoind.1 @@ -581,7 +581,3 @@ The source code is available from <https://github.com/bitcoin/bitcoin>. This is experimental software. Distributed under the MIT software license, see the accompanying file COPYING or <https://opensource.org/licenses/MIT> - -This product includes software developed by the OpenSSL Project for use in the -OpenSSL Toolkit <https://www.openssl.org> and cryptographic software written by -Eric Young and UPnP software written by Thomas Bernard. diff --git a/doc/release-notes.md b/doc/release-notes.md index a47c8802b0..eec1ef9c46 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -46,16 +46,15 @@ Compatibility ============== Bitcoin Core is supported and extensively tested on operating systems using -the Linux kernel, macOS 10.10+, and Windows 7 and newer. It is not recommended +the Linux kernel, macOS 10.12+, and Windows 7 and newer. It is not recommended to use Bitcoin Core on unsupported systems. Bitcoin Core should also work on most other Unix-like systems but is not as frequently tested on them. -From Bitcoin Core 0.17.0 onwards, macOS versions earlier than 10.10 are no -longer supported, as Bitcoin Core is now built using Qt 5.9.x which requires -macOS 10.10+. Additionally, Bitcoin Core does not yet change appearance when -macOS "dark mode" is activated. +From Bitcoin Core 0.20.0 onwards, macOS versions earlier than 10.12 are no +longer supported. Additionally, Bitcoin Core does not yet change appearance +when macOS "dark mode" is activated. In addition to previously supported CPU platforms, this release's pre-compiled distribution provides binaries for the RISC-V platform. @@ -63,12 +62,25 @@ distribution provides binaries for the RISC-V platform. Notable changes =============== +Build System +------------ + +- OpenSSL is no longer used by Bitcoin Core. The last usage of the library +was removed in #17265. + +- glibc 2.17 or greater is now required to run the release binaries. This +retains compatibility with RHEL 7, CentOS 7, Debian 8 and Ubuntu 14.04 LTS. +Further details can be found in #17538. + New RPCs -------- New settings ------------ +- RPC Whitelist system. It can give certain RPC users permissions to only some RPC calls. +It can be set with two command line arguments (`rpcwhitelist` and `rpcwhitelistdefault`). (#12763) + Updated settings ---------------- @@ -81,6 +93,8 @@ Low-level Changes section below. GUI changes ----------- +- The "Start Bitcoin Core on system login" option has been removed on macOS. + Wallet ------ @@ -93,6 +107,10 @@ Low-level changes Tests ----- +- It is now an error to use an unqualified `walletdir=path` setting in the config file if running on testnet or regtest + networks. The setting now needs to be qualified as `chain.walletdir=path` or placed in the appropriate `[chain]` + section. (#17447) + - `-fallbackfee` was 0 (disabled) by default for the main chain, but 0.0002 by default for the test chains. Now it is 0 by default for all chains. Testnet and regtest users will have to add `fallbackfee=0.0002` to their configuration if they weren't setting it and they want it to keep working like before. (#16524) diff --git a/doc/release-notes/release-notes-0.19.0.1.md b/doc/release-notes/release-notes-0.19.0.1.md new file mode 100644 index 0000000000..70b8ec589f --- /dev/null +++ b/doc/release-notes/release-notes-0.19.0.1.md @@ -0,0 +1,1089 @@ +Bitcoin Core version 0.19.0.1 is now available from: + + <https://bitcoincore.org/bin/bitcoin-core-0.19.0.1/> + +This release includes new features, various bug fixes and performance +improvements, as well as updated translations. + +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). + +Upgrading directly from a version of Bitcoin Core that has reached its EOL is +possible, but might take some time if the datadir needs to be migrated. Old +wallet versions of Bitcoin Core are generally supported. + +Compatibility +============== + +Bitcoin Core is supported and extensively tested on operating systems using +the Linux kernel, macOS 10.10+, and Windows 7 and newer. It is not recommended +to use Bitcoin Core on unsupported systems. + +Bitcoin Core should also work on most other Unix-like systems but is not +as frequently tested on them. + +From 0.17.0 onwards, macOS <10.10 is no longer supported. 0.17.0 is +built using Qt 5.9.x, which doesn't support versions of macOS older than +10.10. Additionally, Bitcoin Core does not yet change appearance when +macOS "dark mode" is activated. + +Users running macOS Catalina may need to "right-click" and then choose "Open" +to open the Bitcoin Core .dmg. This is due to new signing requirements +imposed by Apple, which the Bitcoin Core project does not yet adhere too. + +Notable changes +=============== + +New user documentation +---------------------- + +- [Reduce memory](https://github.com/bitcoin/bitcoin/blob/master/doc/reduce-memory.md) + suggests configuration tweaks for running Bitcoin Core on systems with + limited memory. (#16339) + +New RPCs +-------- + +- `getbalances` returns an object with all balances (`mine`, + `untrusted_pending` and `immature`). Please refer to the RPC help of + `getbalances` for details. The new RPC is intended to replace + `getbalance`, `getunconfirmedbalance`, and the balance fields in + `getwalletinfo`. These old calls and fields may be removed in a + future version. (#15930, #16239) + +- `setwalletflag` sets and unsets wallet flags that enable or disable + features specific to that existing wallet, such as the new + `avoid_reuse` feature documented elsewhere in these release notes. + (#13756) + +- `getblockfilter` gets the BIP158 filter for the specified block. This + RPC is only enabled if block filters have been created using the + `-blockfilterindex` configuration option. (#14121) + +New settings +------------ + +- `-blockfilterindex` enables the creation of BIP158 block filters for + the entire blockchain. Filters will be created in the background and + currently use about 4 GiB of space. Note: this version of Bitcoin + Core does not serve block filters over the P2P network, although the + local user may obtain block filters using the `getblockfilter` RPC. + (#14121) + +Updated settings +---------------- + +- `whitebind` and `whitelist` now accept a list of permissions to + provide peers connecting using the indicated interfaces or IP + addresses. If no permissions are specified with an address or CIDR + network, the implicit default permissions are the same as previous + releases. See the `bitcoind -help` output for these two options for + details about the available permissions. (#16248) + +- Users setting custom `dbcache` values can increase their setting slightly + without using any more real memory. Recent changes reduced the memory use + by about 9% and made chainstate accounting more accurate (it was underestimating + the use of memory before). For example, if you set a value of "450" before, you + may now set a value of "500" to use about the same real amount of memory. (#16957) + + +Updated RPCs +------------ + +Note: some low-level RPC changes mainly useful for testing are described in the +Low-level Changes section below. + +- `sendmany` no longer has a `minconf` argument. This argument was not + well-specified and would lead to RPC errors even when the wallet's + coin selection succeeded. Users who want to influence coin selection + can use the existing `-spendzeroconfchange`, `-limitancestorcount`, + `-limitdescendantcount` and `-walletrejectlongchains` configuration + arguments. (#15596) + +- `getbalance` and `sendtoaddress`, plus the new RPCs `getbalances` and + `createwallet`, now accept an "avoid_reuse" parameter that controls + whether already used addresses should be included in the operation. + Additionally, `sendtoaddress` will avoid partial spends when + `avoid_reuse` is enabled even if this feature is not already enabled + via the `-avoidpartialspends` command line flag because not doing so + would risk using up the "wrong" UTXO for an address reuse case. + (#13756) + +- RPCs which have an `include_watchonly` argument or `includeWatching` option now default to `true` for watch-only + wallets. Affected RPCs are: `getbalance`, `listreceivedbyaddress`, `listreceivedbylabel`, `listtransactions`, + `listsinceblock`, `gettransaction`, `walletcreatefundedpsbt`, and `fundrawtransaction`. (#16383) + +- `listunspent` now returns a "reused" bool for each output if the + wallet flag "avoid_reuse" is enabled. (#13756) + +- `getblockstats` now uses BlockUndo data instead of the transaction + index, making it much faster, no longer dependent on the `-txindex` + configuration option, and functional for all non-pruned blocks. + (#14802) + +- `utxoupdatepsbt` now accepts a `descriptors` parameter that will fill + out input and output scripts and keys when known. P2SH-witness inputs + will be filled in from the UTXO set when a descriptor is provided that + shows they're spending segwit outputs. See the RPC help text for full + details. (#15427) + +- `sendrawtransaction` and `testmempoolaccept` no longer accept a + `allowhighfees` parameter to fail mempool acceptance if the + transaction fee exceeds the value of the configuration option + `-maxtxfee`. Now there is a hardcoded default maximum feerate that + can be changed when calling either RPC using a `maxfeerate` parameter. + (#15620) + +- `getmempoolancestors`, `getmempooldescendants`, `getmempoolentry`, and + `getrawmempool` no longer return a `size` field unless the + configuration option `-deprecatedrpc=size` is used. Instead a new + `vsize` field is returned with the transaction's virtual size + (consistent with other RPCs such as `getrawtransaction`). (#15637) + +- `getwalletinfo` now includes a `scanning` field that is either `false` + (no scanning) or an object with information about the duration and + progress of the wallet's scanning historical blocks for transactions + affecting its balances. (#15730) + +- `gettransaction` now accepts a third (boolean) argument `verbose`. If + set to `true`, a new `decoded` field will be added to the response containing + the decoded transaction. This field is equivalent to RPC `decoderawtransaction`, + or RPC `getrawtransaction` when `verbose` is passed. (#16185, #16866, #16873) + +- `createwallet` accepts a new `passphrase` parameter. If set, this + will create the new wallet encrypted with the given passphrase. If + unset (the default) or set to an empty string, no encryption will be + used. (#16394) + +- `getchaintxstats` RPC now returns the additional key of + `window_final_block_height`. (#16695) + +- `getmempoolentry` now provides a `weight` field containing the + transaction weight as defined in BIP141. (#16647) + +- The `getnetworkinfo` and `getpeerinfo` commands now contain a new field with decoded network service flags. (#16786) + +- `getdescriptorinfo` now returns an additional `checksum` field + containing the checksum for the unmodified descriptor provided by the + user (that is, before the descriptor is normalized for the + `descriptor` field). (#15986) + +- `joinpsbts` now shuffles the order of the inputs and outputs of the resulting + joined PSBT. Previously, inputs and outputs were added in the order PSBTs were + provided. This made it easy to correlate inputs to outputs, representing a + privacy leak. (#16512) + +- `walletcreatefundedpsbt` now signals BIP125 Replace-by-Fee if the + `-walletrbf` configuration option is set to true. (#15911) + +GUI changes +----------- + +- The GUI wallet now provides bech32 addresses by default. The user may change the address type + during invoice generation using a GUI toggle, or the default address + type may be changed with the `-addresstype` configuration option. + (#15711, #16497) + +- In 0.18.0, a `./configure` flag was introduced to allow disabling BIP70 support in the GUI (support was enabled by default). In 0.19.0, this flag is now __disabled__ by default. If you want to compile Bitcoin Core with BIP70 support in the GUI, you can pass `--enable-bip70` to `./configure`. (#15584) + +Deprecated or removed configuration options +------------------------------------------- + +- `-mempoolreplacement` is removed, although default node behavior + remains the same. This option previously allowed the user to prevent + the node from accepting or relaying BIP125 transaction replacements. + This is different from the remaining configuration option + `-walletrbf`. (#16171) + +Deprecated or removed RPCs +-------------------------- + +- `bumpfee` no longer accepts a `totalFee` option unless the + configuration parameter `deprecatedrpc=totalFee` is specified. This + parameter will be fully removed in a subsequent release. (#15996) + +- `bumpfee` has a new `fee_rate` option as a replacement for the deprecated `totalFee`. (#16727) + +- `generate` is now removed after being deprecated in Bitcoin Core 0.18. + Use the `generatetoaddress` RPC instead. (#15492) + +P2P changes +----------- + +- BIP 61 reject messages were deprecated in v0.18. They are now disabled + by default, but can be enabled by setting the `-enablebip61` command + line option. BIP 61 reject messages will be removed entirely in a + future version of Bitcoin Core. (#14054) + +- To eliminate well-known denial-of-service vectors in Bitcoin Core, + especially for nodes with spinning disks, the default value for the + `-peerbloomfilters` configuration option has been changed to false. + This prevents Bitcoin Core from sending the BIP111 NODE_BLOOM service + flag, accepting BIP37 bloom filters, or serving merkle blocks or + transactions matching a bloom filter. Users who still want to provide + bloom filter support may either set the configuration option to true + to re-enable both BIP111 and BIP37 support or enable just BIP37 + support for specific peers using the updated `-whitelist` and + `-whitebind` configuration options described elsewhere in these + release notes. For the near future, lightweight clients using public + BIP111/BIP37 nodes should still be able to connect to older versions + of Bitcoin Core and nodes that have manually enabled BIP37 support, + but developers of such software should consider migrating to either + using specific BIP37 nodes or an alternative transaction filtering + system. (#16152) + +- By default, Bitcoin Core will now make two additional outbound connections that are exclusively used for block-relay. No transactions or addr messages will be processed on these connections. These connections are designed to add little additional memory or bandwidth resource requirements but should make some partitioning attacks more difficult to carry out. (#15759) + +Miscellaneous CLI Changes +------------------------- + +- The `testnet` field in `bitcoin-cli -getinfo` has been renamed to + `chain` and now returns the current network name as defined in BIP70 + (main, test, regtest). (#15566) + +Low-level changes +================= + +RPC +--- + +- `getblockchaininfo` no longer returns a `bip9_softforks` object. + Instead, information has been moved into the `softforks` object and + an additional `type` field describes how Bitcoin Core determines + whether that soft fork is active (e.g. BIP9 or BIP90). See the RPC + help for details. (#16060) + +- `getblocktemplate` no longer returns a `rules` array containing `CSV` + and `segwit` (the BIP9 deployments that are currently in active + state). (#16060) + +- `getrpcinfo` now returns a `logpath` field with the path to + `debug.log`. (#15483) + +Tests +----- + +- The regression test chain enabled by the `-regtest` command line flag + now requires transactions to not violate standard policy by default. + This is the same default used for mainnet and makes it easier to test + mainnet behavior on regtest. Note that the testnet still allows + non-standard txs by default and that the policy can be locally + adjusted with the `-acceptnonstdtxn` command line flag for both test + chains. (#15891) + +Configuration +------------ + +- A setting specified in the default section but not also specified in a + network-specific section (e.g. testnet) will now produce an error + preventing startup instead of just a warning unless the network is + mainnet. This prevents settings intended for mainnet from being + applied to testnet or regtest. (#15629) + +- On platforms supporting `thread_local`, log lines can be prefixed with + the name of the thread that caused the log. To enable this behavior, + use `-logthreadnames=1`. (#15849) + +Network +------- + +- When fetching a transaction announced by multiple peers, previous versions of + Bitcoin Core would sequentially attempt to download the transaction from each + announcing peer until the transaction is received, in the order that those + peers' announcements were received. In this release, the download logic has + changed to randomize the fetch order across peers and to prefer sending + download requests to outbound peers over inbound peers. This fixes an issue + where inbound peers could prevent a node from getting a transaction. + (#14897, #15834) + +- If a Tor hidden service is being used, Bitcoin Core will be bound to + the standard port 8333 even if a different port is configured for + clearnet connections. This prevents leaking node identity through use + of identical non-default port numbers. (#15651) + +Mempool and transaction relay +----------------------------- + +- Allows one extra single-ancestor transaction per package. Previously, + if a transaction in the mempool had 25 descendants, or it and all of + its descendants were over 101,000 vbytes, any newly-received + transaction that was also a descendant would be ignored. Now, one + extra descendant will be allowed provided it is an immediate + descendant (child) and the child's size is 10,000 vbytes or less. + This makes it possible for two-party contract protocols such as + Lightning Network to give each participant an output they can spend + immediately for Child-Pays-For-Parent (CPFP) fee bumping without + allowing one malicious participant to fill the entire package and thus + prevent the other participant from spending their output. (#15681) + +- Transactions with outputs paying v1 to v16 witness versions (future + segwit versions) are now accepted into the mempool, relayed, and + mined. Attempting to spend those outputs remains forbidden by policy + ("non-standard"). When this change has been widely deployed, wallets + and services can accept any valid bech32 Bitcoin address without + concern that transactions paying future segwit versions will become + stuck in an unconfirmed state. (#15846) + +- Legacy transactions (transactions with no segwit inputs) must now be + sent using the legacy encoding format, enforcing the rule specified in + BIP144. (#14039) + +Wallet +------ + +- When in pruned mode, a rescan that was triggered by an `importwallet`, + `importpubkey`, `importaddress`, or `importprivkey` RPC will only fail + when blocks have been pruned. Previously it would fail when `-prune` + has been set. This change allows setting `-prune` to a high value + (e.g. the disk size) without the calls to any of the import RPCs + failing until the first block is pruned. (#15870) + +- When creating a transaction with a fee above `-maxtxfee` (default 0.1 + BTC), the RPC commands `walletcreatefundedpsbt` and + `fundrawtransaction` will now fail instead of rounding down the fee. + Be aware that the `feeRate` argument is specified in BTC per 1,000 + vbytes, not satoshi per vbyte. (#16257) + +- A new wallet flag `avoid_reuse` has been added (default off). When + enabled, a wallet will distinguish between used and unused addresses, + and default to not use the former in coin selection. When setting + this flag on an existing wallet, rescanning the blockchain is required + to correctly mark previously used destinations. Together with "avoid + partial spends" (added in Bitcoin Core v0.17.0), this can eliminate a + serious privacy issue where a malicious user can track spends by + sending small payments to a previously-paid address that would then + be included with unrelated inputs in future payments. (#13756) + +Build system changes +-------------------- + +- Python >=3.5 is now required by all aspects of the project. This + includes the build systems, test framework and linters. The previously + supported minimum (3.4), was EOL in March 2019. (#14954) + +- The minimum supported miniUPnPc API version is set to 10. This keeps + compatibility with Ubuntu 16.04 LTS and Debian 8 `libminiupnpc-dev` + packages. Please note, on Debian this package is still vulnerable to + [CVE-2017-8798](https://security-tracker.debian.org/tracker/CVE-2017-8798) + (in jessie only) and + [CVE-2017-1000494](https://security-tracker.debian.org/tracker/CVE-2017-1000494) + (both in jessie and in stretch). (#15993) + +0.19.0 change log +================= + +### Consensus +- #16128 Delete error-prone CScript constructor only used with FindAndDelete (instagibbs) +- #16060 Bury bip9 deployments (jnewbery) + +### Policy +- #15557 Enhance `bumpfee` to include inputs when targeting a feerate (instagibbs) +- #15846 Make sending to future native witness outputs standard (sipa) + +### Block and transaction handling +- #15632 Remove ResendWalletTransactions from the Validation Interface (jnewbery) +- #14121 Index for BIP 157 block filters (jimpo) +- #15141 Rewrite DoS interface between validation and net_processing (sdaftuar) +- #15880 utils and libraries: Replace deprecated Boost Filesystem functions (hebasto) +- #15971 validation: Add compile-time checking for negative locking requirement in LimitValidationInterfaceQueue (practicalswift) +- #15999 init: Remove dead code in LoadChainTip (MarcoFalke) +- #16015 validation: Hold cs_main when reading chainActive in RewindBlockIndex (practicalswift) +- #16056 remove unused magic number from consistency check (instagibbs) +- #16171 Remove -mempoolreplacement to prevent needless block prop slowness (TheBlueMatt) +- #15894 Remove duplicated "Error: " prefix in logs (hebasto) +- #14193 validation: Add missing mempool locks (MarcoFalke) +- #15681 Allow one extra single-ancestor transaction per package (TheBlueMatt) +- #15305 [validation] Crash if disconnecting a block fails (sdaftuar) +- #16471 log correct messages when CPFP fails (jnewbery) +- #16433 txmempool: Remove unused default value MemPoolRemovalReason::UNKNOWN (MarcoFalke) +- #13868 Remove unused fScriptChecks parameter from CheckInputs (Empact) +- #16421 Conservatively accept RBF bumps bumping one tx at the package limits (TheBlueMatt) +- #16854 Prevent UpdateTip log message from being broken up (stevenroose) +- #16956 validation: Make GetWitnessCommitmentIndex public (MarcoFalke) +- #16713 Ignore old versionbit activations to avoid 'unknown softforks' warning (jnewbery) +- #17002 chainparams: Bump assumed chain params (MarcoFalke) +- #16849 Fix block index inconsistency in InvalidateBlock() (sdaftuar) + +### P2P protocol and network code +- #15597 Generate log entry when blocks messages are received unexpectedly (pstratem) +- #15654 Remove unused unsanitized user agent string CNode::strSubVer (MarcoFalke) +- #15689 netaddress: Update CNetAddr for ORCHIDv2 (dongcarl) +- #15834 Fix transaction relay bugs introduced in #14897 and expire transactions from peer in-flight map (sdaftuar) +- #15651 torcontrol: Use the default/standard network port for Tor hidden services, even if the internal port is set differently (luke-jr) +- #16188 Document what happens to getdata of unknown type (MarcoFalke) +- #15649 Add ChaCha20Poly1305@Bitcoin AEAD (jonasschnelli) +- #16152 Disable bloom filtering by default (TheBlueMatt) +- #15993 Drop support of the insecure miniUPnPc versions (hebasto) +- #16197 Use mockable time for tx download (MarcoFalke) +- #16248 Make whitebind/whitelist permissions more flexible (NicolasDorier) +- #16618 [Fix] Allow connection of a noban banned peer (NicolasDorier) +- #16631 Restore default whitelistrelay to true (NicolasDorier) +- #15759 Add 2 outbound block-relay-only connections (sdaftuar) +- #15558 Don't query all DNS seeds at once (sipa) +- #16999 0.19 seeds update (laanwj) + +### Wallet +- #15288 Remove wallet -> node global function calls (ryanofsky) +- #15491 Improve log output for errors during load (gwillen) +- #13541 wallet/rpc: sendrawtransaction maxfeerate (kallewoof) +- #15680 Remove resendwallettransactions RPC method (jnewbery) +- #15508 Refactor analyzepsbt for use outside RPC code (gwillen) +- #15747 Remove plethora of Get*Balance (MarcoFalke) +- #15728 Refactor relay transactions (jnewbery) +- #15639 bitcoin-wallet tool: Drop libbitcoin_server.a dependency (ryanofsky) +- #15853 Remove unused import checkpoints.h (MarcoFalke) +- #15780 add cachable amounts for caching credit/debit values (kallewoof) +- #15778 Move maxtxfee from node to wallet (jnewbery) +- #15901 log on rescan completion (andrewtoth) +- #15917 Avoid logging no_such_file_or_directory error (promag) +- #15452 Replace CScriptID and CKeyID in CTxDestination with dedicated types (instagibbs) +- #15870 Only fail rescan when blocks have actually been pruned (MarcoFalke) +- #15006 Add option to create an encrypted wallet (achow101) +- #16001 Give WalletModel::UnlockContext move semantics (sipa) +- #15741 Batch write imported stuff in importmulti (achow101) +- #16144 do not encrypt wallets with disabled private keys (mrwhythat) +- #15024 Allow specific private keys to be derived from descriptor (meshcollider) +- #13756 "avoid_reuse" wallet flag for improved privacy (kallewoof) +- #16226 Move ismine to the wallet module (achow101) +- #16239 wallet/rpc: follow-up clean-up/fixes to avoid_reuse (kallewoof) +- #16286 refactoring: wallet: Fix GCC 7.4.0 warning (hebasto) +- #16257 abort when attempting to fund a transaction above -maxtxfee (Sjors) +- #16237 Have the wallet give out destinations instead of keys (achow101) +- #16322 Fix -maxtxfee check by moving it to CWallet::CreateTransaction (promag) +- #16361 Remove redundant pre-TopUpKeypool check (instagibbs) +- #16244 Move wallet creation out of the createwallet rpc into its own function (achow101) +- #16227 Refactor CWallet's inheritance chain (achow101) +- #16208 Consume ReserveDestination on successful CreateTransaction (instagibbs) +- #16301 Use CWallet::Import* functions in all import* RPCs (achow101) +- #16402 Remove wallet settings from chainparams (MarcoFalke) +- #16415 Get rid of PendingWalletTx class (ryanofsky) +- #15588 Log the actual wallet file version and no longer publicly expose the "version" record (achow101) +- #16399 Improve wallet creation (fjahr) +- #16475 Enumerate walletdb keys (MarcoFalke) +- #15709 Do not add "setting" key as unknown (Bushstar) +- #16451 Remove CMerkleTx (jnewbery) +- #15906 Move min_depth and max_depth to coin control (amitiuttarwar) +- #16502 Drop unused OldKey (promag) +- #16394 Allow createwallet to take empty passwords to make unencrypted wallets (achow101) +- #15911 Use wallet RBF default for walletcreatefundedpsbt (Sjors) +- #16503 Remove p2pEnabled from Chain interface (ariard) +- #16557 restore coinbase and confirmed/conflicted checks in SubmitMemoryPoolAndRelay() (jnewbery) +- #14934 Descriptor expansion cache clarifications (Sjors) +- #16383 rpcwallet: default include_watchonly to true for watchonly wallets (jb55) +- #16542 Return more specific errors about invalid descriptors (achow101) +- #16572 Fix Char as Bool in Wallet (JeremyRubin) +- #16753 extract PubKey from P2PK script with Solver (theStack) +- #16716 Use wallet name instead of pointer on unload/release (promag) +- #16185 gettransaction: add an argument to decode the transaction (darosior) +- #16745 Translate all initErrors in CreateWalletFromFile (MarcoFalke) +- #16792 Assert that the HRP is lowercase in Bech32::Encode (meshcollider) +- #16624 encapsulate transactions state (ariard) +- #16830 Cleanup walletinitinterface.h (hebasto) +- #16796 Fix segfault in CreateWalletFromFile (MarcoFalke) +- #16866 Rename 'decode' argument in gettransaction method to 'verbose' (jnewbery) +- #16727 Explicit feerate for bumpfee (instagibbs) +- #16609 descriptor: fix missed m_script_arg arg renaming in #14934 (fanquake) + +### RPC and other APIs +- #15492 remove deprecated generate method (Sjors) +- #15566 cli: Replace testnet with chain and return network name as per bip70 (fanquake) +- #15564 cli: Remove duplicate wallet fields from -getinfo (fanquake) +- #15642 Remove deprecated rpc warnings (jnewbery) +- #15637 Rename size to vsize in mempool related calls (fanquake) +- #15620 Uncouple non-wallet rpcs from maxTxFee global (MarcoFalke) +- #15616 Clarify decodescript RPCResult doc (MarcoFalke) +- #15669 Fix help text for signtransactionwithXXX (torkelrogstad) +- #15596 Ignore sendmany::minconf as dummy value (MarcoFalke) +- #15755 remove unused var in rawtransaction.cpp (Bushstar) +- #15746 RPCHelpMan: Always name dictionary keys (MarcoFalke) +- #15748 remove dead mining code (jnewbery) +- #15751 Speed up deriveaddresses for large ranges (sipa) +- #15770 Validate maxfeerate with AmountFromValue (promag) +- #15474 rest/rpc: Make mempoolinfo atomic (promag) +- #15463 Speedup getaddressesbylabel (promag) +- #15784 Remove dependency on interfaces::Chain in SignTransaction (ariard) +- #15323 Expose g_is_mempool_loaded via getmempoolinfo (Empact) +- #15932 Serialize in getblock without cs_main (MarcoFalke) +- #15930 Add balances RPC (MarcoFalke) +- #15730 Show scanning details in getwalletinfo (promag) +- #14802 faster getblockstats using BlockUndo data (FelixWeis) +- #14984 Speedup getrawmempool when verbose=true (promag) +- #16071 Hint for importmulti in help output of importpubkey and importaddress (kristapsk) +- #16063 Mention getwalletinfo where a rescan is triggered (promag) +- #16024 deriveaddresses: Correction of descriptor checksum in RPC example (ccapo) +- #16217 getrawtransaction: inform about blockhash argument when lookup fails (darosior) +- #15427 Add support for descriptors to utxoupdatepsbt (sipa) +- #16262 Allow shutdown while in generateblocks (pstratem) +- #15483 Adding a 'logpath' entry to getrpcinfo (darosior) +- #16325 Clarify that block count means height excl genesis (MarcoFalke) +- #16326 add new utxoupdatepsbt arguments to the CRPCCommand and CPRCCvertParam tables (jnewbery) +- #16332 Add logpath description for getrpcinfo (instagibbs) +- #16240 JSONRPCRequest-aware RPCHelpMan (kallewoof) +- #15996 Deprecate totalfee argument in `bumpfee` (instagibbs) +- #16467 sendrawtransaction help privacy note (jonatack) +- #16596 Fix getblocktemplate CLI example (emilengler) +- #15986 Add checksum to getdescriptorinfo (sipa) +- #16647 add weight to getmempoolentry output (fanquake) +- #16695 Add window final block height to getchaintxstats (leto) +- #16798 Refactor rawtransaction_util's SignTransaction to separate prevtx parsing (achow101) +- #16285 Improve scantxoutset response and help message (promag) +- #16725 Don't show addresses or P2PK in decoderawtransaction (NicolasDorier) +- #16787 Human readable network services (darosior) +- #16251 Improve signrawtransaction error reporting (ajtowns) +- #16873 fix regression in gettransaction (jonatack) +- #16512 Shuffle inputs and outputs after joining psbts (achow101) +- #16521 Use the default maxfeerate value as BTC/kB (Remagpie) +- #16817 Fix casing in getblockchaininfo to be inline with other fields (dangershony) +- #17131 fix -rpcclienttimeout 0 option (fjahr) +- #17249 Add missing deque include to fix build (jbeich) +- #17368 cli: fix -getinfo output when compiled with no wallet (fanquake) + +### GUI +- #15464 Drop unused return values in WalletFrame (promag) +- #15614 Defer removeAndDeleteWallet when no modal widget is active (promag) +- #15711 Generate bech32 addresses by default (MarcoFalke) +- #15829 update request payment button text and tab description (fanquake) +- #15874 Resolve the qt/guiutil <-> qt/optionsmodel CD (251Labs) +- #15371 Uppercase bech32 addresses in qr codes (benthecarman) +- #15928 Move QRImageWidget to its own file-pair (luke-jr) +- #16113 move coin control "OK" to the right hand side of the dialog (fanquake) +- #16090 Add vertical spacer to peer detail widget (JosuGZ) +- #15886 qt, wallet: Revamp SendConfirmationDialog (hebasto) +- #16263 Use qInfo() if no error occurs (hebasto) +- #16153 Add antialiasing to traffic graph widget (JosuGZ) +- #16350 Remove unused guard (hebasto) +- #16106 Sort wallets in open wallet menu (promag) +- #16291 Stop translating PACKAGE_NAME (MarcoFalke) +- #16380 Remove unused bits from the service flags enum (MarcoFalke) +- #16379 Fix autostart filenames on Linux for testnet/regtest (hebasto) +- #16366 init: Use InitError for all errors in bitcoind/qt (MarcoFalke) +- #16436 Do not create payment server if -disablewallet option provided (hebasto) +- #16514 Remove unused RPCConsole::tabFocus (promag) +- #16497 Generate bech32 addresses by default (take 2, fixup) (MarcoFalke) +- #16349 Remove redundant WalletController::addWallet slot (hebasto) +- #16578 Do not pass in command line arguments to QApplication (achow101) +- #16612 Remove menu icons (laanwj) +- #16677 remove unused PlatformStyle::TextColorIcon (fanquake) +- #16694 Ensure transaction send error is always visible (fanquake) +- #14879 Add warning messages to the debug window (hebasto) +- #16708 Replace obsolete functions of QSslSocket (hebasto) +- #16701 Replace functions deprecated in Qt 5.13 (hebasto) +- #16706 Replace deprecated QSignalMapper by lambda expressions (hebasto) +- #16707 Remove obsolete QModelIndex::child() (hebasto) +- #16758 Replace QFontMetrics::width() with TextWidth() (hebasto) +- #16760 Change uninstall icon on Windows (GChuf) +- #16720 Replace objc_msgSend() function calls with the native Objective-C syntax (hebasto) +- #16788 Update transifex slug for 0.19 (laanwj) +- #15450 Create wallet menu option (achow101) +- #16735 Remove unused menu items for Windows and Linux (GChuf) +- #16826 Do additional character escaping for wallet names and address labels (achow101) +- #15529 Add Qt programs to msvc build (updated, no code changes) (sipsorcery) +- #16714 add prune to intro screen with smart default (Sjors) +- #16858 advise users not to switch wallets when opening a BIP70 URI (jameshilliard) +- #16822 Create wallet menu option follow-ups (jonatack) +- #16882 Re-generate translations before 0.19.0 (MarcoFalke) +- #16928 Rename address checkbox back to bech32 (MarcoFalke) +- #16837 Fix {C{,XX},LD}FLAGS pickup (dongcarl) +- #16971 Change default size of intro frame (emilengler) +- #16988 Periodic translations update (laanwj) +- #16852 When BIP70 is disabled, get PaymentRequest merchant using string search (achow101) +- #16952 make sure to update the UI when deleting a transaction (jonasschnelli) +- #17031 Prevent processing duplicate payment requests (promag) +- #17135 Make polling in ClientModel asynchronous (promag) +- #17120 Fix start timer from non QThread (promag) +- #17257 disable font antialiasing for QR image address (fanquake) + +### Build system +- #14954 Require python 3.5 (MarcoFalke) +- #15580 native_protobuf: avoid system zlib (dongcarl) +- #15601 Switch to python3 (take 3) (MarcoFalke) +- #15581 Make less assumptions about build env (dongcarl) +- #14853 latest RapidCheck (fanquake) +- #15446 Improve depends debuggability (dongcarl) +- #13788 Fix --disable-asm for newer assembly checks/code (luke-jr) +- #12051 add missing debian contrib file to tarball (puchu) +- #15919 Remove unused OpenSSL includes to make it more clear where OpenSSL is used (practicalswift) +- #15978 .gitignore: Don't ignore depends patches (dongcarl) +- #15939 gitian: Remove windows 32 bit build (MarcoFalke) +- #15239 scripts and tools: Move non-linux build source tarballs to "bitcoin-binaries/version" directory (hebasto) +- #14047 Add HKDF_HMAC256_L32 and method to negate a private key (jonasschnelli) +- #16051 add patch to common dependencies (fanquake) +- #16049 switch to secure download of all dependencies (Kemu) +- #16059 configure: Fix thread_local detection (dongcarl) +- #16089 add ability to skip building zeromq (fanquake) +- #15844 Purge libtool archives (dongcarl) +- #15461 update to Boost 1.70 (Sjors) +- #16141 remove GZIP export from gitian descriptors (fanquake) +- #16235 Cleaned up and consolidated msbuild files (no code changes) (sipsorcery) +- #16246 MSVC: Fix error in debug mode (Fix #16245) (NicolasDorier) +- #16183 xtrans: Configure flags cleanup (dongcarl) +- #16258 [MSVC]: Create the config.ini as part of bitcoind build (NicolasDorier) +- #16271 remove -Wall from rapidcheck build flags (fanquake) +- #16309 [MSVC] allow user level project customization (NicolasDorier) +- #16308 [MSVC] Copy build output to src/ automatically after build (NicolasDorier) +- #15457 Check std::system for -[alert|block|wallet]notify (Sjors) +- #16344 use #if HAVE_SYSTEM instead of defined(HAVE_SYSTEM) (Sjors) +- #16352 prune dbus from depends (fanquake) +- #16270 expat 2.2.7 (fanquake) +- #16408 Prune X packages (dongcarl) +- #16386 disable unused Qt features (fanquake) +- #16424 Treat -Wswitch as error when --enable-werror (MarcoFalke) +- #16441 remove qt libjpeg check from bitcoin_qt.m4 (fanquake) +- #16434 Specify AM_CPPFLAGS for ZMQ (domob1812) +- #16534 add Qt Creator Makefile.am.user to .gitignore (Bushstar) +- #16573 disable building libsecp256k1 benchmarks (fanquake) +- #16533 disable libxcb extensions (fanquake) +- #16589 Remove unused src/obj-test folder (MarcoFalke) +- #16435 autoconf: Sane `--enable-debug` defaults (dongcarl) +- #16622 echo property tests status during build (jonatack) +- #16611 Remove src/obj directory from repository (laanwj) +- #16371 ignore macOS make deploy artefacts & add them to clean-local (fanquake) +- #16654 build: update RapidCheck Makefile (jonatack) +- #16370 cleanup package configure flags (fanquake) +- #16746 msbuild: Ignore linker warning (sipsorcery) +- #16750 msbuild: adds bench_bitcoin to auto generated project files (sipsorcery) +- #16810 guix: Remove ssp spec file hack (dongcarl) +- #16477 skip deploying plugins we dont use in macdeployqtplus (fanquake) +- #16413 Bump QT to LTS release 5.9.8 (THETCR) +- #15584 disable BIP70 support by default (fanquake) +- #16871 make building protobuf optional in depends (fanquake) +- #16879 remove redundant sed patching (fanquake) +- #16809 zlib: Move toolchain options to configure (dongcarl) +- #15146 Solve SmartOS FD_ZERO build issue (Empact) +- #16870 update boost macros to latest upstream for improved error reporting (fanquake) +- #16982 Factor out qt translations from build system (laanwj) +- #16926 Add OpenSSL termios fix for musl libc (nmarley) +- #16927 Refresh ZeroMQ 4.3.1 patch (nmarley) +- #17005 Qt version appears only if GUI is being built (ch4ot1c) +- #16468 Exclude depends/Makefile in .gitignore (promag) + +### Tests and QA +- #15296 Add script checking for deterministic line coverage in unit tests (practicalswift) +- #15338 ci: Build and run tests once on freebsd (MarcoFalke) +- #15479 Add .style.yapf (MarcoFalke) +- #15534 lint-format-strings: open files sequentially (fix for OS X) (gwillen) +- #15504 fuzz: Link BasicTestingSetup (shared with unit tests) (MarcoFalke) +- #15473 bench: Benchmark mempooltojson (MarcoFalke) +- #15466 Print remaining jobs in test_runner.py (stevenroose) +- #15631 mininode: Clearer error message on invalid magic bytes (MarcoFalke) +- #15255 Remove travis_wait from lint script (gkrizek) +- #15686 make pruning test faster (jnewbery) +- #15533 .style.yapf: Set column_limit=160 (MarcoFalke) +- #15660 Overhaul p2p_compactblocks.py (sdaftuar) +- #15495 Add regtests for HTTP status codes (domob1812) +- #15772 Properly log named args in authproxy (MarcoFalke) +- #15771 Prevent concurrency issues reading .cookie file (promag) +- #15693 travis: Switch to ubuntu keyserver to avoid timeouts (MarcoFalke) +- #15629 init: Throw error when network specific config is ignored (MarcoFalke) +- #15773 Add BitcoinTestFramework::sync_* methods (MarcoFalke) +- #15797 travis: Bump second timeout to 33 minutes, add rationale (MarcoFalke) +- #15788 Unify testing setups for fuzz, bench, and unit tests (MarcoFalke) +- #15352 Reduce noise level in test_bitcoin output (practicalswift) +- #15779 Add wallet_balance benchmark (MarcoFalke) +- #15843 fix outdated include in blockfilter_index_tests (jamesob) +- #15866 Add missing syncwithvalidationinterfacequeue to wallet_import_rescan (MarcoFalke) +- #15697 Make swap_magic_bytes in p2p_invalid_messages atomic (MarcoFalke) +- #15895 Avoid re-reading config.ini unnecessarily (luke-jr) +- #15896 feature_filelock, interface_bitcoin_cli: Use PACKAGE_NAME in messages rather than hardcoding Bitcoin Core (luke-jr) +- #15897 QA/mininode: Send all headers upfront in send_blocks_and_test to avoid sending an unconnected one (luke-jr) +- #15696 test_runner: Move feature_pruning to base tests (MarcoFalke) +- #15869 Add settings merge test to prevent regresssions (ryanofsky) +- #15758 Add further tests to wallet_balance (MarcoFalke) +- #15841 combine_logs: append node stderr and stdout if it exists (MarcoFalke) +- #15949 test_runner: Move pruning back to extended (MarcoFalke) +- #15927 log thread names by default in functional tests (jnewbery) +- #15664 change default Python block serialization to witness (instagibbs) +- #15988 Add test for ArgsManager::GetChainName (ryanofsky) +- #15963 Make random seed logged and settable (jnewbery) +- #15943 Fail if RPC has been added without tests (MarcoFalke) +- #16036 travis: Run all lint scripts even if one fails (scravy) +- #13555 parameterize adjustment period in versionbits_computeblockversion (JBaczuk) +- #16079 wallet_balance.py: Prevent edge cases (stevenroose) +- #16078 replace tx hash with txid in rawtransaction test (LongShao007) +- #16042 Bump MAX_NODES to 12 (MarcoFalke) +- #16124 Limit Python linting to files in the repo (practicalswift) +- #16143 Mark unit test blockfilter_index_initial_sync as non-deterministic (practicalswift) +- #16214 travis: Fix caching issues (MarcoFalke) +- #15982 Make msg_block a witness block (MarcoFalke) +- #16225 Make coins_tests/updatecoins_simulation_test deterministic (practicalswift) +- #16236 fuzz: Log output even if fuzzer failed (MarcoFalke) +- #15520 cirrus: Run extended test feature_pruning (MarcoFalke) +- #16234 Add test for unknown args (MarcoFalke) +- #16207 stop generating lcov coverage when functional tests fail (asood123) +- #16252 Log to debug.log in all unit tests (MarcoFalke) +- #16289 Add missing ECC_Stop() in GUI rpcnestedtests.cpp (jonasschnelli) +- #16278 Remove unused includes (practicalswift) +- #16302 Add missing syncwithvalidationinterfacequeue to wallet_balance test (MarcoFalke) +- #15538 wallet_bumpfee.py: Make sure coin selection produces change (instagibbs) +- #16294 Create at most one testing setup (MarcoFalke) +- #16299 bench: Move generated data to a dedicated translation unit (promag) +- #16329 Add tests for getblockchaininfo.softforks (MarcoFalke) +- #15687 tool wallet test coverage for unexpected writes to wallet (jonatack) +- #16267 bench: Benchmark blocktojson (fanatid) +- #14505 Add linter to make sure single parameter constructors are marked explicit (practicalswift) +- #16338 Disable other targets when enable-fuzz is set (qmma70) +- #16334 rpc_users: Also test rpcauth.py with password (dongcarl) +- #15282 Replace hard-coded hex tx with class in test framework (stevenroose) +- #16390 Add --filter option to test_runner.py (promag) +- #15891 Require standard txs in regtest by default (MarcoFalke) +- #16374 Enable passing wildcard test names to test runner from root (jonatack) +- #16420 Fix race condition in wallet_encryption test (jonasschnelli) +- #16422 remove redundant setup in addrman_tests (zenosage) +- #16438 travis: Print memory and number of cpus (MarcoFalke) +- #16445 Skip flaky p2p_invalid_messages test on macOS (fjahr) +- #16459 Fix race condition in example_test.py (sdaftuar) +- #16464 Ensure we don't generate a too-big block in p2sh sigops test (sdaftuar) +- #16491 fix deprecated log.warn in feature_dbcrash test (jonatack) +- #15134 Switch one of the Travis jobs to an unsigned char environment (-funsigned-char) (practicalswift) +- #16505 Changes verbosity of msbuild from quiet to normal in the appveyor script (sipsorcery) +- #16293 Make test cases separate functions (MarcoFalke) +- #16470 Fail early on disconnect in mininode.wait_for_* (MarcoFalke) +- #16277 Suppress output in test_bitcoin for expected errors (gertjaap) +- #16493 Fix test failures (MarcoFalke) +- #16538 Add missing sync_blocks to feature_pruning (MarcoFalke) +- #16509 Adapt test framework for chains other than "regtest" (MarcoFalke) +- #16363 Add test for BIP30 duplicate tx (MarcoFalke) +- #16535 Explain why -whitelist is used in feature_fee_estimation (MarcoFalke) +- #16554 only include and use OpenSSL where it's actually needed (BIP70) (fanquake) +- #16598 Remove confusing hash256 function in util (elichai) +- #16595 travis: Use extended 90 minute timeout when available (MarcoFalke) +- #16563 Add unit test for AddTimeData (mzumsande) +- #16561 Use colors and dots in test_runner.py output only if standard output is a terminal (practicalswift) +- #16465 Test p2sh-witness and bech32 in wallet_import_rescan (MarcoFalke) +- #16582 Rework ci (Use travis only as fallback env) (MarcoFalke) +- #16633 travis: Fix test_runner.py timeouts (MarcoFalke) +- #16646 Run tests with UPnP disabled (fanquake) +- #16623 ci: Add environment files for all settings (MarcoFalke) +- #16656 fix rpc_setban.py race (jonasschnelli) +- #16570 Make descriptor tests deterministic (davereikher) +- #16404 Test ZMQ notification after chain reorg (promag) +- #16726 Avoid common Python default parameter gotcha when mutable dict/list:s are used as default parameter values (practicalswift) +- #16739 ci: Pass down $makejobs to test_runner.py, other improvements (MarcoFalke) +- #16767 Check for codespell in lint-spelling.sh (kristapsk) +- #16768 Make lint-includes.sh work from any directory (kristapsk) +- #15257 Scripts and tools: Bump flake8 to 3.7.8 (Empact) +- #16804 Remove unused try-block in assert_debug_log (MarcoFalke) +- #16850 `servicesnames` field in `getpeerinfo` and `getnetworkinfo` (darosior) +- #16551 Test that low difficulty chain fork is rejected (MarcoFalke) +- #16737 Establish only one connection between nodes in rpc_invalidateblock (MarcoFalke) +- #16845 Add notes on how to generate data/wallets/high_minversion (MarcoFalke) +- #16888 Bump timeouts in slow running tests (MarcoFalke) +- #16864 Add python bech32 impl round-trip test (instagibbs) +- #16865 add some unit tests for merkle.cpp (soroosh-sdi) +- #14696 Add explicit references to related CVE's in p2p_invalid_block test (lucash-dev) +- #16907 lint: Add DisabledOpcodeTemplates to whitelist (MarcoFalke) +- #16898 Remove connect_nodes_bi (MarcoFalke) +- #16917 Move common function assert_approx() into util.py (fridokus) +- #16921 Add information on how to add Vulture suppressions (practicalswift) +- #16920 Fix extra_args in wallet_import_rescan.py (MarcoFalke) +- #16918 Make PORT_MIN in test runner configurable (MarcoFalke) +- #16941 travis: Disable feature_block in tsan run due to oom (MarcoFalke) +- #16929 follow-up to rpc: default maxfeerate value as BTC/kB (jonatack) +- #16959 ci: Set $host before setting fallback values (MarcoFalke) +- #16961 Remove python dead code linter (laanwj) +- #16931 add unittests for CheckProofOfWork (soroosh-sdi) +- #16991 Fix service flag comparison check in rpc_net test (luke-jr) (laanwj) +- #16987 Correct docstring param name (jbampton) +- #17015 Explain QT_QPA_PLATFORM for gui tests (MarcoFalke) +- #17006 Enable UBSan for Travis fuzzing job (practicalswift) +- #17086 Fix fs_tests for unknown locales (carnhofdaki) +- #15903 appveyor: Write @PACKAGE_NAME@ to config (MarcoFalke) +- #16742 test: add executable flag for wallet_watchonly.py (theStack) +- #16740 qa: Relax so that the subscriber is ready before publishing zmq messages (#16740) + +### Miscellaneous +- #15335 Fix lack of warning of unrecognized section names (AkioNak) +- #15528 contrib: Bump gitian descriptors for 0.19 (MarcoFalke) +- #15609 scripts and tools: Set 'distro' explicitly (hebasto) +- #15519 Add Poly1305 implementation (jonasschnelli) +- #15643 contrib: Gh-merge: include acks in merge commit (MarcoFalke) +- #15838 scripts and tools: Fetch missing review comments in github-merge.py (nkostoulas) +- #15920 lint: Check that all wallet args are hidden (MarcoFalke) +- #15849 Thread names in logs and deadlock debug tools (jamesob) +- #15650 Handle the result of posix_fallocate system call (lucayepa) +- #15766 scripts and tools: Upgrade gitian image before signing (hebasto) +- #15512 Add ChaCha20 encryption option (XOR) (jonasschnelli) +- #15968 Fix portability issue with pthreads (grim-trigger) +- #15970 Utils and libraries: fix static_assert for macro HAVE_THREAD_LOCAL (orientye) +- #15863 scripts and tools: Ensure repos are up-to-date in gitian-build.py (hebasto) +- #15224 Add RNG strengthening (10ms once every minute) (sipa) +- #15840 Contrib scripts: Filter IPv6 by ASN (abitfan) +- #13998 Scripts and tools: gitian-build.py improvements and corrections (hebasto) +- #15236 scripts and tools: Make --setup command independent (hebasto) +- #16114 contrib: Add curl as a required program in gitian-build.py (fanquake) +- #16046 util: Add type safe gettime (MarcoFalke) +- #15703 Update secp256k1 subtree to latest upstream (sipa) +- #16086 contrib: Use newer config.guess & config.sub in install_db4.sh (fanquake) +- #16130 Don't GPG sign intermediate commits with github-merge tool (stevenroose) +- #16162 scripts: Add key for michael ford (fanquake) to trusted keys list (fanquake) +- #16201 devtools: Always use unabbreviated commit IDs in github-merge.py (laanwj) +- #16112 util: Log early messages (MarcoFalke) +- #16223 devtools: Fetch and display ACKs at sign-off time in github-merge (laanwj) +- #16300 util: Explain why the path is cached (MarcoFalke) +- #16314 scripts and tools: Update copyright_header.py script (hebasto) +- #16158 Fix logic of memory_cleanse() on MSVC and clean up docs (real-or-random) +- #14734 fix an undefined behavior in uint::SetHex (kazcw) +- #16327 scripts and tools: Update ShellCheck linter (hebasto) +- #15277 contrib: Enable building in guix containers (dongcarl) +- #16362 Add bilingual_str type (hebasto) +- #16481 logs: add missing space (harding) +- #16581 sipsorcery gitian key (sipsorcery) +- #16566 util: Refactor upper/lowercase functions (kallewoof) +- #16620 util: Move resolveerrmsg to util/error (MarcoFalke) +- #16625 scripts: Remove github-merge.py (fanquake) +- #15864 Fix datadir handling (hebasto) +- #16670 util: Add join helper to join a list of strings (MarcoFalke) +- #16665 scripts: Move update-translations.py to maintainer-tools repo (fanquake) +- #16730 Support serialization of `std::vector<bool>` (sipa) +- #16556 Fix systemd service file configuration directory setup (setpill) +- #15615 Add log output during initial header sync (jonasschnelli) +- #16774 Avoid unnecessary "Synchronizing blockheaders" log messages (jonasschnelli) +- #16489 log: harmonize bitcoind logging (jonatack) +- #16577 util: Cbufferedfile fixes and unit test (LarryRuane) +- #16984 util: Make thread names shorter (hebasto) +- #17038 Don't rename main thread at process level (laanwj) +- #17184 util: Filter out macos process serial number (hebasto) +- #17095 util: Filter control characters out of log messages (laanwj) +- #17085 init: Change fallback locale to C.UTF-8 (laanwj) +- #16957 9% less memory: make SaltedOutpointHasher noexcept (martinus) +- #17449 fix uninitialized variable nMinerConfirmationWindow (bitcoinVBR) + +### Documentation +- #15514 Update Transifex links (fanquake) +- #15513 add "sections" info to example bitcoin.conf (fanquake) +- #15530 Move wallet lock annotations to header (MarcoFalke) +- #15562 remove duplicate clone step in build-windows.md (fanquake) +- #15565 remove release note fragments (fanquake) +- #15444 Additional productivity tips (Sjors) +- #15577 Enable TLS in link to chris.beams.io (JeremyRand) +- #15604 release note for disabling reject messages by default (jnewbery) +- #15611 Add Gitian key for droark (droark) +- #15626 Update ACK description in CONTRIBUTING.md (jonatack) +- #15603 Add more tips to productivity.md (gwillen) +- #15683 Comment for seemingly duplicate LIBBITCOIN_SERVER (Bushstar) +- #15685 rpc-mining: Clarify error messages (MarcoFalke) +- #15760 Clarify sendrawtransaction::maxfeerate==0 help (MarcoFalke) +- #15659 fix findFork comment (r8921039) +- #15718 Improve netaddress comments (dongcarl) +- #15833 remove out-of-date comment on pay-to-witness support (r8921039) +- #15821 Remove upgrade note in release notes from EOL versions (MarcoFalke) +- #15267 explain AcceptToMemoryPoolWorker's coins_to_uncache (jamesob) +- #15887 Align code example style with clang-format (hebasto) +- #15877 Fix -dustrelayfee= argument docs grammar (keepkeyjon) +- #15908 Align MSVC build options with Linux build ones (hebasto) +- #15941 Add historical release notes for 0.18.0 (laanwj) +- #15794 Clarify PR guidelines w/re documentation (dongcarl) +- #15607 Release process updates (jonatack) +- #14364 Clarify -blocksdir usage (sangaman) +- #15777 Add doxygen comments for keypool classes (jnewbery) +- #15820 Add productivity notes for dummy rebases (dongcarl) +- #15922 Explain how to pass in non-fundamental types into functions (MarcoFalke) +- #16080 build/doc: update bitcoin_config.h packages, release process (jonatack) +- #16047 analyzepsbt description in doc/psbt.md (jonatack) +- #16039 add release note for 14954 (fanquake) +- #16139 Add riscv64 to outputs list in release-process.md (JeremyRand) +- #16140 create security policy (narula) +- #16164 update release process for SECURITY.md (jonatack) +- #16213 Remove explicit mention of versions from SECURITY.md (MarcoFalke) +- #16186 doc/lint: Fix spelling errors identified by codespell 1.15.0 (Empact) +- #16149 Rework section on ACK in CONTRIBUTING.md (MarcoFalke) +- #16196 Add release notes for 14897 & 15834 (MarcoFalke) +- #16241 add rapidcheck to vcpkg install list (fanquake) +- #16243 Remove travis badge from readme (MarcoFalke) +- #16256 remove orphaned header in developer notes (jonatack) +- #15964 Improve build-osx document formatting (giulio92) +- #16313 Fix broken link in doc/build-osx.md (jonatack) +- #16330 Use placeholder instead of key expiration date (hebasto) +- #16339 add reduce-memory.md (fanquake) +- #16347 Include static members in Doxygen (dongcarl) +- #15824 Improve netbase comments (dongcarl) +- #16430 Update bips 35, 37 and 111 status (MarcoFalke) +- #16455 Remove downgrading warning in release notes, per 0.18 branch (MarcoFalke) +- #16484 update labels in CONTRIBUTING.md (MarcoFalke) +- #16483 update Python command in msvc readme (sipsorcery) +- #16504 Add release note for the deprecated totalFee option of bumpfee (promag) +- #16448 add note on precedence of options in bitcoin.conf (fanquake) +- #16536 Update and extend benchmarking.md (ariard) +- #16530 Fix grammar and punctuation in developer notes (Tech1k) +- #16574 Add historical release notes for 0.18.1 (laanwj) +- #16585 Update Markdown syntax for bdb packages (emilengler) +- #16586 Mention other ways to conserve memory on compilation (MarcoFalke) +- #16605 Add missing contributor to 0.18.1 release notes (meshcollider) +- #16615 Fix typos in COPYRIGHT (gapeman) +- #16626 Fix spelling error chache -> cache (nilswloewen) +- #16587 Improve versionbits.h documentation (ariard) +- #16643 Add ZMQ dependencies to the Fedora build instructions (hebasto) +- #16634 Refer in rpcbind doc to the manpage (MarcoFalke) +- #16555 mention whitelist is inbound, and applies to blocksonly (Sjors) +- #16645 initial RapidCheck property-based testing documentation (jonatack) +- #16691 improve depends prefix documentation (fanquake) +- #16629 Add documentation for the new whitelist permissions (NicolasDorier) +- #16723 Update labels in CONTRIBUTING.md (hebasto) +- #16461 Tidy up shadowing section (promag) +- #16621 add default bitcoin.conf locations (GChuf) +- #16752 Delete stale URL in test README (michaelfolkson) +- #14862 Declare BLOCK_VALID_HEADER reserved (MarcoFalke) +- #16806 Add issue templates for bug and feature request (MarcoFalke) +- #16857 Elaborate need to re-login on Debian-based after usermod for Tor group (clashicly) +- #16863 Add a missing closing parenthesis in the bitcoin-wallet's help (darosior) +- #16757 CChainState return values (MarcoFalke) +- #16847 add comments clarifying how local services are advertised (jamesob) +- #16812 Fix whitespace errs in .md files, bitcoin.conf, and Info.plist.in (ch4ot1c) +- #16885 Update tx-size-small comment with relevant CVE disclosure (instagibbs) +- #16900 Fix doxygen comment for SignTransaction in rpc/rawtransaction_util (MarcoFalke) +- #16914 Update homebrew instruction for doxygen (Sjors) +- #16912 Remove Doxygen intro from src/bitcoind.cpp (ch4ot1c) +- #16960 replace outdated OpenSSL comment in test README (fanquake) +- #16968 Remove MSVC update step from translation process (laanwj) +- #16953 Improve test READMEs (fjahr) +- #16962 Put PR template in comments (laanwj) +- #16397 Clarify includeWatching for fundrawtransaction (stevenroose) +- #15459 add how to calculate blockchain and chainstate size variables to release process (marcoagner) +- #16997 Update bips.md for 0.19 (laanwj) +- #17001 Remove mention of renamed mapBlocksUnlinked (MarcoFalke) +- #17014 Consolidate release notes before 0.19.0 (move-only) (MarcoFalke) +- #17111 update bips.md with buried BIP9 deployments (MarcoFalke) + +Credits +======= + +Thanks to everyone who directly contributed to this release: + +- 251 +- Aaron Clauson +- Akio Nakamura +- Alistair Mann +- Amiti Uttarwar +- Andrew Chow +- andrewtoth +- Anthony Towns +- Antoine Riard +- Aseem Sood +- Ben Carman +- Ben Woosley +- bpay +- Carl Dong +- Carnhof Daki +- Chris Capobianco +- Chris Moore +- Chuf +- clashic +- clashicly +- Cory Fields +- Daki Carnhof +- Dan Gershony +- Daniel Edgecumbe +- Daniel Kraft +- Daniel McNally +- darosior +- David A. Harding +- David Reikher +- Douglas Roark +- Elichai Turkel +- Emil +- Emil Engler +- ezegom +- Fabian Jahr +- fanquake +- Felix Weis +- Ferdinando M. Ametrano +- fridokus +- gapeman +- GChuf +- Gert-Jaap Glasbergen +- Giulio Lombardo +- Glenn Willen +- Graham Krizek +- Gregory Sanders +- grim-trigger +- gwillen +- Hennadii Stepanov +- Jack Mallers +- James Hilliard +- James O'Beirne +- Jan Beich +- Jeremy Rubin +- JeremyRand +- Jim Posen +- John Bampton +- John Newbery +- Jon Atack +- Jon Layton +- Jonas Schnelli +- Jonathan "Duke" Leto +- João Barbosa +- Joonmo Yang +- Jordan Baczuk +- Jorge Timón +- Josu Goñi +- Julian Fleischer +- Karl-Johan Alm +- Kaz Wesley +- keepkeyjon +- Kirill Fomichev +- Kristaps Kaupe +- Kristian Kramer +- Larry Ruane +- Lenny Maiorani +- LongShao007 +- Luca Venturini +- lucash-dev +- Luke Dashjr +- marcoagner +- MarcoFalke +- marcuswin +- Martin Ankerl +- Martin Zumsande +- Matt Corallo +- MeshCollider +- Michael Folkson +- Miguel Herranz +- Nathan Marley +- Neha Narula +- nicolas.dorier +- Nils Loewen +- nkostoulas +- NullFunctor +- orient +- Patrick Strateman +- Peter Bushnell +- Peter Wagner +- Pieter Wuille +- practicalswift +- qmma +- r8921039 +- RJ Rybarczyk +- Russell Yanofsky +- Samuel Dobson +- Sebastian Falbesoner +- setpill +- shannon1916 +- Sjors Provoost +- soroosh-sdi +- Steven Roose +- Suhas Daftuar +- tecnovert +- THETCR +- Tim Ruffing +- Tobias Kaderle +- Torkel Rogstad +- Ulrich Kempken +- whythat +- William Casarin +- Wladimir J. van der Laan +- zenosage + +As well as everyone that helped translating on [Transifex](https://www.transifex.com/bitcoin/bitcoin/). diff --git a/doc/release-notes/release-notes-0.19.0.md b/doc/release-notes/release-notes-0.19.0.md index 153da68a6e..086491e92d 100644 --- a/doc/release-notes/release-notes-0.19.0.md +++ b/doc/release-notes/release-notes-0.19.0.md @@ -1,1086 +1,6 @@ -Bitcoin Core version 0.19.0 is now available from: - - <https://bitcoincore.org/bin/bitcoin-core-0.19.0/> - -This release includes new features, various bug fixes and performance -improvements, as well as updated translations. - -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). - -Upgrading directly from a version of Bitcoin Core that has reached its EOL is -possible, but might take some time if the datadir needs to be migrated. Old -wallet versions of Bitcoin Core are generally supported. - -Compatibility -============== - -Bitcoin Core is supported and extensively tested on operating systems using -the Linux kernel, macOS 10.10+, and Windows 7 and newer. It is not recommended -to use Bitcoin Core on unsupported systems. - -Bitcoin Core should also work on most other Unix-like systems but is not -as frequently tested on them. - -From 0.17.0 onwards, macOS <10.10 is no longer supported. 0.17.0 is -built using Qt 5.9.x, which doesn't support versions of macOS older than -10.10. Additionally, Bitcoin Core does not yet change appearance when -macOS "dark mode" is activated. - -Users running macOS Catalina may need to "right-click" and then choose "Open" -to open the Bitcoin Core .dmg. This is due to new signing requirements -imposed by Apple, which the Bitcoin Core project does not yet adhere too. - -Notable changes -=============== - -New user documentation ----------------------- - -- [Reduce memory](https://github.com/bitcoin/bitcoin/blob/master/doc/reduce-memory.md) - suggests configuration tweaks for running Bitcoin Core on systems with - limited memory. (#16339) - -New RPCs --------- - -- `getbalances` returns an object with all balances (`mine`, - `untrusted_pending` and `immature`). Please refer to the RPC help of - `getbalances` for details. The new RPC is intended to replace - `getbalance`, `getunconfirmedbalance`, and the balance fields in - `getwalletinfo`. These old calls and fields may be removed in a - future version. (#15930, #16239) - -- `setwalletflag` sets and unsets wallet flags that enable or disable - features specific to that existing wallet, such as the new - `avoid_reuse` feature documented elsewhere in these release notes. - (#13756) - -- `getblockfilter` gets the BIP158 filter for the specified block. This - RPC is only enabled if block filters have been created using the - `-blockfilterindex` configuration option. (#14121) - -New settings ------------- - -- `-blockfilterindex` enables the creation of BIP158 block filters for - the entire blockchain. Filters will be created in the background and - currently use about 4 GiB of space. Note: this version of Bitcoin - Core does not serve block filters over the P2P network, although the - local user may obtain block filters using the `getblockfilter` RPC. - (#14121) - -Updated settings ----------------- - -- `whitebind` and `whitelist` now accept a list of permissions to - provide peers connecting using the indicated interfaces or IP - addresses. If no permissions are specified with an address or CIDR - network, the implicit default permissions are the same as previous - releases. See the `bitcoind -help` output for these two options for - details about the available permissions. (#16248) - -- Users setting custom `dbcache` values can increase their setting slightly - without using any more real memory. Recent changes reduced the memory use - by about 9% and made chainstate accounting more accurate (it was underestimating - the use of memory before). For example, if you set a value of "450" before, you - may now set a value of "500" to use about the same real amount of memory. (#16957) - - -Updated RPCs ------------- - -Note: some low-level RPC changes mainly useful for testing are described in the -Low-level Changes section below. - -- `sendmany` no longer has a `minconf` argument. This argument was not - well-specified and would lead to RPC errors even when the wallet's - coin selection succeeded. Users who want to influence coin selection - can use the existing `-spendzeroconfchange`, `-limitancestorcount`, - `-limitdescendantcount` and `-walletrejectlongchains` configuration - arguments. (#15596) - -- `getbalance` and `sendtoaddress`, plus the new RPCs `getbalances` and - `createwallet`, now accept an "avoid_reuse" parameter that controls - whether already used addresses should be included in the operation. - Additionally, `sendtoaddress` will avoid partial spends when - `avoid_reuse` is enabled even if this feature is not already enabled - via the `-avoidpartialspends` command line flag because not doing so - would risk using up the "wrong" UTXO for an address reuse case. - (#13756) - -- RPCs which have an `include_watchonly` argument or `includeWatching` option now default to `true` for watch-only - wallets. Affected RPCs are: `getbalance`, `listreceivedbyaddress`, `listreceivedbylabel`, `listtransactions`, - `listsinceblock`, `gettransaction`, `walletcreatefundedpsbt`, and `fundrawtransaction`. (#16383) - -- `listunspent` now returns a "reused" bool for each output if the - wallet flag "avoid_reuse" is enabled. (#13756) - -- `getblockstats` now uses BlockUndo data instead of the transaction - index, making it much faster, no longer dependent on the `-txindex` - configuration option, and functional for all non-pruned blocks. - (#14802) - -- `utxoupdatepsbt` now accepts a `descriptors` parameter that will fill - out input and output scripts and keys when known. P2SH-witness inputs - will be filled in from the UTXO set when a descriptor is provided that - shows they're spending segwit outputs. See the RPC help text for full - details. (#15427) - -- `sendrawtransaction` and `testmempoolaccept` no longer accept a - `allowhighfees` parameter to fail mempool acceptance if the - transaction fee exceeds the value of the configuration option - `-maxtxfee`. Now there is a hardcoded default maximum feerate that - can be changed when calling either RPC using a `maxfeerate` parameter. - (#15620) - -- `getmempoolancestors`, `getmempooldescendants`, `getmempoolentry`, and - `getrawmempool` no longer return a `size` field unless the - configuration option `-deprecatedrpc=size` is used. Instead a new - `vsize` field is returned with the transaction's virtual size - (consistent with other RPCs such as `getrawtransaction`). (#15637) - -- `getwalletinfo` now includes a `scanning` field that is either `false` - (no scanning) or an object with information about the duration and - progress of the wallet's scanning historical blocks for transactions - affecting its balances. (#15730) - -- `gettransaction` now accepts a third (boolean) argument `verbose`. If - set to `true`, a new `decoded` field will be added to the response containing - the decoded transaction. This field is equivalent to RPC `decoderawtransaction`, - or RPC `getrawtransaction` when `verbose` is passed. (#16185, #16866, #16873) - -- `createwallet` accepts a new `passphrase` parameter. If set, this - will create the new wallet encrypted with the given passphrase. If - unset (the default) or set to an empty string, no encryption will be - used. (#16394) - -- `getchaintxstats` RPC now returns the additional key of - `window_final_block_height`. (#16695) - -- `getmempoolentry` now provides a `weight` field containing the - transaction weight as defined in BIP141. (#16647) - -- The `getnetworkinfo` and `getpeerinfo` commands now contain a new field with decoded network service flags. (#16786) - -- `getdescriptorinfo` now returns an additional `checksum` field - containing the checksum for the unmodified descriptor provided by the - user (that is, before the descriptor is normalized for the - `descriptor` field). (#15986) - -- `joinpsbts` now shuffles the order of the inputs and outputs of the resulting - joined PSBT. Previously, inputs and outputs were added in the order PSBTs were - provided. This made it easy to correlate inputs to outputs, representing a - privacy leak. (#16512) - -- `walletcreatefundedpsbt` now signals BIP125 Replace-by-Fee if the - `-walletrbf` configuration option is set to true. (#15911) - -GUI changes ------------ - -- The GUI wallet now provides bech32 addresses by default. The user may change the address type - during invoice generation using a GUI toggle, or the default address - type may be changed with the `-addresstype` configuration option. - (#15711, #16497) - -- In 0.18.0, a `./configure` flag was introduced to allow disabling BIP70 support in the GUI (support was enabled by default). In 0.19.0, this flag is now __disabled__ by default. If you want to compile Bitcoin Core with BIP70 support in the GUI, you can pass `--enable-bip70` to `./configure`. (#15584) - -Deprecated or removed configuration options -------------------------------------------- - -- `-mempoolreplacement` is removed, although default node behavior - remains the same. This option previously allowed the user to prevent - the node from accepting or relaying BIP125 transaction replacements. - This is different from the remaining configuration option - `-walletrbf`. (#16171) - -Deprecated or removed RPCs --------------------------- - -- `bumpfee` no longer accepts a `totalFee` option unless the - configuration parameter `deprecatedrpc=totalFee` is specified. This - parameter will be fully removed in a subsequent release. (#15996) - -- `bumpfee` has a new `fee_rate` option as a replacement for the deprecated `totalFee`. (#16727) - -- `generate` is now removed after being deprecated in Bitcoin Core 0.18. - Use the `generatetoaddress` RPC instead. (#15492) - -P2P changes +0.19.0 note ----------- -- BIP 61 reject messages were deprecated in v0.18. They are now disabled - by default, but can be enabled by setting the `-enablebip61` command - line option. BIP 61 reject messages will be removed entirely in a - future version of Bitcoin Core. (#14054) - -- To eliminate well-known denial-of-service vectors in Bitcoin Core, - especially for nodes with spinning disks, the default value for the - `-peerbloomfilters` configuration option has been changed to false. - This prevents Bitcoin Core from sending the BIP111 NODE_BLOOM service - flag, accepting BIP37 bloom filters, or serving merkle blocks or - transactions matching a bloom filter. Users who still want to provide - bloom filter support may either set the configuration option to true - to re-enable both BIP111 and BIP37 support or enable just BIP37 - support for specific peers using the updated `-whitelist` and - `-whitebind` configuration options described elsewhere in these - release notes. For the near future, lightweight clients using public - BIP111/BIP37 nodes should still be able to connect to older versions - of Bitcoin Core and nodes that have manually enabled BIP37 support, - but developers of such software should consider migrating to either - using specific BIP37 nodes or an alternative transaction filtering - system. (#16152) - -- By default, Bitcoin Core will now make two additional outbound connections that are exclusively used for block-relay. No transactions or addr messages will be processed on these connections. These connections are designed to add little additional memory or bandwidth resource requirements but should make some partitioning attacks more difficult to carry out. (#15759) - -Miscellaneous CLI Changes -------------------------- - -- The `testnet` field in `bitcoin-cli -getinfo` has been renamed to - `chain` and now returns the current network name as defined in BIP70 - (main, test, regtest). (#15566) - -Low-level changes -================= - -RPC ---- - -- `getblockchaininfo` no longer returns a `bip9_softforks` object. - Instead, information has been moved into the `softforks` object and - an additional `type` field describes how Bitcoin Core determines - whether that soft fork is active (e.g. BIP9 or BIP90). See the RPC - help for details. (#16060) - -- `getblocktemplate` no longer returns a `rules` array containing `CSV` - and `segwit` (the BIP9 deployments that are currently in active - state). (#16060) - -- `getrpcinfo` now returns a `logpath` field with the path to - `debug.log`. (#15483) - -Tests ------ - -- The regression test chain enabled by the `-regtest` command line flag - now requires transactions to not violate standard policy by default. - This is the same default used for mainnet and makes it easier to test - mainnet behavior on regtest. Note that the testnet still allows - non-standard txs by default and that the policy can be locally - adjusted with the `-acceptnonstdtxn` command line flag for both test - chains. (#15891) - -Configuration ------------- - -- A setting specified in the default section but not also specified in a - network-specific section (e.g. testnet) will now produce an error - preventing startup instead of just a warning unless the network is - mainnet. This prevents settings intended for mainnet from being - applied to testnet or regtest. (#15629) - -- On platforms supporting `thread_local`, log lines can be prefixed with - the name of the thread that caused the log. To enable this behavior, - use `-logthreadnames=1`. (#15849) - -Network -------- - -- When fetching a transaction announced by multiple peers, previous versions of - Bitcoin Core would sequentially attempt to download the transaction from each - announcing peer until the transaction is received, in the order that those - peers' announcements were received. In this release, the download logic has - changed to randomize the fetch order across peers and to prefer sending - download requests to outbound peers over inbound peers. This fixes an issue - where inbound peers could prevent a node from getting a transaction. - (#14897, #15834) - -- If a Tor hidden service is being used, Bitcoin Core will be bound to - the standard port 8333 even if a different port is configured for - clearnet connections. This prevents leaking node identity through use - of identical non-default port numbers. (#15651) - -Mempool and transaction relay ------------------------------ - -- Allows one extra single-ancestor transaction per package. Previously, - if a transaction in the mempool had 25 descendants, or it and all of - its descendants were over 101,000 vbytes, any newly-received - transaction that was also a descendant would be ignored. Now, one - extra descendant will be allowed provided it is an immediate - descendant (child) and the child's size is 10,000 vbytes or less. - This makes it possible for two-party contract protocols such as - Lightning Network to give each participant an output they can spend - immediately for Child-Pays-For-Parent (CPFP) fee bumping without - allowing one malicious participant to fill the entire package and thus - prevent the other participant from spending their output. (#15681) - -- Transactions with outputs paying v1 to v16 witness versions (future - segwit versions) are now accepted into the mempool, relayed, and - mined. Attempting to spend those outputs remains forbidden by policy - ("non-standard"). When this change has been widely deployed, wallets - and services can accept any valid bech32 Bitcoin address without - concern that transactions paying future segwit versions will become - stuck in an unconfirmed state. (#15846) - -- Legacy transactions (transactions with no segwit inputs) must now be - sent using the legacy encoding format, enforcing the rule specified in - BIP144. (#14039) - -Wallet ------- - -- When in pruned mode, a rescan that was triggered by an `importwallet`, - `importpubkey`, `importaddress`, or `importprivkey` RPC will only fail - when blocks have been pruned. Previously it would fail when `-prune` - has been set. This change allows setting `-prune` to a high value - (e.g. the disk size) without the calls to any of the import RPCs - failing until the first block is pruned. (#15870) - -- When creating a transaction with a fee above `-maxtxfee` (default 0.1 - BTC), the RPC commands `walletcreatefundedpsbt` and - `fundrawtransaction` will now fail instead of rounding down the fee. - Be aware that the `feeRate` argument is specified in BTC per 1,000 - vbytes, not satoshi per vbyte. (#16257) - -- A new wallet flag `avoid_reuse` has been added (default off). When - enabled, a wallet will distinguish between used and unused addresses, - and default to not use the former in coin selection. When setting - this flag on an existing wallet, rescanning the blockchain is required - to correctly mark previously used destinations. Together with "avoid - partial spends" (added in Bitcoin Core v0.17.0), this can eliminate a - serious privacy issue where a malicious user can track spends by - sending small payments to a previously-paid address that would then - be included with unrelated inputs in future payments. (#13756) - -Build system changes --------------------- - -- Python >=3.5 is now required by all aspects of the project. This - includes the build systems, test framework and linters. The previously - supported minimum (3.4), was EOL in March 2019. (#14954) - -- The minimum supported miniUPnPc API version is set to 10. This keeps - compatibility with Ubuntu 16.04 LTS and Debian 8 `libminiupnpc-dev` - packages. Please note, on Debian this package is still vulnerable to - [CVE-2017-8798](https://security-tracker.debian.org/tracker/CVE-2017-8798) - (in jessie only) and - [CVE-2017-1000494](https://security-tracker.debian.org/tracker/CVE-2017-1000494) - (both in jessie and in stretch). (#15993) - -0.19.0 change log -================= - -### Consensus -- #16128 Delete error-prone CScript constructor only used with FindAndDelete (instagibbs) -- #16060 Bury bip9 deployments (jnewbery) - -### Policy -- #15557 Enhance `bumpfee` to include inputs when targeting a feerate (instagibbs) -- #15846 Make sending to future native witness outputs standard (sipa) - -### Block and transaction handling -- #15632 Remove ResendWalletTransactions from the Validation Interface (jnewbery) -- #14121 Index for BIP 157 block filters (jimpo) -- #15141 Rewrite DoS interface between validation and net_processing (sdaftuar) -- #15880 utils and libraries: Replace deprecated Boost Filesystem functions (hebasto) -- #15971 validation: Add compile-time checking for negative locking requirement in LimitValidationInterfaceQueue (practicalswift) -- #15999 init: Remove dead code in LoadChainTip (MarcoFalke) -- #16015 validation: Hold cs_main when reading chainActive in RewindBlockIndex (practicalswift) -- #16056 remove unused magic number from consistency check (instagibbs) -- #16171 Remove -mempoolreplacement to prevent needless block prop slowness (TheBlueMatt) -- #15894 Remove duplicated "Error: " prefix in logs (hebasto) -- #14193 validation: Add missing mempool locks (MarcoFalke) -- #15681 Allow one extra single-ancestor transaction per package (TheBlueMatt) -- #15305 [validation] Crash if disconnecting a block fails (sdaftuar) -- #16471 log correct messages when CPFP fails (jnewbery) -- #16433 txmempool: Remove unused default value MemPoolRemovalReason::UNKNOWN (MarcoFalke) -- #13868 Remove unused fScriptChecks parameter from CheckInputs (Empact) -- #16421 Conservatively accept RBF bumps bumping one tx at the package limits (TheBlueMatt) -- #16854 Prevent UpdateTip log message from being broken up (stevenroose) -- #16956 validation: Make GetWitnessCommitmentIndex public (MarcoFalke) -- #16713 Ignore old versionbit activations to avoid 'unknown softforks' warning (jnewbery) -- #17002 chainparams: Bump assumed chain params (MarcoFalke) -- #16849 Fix block index inconsistency in InvalidateBlock() (sdaftuar) - -### P2P protocol and network code -- #15597 Generate log entry when blocks messages are received unexpectedly (pstratem) -- #15654 Remove unused unsanitized user agent string CNode::strSubVer (MarcoFalke) -- #15689 netaddress: Update CNetAddr for ORCHIDv2 (dongcarl) -- #15834 Fix transaction relay bugs introduced in #14897 and expire transactions from peer in-flight map (sdaftuar) -- #15651 torcontrol: Use the default/standard network port for Tor hidden services, even if the internal port is set differently (luke-jr) -- #16188 Document what happens to getdata of unknown type (MarcoFalke) -- #15649 Add ChaCha20Poly1305@Bitcoin AEAD (jonasschnelli) -- #16152 Disable bloom filtering by default (TheBlueMatt) -- #15993 Drop support of the insecure miniUPnPc versions (hebasto) -- #16197 Use mockable time for tx download (MarcoFalke) -- #16248 Make whitebind/whitelist permissions more flexible (NicolasDorier) -- #16618 [Fix] Allow connection of a noban banned peer (NicolasDorier) -- #16631 Restore default whitelistrelay to true (NicolasDorier) -- #15759 Add 2 outbound block-relay-only connections (sdaftuar) -- #15558 Don't query all DNS seeds at once (sipa) -- #16999 0.19 seeds update (laanwj) - -### Wallet -- #15288 Remove wallet -> node global function calls (ryanofsky) -- #15491 Improve log output for errors during load (gwillen) -- #13541 wallet/rpc: sendrawtransaction maxfeerate (kallewoof) -- #15680 Remove resendwallettransactions RPC method (jnewbery) -- #15508 Refactor analyzepsbt for use outside RPC code (gwillen) -- #15747 Remove plethora of Get*Balance (MarcoFalke) -- #15728 Refactor relay transactions (jnewbery) -- #15639 bitcoin-wallet tool: Drop libbitcoin_server.a dependency (ryanofsky) -- #15853 Remove unused import checkpoints.h (MarcoFalke) -- #15780 add cachable amounts for caching credit/debit values (kallewoof) -- #15778 Move maxtxfee from node to wallet (jnewbery) -- #15901 log on rescan completion (andrewtoth) -- #15917 Avoid logging no_such_file_or_directory error (promag) -- #15452 Replace CScriptID and CKeyID in CTxDestination with dedicated types (instagibbs) -- #15870 Only fail rescan when blocks have actually been pruned (MarcoFalke) -- #15006 Add option to create an encrypted wallet (achow101) -- #16001 Give WalletModel::UnlockContext move semantics (sipa) -- #15741 Batch write imported stuff in importmulti (achow101) -- #16144 do not encrypt wallets with disabled private keys (mrwhythat) -- #15024 Allow specific private keys to be derived from descriptor (meshcollider) -- #13756 "avoid_reuse" wallet flag for improved privacy (kallewoof) -- #16226 Move ismine to the wallet module (achow101) -- #16239 wallet/rpc: follow-up clean-up/fixes to avoid_reuse (kallewoof) -- #16286 refactoring: wallet: Fix GCC 7.4.0 warning (hebasto) -- #16257 abort when attempting to fund a transaction above -maxtxfee (Sjors) -- #16237 Have the wallet give out destinations instead of keys (achow101) -- #16322 Fix -maxtxfee check by moving it to CWallet::CreateTransaction (promag) -- #16361 Remove redundant pre-TopUpKeypool check (instagibbs) -- #16244 Move wallet creation out of the createwallet rpc into its own function (achow101) -- #16227 Refactor CWallet's inheritance chain (achow101) -- #16208 Consume ReserveDestination on successful CreateTransaction (instagibbs) -- #16301 Use CWallet::Import* functions in all import* RPCs (achow101) -- #16402 Remove wallet settings from chainparams (MarcoFalke) -- #16415 Get rid of PendingWalletTx class (ryanofsky) -- #15588 Log the actual wallet file version and no longer publicly expose the "version" record (achow101) -- #16399 Improve wallet creation (fjahr) -- #16475 Enumerate walletdb keys (MarcoFalke) -- #15709 Do not add "setting" key as unknown (Bushstar) -- #16451 Remove CMerkleTx (jnewbery) -- #15906 Move min_depth and max_depth to coin control (amitiuttarwar) -- #16502 Drop unused OldKey (promag) -- #16394 Allow createwallet to take empty passwords to make unencrypted wallets (achow101) -- #15911 Use wallet RBF default for walletcreatefundedpsbt (Sjors) -- #16503 Remove p2pEnabled from Chain interface (ariard) -- #16557 restore coinbase and confirmed/conflicted checks in SubmitMemoryPoolAndRelay() (jnewbery) -- #14934 Descriptor expansion cache clarifications (Sjors) -- #16383 rpcwallet: default include_watchonly to true for watchonly wallets (jb55) -- #16542 Return more specific errors about invalid descriptors (achow101) -- #16572 Fix Char as Bool in Wallet (JeremyRubin) -- #16753 extract PubKey from P2PK script with Solver (theStack) -- #16716 Use wallet name instead of pointer on unload/release (promag) -- #16185 gettransaction: add an argument to decode the transaction (darosior) -- #16745 Translate all initErrors in CreateWalletFromFile (MarcoFalke) -- #16792 Assert that the HRP is lowercase in Bech32::Encode (meshcollider) -- #16624 encapsulate transactions state (ariard) -- #16830 Cleanup walletinitinterface.h (hebasto) -- #16796 Fix segfault in CreateWalletFromFile (MarcoFalke) -- #16866 Rename 'decode' argument in gettransaction method to 'verbose' (jnewbery) -- #16727 Explicit feerate for bumpfee (instagibbs) -- #16609 descriptor: fix missed m_script_arg arg renaming in #14934 (fanquake) - -### RPC and other APIs -- #15492 remove deprecated generate method (Sjors) -- #15566 cli: Replace testnet with chain and return network name as per bip70 (fanquake) -- #15564 cli: Remove duplicate wallet fields from -getinfo (fanquake) -- #15642 Remove deprecated rpc warnings (jnewbery) -- #15637 Rename size to vsize in mempool related calls (fanquake) -- #15620 Uncouple non-wallet rpcs from maxTxFee global (MarcoFalke) -- #15616 Clarify decodescript RPCResult doc (MarcoFalke) -- #15669 Fix help text for signtransactionwithXXX (torkelrogstad) -- #15596 Ignore sendmany::minconf as dummy value (MarcoFalke) -- #15755 remove unused var in rawtransaction.cpp (Bushstar) -- #15746 RPCHelpMan: Always name dictionary keys (MarcoFalke) -- #15748 remove dead mining code (jnewbery) -- #15751 Speed up deriveaddresses for large ranges (sipa) -- #15770 Validate maxfeerate with AmountFromValue (promag) -- #15474 rest/rpc: Make mempoolinfo atomic (promag) -- #15463 Speedup getaddressesbylabel (promag) -- #15784 Remove dependency on interfaces::Chain in SignTransaction (ariard) -- #15323 Expose g_is_mempool_loaded via getmempoolinfo (Empact) -- #15932 Serialize in getblock without cs_main (MarcoFalke) -- #15930 Add balances RPC (MarcoFalke) -- #15730 Show scanning details in getwalletinfo (promag) -- #14802 faster getblockstats using BlockUndo data (FelixWeis) -- #14984 Speedup getrawmempool when verbose=true (promag) -- #16071 Hint for importmulti in help output of importpubkey and importaddress (kristapsk) -- #16063 Mention getwalletinfo where a rescan is triggered (promag) -- #16024 deriveaddresses: Correction of descriptor checksum in RPC example (ccapo) -- #16217 getrawtransaction: inform about blockhash argument when lookup fails (darosior) -- #15427 Add support for descriptors to utxoupdatepsbt (sipa) -- #16262 Allow shutdown while in generateblocks (pstratem) -- #15483 Adding a 'logpath' entry to getrpcinfo (darosior) -- #16325 Clarify that block count means height excl genesis (MarcoFalke) -- #16326 add new utxoupdatepsbt arguments to the CRPCCommand and CPRCCvertParam tables (jnewbery) -- #16332 Add logpath description for getrpcinfo (instagibbs) -- #16240 JSONRPCRequest-aware RPCHelpMan (kallewoof) -- #15996 Deprecate totalfee argument in `bumpfee` (instagibbs) -- #16467 sendrawtransaction help privacy note (jonatack) -- #16596 Fix getblocktemplate CLI example (emilengler) -- #15986 Add checksum to getdescriptorinfo (sipa) -- #16647 add weight to getmempoolentry output (fanquake) -- #16695 Add window final block height to getchaintxstats (leto) -- #16798 Refactor rawtransaction_util's SignTransaction to separate prevtx parsing (achow101) -- #16285 Improve scantxoutset response and help message (promag) -- #16725 Don't show addresses or P2PK in decoderawtransaction (NicolasDorier) -- #16787 Human readable network services (darosior) -- #16251 Improve signrawtransaction error reporting (ajtowns) -- #16873 fix regression in gettransaction (jonatack) -- #16512 Shuffle inputs and outputs after joining psbts (achow101) -- #16521 Use the default maxfeerate value as BTC/kB (Remagpie) -- #16817 Fix casing in getblockchaininfo to be inline with other fields (dangershony) -- #17131 fix -rpcclienttimeout 0 option (fjahr) -- #17249 Add missing deque include to fix build (jbeich) - -### GUI -- #15464 Drop unused return values in WalletFrame (promag) -- #15614 Defer removeAndDeleteWallet when no modal widget is active (promag) -- #15711 Generate bech32 addresses by default (MarcoFalke) -- #15829 update request payment button text and tab description (fanquake) -- #15874 Resolve the qt/guiutil <-> qt/optionsmodel CD (251Labs) -- #15371 Uppercase bech32 addresses in qr codes (benthecarman) -- #15928 Move QRImageWidget to its own file-pair (luke-jr) -- #16113 move coin control "OK" to the right hand side of the dialog (fanquake) -- #16090 Add vertical spacer to peer detail widget (JosuGZ) -- #15886 qt, wallet: Revamp SendConfirmationDialog (hebasto) -- #16263 Use qInfo() if no error occurs (hebasto) -- #16153 Add antialiasing to traffic graph widget (JosuGZ) -- #16350 Remove unused guard (hebasto) -- #16106 Sort wallets in open wallet menu (promag) -- #16291 Stop translating PACKAGE_NAME (MarcoFalke) -- #16380 Remove unused bits from the service flags enum (MarcoFalke) -- #16379 Fix autostart filenames on Linux for testnet/regtest (hebasto) -- #16366 init: Use InitError for all errors in bitcoind/qt (MarcoFalke) -- #16436 Do not create payment server if -disablewallet option provided (hebasto) -- #16514 Remove unused RPCConsole::tabFocus (promag) -- #16497 Generate bech32 addresses by default (take 2, fixup) (MarcoFalke) -- #16349 Remove redundant WalletController::addWallet slot (hebasto) -- #16578 Do not pass in command line arguments to QApplication (achow101) -- #16612 Remove menu icons (laanwj) -- #16677 remove unused PlatformStyle::TextColorIcon (fanquake) -- #16694 Ensure transaction send error is always visible (fanquake) -- #14879 Add warning messages to the debug window (hebasto) -- #16708 Replace obsolete functions of QSslSocket (hebasto) -- #16701 Replace functions deprecated in Qt 5.13 (hebasto) -- #16706 Replace deprecated QSignalMapper by lambda expressions (hebasto) -- #16707 Remove obsolete QModelIndex::child() (hebasto) -- #16758 Replace QFontMetrics::width() with TextWidth() (hebasto) -- #16760 Change uninstall icon on Windows (GChuf) -- #16720 Replace objc_msgSend() function calls with the native Objective-C syntax (hebasto) -- #16788 Update transifex slug for 0.19 (laanwj) -- #15450 Create wallet menu option (achow101) -- #16735 Remove unused menu items for Windows and Linux (GChuf) -- #16826 Do additional character escaping for wallet names and address labels (achow101) -- #15529 Add Qt programs to msvc build (updated, no code changes) (sipsorcery) -- #16714 add prune to intro screen with smart default (Sjors) -- #16858 advise users not to switch wallets when opening a BIP70 URI (jameshilliard) -- #16822 Create wallet menu option follow-ups (jonatack) -- #16882 Re-generate translations before 0.19.0 (MarcoFalke) -- #16928 Rename address checkbox back to bech32 (MarcoFalke) -- #16837 Fix {C{,XX},LD}FLAGS pickup (dongcarl) -- #16971 Change default size of intro frame (emilengler) -- #16988 Periodic translations update (laanwj) -- #16852 When BIP70 is disabled, get PaymentRequest merchant using string search (achow101) -- #16952 make sure to update the UI when deleting a transaction (jonasschnelli) -- #17031 Prevent processing duplicate payment requests (promag) -- #17135 Make polling in ClientModel asynchronous (promag) -- #17120 Fix start timer from non QThread (promag) -- #17257 disable font antialiasing for QR image address (fanquake) - -### Build system -- #14954 Require python 3.5 (MarcoFalke) -- #15580 native_protobuf: avoid system zlib (dongcarl) -- #15601 Switch to python3 (take 3) (MarcoFalke) -- #15581 Make less assumptions about build env (dongcarl) -- #14853 latest RapidCheck (fanquake) -- #15446 Improve depends debuggability (dongcarl) -- #13788 Fix --disable-asm for newer assembly checks/code (luke-jr) -- #12051 add missing debian contrib file to tarball (puchu) -- #15919 Remove unused OpenSSL includes to make it more clear where OpenSSL is used (practicalswift) -- #15978 .gitignore: Don't ignore depends patches (dongcarl) -- #15939 gitian: Remove windows 32 bit build (MarcoFalke) -- #15239 scripts and tools: Move non-linux build source tarballs to "bitcoin-binaries/version" directory (hebasto) -- #14047 Add HKDF_HMAC256_L32 and method to negate a private key (jonasschnelli) -- #16051 add patch to common dependencies (fanquake) -- #16049 switch to secure download of all dependencies (Kemu) -- #16059 configure: Fix thread_local detection (dongcarl) -- #16089 add ability to skip building zeromq (fanquake) -- #15844 Purge libtool archives (dongcarl) -- #15461 update to Boost 1.70 (Sjors) -- #16141 remove GZIP export from gitian descriptors (fanquake) -- #16235 Cleaned up and consolidated msbuild files (no code changes) (sipsorcery) -- #16246 MSVC: Fix error in debug mode (Fix #16245) (NicolasDorier) -- #16183 xtrans: Configure flags cleanup (dongcarl) -- #16258 [MSVC]: Create the config.ini as part of bitcoind build (NicolasDorier) -- #16271 remove -Wall from rapidcheck build flags (fanquake) -- #16309 [MSVC] allow user level project customization (NicolasDorier) -- #16308 [MSVC] Copy build output to src/ automatically after build (NicolasDorier) -- #15457 Check std::system for -[alert|block|wallet]notify (Sjors) -- #16344 use #if HAVE_SYSTEM instead of defined(HAVE_SYSTEM) (Sjors) -- #16352 prune dbus from depends (fanquake) -- #16270 expat 2.2.7 (fanquake) -- #16408 Prune X packages (dongcarl) -- #16386 disable unused Qt features (fanquake) -- #16424 Treat -Wswitch as error when --enable-werror (MarcoFalke) -- #16441 remove qt libjpeg check from bitcoin_qt.m4 (fanquake) -- #16434 Specify AM_CPPFLAGS for ZMQ (domob1812) -- #16534 add Qt Creator Makefile.am.user to .gitignore (Bushstar) -- #16573 disable building libsecp256k1 benchmarks (fanquake) -- #16533 disable libxcb extensions (fanquake) -- #16589 Remove unused src/obj-test folder (MarcoFalke) -- #16435 autoconf: Sane `--enable-debug` defaults (dongcarl) -- #16622 echo property tests status during build (jonatack) -- #16611 Remove src/obj directory from repository (laanwj) -- #16371 ignore macOS make deploy artefacts & add them to clean-local (fanquake) -- #16654 build: update RapidCheck Makefile (jonatack) -- #16370 cleanup package configure flags (fanquake) -- #16746 msbuild: Ignore linker warning (sipsorcery) -- #16750 msbuild: adds bench_bitcoin to auto generated project files (sipsorcery) -- #16810 guix: Remove ssp spec file hack (dongcarl) -- #16477 skip deploying plugins we dont use in macdeployqtplus (fanquake) -- #16413 Bump QT to LTS release 5.9.8 (THETCR) -- #15584 disable BIP70 support by default (fanquake) -- #16871 make building protobuf optional in depends (fanquake) -- #16879 remove redundant sed patching (fanquake) -- #16809 zlib: Move toolchain options to configure (dongcarl) -- #15146 Solve SmartOS FD_ZERO build issue (Empact) -- #16870 update boost macros to latest upstream for improved error reporting (fanquake) -- #16982 Factor out qt translations from build system (laanwj) -- #16926 Add OpenSSL termios fix for musl libc (nmarley) -- #16927 Refresh ZeroMQ 4.3.1 patch (nmarley) -- #17005 Qt version appears only if GUI is being built (ch4ot1c) -- #16468 Exclude depends/Makefile in .gitignore (promag) - -### Tests and QA -- #15296 Add script checking for deterministic line coverage in unit tests (practicalswift) -- #15338 ci: Build and run tests once on freebsd (MarcoFalke) -- #15479 Add .style.yapf (MarcoFalke) -- #15534 lint-format-strings: open files sequentially (fix for OS X) (gwillen) -- #15504 fuzz: Link BasicTestingSetup (shared with unit tests) (MarcoFalke) -- #15473 bench: Benchmark mempooltojson (MarcoFalke) -- #15466 Print remaining jobs in test_runner.py (stevenroose) -- #15631 mininode: Clearer error message on invalid magic bytes (MarcoFalke) -- #15255 Remove travis_wait from lint script (gkrizek) -- #15686 make pruning test faster (jnewbery) -- #15533 .style.yapf: Set column_limit=160 (MarcoFalke) -- #15660 Overhaul p2p_compactblocks.py (sdaftuar) -- #15495 Add regtests for HTTP status codes (domob1812) -- #15772 Properly log named args in authproxy (MarcoFalke) -- #15771 Prevent concurrency issues reading .cookie file (promag) -- #15693 travis: Switch to ubuntu keyserver to avoid timeouts (MarcoFalke) -- #15629 init: Throw error when network specific config is ignored (MarcoFalke) -- #15773 Add BitcoinTestFramework::sync_* methods (MarcoFalke) -- #15797 travis: Bump second timeout to 33 minutes, add rationale (MarcoFalke) -- #15788 Unify testing setups for fuzz, bench, and unit tests (MarcoFalke) -- #15352 Reduce noise level in test_bitcoin output (practicalswift) -- #15779 Add wallet_balance benchmark (MarcoFalke) -- #15843 fix outdated include in blockfilter_index_tests (jamesob) -- #15866 Add missing syncwithvalidationinterfacequeue to wallet_import_rescan (MarcoFalke) -- #15697 Make swap_magic_bytes in p2p_invalid_messages atomic (MarcoFalke) -- #15895 Avoid re-reading config.ini unnecessarily (luke-jr) -- #15896 feature_filelock, interface_bitcoin_cli: Use PACKAGE_NAME in messages rather than hardcoding Bitcoin Core (luke-jr) -- #15897 QA/mininode: Send all headers upfront in send_blocks_and_test to avoid sending an unconnected one (luke-jr) -- #15696 test_runner: Move feature_pruning to base tests (MarcoFalke) -- #15869 Add settings merge test to prevent regresssions (ryanofsky) -- #15758 Add further tests to wallet_balance (MarcoFalke) -- #15841 combine_logs: append node stderr and stdout if it exists (MarcoFalke) -- #15949 test_runner: Move pruning back to extended (MarcoFalke) -- #15927 log thread names by default in functional tests (jnewbery) -- #15664 change default Python block serialization to witness (instagibbs) -- #15988 Add test for ArgsManager::GetChainName (ryanofsky) -- #15963 Make random seed logged and settable (jnewbery) -- #15943 Fail if RPC has been added without tests (MarcoFalke) -- #16036 travis: Run all lint scripts even if one fails (scravy) -- #13555 parameterize adjustment period in versionbits_computeblockversion (JBaczuk) -- #16079 wallet_balance.py: Prevent edge cases (stevenroose) -- #16078 replace tx hash with txid in rawtransaction test (LongShao007) -- #16042 Bump MAX_NODES to 12 (MarcoFalke) -- #16124 Limit Python linting to files in the repo (practicalswift) -- #16143 Mark unit test blockfilter_index_initial_sync as non-deterministic (practicalswift) -- #16214 travis: Fix caching issues (MarcoFalke) -- #15982 Make msg_block a witness block (MarcoFalke) -- #16225 Make coins_tests/updatecoins_simulation_test deterministic (practicalswift) -- #16236 fuzz: Log output even if fuzzer failed (MarcoFalke) -- #15520 cirrus: Run extended test feature_pruning (MarcoFalke) -- #16234 Add test for unknown args (MarcoFalke) -- #16207 stop generating lcov coverage when functional tests fail (asood123) -- #16252 Log to debug.log in all unit tests (MarcoFalke) -- #16289 Add missing ECC_Stop() in GUI rpcnestedtests.cpp (jonasschnelli) -- #16278 Remove unused includes (practicalswift) -- #16302 Add missing syncwithvalidationinterfacequeue to wallet_balance test (MarcoFalke) -- #15538 wallet_bumpfee.py: Make sure coin selection produces change (instagibbs) -- #16294 Create at most one testing setup (MarcoFalke) -- #16299 bench: Move generated data to a dedicated translation unit (promag) -- #16329 Add tests for getblockchaininfo.softforks (MarcoFalke) -- #15687 tool wallet test coverage for unexpected writes to wallet (jonatack) -- #16267 bench: Benchmark blocktojson (fanatid) -- #14505 Add linter to make sure single parameter constructors are marked explicit (practicalswift) -- #16338 Disable other targets when enable-fuzz is set (qmma70) -- #16334 rpc_users: Also test rpcauth.py with password (dongcarl) -- #15282 Replace hard-coded hex tx with class in test framework (stevenroose) -- #16390 Add --filter option to test_runner.py (promag) -- #15891 Require standard txs in regtest by default (MarcoFalke) -- #16374 Enable passing wildcard test names to test runner from root (jonatack) -- #16420 Fix race condition in wallet_encryption test (jonasschnelli) -- #16422 remove redundant setup in addrman_tests (zenosage) -- #16438 travis: Print memory and number of cpus (MarcoFalke) -- #16445 Skip flaky p2p_invalid_messages test on macOS (fjahr) -- #16459 Fix race condition in example_test.py (sdaftuar) -- #16464 Ensure we don't generate a too-big block in p2sh sigops test (sdaftuar) -- #16491 fix deprecated log.warn in feature_dbcrash test (jonatack) -- #15134 Switch one of the Travis jobs to an unsigned char environment (-funsigned-char) (practicalswift) -- #16505 Changes verbosity of msbuild from quiet to normal in the appveyor script (sipsorcery) -- #16293 Make test cases separate functions (MarcoFalke) -- #16470 Fail early on disconnect in mininode.wait_for_* (MarcoFalke) -- #16277 Suppress output in test_bitcoin for expected errors (gertjaap) -- #16493 Fix test failures (MarcoFalke) -- #16538 Add missing sync_blocks to feature_pruning (MarcoFalke) -- #16509 Adapt test framework for chains other than "regtest" (MarcoFalke) -- #16363 Add test for BIP30 duplicate tx (MarcoFalke) -- #16535 Explain why -whitelist is used in feature_fee_estimation (MarcoFalke) -- #16554 only include and use OpenSSL where it's actually needed (BIP70) (fanquake) -- #16598 Remove confusing hash256 function in util (elichai) -- #16595 travis: Use extended 90 minute timeout when available (MarcoFalke) -- #16563 Add unit test for AddTimeData (mzumsande) -- #16561 Use colors and dots in test_runner.py output only if standard output is a terminal (practicalswift) -- #16465 Test p2sh-witness and bech32 in wallet_import_rescan (MarcoFalke) -- #16582 Rework ci (Use travis only as fallback env) (MarcoFalke) -- #16633 travis: Fix test_runner.py timeouts (MarcoFalke) -- #16646 Run tests with UPnP disabled (fanquake) -- #16623 ci: Add environment files for all settings (MarcoFalke) -- #16656 fix rpc_setban.py race (jonasschnelli) -- #16570 Make descriptor tests deterministic (davereikher) -- #16404 Test ZMQ notification after chain reorg (promag) -- #16726 Avoid common Python default parameter gotcha when mutable dict/list:s are used as default parameter values (practicalswift) -- #16739 ci: Pass down $makejobs to test_runner.py, other improvements (MarcoFalke) -- #16767 Check for codespell in lint-spelling.sh (kristapsk) -- #16768 Make lint-includes.sh work from any directory (kristapsk) -- #15257 Scripts and tools: Bump flake8 to 3.7.8 (Empact) -- #16804 Remove unused try-block in assert_debug_log (MarcoFalke) -- #16850 `servicesnames` field in `getpeerinfo` and `getnetworkinfo` (darosior) -- #16551 Test that low difficulty chain fork is rejected (MarcoFalke) -- #16737 Establish only one connection between nodes in rpc_invalidateblock (MarcoFalke) -- #16845 Add notes on how to generate data/wallets/high_minversion (MarcoFalke) -- #16888 Bump timeouts in slow running tests (MarcoFalke) -- #16864 Add python bech32 impl round-trip test (instagibbs) -- #16865 add some unit tests for merkle.cpp (soroosh-sdi) -- #14696 Add explicit references to related CVE's in p2p_invalid_block test (lucash-dev) -- #16907 lint: Add DisabledOpcodeTemplates to whitelist (MarcoFalke) -- #16898 Remove connect_nodes_bi (MarcoFalke) -- #16917 Move common function assert_approx() into util.py (fridokus) -- #16921 Add information on how to add Vulture suppressions (practicalswift) -- #16920 Fix extra_args in wallet_import_rescan.py (MarcoFalke) -- #16918 Make PORT_MIN in test runner configurable (MarcoFalke) -- #16941 travis: Disable feature_block in tsan run due to oom (MarcoFalke) -- #16929 follow-up to rpc: default maxfeerate value as BTC/kB (jonatack) -- #16959 ci: Set $host before setting fallback values (MarcoFalke) -- #16961 Remove python dead code linter (laanwj) -- #16931 add unittests for CheckProofOfWork (soroosh-sdi) -- #16991 Fix service flag comparison check in rpc_net test (luke-jr) (laanwj) -- #16987 Correct docstring param name (jbampton) -- #17015 Explain QT_QPA_PLATFORM for gui tests (MarcoFalke) -- #17006 Enable UBSan for Travis fuzzing job (practicalswift) -- #17086 Fix fs_tests for unknown locales (carnhofdaki) -- #15903 appveyor: Write @PACKAGE_NAME@ to config (MarcoFalke) -- #16742 test: add executable flag for wallet_watchonly.py (theStack) -- #16740 qa: Relax so that the subscriber is ready before publishing zmq messages (#16740) - -### Miscellaneous -- #15335 Fix lack of warning of unrecognized section names (AkioNak) -- #15528 contrib: Bump gitian descriptors for 0.19 (MarcoFalke) -- #15609 scripts and tools: Set 'distro' explicitly (hebasto) -- #15519 Add Poly1305 implementation (jonasschnelli) -- #15643 contrib: Gh-merge: include acks in merge commit (MarcoFalke) -- #15838 scripts and tools: Fetch missing review comments in github-merge.py (nkostoulas) -- #15920 lint: Check that all wallet args are hidden (MarcoFalke) -- #15849 Thread names in logs and deadlock debug tools (jamesob) -- #15650 Handle the result of posix_fallocate system call (lucayepa) -- #15766 scripts and tools: Upgrade gitian image before signing (hebasto) -- #15512 Add ChaCha20 encryption option (XOR) (jonasschnelli) -- #15968 Fix portability issue with pthreads (grim-trigger) -- #15970 Utils and libraries: fix static_assert for macro HAVE_THREAD_LOCAL (orientye) -- #15863 scripts and tools: Ensure repos are up-to-date in gitian-build.py (hebasto) -- #15224 Add RNG strengthening (10ms once every minute) (sipa) -- #15840 Contrib scripts: Filter IPv6 by ASN (abitfan) -- #13998 Scripts and tools: gitian-build.py improvements and corrections (hebasto) -- #15236 scripts and tools: Make --setup command independent (hebasto) -- #16114 contrib: Add curl as a required program in gitian-build.py (fanquake) -- #16046 util: Add type safe gettime (MarcoFalke) -- #15703 Update secp256k1 subtree to latest upstream (sipa) -- #16086 contrib: Use newer config.guess & config.sub in install_db4.sh (fanquake) -- #16130 Don't GPG sign intermediate commits with github-merge tool (stevenroose) -- #16162 scripts: Add key for michael ford (fanquake) to trusted keys list (fanquake) -- #16201 devtools: Always use unabbreviated commit IDs in github-merge.py (laanwj) -- #16112 util: Log early messages (MarcoFalke) -- #16223 devtools: Fetch and display ACKs at sign-off time in github-merge (laanwj) -- #16300 util: Explain why the path is cached (MarcoFalke) -- #16314 scripts and tools: Update copyright_header.py script (hebasto) -- #16158 Fix logic of memory_cleanse() on MSVC and clean up docs (real-or-random) -- #14734 fix an undefined behavior in uint::SetHex (kazcw) -- #16327 scripts and tools: Update ShellCheck linter (hebasto) -- #15277 contrib: Enable building in guix containers (dongcarl) -- #16362 Add bilingual_str type (hebasto) -- #16481 logs: add missing space (harding) -- #16581 sipsorcery gitian key (sipsorcery) -- #16566 util: Refactor upper/lowercase functions (kallewoof) -- #16620 util: Move resolveerrmsg to util/error (MarcoFalke) -- #16625 scripts: Remove github-merge.py (fanquake) -- #15864 Fix datadir handling (hebasto) -- #16670 util: Add join helper to join a list of strings (MarcoFalke) -- #16665 scripts: Move update-translations.py to maintainer-tools repo (fanquake) -- #16730 Support serialization of `std::vector<bool>` (sipa) -- #16556 Fix systemd service file configuration directory setup (setpill) -- #15615 Add log output during initial header sync (jonasschnelli) -- #16774 Avoid unnecessary "Synchronizing blockheaders" log messages (jonasschnelli) -- #16489 log: harmonize bitcoind logging (jonatack) -- #16577 util: Cbufferedfile fixes and unit test (LarryRuane) -- #16984 util: Make thread names shorter (hebasto) -- #17038 Don't rename main thread at process level (laanwj) -- #17184 util: Filter out macos process serial number (hebasto) -- #17095 util: Filter control characters out of log messages (laanwj) -- #17085 init: Change fallback locale to C.UTF-8 (laanwj) -- #16957 9% less memory: make SaltedOutpointHasher noexcept (martinus) - -### Documentation -- #15514 Update Transifex links (fanquake) -- #15513 add "sections" info to example bitcoin.conf (fanquake) -- #15530 Move wallet lock annotations to header (MarcoFalke) -- #15562 remove duplicate clone step in build-windows.md (fanquake) -- #15565 remove release note fragments (fanquake) -- #15444 Additional productivity tips (Sjors) -- #15577 Enable TLS in link to chris.beams.io (JeremyRand) -- #15604 release note for disabling reject messages by default (jnewbery) -- #15611 Add Gitian key for droark (droark) -- #15626 Update ACK description in CONTRIBUTING.md (jonatack) -- #15603 Add more tips to productivity.md (gwillen) -- #15683 Comment for seemingly duplicate LIBBITCOIN_SERVER (Bushstar) -- #15685 rpc-mining: Clarify error messages (MarcoFalke) -- #15760 Clarify sendrawtransaction::maxfeerate==0 help (MarcoFalke) -- #15659 fix findFork comment (r8921039) -- #15718 Improve netaddress comments (dongcarl) -- #15833 remove out-of-date comment on pay-to-witness support (r8921039) -- #15821 Remove upgrade note in release notes from EOL versions (MarcoFalke) -- #15267 explain AcceptToMemoryPoolWorker's coins_to_uncache (jamesob) -- #15887 Align code example style with clang-format (hebasto) -- #15877 Fix -dustrelayfee= argument docs grammar (keepkeyjon) -- #15908 Align MSVC build options with Linux build ones (hebasto) -- #15941 Add historical release notes for 0.18.0 (laanwj) -- #15794 Clarify PR guidelines w/re documentation (dongcarl) -- #15607 Release process updates (jonatack) -- #14364 Clarify -blocksdir usage (sangaman) -- #15777 Add doxygen comments for keypool classes (jnewbery) -- #15820 Add productivity notes for dummy rebases (dongcarl) -- #15922 Explain how to pass in non-fundamental types into functions (MarcoFalke) -- #16080 build/doc: update bitcoin_config.h packages, release process (jonatack) -- #16047 analyzepsbt description in doc/psbt.md (jonatack) -- #16039 add release note for 14954 (fanquake) -- #16139 Add riscv64 to outputs list in release-process.md (JeremyRand) -- #16140 create security policy (narula) -- #16164 update release process for SECURITY.md (jonatack) -- #16213 Remove explicit mention of versions from SECURITY.md (MarcoFalke) -- #16186 doc/lint: Fix spelling errors identified by codespell 1.15.0 (Empact) -- #16149 Rework section on ACK in CONTRIBUTING.md (MarcoFalke) -- #16196 Add release notes for 14897 & 15834 (MarcoFalke) -- #16241 add rapidcheck to vcpkg install list (fanquake) -- #16243 Remove travis badge from readme (MarcoFalke) -- #16256 remove orphaned header in developer notes (jonatack) -- #15964 Improve build-osx document formatting (giulio92) -- #16313 Fix broken link in doc/build-osx.md (jonatack) -- #16330 Use placeholder instead of key expiration date (hebasto) -- #16339 add reduce-memory.md (fanquake) -- #16347 Include static members in Doxygen (dongcarl) -- #15824 Improve netbase comments (dongcarl) -- #16430 Update bips 35, 37 and 111 status (MarcoFalke) -- #16455 Remove downgrading warning in release notes, per 0.18 branch (MarcoFalke) -- #16484 update labels in CONTRIBUTING.md (MarcoFalke) -- #16483 update Python command in msvc readme (sipsorcery) -- #16504 Add release note for the deprecated totalFee option of bumpfee (promag) -- #16448 add note on precedence of options in bitcoin.conf (fanquake) -- #16536 Update and extend benchmarking.md (ariard) -- #16530 Fix grammar and punctuation in developer notes (Tech1k) -- #16574 Add historical release notes for 0.18.1 (laanwj) -- #16585 Update Markdown syntax for bdb packages (emilengler) -- #16586 Mention other ways to conserve memory on compilation (MarcoFalke) -- #16605 Add missing contributor to 0.18.1 release notes (meshcollider) -- #16615 Fix typos in COPYRIGHT (gapeman) -- #16626 Fix spelling error chache -> cache (nilswloewen) -- #16587 Improve versionbits.h documentation (ariard) -- #16643 Add ZMQ dependencies to the Fedora build instructions (hebasto) -- #16634 Refer in rpcbind doc to the manpage (MarcoFalke) -- #16555 mention whitelist is inbound, and applies to blocksonly (Sjors) -- #16645 initial RapidCheck property-based testing documentation (jonatack) -- #16691 improve depends prefix documentation (fanquake) -- #16629 Add documentation for the new whitelist permissions (NicolasDorier) -- #16723 Update labels in CONTRIBUTING.md (hebasto) -- #16461 Tidy up shadowing section (promag) -- #16621 add default bitcoin.conf locations (GChuf) -- #16752 Delete stale URL in test README (michaelfolkson) -- #14862 Declare BLOCK_VALID_HEADER reserved (MarcoFalke) -- #16806 Add issue templates for bug and feature request (MarcoFalke) -- #16857 Elaborate need to re-login on Debian-based after usermod for Tor group (clashicly) -- #16863 Add a missing closing parenthesis in the bitcoin-wallet's help (darosior) -- #16757 CChainState return values (MarcoFalke) -- #16847 add comments clarifying how local services are advertised (jamesob) -- #16812 Fix whitespace errs in .md files, bitcoin.conf, and Info.plist.in (ch4ot1c) -- #16885 Update tx-size-small comment with relevant CVE disclosure (instagibbs) -- #16900 Fix doxygen comment for SignTransaction in rpc/rawtransaction_util (MarcoFalke) -- #16914 Update homebrew instruction for doxygen (Sjors) -- #16912 Remove Doxygen intro from src/bitcoind.cpp (ch4ot1c) -- #16960 replace outdated OpenSSL comment in test README (fanquake) -- #16968 Remove MSVC update step from translation process (laanwj) -- #16953 Improve test READMEs (fjahr) -- #16962 Put PR template in comments (laanwj) -- #16397 Clarify includeWatching for fundrawtransaction (stevenroose) -- #15459 add how to calculate blockchain and chainstate size variables to release process (marcoagner) -- #16997 Update bips.md for 0.19 (laanwj) -- #17001 Remove mention of renamed mapBlocksUnlinked (MarcoFalke) -- #17014 Consolidate release notes before 0.19.0 (move-only) (MarcoFalke) -- #17111 update bips.md with buried BIP9 deployments (MarcoFalke) - -Credits -======= - -Thanks to everyone who directly contributed to this release: - -- 251 -- Aaron Clauson -- Akio Nakamura -- Alistair Mann -- Amiti Uttarwar -- Andrew Chow -- andrewtoth -- Anthony Towns -- Antoine Riard -- Aseem Sood -- Ben Carman -- Ben Woosley -- bpay -- Carl Dong -- Carnhof Daki -- Chris Capobianco -- Chris Moore -- Chuf -- clashic -- clashicly -- Cory Fields -- Daki Carnhof -- Dan Gershony -- Daniel Edgecumbe -- Daniel Kraft -- Daniel McNally -- darosior -- David A. Harding -- David Reikher -- Douglas Roark -- Elichai Turkel -- Emil -- Emil Engler -- ezegom -- Fabian Jahr -- fanquake -- Felix Weis -- Ferdinando M. Ametrano -- fridokus -- gapeman -- GChuf -- Gert-Jaap Glasbergen -- Giulio Lombardo -- Glenn Willen -- Graham Krizek -- Gregory Sanders -- grim-trigger -- gwillen -- Hennadii Stepanov -- Jack Mallers -- James Hilliard -- James O'Beirne -- Jan Beich -- Jeremy Rubin -- JeremyRand -- Jim Posen -- John Bampton -- John Newbery -- Jon Atack -- Jon Layton -- Jonas Schnelli -- Jonathan "Duke" Leto -- João Barbosa -- Joonmo Yang -- Jordan Baczuk -- Jorge Timón -- Josu Goñi -- Julian Fleischer -- Karl-Johan Alm -- Kaz Wesley -- keepkeyjon -- Kirill Fomichev -- Kristaps Kaupe -- Kristian Kramer -- Larry Ruane -- Lenny Maiorani -- LongShao007 -- Luca Venturini -- lucash-dev -- Luke Dashjr -- marcoagner -- MarcoFalke -- marcuswin -- Martin Ankerl -- Martin Zumsande -- Matt Corallo -- MeshCollider -- Michael Folkson -- Miguel Herranz -- Nathan Marley -- Neha Narula -- nicolas.dorier -- Nils Loewen -- nkostoulas -- orient -- Patrick Strateman -- Peter Bushnell -- Peter Wagner -- Pieter Wuille -- practicalswift -- qmma -- r8921039 -- RJ Rybarczyk -- Russell Yanofsky -- Samuel Dobson -- Sebastian Falbesoner -- setpill -- shannon1916 -- Sjors Provoost -- soroosh-sdi -- Steven Roose -- Suhas Daftuar -- tecnovert -- THETCR -- Tim Ruffing -- Tobias Kaderle -- Torkel Rogstad -- Ulrich Kempken -- whythat -- William Casarin -- Wladimir J. van der Laan -- zenosage +Due to a last-minute issue (#17449), 0.19.0, although it was tagged, was never released. -As well as everyone that helped translating on [Transifex](https://www.transifex.com/bitcoin/bitcoin/). +See the release notes for 0.19.0.1 instead. diff --git a/doc/release-process.md b/doc/release-process.md index 2c3c4e3869..1ffef3e106 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -44,7 +44,8 @@ Release Process #### After branch-off (on the major release branch) -- Update the versions and the link to the release notes draft in `doc/release-notes.md`. +- Update the versions. +- Create a pinned meta-issue for testing the release candidate (see [this issue](https://github.com/bitcoin/bitcoin/issues/17079) for an example) and provide a link to it in the release announcements where useful. #### Before final release @@ -116,10 +117,8 @@ Ensure gitian-builder is up-to-date: pushd ./gitian-builder mkdir -p inputs - wget -P inputs https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch - echo 'a8c4e9cafba922f89de0df1f2152e7be286aba73f78505169bc351a7938dd911 inputs/osslsigncode-Backports-to-1.7.1.patch' | sha256sum -c - wget -P inputs https://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz - echo 'f9a8cdb38b9c309326764ebc937cba1523a3a751a7ab05df3ecc99d18ae466c9 inputs/osslsigncode-1.7.1.tar.gz' | sha256sum -c + wget -O inputs/osslsigncode-2.0.tar.gz https://github.com/mtrojnar/osslsigncode/archive/2.0.tar.gz + echo '5a60e0a4b3e0b4d655317b2f12a810211c50242138322b16e7e01c6fbb89d92f inputs/osslsigncode-2.0.tar.gz' | sha256sum -c popd Create the macOS SDK tarball, see the [macOS build instructions](build-osx.md#deterministic-macos-dmg-notes) for details, and copy it into the inputs directory. @@ -317,7 +316,7 @@ bitcoin.org (see below for bitcoin.org update instructions). instructions: https://github.com/bitcoin-dot-org/bitcoin.org/blob/master/docs/adding-events-release-notes-and-alerts.md#release-notes - After the pull request is merged, the website will automatically show the newest version within 15 minutes, as well - as update the OS download links. Ping @saivann/@harding (saivann/harding on Freenode) in case anything goes wrong + as update the OS download links. - Update other repositories and websites for new version @@ -332,7 +331,12 @@ bitcoin.org (see below for bitcoin.org update instructions). - Notify BlueMatt so that he can start building [the PPAs](https://launchpad.net/~bitcoin/+archive/ubuntu/bitcoin) - - Create a new branch for the major release "0.xx" (used to build the snap package) + - Push the flatpak to flathub, e.g. https://github.com/flathub/org.bitcoincore.bitcoin-qt/pull/2 + + - Push the latest version to master (if applicable), e.g. https://github.com/bitcoin-core/packaging/pull/32 + + - Create a new branch for the major release "0.xx" from master (used to build the snap package) and request the + track (if applicable), e.g. https://forum.snapcraft.io/t/track-request-for-bitcoin-core-snap/10112/7 - Notify MarcoFalke so that he can start building the snap package @@ -356,8 +360,6 @@ bitcoin.org (see below for bitcoin.org update instructions). - Create a [new GitHub release](https://github.com/bitcoin/bitcoin/releases/new) with a link to the archived release notes - - Create a pinned meta-issue for testing the release candidate (see [this issue](https://github.com/bitcoin/bitcoin/issues/15555) for an example) and provide a link to it in the release announcements where useful - - Announce the release: - bitcoin-dev and bitcoin-core-dev mailing list diff --git a/libbitcoinconsensus.pc.in b/libbitcoinconsensus.pc.in index eb920c47eb..1ceab280bb 100644 --- a/libbitcoinconsensus.pc.in +++ b/libbitcoinconsensus.pc.in @@ -8,4 +8,3 @@ Description: Library for the Bitcoin consensus protocol. Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lbitcoinconsensus Cflags: -I${includedir} -Requires.private: libcrypto diff --git a/share/qt/Info.plist.in b/share/qt/Info.plist.in index ffa001bb7f..275078c88d 100644 --- a/share/qt/Info.plist.in +++ b/share/qt/Info.plist.in @@ -3,7 +3,7 @@ <plist version="0.9"> <dict> <key>LSMinimumSystemVersion</key> - <string>10.10.0</string> + <string>10.12.0</string> <key>LSArchitecturePriority</key> <array> diff --git a/share/setup.nsi.in b/share/setup.nsi.in index 649483c732..be482ae741 100644 --- a/share/setup.nsi.in +++ b/share/setup.nsi.in @@ -1,4 +1,4 @@ -Name "@PACKAGE_NAME@ (@WINDOWS_BITS@-bit)" +Name "@PACKAGE_NAME@ (64-bit)" RequestExecutionLevel highest SetCompressor /SOLID lzma @@ -28,9 +28,7 @@ SetCompressor /SOLID lzma # Included files !include Sections.nsh !include MUI2.nsh -!if "@WINDOWS_BITS@" == "64" !include x64.nsh -!endif # Variables Var StartMenuGroup @@ -48,12 +46,8 @@ Var StartMenuGroup !insertmacro MUI_LANGUAGE English # Installer attributes -OutFile @abs_top_srcdir@/@PACKAGE_TARNAME@-@PACKAGE_VERSION@-win@WINDOWS_BITS@-setup-unsigned.exe -!if "@WINDOWS_BITS@" == "64" +OutFile @abs_top_srcdir@/@PACKAGE_TARNAME@-@PACKAGE_VERSION@-win64-setup-unsigned.exe InstallDir $PROGRAMFILES64\Bitcoin -!else -InstallDir $PROGRAMFILES\Bitcoin -!endif CRCCheck on XPStyle on BrandingText " " @@ -94,7 +88,7 @@ Section -post SEC0001 !insertmacro MUI_STARTMENU_WRITE_BEGIN Application CreateDirectory $SMPROGRAMS\$StartMenuGroup CreateShortcut "$SMPROGRAMS\$StartMenuGroup\$(^Name).lnk" $INSTDIR\@BITCOIN_GUI_NAME@@EXEEXT@ - CreateShortcut "$SMPROGRAMS\$StartMenuGroup\@PACKAGE_NAME@ (testnet, @WINDOWS_BITS@-bit).lnk" "$INSTDIR\@BITCOIN_GUI_NAME@@EXEEXT@" "-testnet" "$INSTDIR\@BITCOIN_GUI_NAME@@EXEEXT@" 1 + CreateShortcut "$SMPROGRAMS\$StartMenuGroup\@PACKAGE_NAME@ (testnet, 64-bit).lnk" "$INSTDIR\@BITCOIN_GUI_NAME@@EXEEXT@" "-testnet" "$INSTDIR\@BITCOIN_GUI_NAME@@EXEEXT@" 1 CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall $(^Name).lnk" $INSTDIR\uninstall.exe !insertmacro MUI_STARTMENU_WRITE_END WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayName "$(^Name)" @@ -138,7 +132,7 @@ Section -un.post UNSEC0001 DeleteRegKey HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\Uninstall $(^Name).lnk" Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\$(^Name).lnk" - Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\@PACKAGE_NAME@ (testnet, @WINDOWS_BITS@-bit).lnk" + Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\@PACKAGE_NAME@ (testnet, 64-bit).lnk" Delete /REBOOTOK "$SMSTARTUP\Bitcoin.lnk" Delete /REBOOTOK $INSTDIR\uninstall.exe Delete /REBOOTOK $INSTDIR\debug.log @@ -160,7 +154,6 @@ SectionEnd # Installer functions Function .onInit InitPluginsDir -!if "@WINDOWS_BITS@" == "64" ${If} ${RunningX64} ; disable registry redirection (enable access to 64-bit portion of registry) SetRegView 64 @@ -168,7 +161,6 @@ Function .onInit MessageBox MB_OK|MB_ICONSTOP "Cannot install 64-bit version on a 32-bit system." Abort ${EndIf} -!endif FunctionEnd # Uninstaller functions diff --git a/src/Makefile.am b/src/Makefile.am index a14e44d2c0..27c87688b4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,7 +19,7 @@ else LIBUNIVALUE = $(UNIVALUE_LIBS) endif -BITCOIN_INCLUDES=-I$(builddir) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) +BITCOIN_INCLUDES=-I$(builddir) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include BITCOIN_INCLUDES += $(UNIVALUE_CFLAGS) @@ -119,6 +119,7 @@ BITCOIN_CORE_H = \ compat.h \ compat/assumptions.h \ compat/byteswap.h \ + compat/cpuid.h \ compat/endian.h \ compat/sanity.h \ compressor.h \ @@ -175,6 +176,7 @@ BITCOIN_CORE_H = \ protocol.h \ psbt.h \ random.h \ + randomenv.h \ reverse_iterator.h \ reverselock.h \ rpc/blockchain.h \ @@ -503,6 +505,7 @@ libbitcoin_util_a_SOURCES = \ interfaces/handler.cpp \ logging.cpp \ random.cpp \ + randomenv.cpp \ rpc/request.cpp \ support/cleanse.cpp \ sync.cpp \ @@ -568,7 +571,7 @@ bitcoind_LDADD = \ $(LIBMEMENV) \ $(LIBSECP256K1) -bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) +bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) # bitcoin-cli binary # bitcoin_cli_SOURCES = bitcoin-cli.cpp @@ -586,7 +589,7 @@ bitcoin_cli_LDADD = \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CRYPTO) -bitcoin_cli_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS) +bitcoin_cli_LDADD += $(BOOST_LIBS) $(EVENT_LIBS) # # bitcoin-tx binary # @@ -607,7 +610,7 @@ bitcoin_tx_LDADD = \ $(LIBBITCOIN_CRYPTO) \ $(LIBSECP256K1) -bitcoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) +bitcoin_tx_LDADD += $(BOOST_LIBS) # # bitcoin-wallet binary # @@ -634,7 +637,7 @@ bitcoin_wallet_LDADD = \ $(LIBSECP256K1) \ $(LIBUNIVALUE) -bitcoin_wallet_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(ZMQ_LIBS) +bitcoin_wallet_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(ZMQ_LIBS) # # bitcoinconsensus library # @@ -714,6 +717,8 @@ if EMBEDDED_LEVELDB include Makefile.leveldb.include endif +include Makefile.test_util.include + if ENABLE_TESTS include Makefile.test.include endif diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index c9e4fcc4bc..1c97e22de8 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -39,13 +39,7 @@ bench_bench_bitcoin_SOURCES = \ bench/bech32.cpp \ bench/lockedpool.cpp \ bench/poly1305.cpp \ - bench/prevector.cpp \ - test/util/transaction_utils.h \ - test/util/transaction_utils.cpp \ - test/util/setup_common.h \ - test/util/setup_common.cpp \ - test/util.h \ - test/util.cpp + bench/prevector.cpp nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES) @@ -59,6 +53,7 @@ bench_bench_bitcoin_LDADD = \ $(LIBBITCOIN_UTIL) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_CRYPTO) \ + $(LIBTEST_UTIL) \ $(LIBLEVELDB) \ $(LIBLEVELDB_SSE42) \ $(LIBMEMENV) \ @@ -76,7 +71,7 @@ bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp endif -bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) +bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_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.qt.include b/src/Makefile.qt.include index 13b1470b58..cf09eee2cb 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -136,6 +136,7 @@ BITCOIN_QT_H = \ qt/rpcconsole.h \ qt/sendcoinsdialog.h \ qt/sendcoinsentry.h \ + qt/sendcoinsrecipient.h \ qt/signverifymessagedialog.h \ qt/splashscreen.h \ qt/trafficgraphwidget.h \ @@ -314,7 +315,6 @@ endif qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \ $(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \ $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) -qt_bitcoin_qt_LDADD += $(CRYPTO_LIBS) qt_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) qt_bitcoin_qt_LIBTOOLFLAGS = $(AM_LIBTOOLFLAGS) --tag CXX diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index 562b393b22..8c47fabad9 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -26,12 +26,6 @@ TEST_QT_H = \ qt/test/util.h \ qt/test/wallettests.h -TEST_BITCOIN_CPP = \ - test/util/setup_common.cpp - -TEST_BITCOIN_H = \ - test/util/setup_common.h - qt_test_test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \ $(QT_INCLUDES) $(QT_TEST_INCLUDES) @@ -42,9 +36,7 @@ qt_test_test_bitcoin_qt_SOURCES = \ qt/test/test_main.cpp \ qt/test/uritests.cpp \ qt/test/util.cpp \ - $(TEST_QT_H) \ - $(TEST_BITCOIN_CPP) \ - $(TEST_BITCOIN_H) + $(TEST_QT_H) if ENABLE_WALLET qt_test_test_bitcoin_qt_SOURCES += \ qt/test/addressbooktests.cpp \ @@ -54,7 +46,7 @@ endif # ENABLE_WALLET nodist_qt_test_test_bitcoin_qt_SOURCES = $(TEST_QT_MOC_CPP) -qt_test_test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER) +qt_test_test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER) $(LIBTEST_UTIL) if ENABLE_WALLET qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_UTIL) $(LIBBITCOIN_WALLET) endif @@ -63,7 +55,7 @@ qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) endif qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) \ $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \ - $(QR_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \ + $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \ $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) qt_test_test_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) qt_test_test_bitcoin_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index dd1ade5496..091ef50349 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -4,11 +4,17 @@ FUZZ_TARGETS = \ + test/fuzz/addr_info_deserialize \ test/fuzz/address_deserialize \ test/fuzz/addrman_deserialize \ test/fuzz/banentry_deserialize \ + test/fuzz/base_encode_decode \ test/fuzz/bech32 \ + test/fuzz/block \ test/fuzz/block_deserialize \ + test/fuzz/block_file_info_deserialize \ + test/fuzz/block_filter_deserialize \ + test/fuzz/block_header_and_short_txids_deserialize \ test/fuzz/blockheader_deserialize \ test/fuzz/blocklocator_deserialize \ test/fuzz/blockmerkleroot \ @@ -20,15 +26,38 @@ FUZZ_TARGETS = \ test/fuzz/descriptor_parse \ test/fuzz/diskblockindex_deserialize \ test/fuzz/eval_script \ + test/fuzz/fee_rate_deserialize \ + test/fuzz/flat_file_pos_deserialize \ + test/fuzz/hex \ + test/fuzz/integer \ test/fuzz/inv_deserialize \ + test/fuzz/key_origin_info_deserialize \ + test/fuzz/merkle_block_deserialize \ test/fuzz/messageheader_deserialize \ test/fuzz/netaddr_deserialize \ + test/fuzz/out_point_deserialize \ + test/fuzz/parse_hd_keypath \ test/fuzz/parse_iso8601 \ + test/fuzz/partial_merkle_tree_deserialize \ + test/fuzz/partially_signed_transaction_deserialize \ + test/fuzz/prefilled_transaction_deserialize \ + test/fuzz/parse_numbers \ + test/fuzz/parse_script \ + test/fuzz/parse_univalue \ + test/fuzz/psbt \ + test/fuzz/psbt_input_deserialize \ + test/fuzz/psbt_output_deserialize \ + test/fuzz/pub_key_deserialize \ test/fuzz/script \ + test/fuzz/script_deserialize \ test/fuzz/script_flags \ test/fuzz/service_deserialize \ test/fuzz/spanparsing \ + test/fuzz/sub_net_deserialize \ test/fuzz/transaction \ + test/fuzz/tx_in \ + test/fuzz/tx_in_deserialize \ + test/fuzz/tx_out \ test/fuzz/txoutcompressor_deserialize \ test/fuzz/txundo_deserialize @@ -57,33 +86,22 @@ RAW_TEST_FILES = GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h) BITCOIN_TEST_SUITE = \ - test/util/blockfilter.cpp \ - test/util/blockfilter.h \ - test/util/logging.cpp \ - test/util/logging.h \ - test/util/transaction_utils.cpp \ - test/util/transaction_utils.h \ test/main.cpp \ - test/util/setup_common.h \ - test/util/setup_common.cpp \ - test/util/str.h \ - test/util/str.cpp + $(TEST_UTIL_H) FUZZ_SUITE = \ test/fuzz/fuzz.cpp \ test/fuzz/fuzz.h \ - test/fuzz/FuzzedDataProvider.h \ - test/util/setup_common.cpp \ - test/util/setup_common.h \ - test/util/str.cpp \ - test/util/str.h + test/fuzz/FuzzedDataProvider.h FUZZ_SUITE_LD_COMMON = \ $(LIBBITCOIN_SERVER) \ $(LIBBITCOIN_COMMON) \ $(LIBBITCOIN_UTIL) \ + $(LIBTEST_UTIL) \ $(LIBBITCOIN_CONSENSUS) \ $(LIBBITCOIN_CRYPTO) \ + $(LIBBITCOIN_CLI) \ $(LIBUNIVALUE) \ $(LIBLEVELDB) \ $(LIBLEVELDB_SSE42) \ @@ -91,7 +109,6 @@ FUZZ_SUITE_LD_COMMON = \ $(LIBMEMENV) \ $(LIBSECP256K1) \ $(EVENT_LIBS) \ - $(CRYPTO_LIBS) \ $(EVENT_PTHREADS_LIBS) # test_bitcoin binary # @@ -198,7 +215,7 @@ endif test_test_bitcoin_SOURCES = $(BITCOIN_TEST_SUITE) $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES) test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(TESTDEFS) $(EVENT_CFLAGS) -test_test_bitcoin_LDADD = +test_test_bitcoin_LDADD = $(LIBTEST_UTIL) if ENABLE_WALLET test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET) endif @@ -207,7 +224,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 += $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(RAPIDCHECK_LIBS) +test_test_bitcoin_LDADD += $(BDB_LIBS) $(MINIUPNPC_LIBS) $(RAPIDCHECK_LIBS) test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static if ENABLE_ZMQ @@ -215,6 +232,12 @@ test_test_bitcoin_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS) endif if ENABLE_FUZZ +test_fuzz_block_SOURCES = $(FUZZ_SUITE) test/fuzz/block.cpp +test_fuzz_block_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_block_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_block_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_block_LDADD = $(FUZZ_SUITE_LD_COMMON) + test_fuzz_block_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp test_fuzz_block_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCK_DESERIALIZE=1 test_fuzz_block_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -257,6 +280,12 @@ test_fuzz_bech32_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_bech32_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) test_fuzz_bech32_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_base_encode_decode_SOURCES = $(FUZZ_SUITE) test/fuzz/base_encode_decode.cpp +test_fuzz_base_encode_decode_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_base_encode_decode_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_base_encode_decode_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_base_encode_decode_LDADD = $(FUZZ_SUITE_LD_COMMON) + test_fuzz_txundo_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp test_fuzz_txundo_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTXUNDO_DESERIALIZE=1 test_fuzz_txundo_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -293,6 +322,12 @@ test_fuzz_parse_iso8601_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_parse_iso8601_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) test_fuzz_parse_iso8601_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_psbt_SOURCES = $(FUZZ_SUITE) test/fuzz/psbt.cpp +test_fuzz_psbt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_psbt_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_psbt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_psbt_LDADD = $(FUZZ_SUITE_LD_COMMON) + test_fuzz_script_SOURCES = $(FUZZ_SUITE) test/fuzz/script.cpp test_fuzz_script_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) test_fuzz_script_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -329,6 +364,12 @@ test_fuzz_address_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_address_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) test_fuzz_address_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_hex_SOURCES = $(FUZZ_SUITE) test/fuzz/hex.cpp +test_fuzz_hex_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_hex_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_hex_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_hex_LDADD = $(FUZZ_SUITE_LD_COMMON) + test_fuzz_inv_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp test_fuzz_inv_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DINV_DESERIALIZE=1 test_fuzz_inv_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -353,6 +394,12 @@ test_fuzz_eval_script_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_eval_script_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) test_fuzz_eval_script_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_integer_SOURCES = $(FUZZ_SUITE) test/fuzz/integer.cpp +test_fuzz_integer_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_integer_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_integer_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_integer_LDADD = $(FUZZ_SUITE_LD_COMMON) + test_fuzz_txoutcompressor_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp test_fuzz_txoutcompressor_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTXOUTCOMPRESSOR_DESERIALIZE=1 test_fuzz_txoutcompressor_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -377,6 +424,150 @@ test_fuzz_transaction_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_transaction_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) test_fuzz_transaction_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_addr_info_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_addr_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDR_INFO_DESERIALIZE=1 +test_fuzz_addr_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_addr_info_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_addr_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_block_file_info_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_block_file_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCK_FILE_INFO_DESERIALIZE=1 +test_fuzz_block_file_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_block_file_info_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_block_file_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_block_filter_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_block_filter_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCK_FILTER_DESERIALIZE=1 +test_fuzz_block_filter_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_block_filter_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_block_filter_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_block_header_and_short_txids_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_block_header_and_short_txids_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBLOCK_HEADER_AND_SHORT_TXIDS_DESERIALIZE=1 +test_fuzz_block_header_and_short_txids_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_block_header_and_short_txids_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_block_header_and_short_txids_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_fee_rate_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_fee_rate_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DFEE_RATE_DESERIALIZE=1 +test_fuzz_fee_rate_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_fee_rate_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_fee_rate_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_flat_file_pos_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_flat_file_pos_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DFLAT_FILE_POS_DESERIALIZE=1 +test_fuzz_flat_file_pos_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_flat_file_pos_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_flat_file_pos_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_key_origin_info_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_key_origin_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DKEY_ORIGIN_INFO_DESERIALIZE=1 +test_fuzz_key_origin_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_key_origin_info_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_key_origin_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_merkle_block_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_merkle_block_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DMERKLE_BLOCK_DESERIALIZE=1 +test_fuzz_merkle_block_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_merkle_block_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_merkle_block_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_out_point_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_out_point_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DOUT_POINT_DESERIALIZE=1 +test_fuzz_out_point_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_out_point_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_out_point_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_partially_signed_transaction_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_partially_signed_transaction_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPARTIALLY_SIGNED_TRANSACTION_DESERIALIZE=1 +test_fuzz_partially_signed_transaction_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_partially_signed_transaction_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_partially_signed_transaction_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_partial_merkle_tree_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_partial_merkle_tree_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPARTIAL_MERKLE_TREE_DESERIALIZE=1 +test_fuzz_partial_merkle_tree_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_partial_merkle_tree_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_partial_merkle_tree_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_prefilled_transaction_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_prefilled_transaction_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPREFILLED_TRANSACTION_DESERIALIZE=1 +test_fuzz_prefilled_transaction_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_prefilled_transaction_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_prefilled_transaction_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_psbt_input_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_psbt_input_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPSBT_INPUT_DESERIALIZE=1 +test_fuzz_psbt_input_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_psbt_input_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_psbt_input_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_psbt_output_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_psbt_output_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPSBT_OUTPUT_DESERIALIZE=1 +test_fuzz_psbt_output_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_psbt_output_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_psbt_output_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_pub_key_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_pub_key_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DPUB_KEY_DESERIALIZE=1 +test_fuzz_pub_key_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_pub_key_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_pub_key_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_script_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_script_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSCRIPT_DESERIALIZE=1 +test_fuzz_script_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_script_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_script_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_sub_net_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_sub_net_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSUB_NET_DESERIALIZE=1 +test_fuzz_sub_net_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_sub_net_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_sub_net_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_tx_in_deserialize_SOURCES = $(FUZZ_SUITE) test/fuzz/deserialize.cpp +test_fuzz_tx_in_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DTX_IN_DESERIALIZE=1 +test_fuzz_tx_in_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_tx_in_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_tx_in_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_tx_in_SOURCES = $(FUZZ_SUITE) test/fuzz/tx_in.cpp +test_fuzz_tx_in_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_tx_in_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_tx_in_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_tx_in_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_tx_out_SOURCES = $(FUZZ_SUITE) test/fuzz/tx_out.cpp +test_fuzz_tx_out_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_tx_out_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_tx_out_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_tx_out_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_parse_hd_keypath_SOURCES = $(FUZZ_SUITE) test/fuzz/parse_hd_keypath.cpp +test_fuzz_parse_hd_keypath_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_parse_hd_keypath_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_parse_hd_keypath_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_parse_hd_keypath_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_parse_script_SOURCES = $(FUZZ_SUITE) test/fuzz/parse_script.cpp +test_fuzz_parse_script_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_parse_script_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_parse_script_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_parse_script_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_parse_numbers_SOURCES = $(FUZZ_SUITE) test/fuzz/parse_numbers.cpp +test_fuzz_parse_numbers_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_parse_numbers_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_parse_numbers_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_parse_numbers_LDADD = $(FUZZ_SUITE_LD_COMMON) + +test_fuzz_parse_univalue_SOURCES = $(FUZZ_SUITE) test/fuzz/parse_univalue.cpp +test_fuzz_parse_univalue_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_parse_univalue_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_parse_univalue_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_parse_univalue_LDADD = $(FUZZ_SUITE_LD_COMMON) + endif # ENABLE_FUZZ nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES) diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include new file mode 100644 index 0000000000..505d630b7d --- /dev/null +++ b/src/Makefile.test_util.include @@ -0,0 +1,34 @@ +# Copyright (c) 2013-2019 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +LIBTEST_UTIL=libtest_util.a + +EXTRA_LIBRARIES += \ + $(LIBTEST_UTIL) + +TEST_UTIL_H = \ + test/util/blockfilter.h \ + test/util/logging.h \ + test/util/mining.h \ + test/util/setup_common.h \ + test/util/str.h \ + test/util/transaction_utils.h \ + test/util/wallet.h + +libtest_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS) +libtest_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +libtest_util_a_SOURCES = \ + test/util/blockfilter.cpp \ + test/util/logging.cpp \ + test/util/mining.cpp \ + test/util/setup_common.cpp \ + test/util/str.cpp \ + test/util/transaction_utils.cpp \ + test/util/wallet.cpp \ + $(TEST_UTIL_H) + +LIBTEST_UTIL += $(LIBBITCOIN_SERVER) +LIBTEST_UTIL += $(LIBBITCOIN_COMMON) +LIBTEST_UTIL += $(LIBBITCOIN_UTIL) +LIBTEST_UTIL += $(LIBBITCOIN_CRYPTO_BASE) diff --git a/src/base58.cpp b/src/base58.cpp index e3d2853399..17d3f86ba8 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -7,10 +7,13 @@ #include <hash.h> #include <uint256.h> #include <util/strencodings.h> +#include <util/string.h> #include <assert.h> #include <string.h> +#include <limits> + /** All alphanumeric characters except for "0", "I", "O", and "l" */ static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; static const int8_t mapBase58[256] = { @@ -32,7 +35,7 @@ static const int8_t mapBase58[256] = { -1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1, }; -bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch) +bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch, int max_ret_len) { // Skip leading spaces. while (*psz && IsSpace(*psz)) @@ -42,6 +45,7 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch) int length = 0; while (*psz == '1') { zeroes++; + if (zeroes > max_ret_len) return false; psz++; } // Allocate enough space in big-endian base256 representation. @@ -62,6 +66,7 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch) } assert(carry == 0); length = i; + if (length + zeroes > max_ret_len) return false; psz++; } // Skip trailing spaces. @@ -71,8 +76,6 @@ bool DecodeBase58(const char* psz, std::vector<unsigned char>& vch) return false; // Skip leading zeroes in b256. std::vector<unsigned char>::iterator it = b256.begin() + (size - length); - while (it != b256.end() && *it == 0) - it++; // Copy result into output vector. vch.reserve(zeroes + (b256.end() - it)); vch.assign(zeroes, 0x00); @@ -126,9 +129,12 @@ std::string EncodeBase58(const std::vector<unsigned char>& vch) return EncodeBase58(vch.data(), vch.data() + vch.size()); } -bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet) +bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret_len) { - return DecodeBase58(str.c_str(), vchRet); + if (!ValidAsCString(str)) { + return false; + } + return DecodeBase58(str.c_str(), vchRet, max_ret_len); } std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn) @@ -140,9 +146,9 @@ std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn) return EncodeBase58(vch); } -bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet) +bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len) { - if (!DecodeBase58(psz, vchRet) || + if (!DecodeBase58(psz, vchRet, max_ret_len > std::numeric_limits<int>::max() - 4 ? std::numeric_limits<int>::max() : max_ret_len + 4) || (vchRet.size() < 4)) { vchRet.clear(); return false; @@ -157,7 +163,10 @@ bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet) return true; } -bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet) +bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret) { - return DecodeBase58Check(str.c_str(), vchRet); + if (!ValidAsCString(str)) { + return false; + } + return DecodeBase58Check(str.c_str(), vchRet, max_ret); } diff --git a/src/base58.h b/src/base58.h index d6e0299a1e..90eded4992 100644 --- a/src/base58.h +++ b/src/base58.h @@ -35,13 +35,13 @@ std::string EncodeBase58(const std::vector<unsigned char>& vch); * return true if decoding is successful. * psz cannot be nullptr. */ -NODISCARD bool DecodeBase58(const char* psz, std::vector<unsigned char>& vchRet); +NODISCARD bool DecodeBase58(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len); /** * Decode a base58-encoded string (str) into a byte vector (vchRet). * return true if decoding is successful. */ -NODISCARD bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet); +NODISCARD bool DecodeBase58(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret_len); /** * Encode a byte vector into a base58-encoded string, including checksum @@ -52,12 +52,12 @@ std::string EncodeBase58Check(const std::vector<unsigned char>& vchIn); * Decode a base58-encoded string (psz) that includes a checksum into a byte * vector (vchRet), return true if decoding is successful */ -NODISCARD bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet); +NODISCARD bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet, int max_ret_len); /** * Decode a base58-encoded string (str) that includes a checksum into a byte * vector (vchRet), return true if decoding is successful */ -NODISCARD bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet); +NODISCARD bool DecodeBase58Check(const std::string& str, std::vector<unsigned char>& vchRet, int max_ret_len); #endif // BITCOIN_BASE58_H diff --git a/src/bench/base58.cpp b/src/bench/base58.cpp index 40a7b5e320..d8c9b2e01a 100644 --- a/src/bench/base58.cpp +++ b/src/bench/base58.cpp @@ -47,7 +47,7 @@ static void Base58Decode(benchmark::State& state) const char* addr = "17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem"; std::vector<unsigned char> vch; while (state.KeepRunning()) { - (void) DecodeBase58(addr, vch); + (void) DecodeBase58(addr, vch, 64); } } diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp index 2f47398d99..184367e1e5 100644 --- a/src/bench/block_assemble.cpp +++ b/src/bench/block_assemble.cpp @@ -5,7 +5,8 @@ #include <bench/bench.h> #include <consensus/validation.h> #include <crypto/sha256.h> -#include <test/util.h> +#include <test/util/mining.h> +#include <test/util/wallet.h> #include <txmempool.h> #include <validation.h> diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp index 0e660d6bcd..f39dcc0b71 100644 --- a/src/bench/wallet_balance.cpp +++ b/src/bench/wallet_balance.cpp @@ -6,7 +6,8 @@ #include <interfaces/chain.h> #include <node/context.h> #include <optional.h> -#include <test/util.h> +#include <test/util/mining.h> +#include <test/util/wallet.h> #include <validationinterface.h> #include <wallet/wallet.h> diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 592fcbe8dd..1035e730b8 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -256,7 +256,6 @@ public: return batch[ID_BLOCKCHAININFO]; } result.pushKV("version", batch[ID_NETWORKINFO]["result"]["version"]); - result.pushKV("protocolversion", batch[ID_NETWORKINFO]["result"]["protocolversion"]); result.pushKV("blocks", batch[ID_BLOCKCHAININFO]["result"]["blocks"]); result.pushKV("headers", batch[ID_BLOCKCHAININFO]["result"]["headers"]); result.pushKV("verificationprogress", batch[ID_BLOCKCHAININFO]["result"]["verificationprogress"]); @@ -266,9 +265,7 @@ public: result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]); result.pushKV("chain", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"])); if (!batch[ID_WALLETINFO]["result"].isNull()) { - result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]); result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]); - result.pushKV("keypoololdest", batch[ID_WALLETINFO]["result"]["keypoololdest"]); result.pushKV("keypoolsize", batch[ID_WALLETINFO]["result"]["keypoolsize"]); if (!batch[ID_WALLETINFO]["result"]["unlocked_until"].isNull()) { result.pushKV("unlocked_until", batch[ID_WALLETINFO]["result"]["unlocked_until"]); diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp index 917ecd71c5..a2013edc05 100644 --- a/src/bitcoin-wallet.cpp +++ b/src/bitcoin-wallet.cpp @@ -41,8 +41,8 @@ static bool WalletAppInit(int argc, char* argv[]) } if (argc < 2 || HelpRequested(gArgs)) { std::string usage = strprintf("%s bitcoin-wallet version", PACKAGE_NAME) + " " + FormatFullVersion() + "\n\n" + - "wallet-tool is an offline tool for creating and interacting with Bitcoin Core wallet files.\n" + - "By default wallet-tool will act on wallets in the default mainnet wallet directory in the datadir.\n" + + "bitcoin-wallet is an offline tool for creating and interacting with Bitcoin Core wallet files.\n" + + "By default bitcoin-wallet will act on wallets in the default mainnet wallet directory in the datadir.\n" + "To change the target wallet, use the -datadir, -wallet and -testnet/-regtest arguments.\n\n" + "Usage:\n" + " bitcoin-wallet [options] <command>\n\n" + diff --git a/src/blockfilter.cpp b/src/blockfilter.cpp index 787390be31..5ad22d46eb 100644 --- a/src/blockfilter.cpp +++ b/src/blockfilter.cpp @@ -4,6 +4,7 @@ #include <mutex> #include <sstream> +#include <set> #include <blockfilter.h> #include <crypto/siphash.h> @@ -221,15 +222,14 @@ bool BlockFilterTypeByName(const std::string& name, BlockFilterType& filter_type return false; } -const std::vector<BlockFilterType>& AllBlockFilterTypes() +const std::set<BlockFilterType>& AllBlockFilterTypes() { - static std::vector<BlockFilterType> types; + static std::set<BlockFilterType> types; static std::once_flag flag; std::call_once(flag, []() { - types.reserve(g_filter_types.size()); for (auto entry : g_filter_types) { - types.push_back(entry.first); + types.insert(entry.first); } }); diff --git a/src/blockfilter.h b/src/blockfilter.h index 914b94fec1..828204b875 100644 --- a/src/blockfilter.h +++ b/src/blockfilter.h @@ -7,6 +7,7 @@ #include <stdint.h> #include <string> +#include <set> #include <unordered_set> #include <vector> @@ -97,7 +98,7 @@ const std::string& BlockFilterTypeName(BlockFilterType filter_type); bool BlockFilterTypeByName(const std::string& name, BlockFilterType& filter_type); /** Get a list of known filter types. */ -const std::vector<BlockFilterType>& AllBlockFilterTypes(); +const std::set<BlockFilterType>& AllBlockFilterTypes(); /** Get a comma-separated list of known filter type names. */ const std::string& ListBlockFilterTypes(); diff --git a/src/chainparams.cpp b/src/chainparams.cpp index dd4d3e97ac..0a1cb858ef 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -71,7 +71,7 @@ public: consensus.BIP66Height = 363725; // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931 consensus.CSVHeight = 419328; // 000000000000000004a1b34462cb8aeebd5799177f7a29cf28f2d1961716b5b5 consensus.SegwitHeight = 481824; // 0000000000000000001c8018d9cb3b742ef25114f27563e3fc4a1902167f9893 - consensus.MinBIP9WarningHeight = consensus.SegwitHeight + consensus.nMinerConfirmationWindow; + consensus.MinBIP9WarningHeight = 483840; // segwit activation height + miner confirmation window consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks consensus.nPowTargetSpacing = 10 * 60; @@ -178,7 +178,7 @@ public: consensus.BIP66Height = 330776; // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182 consensus.CSVHeight = 770112; // 00000000025e930139bac5c6c31a403776da130831ab85be56578f3fa75369bb consensus.SegwitHeight = 834624; // 00000000002b980fcd729daaa248fd9316a5200e9b367f4ff2c42453e84201ca - consensus.MinBIP9WarningHeight = consensus.SegwitHeight + consensus.nMinerConfirmationWindow; + consensus.MinBIP9WarningHeight = 836640; // segwit activation height + miner confirmation window consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks consensus.nPowTargetSpacing = 10 * 60; diff --git a/src/compat/cpuid.h b/src/compat/cpuid.h new file mode 100644 index 0000000000..0877ad47d3 --- /dev/null +++ b/src/compat/cpuid.h @@ -0,0 +1,24 @@ +// Copyright (c) 2017-2019 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_COMPAT_CPUID_H +#define BITCOIN_COMPAT_CPUID_H + +#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) +#define HAVE_GETCPUID + +#include <cpuid.h> + +// We can't use cpuid.h's __get_cpuid as it does not support subleafs. +void static inline GetCPUID(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d) +{ +#ifdef __GNUC__ + __cpuid_count(leaf, subleaf, a, b, c, d); +#else + __asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf)); +#endif +} + +#endif // defined(__x86_64__) || defined(__amd64__) || defined(__i386__) +#endif // BITCOIN_COMPAT_CPUID_H diff --git a/src/consensus/validation.h b/src/consensus/validation.h index e602b9d5f3..3401eb64ca 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -114,7 +114,7 @@ inline ValidationState::~ValidationState() {}; class TxValidationState : public ValidationState { private: - TxValidationResult m_result; + TxValidationResult m_result = TxValidationResult::TX_RESULT_UNSET; public: bool Invalid(TxValidationResult result, const std::string &reject_reason="", @@ -129,7 +129,7 @@ public: class BlockValidationState : public ValidationState { private: - BlockValidationResult m_result; + BlockValidationResult m_result = BlockValidationResult::BLOCK_RESULT_UNSET; public: bool Invalid(BlockValidationResult result, const std::string &reject_reason="", diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp index 3257ee7f97..dda7e5230f 100644 --- a/src/crypto/sha256.cpp +++ b/src/crypto/sha256.cpp @@ -8,9 +8,10 @@ #include <assert.h> #include <string.h> +#include <compat/cpuid.h> + #if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) #if defined(USE_ASM) -#include <cpuid.h> namespace sha256_sse4 { void Transform(uint32_t* s, const unsigned char* chunk, size_t blocks); @@ -546,18 +547,7 @@ bool SelfTest() { return true; } - #if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__) || defined(__i386__)) -// We can't use cpuid.h's __get_cpuid as it does not support subleafs. -void inline cpuid(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d) -{ -#ifdef __GNUC__ - __cpuid_count(leaf, subleaf, a, b, c, d); -#else - __asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf)); -#endif -} - /** Check whether the OS has enabled AVX registers. */ bool AVXEnabled() { @@ -572,7 +562,7 @@ bool AVXEnabled() std::string SHA256AutoDetect() { std::string ret = "standard"; -#if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__) || defined(__i386__)) +#if defined(USE_ASM) && defined(HAVE_GETCPUID) bool have_sse4 = false; bool have_xsave = false; bool have_avx = false; @@ -589,7 +579,7 @@ std::string SHA256AutoDetect() (void)enabled_avx; uint32_t eax, ebx, ecx, edx; - cpuid(1, 0, eax, ebx, ecx, edx); + GetCPUID(1, 0, eax, ebx, ecx, edx); have_sse4 = (ecx >> 19) & 1; have_xsave = (ecx >> 27) & 1; have_avx = (ecx >> 28) & 1; @@ -597,7 +587,7 @@ std::string SHA256AutoDetect() enabled_avx = AVXEnabled(); } if (have_sse4) { - cpuid(7, 0, eax, ebx, ecx, edx); + GetCPUID(7, 0, eax, ebx, ecx, edx); have_avx2 = (ebx >> 5) & 1; have_shani = (ebx >> 29) & 1; } diff --git a/src/crypto/sha512.h b/src/crypto/sha512.h index 4118ac1b18..fc7dd1b87e 100644 --- a/src/crypto/sha512.h +++ b/src/crypto/sha512.h @@ -23,6 +23,7 @@ public: CSHA512& Write(const unsigned char* data, size_t len); void Finalize(unsigned char hash[OUTPUT_SIZE]); CSHA512& Reset(); + uint64_t Size() const { return bytes; } }; #endif // BITCOIN_CRYPTO_SHA512_H diff --git a/src/cuckoocache.h b/src/cuckoocache.h index 674f47b956..4ad5818cdc 100644 --- a/src/cuckoocache.h +++ b/src/cuckoocache.h @@ -6,11 +6,11 @@ #define BITCOIN_CUCKOOCACHE_H #include <array> -#include <algorithm> #include <atomic> -#include <cstring> #include <cmath> +#include <cstring> #include <memory> +#include <utility> #include <vector> @@ -11,7 +11,6 @@ #include <ext/stdio_filebuf.h> #endif -#define BOOST_FILESYSTEM_NO_DEPRECATED #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 0437f0c7de..77e09bd5c7 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -15,8 +15,13 @@ #include <util/translation.h> #include <walletinitinterface.h> +#include <algorithm> +#include <iterator> +#include <map> #include <memory> #include <stdio.h> +#include <set> +#include <string> #include <boost/algorithm/string.hpp> // boost::trim @@ -64,6 +69,9 @@ private: static std::string strRPCUserColonPass; /* Stored RPC timer interface (for unregistration) */ static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface; +/* RPC Auth Whitelist */ +static std::map<std::string, std::set<std::string>> g_rpc_whitelist; +static bool g_rpc_whitelist_default = false; static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id) { @@ -183,18 +191,45 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &) jreq.URI = req->GetURI(); std::string strReply; + bool user_has_whitelist = g_rpc_whitelist.count(jreq.authUser); + if (!user_has_whitelist && g_rpc_whitelist_default) { + LogPrintf("RPC User %s not allowed to call any methods\n", jreq.authUser); + req->WriteReply(HTTP_FORBIDDEN); + return false; + // singleton request - if (valRequest.isObject()) { + } else if (valRequest.isObject()) { jreq.parse(valRequest); - + if (user_has_whitelist && !g_rpc_whitelist[jreq.authUser].count(jreq.strMethod)) { + LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, jreq.strMethod); + req->WriteReply(HTTP_FORBIDDEN); + return false; + } UniValue result = tableRPC.execute(jreq); // Send reply strReply = JSONRPCReply(result, NullUniValue, jreq.id); // array of requests - } else if (valRequest.isArray()) + } else if (valRequest.isArray()) { + if (user_has_whitelist) { + for (unsigned int reqIdx = 0; reqIdx < valRequest.size(); reqIdx++) { + if (!valRequest[reqIdx].isObject()) { + throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); + } else { + const UniValue& request = valRequest[reqIdx].get_obj(); + // Parse method + std::string strMethod = find_value(request, "method").get_str(); + if (!g_rpc_whitelist[jreq.authUser].count(strMethod)) { + LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, strMethod); + req->WriteReply(HTTP_FORBIDDEN); + return false; + } + } + } + } strReply = JSONRPCExecBatch(jreq, valRequest.get_array()); + } else throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); @@ -229,6 +264,27 @@ static bool InitRPCAuthentication() { LogPrintf("Using rpcauth authentication.\n"); } + + g_rpc_whitelist_default = gArgs.GetBoolArg("-rpcwhitelistdefault", gArgs.IsArgSet("-rpcwhitelist")); + for (const std::string& strRPCWhitelist : gArgs.GetArgs("-rpcwhitelist")) { + auto pos = strRPCWhitelist.find(':'); + std::string strUser = strRPCWhitelist.substr(0, pos); + bool intersect = g_rpc_whitelist.count(strUser); + std::set<std::string>& whitelist = g_rpc_whitelist[strUser]; + if (pos != std::string::npos) { + std::string strWhitelist = strRPCWhitelist.substr(pos + 1); + std::set<std::string> new_whitelist; + boost::split(new_whitelist, strWhitelist, boost::is_any_of(", ")); + if (intersect) { + std::set<std::string> tmp_whitelist; + std::set_intersection(new_whitelist.begin(), new_whitelist.end(), + whitelist.begin(), whitelist.end(), std::inserter(tmp_whitelist, tmp_whitelist.end())); + new_whitelist = std::move(tmp_whitelist); + } + whitelist = std::move(new_whitelist); + } + } + return true; } diff --git a/src/init.cpp b/src/init.cpp index f02740786d..ab5e3b9342 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -58,6 +58,7 @@ #include <stdint.h> #include <stdio.h> +#include <set> #ifndef WIN32 #include <attributes.h> @@ -281,9 +282,9 @@ void Shutdown(NodeContext& node) node.chain_clients.clear(); UnregisterAllValidationInterfaces(); GetMainSignals().UnregisterBackgroundSignalScheduler(); - GetMainSignals().UnregisterWithMempoolSignals(mempool); globalVerifyHandle.reset(); ECC_Stop(); + if (node.mempool) node.mempool = nullptr; LogPrintf("%s: done\n", __func__); } @@ -497,7 +498,7 @@ void SetupServerArgs() gArgs.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-logthreadnames", strprintf("Prepend debug output with name of the originating thread (only available on platforms supporting thread_local) (default: %u)", DEFAULT_LOGTHREADNAMES), ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); - gArgs.AddArg("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-mocktime=<n>", "Replace actual time with " + UNIX_EPOCH_TIME + " (default: 0)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); gArgs.AddArg("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); @@ -534,6 +535,8 @@ void SetupServerArgs() gArgs.AddArg("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC); gArgs.AddArg("-rpcthreads=<n>", strprintf("Set the number of threads to service RPC calls (default: %d)", DEFAULT_HTTP_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::RPC); gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + gArgs.AddArg("-rpcwhitelist=<whitelist>", "Set a whitelist to filter incoming RPC calls for a specific user. The field <whitelist> comes in the format: <USERNAME>:<rpc 1>,<rpc 2>,...,<rpc n>. If multiple whitelists are set for a given user, they are set-intersected. See -rpcwhitelistdefault documentation for information on default whitelist behavior.", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); + gArgs.AddArg("-rpcwhitelistdefault", "Sets default behavior for rpc whitelisting. Unless rpcwhitelistdefault is set to 0, if any -rpcwhitelist is set, the rpc server acts as if all rpc users are subject to empty-unless-otherwise-specified whitelists. If rpcwhitelistdefault is set to 1 and no -rpcwhitelist is set, rpc server acts as if all rpc users are subject to empty whitelists.", ArgsManager::ALLOW_BOOL, OptionsCategory::RPC); gArgs.AddArg("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC); gArgs.AddArg("-server", "Accept command line and JSON-RPC commands", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); @@ -563,9 +566,7 @@ std::string LicenseInfo() "\n" + "\n" + _("This is experimental software.").translated + "\n" + - strprintf(_("Distributed under the MIT software license, see the accompanying file %s or %s").translated, "COPYING", "<https://opensource.org/licenses/MIT>") + "\n" + - "\n" + - strprintf(_("This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit %s and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard.").translated, "<https://www.openssl.org>") + + strprintf(_("Distributed under the MIT software license, see the accompanying file %s or %s").translated, "COPYING", "<https://opensource.org/licenses/MIT>") + "\n"; } @@ -848,7 +849,7 @@ int nUserMaxConnections; int nFD; ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED); int64_t peer_connect_timeout; -std::vector<BlockFilterType> g_enabled_filter_types; +std::set<BlockFilterType> g_enabled_filter_types; } // namespace @@ -936,13 +937,12 @@ bool AppInitParameterInteraction() g_enabled_filter_types = AllBlockFilterTypes(); } else if (blockfilterindex_value != "0") { const std::vector<std::string> names = gArgs.GetArgs("-blockfilterindex"); - g_enabled_filter_types.reserve(names.size()); for (const auto& name : names) { BlockFilterType filter_type; if (!BlockFilterTypeByName(name, filter_type)) { return InitError(strprintf(_("Unknown -blockfilterindex value %s.").translated, name)); } - g_enabled_filter_types.push_back(filter_type); + g_enabled_filter_types.insert(filter_type); } } @@ -1258,8 +1258,12 @@ bool AppInitMain(NodeContext& node) CScheduler::Function serviceLoop = std::bind(&CScheduler::serviceQueue, &scheduler); threadGroup.create_thread(std::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop)); + // Gather some entropy once per minute. + scheduler.scheduleEvery([]{ + RandAddPeriodic(); + }, 60000); + GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); - GetMainSignals().RegisterWithMempoolSignals(mempool); // Create client interfaces for wallets that are supposed to be loaded // according to -wallet and -disablewallet options. This only constructs @@ -1632,6 +1636,11 @@ bool AppInitMain(NodeContext& node) return false; } + // Now that the chain state is loaded, make mempool generally available in the node context. For example the + // connection manager, wallet, or RPC threads, which are all started after this, may use it from the node context. + assert(!node.mempool); + node.mempool = &::mempool; + fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME; CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK, CLIENT_VERSION); // Allowed to fail as this file IS missing on first startup. diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp index 26856a00d3..ac640aa35a 100644 --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -263,7 +263,7 @@ public: } return true; } - void findCoins(std::map<COutPoint, Coin>& coins) override { return FindCoins(coins); } + void findCoins(std::map<COutPoint, Coin>& coins) override { return FindCoins(m_node, coins); } double guessVerificationProgress(const uint256& block_hash) override { LOCK(cs_main); diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index 1877c92178..529fa793dd 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -68,7 +68,7 @@ public: std::string getNetwork() override { return Params().NetworkIDString(); } void initLogging() override { InitLogging(); } void initParameterInteraction() override { InitParameterInteraction(); } - std::string getWarnings(const std::string& type) override { return GetWarnings(type); } + std::string getWarnings() override { return GetWarnings(true); } uint32_t getLogCategories() override { return LogInstance().GetCategoryMask(); } bool baseInitialize() override { @@ -167,8 +167,8 @@ public: } int64_t getTotalBytesRecv() override { return m_context.connman ? m_context.connman->GetTotalBytesRecv() : 0; } int64_t getTotalBytesSent() override { return m_context.connman ? m_context.connman->GetTotalBytesSent() : 0; } - size_t getMempoolSize() override { return ::mempool.size(); } - size_t getMempoolDynamicUsage() override { return ::mempool.DynamicMemoryUsage(); } + size_t getMempoolSize() override { return m_context.mempool ? m_context.mempool->size() : 0; } + size_t getMempoolDynamicUsage() override { return m_context.mempool ? m_context.mempool->DynamicMemoryUsage() : 0; } bool getHeaderTip(int& height, int64_t& block_time) override { LOCK(::cs_main); diff --git a/src/interfaces/node.h b/src/interfaces/node.h index adf3de7b07..6bc4668beb 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -78,7 +78,7 @@ public: virtual void initParameterInteraction() = 0; //! Get warnings. - virtual std::string getWarnings(const std::string& type) = 0; + virtual std::string getWarnings() = 0; // Get log flags. virtual uint32_t getLogCategories() = 0; diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp index 701a748e55..94c6e8c7b7 100644 --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -18,8 +18,9 @@ #include <wallet/feebumper.h> #include <wallet/fees.h> #include <wallet/ismine.h> -#include <wallet/rpcwallet.h> #include <wallet/load.h> +#include <wallet/psbtwallet.h> +#include <wallet/rpcwallet.h> #include <wallet/wallet.h> #include <memory> @@ -116,8 +117,22 @@ public: std::string error; return m_wallet->GetNewDestination(type, label, dest, error); } - bool getPubKey(const CKeyID& address, CPubKey& pub_key) override { return m_wallet->GetLegacyScriptPubKeyMan()->GetPubKey(address, pub_key); } - bool getPrivKey(const CKeyID& address, CKey& key) override { return m_wallet->GetLegacyScriptPubKeyMan()->GetKey(address, key); } + bool getPubKey(const CScript& script, const CKeyID& address, CPubKey& pub_key) override + { + const SigningProvider* provider = m_wallet->GetSigningProvider(script); + if (provider) { + return provider->GetPubKey(address, pub_key); + } + return false; + } + bool getPrivKey(const CScript& script, const CKeyID& address, CKey& key) override + { + const SigningProvider* provider = m_wallet->GetSigningProvider(script); + if (provider) { + return provider->GetKey(address, key); + } + return false; + } bool isSpendable(const CTxDestination& dest) override { return m_wallet->IsMine(dest) & ISMINE_SPENDABLE; } bool haveWatchOnly() override { @@ -343,6 +358,14 @@ public: } return {}; } + TransactionError fillPSBT(PartiallySignedTransaction& psbtx, + bool& complete, + int sighash_type = 1 /* SIGHASH_ALL */, + bool sign = true, + bool bip32derivs = false) override + { + return FillPSBT(m_wallet.get(), psbtx, complete, sighash_type, sign, bip32derivs); + } WalletBalances getBalances() override { const auto bal = m_wallet->GetBalance(); diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index a96b93b4c3..8d2b8a2eca 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -14,6 +14,7 @@ #include <functional> #include <map> #include <memory> +#include <psbt.h> #include <stdint.h> #include <string> #include <tuple> @@ -81,10 +82,10 @@ public: virtual bool getNewDestination(const OutputType type, const std::string label, CTxDestination& dest) = 0; //! Get public key. - virtual bool getPubKey(const CKeyID& address, CPubKey& pub_key) = 0; + virtual bool getPubKey(const CScript& script, const CKeyID& address, CPubKey& pub_key) = 0; //! Get private key. - virtual bool getPrivKey(const CKeyID& address, CKey& key) = 0; + virtual bool getPrivKey(const CScript& script, const CKeyID& address, CKey& key) = 0; //! Return whether wallet has private key. virtual bool isSpendable(const CTxDestination& dest) = 0; @@ -194,6 +195,13 @@ public: bool& in_mempool, int& num_blocks) = 0; + //! Fill PSBT. + virtual TransactionError fillPSBT(PartiallySignedTransaction& psbtx, + bool& complete, + int sighash_type = 1 /* SIGHASH_ALL */, + bool sign = true, + bool bip32derivs = false) = 0; + //! Get balances. virtual WalletBalances getBalances() = 0; diff --git a/src/key.cpp b/src/key.cpp index 3ba21753a2..10b6668aae 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -83,13 +83,13 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou * <http://www.secg.org/sec1-v2.pdf>. The optional parameters and publicKey fields are * included. * - * privkey must point to an output buffer of length at least CKey::PRIVATE_KEY_SIZE bytes. + * privkey must point to an output buffer of length at least CKey::SIZE bytes. * privkeylen must initially be set to the size of the privkey buffer. Upon return it * 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, bool compressed) { - assert(*privkeylen >= CKey::PRIVATE_KEY_SIZE); + assert(*privkeylen >= CKey::SIZE); secp256k1_pubkey pubkey; size_t pubkeylen = 0; if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) { @@ -115,11 +115,11 @@ static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *pr memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); memcpy(ptr, key32, 32); ptr += 32; memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); - pubkeylen = CPubKey::COMPRESSED_PUBLIC_KEY_SIZE; + pubkeylen = CPubKey::COMPRESSED_SIZE; secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED); ptr += pubkeylen; *privkeylen = ptr - privkey; - assert(*privkeylen == CKey::COMPRESSED_PRIVATE_KEY_SIZE); + assert(*privkeylen == CKey::COMPRESSED_SIZE); } else { static const unsigned char begin[] = { 0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20 @@ -141,11 +141,11 @@ static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *pr memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); memcpy(ptr, key32, 32); ptr += 32; memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); - pubkeylen = CPubKey::PUBLIC_KEY_SIZE; + pubkeylen = CPubKey::SIZE; secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED); ptr += pubkeylen; *privkeylen = ptr - privkey; - assert(*privkeylen == CKey::PRIVATE_KEY_SIZE); + assert(*privkeylen == CKey::SIZE); } return 1; } @@ -173,8 +173,8 @@ CPrivKey CKey::GetPrivKey() const { CPrivKey privkey; int ret; size_t privkeylen; - privkey.resize(PRIVATE_KEY_SIZE); - privkeylen = PRIVATE_KEY_SIZE; + privkey.resize(SIZE); + privkeylen = SIZE; ret = ec_privkey_export_der(secp256k1_context_sign, privkey.data(), &privkeylen, begin(), fCompressed); assert(ret); privkey.resize(privkeylen); @@ -184,7 +184,7 @@ CPrivKey CKey::GetPrivKey() const { CPubKey CKey::GetPubKey() const { assert(fValid); secp256k1_pubkey pubkey; - size_t clen = CPubKey::PUBLIC_KEY_SIZE; + size_t clen = CPubKey::SIZE; CPubKey result; int ret = secp256k1_ec_pubkey_create(secp256k1_context_sign, &pubkey, begin()); assert(ret); @@ -276,7 +276,7 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const std::vector<unsigned char, secure_allocator<unsigned char>> vout(64); if ((nChild >> 31) == 0) { CPubKey pubkey = GetPubKey(); - assert(pubkey.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE); + assert(pubkey.size() == CPubKey::COMPRESSED_SIZE); BIP32Hash(cc, nChild, *pubkey.begin(), pubkey.begin()+1, vout.data()); } else { assert(size() == 32); @@ -19,7 +19,7 @@ /** * secure_allocator is defined in allocators.h * CPrivKey is a serialized private key, with all parameters included - * (PRIVATE_KEY_SIZE bytes) + * (SIZE bytes) */ typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey; @@ -30,15 +30,15 @@ public: /** * secp256k1: */ - static const unsigned int PRIVATE_KEY_SIZE = 279; - static const unsigned int COMPRESSED_PRIVATE_KEY_SIZE = 214; + static const unsigned int SIZE = 279; + static const unsigned int COMPRESSED_SIZE = 214; /** * see www.keylength.com * script supports up to 75 for single byte push */ static_assert( - PRIVATE_KEY_SIZE >= COMPRESSED_PRIVATE_KEY_SIZE, - "COMPRESSED_PRIVATE_KEY_SIZE is larger than PRIVATE_KEY_SIZE"); + SIZE >= COMPRESSED_SIZE, + "COMPRESSED_SIZE is larger than SIZE"); private: //! Whether this private key is valid. We check for correctness when modifying the key diff --git a/src/key_io.cpp b/src/key_io.cpp index 363055d6b3..af06db7343 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -73,7 +73,7 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par { std::vector<unsigned char> data; uint160 hash; - if (DecodeBase58Check(str, data)) { + if (DecodeBase58Check(str, data, 21)) { // base58-encoded Bitcoin addresses. // Public-key-hash-addresses have version 0 (or 111 testnet). // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. @@ -133,7 +133,7 @@ CKey DecodeSecret(const std::string& str) { CKey key; std::vector<unsigned char> data; - if (DecodeBase58Check(str, data)) { + if (DecodeBase58Check(str, data, 34)) { const std::vector<unsigned char>& privkey_prefix = Params().Base58Prefix(CChainParams::SECRET_KEY); if ((data.size() == 32 + privkey_prefix.size() || (data.size() == 33 + privkey_prefix.size() && data.back() == 1)) && std::equal(privkey_prefix.begin(), privkey_prefix.end(), data.begin())) { @@ -164,7 +164,7 @@ CExtPubKey DecodeExtPubKey(const std::string& str) { CExtPubKey key; std::vector<unsigned char> data; - if (DecodeBase58Check(str, data)) { + if (DecodeBase58Check(str, data, 78)) { const std::vector<unsigned char>& prefix = Params().Base58Prefix(CChainParams::EXT_PUBLIC_KEY); if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) { key.Decode(data.data() + prefix.size()); @@ -187,7 +187,7 @@ CExtKey DecodeExtKey(const std::string& str) { CExtKey key; std::vector<unsigned char> data; - if (DecodeBase58Check(str, data)) { + if (DecodeBase58Check(str, data, 78)) { const std::vector<unsigned char>& prefix = Params().Base58Prefix(CChainParams::EXT_SECRET_KEY); if (data.size() == BIP32_EXTKEY_SIZE + prefix.size() && std::equal(prefix.begin(), prefix.end(), data.begin())) { key.Decode(data.data() + prefix.size()); diff --git a/src/miner.cpp b/src/miner.cpp index 1c9174ee07..2f6feb9e02 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -435,7 +435,7 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned ++nExtraNonce; unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2 CMutableTransaction txCoinbase(*pblock->vtx[0]); - txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS; + txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)); assert(txCoinbase.vin[0].scriptSig.size() <= 100); pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase)); diff --git a/src/net.cpp b/src/net.cpp index 84692d2a79..99dae88bab 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -16,6 +16,7 @@ #include <crypto/sha256.h> #include <netbase.h> #include <net_permissions.h> +#include <random.h> #include <scheduler.h> #include <ui_interface.h> #include <util/strencodings.h> @@ -445,6 +446,9 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false, block_relay_only); pnode->AddRef(); + // We're making a new connection, harvest entropy from the time (and our peer count) + RandAddEvent((uint32_t)id); + return pnode; } @@ -693,6 +697,9 @@ CNetMessage V1TransportDeserializer::GetMessage(const CMessageHeader::MessageSta msg.m_message_size = hdr.nMessageSize; msg.m_raw_message_size = hdr.nMessageSize + CMessageHeader::HEADER_SIZE; + // We just received a message off the wire, harvest entropy from the time (and the message checksum) + RandAddEvent(ReadLE32(hash.begin())); + msg.m_valid_checksum = (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) == 0); if (!msg.m_valid_checksum) { LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s\n", @@ -1017,6 +1024,9 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { LOCK(cs_vNodes); vNodes.push_back(pnode); } + + // We received a new connection, harvest entropy from the time (and our peer count) + RandAddEvent((uint32_t)id); } void CConnman::DisconnectNodes() diff --git a/src/netaddress.h b/src/netaddress.h index fbb1553338..32f4476fac 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -54,7 +54,7 @@ class CNetAddr bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) bool IsIPv6() const; // IPv6 address (not mapped IPv4, not Tor) bool IsRFC1918() const; // IPv4 private networks (10.0.0.0/8, 192.168.0.0/16, 172.16.0.0/12) - bool IsRFC2544() const; // IPv4 inter-network communications (192.18.0.0/15) + bool IsRFC2544() const; // IPv4 inter-network communications (198.18.0.0/15) bool IsRFC6598() const; // IPv4 ISP-level NAT (100.64.0.0/10) bool IsRFC5737() const; // IPv4 documentation addresses (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24) bool IsRFC3849() const; // IPv6 documentation address (2001:0DB8::/32) diff --git a/src/node/coin.cpp b/src/node/coin.cpp index ad8d1d3af4..f4f86cdbe9 100644 --- a/src/node/coin.cpp +++ b/src/node/coin.cpp @@ -4,14 +4,16 @@ #include <node/coin.h> +#include <node/context.h> #include <txmempool.h> #include <validation.h> -void FindCoins(std::map<COutPoint, Coin>& coins) +void FindCoins(const NodeContext& node, std::map<COutPoint, Coin>& coins) { - LOCK2(cs_main, ::mempool.cs); + assert(node.mempool); + LOCK2(cs_main, node.mempool->cs); CCoinsViewCache& chain_view = ::ChainstateActive().CoinsTip(); - CCoinsViewMemPool mempool_view(&chain_view, ::mempool); + CCoinsViewMemPool mempool_view(&chain_view, *node.mempool); for (auto& coin : coins) { if (!mempool_view.GetCoin(coin.first, coin.second)) { // Either the coin is not in the CCoinsViewCache or is spent. Clear it. diff --git a/src/node/coin.h b/src/node/coin.h index eb95b75cfb..908850e2a5 100644 --- a/src/node/coin.h +++ b/src/node/coin.h @@ -9,14 +9,16 @@ class COutPoint; class Coin; +struct NodeContext; /** * Look up unspent output information. Returns coins in the mempool and in the * current chain UTXO set. Iterates through all the keys in the map and * populates the values. * + * @param[in] node The node context to use for lookup * @param[in,out] coins map to fill */ -void FindCoins(std::map<COutPoint, Coin>& coins); +void FindCoins(const NodeContext& node, std::map<COutPoint, Coin>& coins); #endif // BITCOIN_NODE_COIN_H diff --git a/src/node/context.h b/src/node/context.h index 2b124af4db..dab5b5d048 100644 --- a/src/node/context.h +++ b/src/node/context.h @@ -10,6 +10,7 @@ class BanMan; class CConnman; +class CTxMemPool; class PeerLogicValidation; namespace interfaces { class Chain; @@ -22,13 +23,13 @@ class ChainClient; //! This is used by init, rpc, and test code to pass object references around //! without needing to declare the same variables and parameters repeatedly, or //! to use globals. More variables could be added to this struct (particularly -//! references to validation and mempool objects) to eliminate use of globals +//! references to validation objects) to eliminate use of globals //! and make code more modular and testable. The struct isn't intended to have //! any member functions. It should just be a collection of references that can //! be used without pulling in unwanted dependencies or functionality. -struct NodeContext -{ +struct NodeContext { std::unique_ptr<CConnman> connman; + CTxMemPool* mempool{nullptr}; // Currently a raw pointer because the memory is not managed by this struct std::unique_ptr<PeerLogicValidation> peer_logic; std::unique_ptr<BanMan> banman; std::unique_ptr<interfaces::Chain> chain; diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp index 12559c5a5f..9a30c3f083 100644 --- a/src/node/psbt.cpp +++ b/src/node/psbt.cpp @@ -7,6 +7,7 @@ #include <node/psbt.h> #include <policy/policy.h> #include <policy/settings.h> +#include <tinyformat.h> #include <numeric> @@ -39,6 +40,11 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) calc_fee = false; } + if (!utxo.IsNull() && utxo.scriptPubKey.IsUnspendable()) { + result.SetInvalid(strprintf("PSBT is not valid. Input %u spends unspendable output", i)); + return result; + } + // Check if it is final if (!utxo.IsNull() && !PSBTInputSigned(input)) { input_analysis.is_final = false; diff --git a/src/node/psbt.h b/src/node/psbt.h index e04366a20f..7384dc415c 100644 --- a/src/node/psbt.h +++ b/src/node/psbt.h @@ -30,6 +30,17 @@ struct PSBTAnalysis { Optional<CAmount> fee; //!< Amount of fee being paid by the transaction std::vector<PSBTInputAnalysis> inputs; //!< More information about the individual inputs of the transaction PSBTRole next; //!< Which of the BIP 174 roles needs to handle the transaction next + std::string error; //!< Error message + + void SetInvalid(std::string err_msg) + { + estimated_vsize = nullopt; + estimated_feerate = nullopt; + fee = nullopt; + inputs.clear(); + next = PSBTRole::CREATOR; + error = err_msg; + } }; /** diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp index 3c0df2b26e..5e2e502015 100644 --- a/src/node/transaction.cpp +++ b/src/node/transaction.cpp @@ -20,6 +20,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t // node.connman is assigned both before chain clients and before RPC server is accepting calls, // and reset after chain clients and RPC sever are stopped. node.connman should never be null here. assert(node.connman); + assert(node.mempool); std::promise<void> promise; uint256 hashTx = tx->GetHash(); bool callback_set = false; @@ -35,10 +36,10 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t // So if the output does exist, then this transaction exists in the chain. if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_CHAIN; } - if (!mempool.exists(hashTx)) { + if (!node.mempool->exists(hashTx)) { // Transaction is not already in the mempool. Submit it. TxValidationState state; - if (!AcceptToMemoryPool(mempool, state, std::move(tx), + if (!AcceptToMemoryPool(*node.mempool, state, std::move(tx), nullptr /* plTxnReplaced */, false /* bypass_limits */, max_tx_fee)) { err_string = FormatStateMessage(state); if (state.IsInvalid()) { diff --git a/src/prevector.h b/src/prevector.h index d307495fbe..4fb07688ff 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -13,6 +13,7 @@ #include <algorithm> #include <cstddef> #include <type_traits> +#include <utility> #pragma pack(push, 1) /** Implements a drop-in replacement for std::vector<T> which stores up to N diff --git a/src/protocol.h b/src/protocol.h index 3032310fa1..db07efb9f9 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -237,6 +237,7 @@ const std::vector<std::string> &getAllNetMessageTypes(); /** nServices flags */ enum ServiceFlags : uint64_t { + // NOTE: When adding here, be sure to update qt/guiutil.cpp's formatServicesStr too // Nothing NODE_NONE = 0, // NODE_NETWORK means that the node is capable of serving the complete block chain. It is currently diff --git a/src/psbt.cpp b/src/psbt.cpp index c306079b1e..9ede62efdf 100644 --- a/src/psbt.cpp +++ b/src/psbt.cpp @@ -348,6 +348,7 @@ TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector std::string PSBTRoleName(PSBTRole role) { switch (role) { + case PSBTRole::CREATOR: return "creator"; case PSBTRole::UPDATER: return "updater"; case PSBTRole::SIGNER: return "signer"; case PSBTRole::FINALIZER: return "finalizer"; diff --git a/src/psbt.h b/src/psbt.h index 6a5c468058..d507d5b6b7 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -171,7 +171,7 @@ struct PSBTInput case PSBT_IN_PARTIAL_SIG: { // 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) { + if (key.size() != CPubKey::SIZE + 1 && key.size() != CPubKey::COMPRESSED_SIZE + 1) { throw std::ios_base::failure("Size of key was not the expected size for the type partial signature pubkey"); } // Read in the pubkey from key @@ -560,6 +560,7 @@ struct PartiallySignedTransaction }; enum class PSBTRole { + CREATOR, UPDATER, SIGNER, FINALIZER, diff --git a/src/pubkey.cpp b/src/pubkey.cpp index d38df716bd..21e51a380d 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -171,6 +171,7 @@ bool CPubKey::Verify(const uint256 &hash, const std::vector<unsigned char>& vchS return false; secp256k1_pubkey pubkey; secp256k1_ecdsa_signature sig; + assert(secp256k1_context_verify && "secp256k1_context_verify must be initialized to use CPubKey."); if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, vch, size())) { return false; } @@ -190,14 +191,15 @@ bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector<unsigned cha bool fComp = ((vchSig[0] - 27) & 4) != 0; secp256k1_pubkey pubkey; secp256k1_ecdsa_recoverable_signature sig; + assert(secp256k1_context_verify && "secp256k1_context_verify must be initialized to use CPubKey."); if (!secp256k1_ecdsa_recoverable_signature_parse_compact(secp256k1_context_verify, &sig, &vchSig[1], recid)) { return false; } if (!secp256k1_ecdsa_recover(secp256k1_context_verify, &pubkey, &sig, hash.begin())) { return false; } - unsigned char pub[PUBLIC_KEY_SIZE]; - size_t publen = PUBLIC_KEY_SIZE; + unsigned char pub[SIZE]; + size_t publen = SIZE; secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, fComp ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED); Set(pub, pub + publen); return true; @@ -207,6 +209,7 @@ bool CPubKey::IsFullyValid() const { if (!IsValid()) return false; secp256k1_pubkey pubkey; + assert(secp256k1_context_verify && "secp256k1_context_verify must be initialized to use CPubKey."); return secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, vch, size()); } @@ -214,11 +217,12 @@ bool CPubKey::Decompress() { if (!IsValid()) return false; secp256k1_pubkey pubkey; + assert(secp256k1_context_verify && "secp256k1_context_verify must be initialized to use CPubKey."); if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, vch, size())) { return false; } - unsigned char pub[PUBLIC_KEY_SIZE]; - size_t publen = PUBLIC_KEY_SIZE; + unsigned char pub[SIZE]; + size_t publen = SIZE; secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, SECP256K1_EC_UNCOMPRESSED); Set(pub, pub + publen); return true; @@ -227,19 +231,20 @@ bool CPubKey::Decompress() { bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const { assert(IsValid()); assert((nChild >> 31) == 0); - assert(size() == COMPRESSED_PUBLIC_KEY_SIZE); + assert(size() == COMPRESSED_SIZE); unsigned char out[64]; BIP32Hash(cc, nChild, *begin(), begin()+1, out); memcpy(ccChild.begin(), out+32, 32); secp256k1_pubkey pubkey; + assert(secp256k1_context_verify && "secp256k1_context_verify must be initialized to use CPubKey."); if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, vch, size())) { return false; } if (!secp256k1_ec_pubkey_tweak_add(secp256k1_context_verify, &pubkey, out)) { return false; } - unsigned char pub[COMPRESSED_PUBLIC_KEY_SIZE]; - size_t publen = COMPRESSED_PUBLIC_KEY_SIZE; + unsigned char pub[COMPRESSED_SIZE]; + size_t publen = COMPRESSED_SIZE; secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, SECP256K1_EC_COMPRESSED); pubkeyChild.Set(pub, pub + publen); return true; @@ -251,8 +256,8 @@ void CExtPubKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const { code[5] = (nChild >> 24) & 0xFF; code[6] = (nChild >> 16) & 0xFF; code[7] = (nChild >> 8) & 0xFF; code[8] = (nChild >> 0) & 0xFF; memcpy(code+9, chaincode.begin(), 32); - assert(pubkey.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE); - memcpy(code+41, pubkey.begin(), CPubKey::COMPRESSED_PUBLIC_KEY_SIZE); + assert(pubkey.size() == CPubKey::COMPRESSED_SIZE); + memcpy(code+41, pubkey.begin(), CPubKey::COMPRESSED_SIZE); } void CExtPubKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) { @@ -273,6 +278,7 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int _nChild) const { /* static */ bool CPubKey::CheckLowS(const std::vector<unsigned char>& vchSig) { secp256k1_ecdsa_signature sig; + assert(secp256k1_context_verify && "secp256k1_context_verify must be initialized to use CPubKey."); if (!ecdsa_signature_parse_der_lax(secp256k1_context_verify, &sig, vchSig.data(), vchSig.size())) { return false; } diff --git a/src/pubkey.h b/src/pubkey.h index fd815a871b..76f743da66 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -33,17 +33,17 @@ public: /** * secp256k1: */ - static constexpr unsigned int PUBLIC_KEY_SIZE = 65; - static constexpr unsigned int COMPRESSED_PUBLIC_KEY_SIZE = 33; - static constexpr unsigned int SIGNATURE_SIZE = 72; - static constexpr unsigned int COMPACT_SIGNATURE_SIZE = 65; + static constexpr unsigned int SIZE = 65; + static constexpr unsigned int COMPRESSED_SIZE = 33; + static constexpr unsigned int SIGNATURE_SIZE = 72; + static constexpr unsigned int COMPACT_SIGNATURE_SIZE = 65; /** * see www.keylength.com * script supports up to 75 for single byte push */ static_assert( - PUBLIC_KEY_SIZE >= COMPRESSED_PUBLIC_KEY_SIZE, - "COMPRESSED_PUBLIC_KEY_SIZE is larger than PUBLIC_KEY_SIZE"); + SIZE >= COMPRESSED_SIZE, + "COMPRESSED_SIZE is larger than SIZE"); private: @@ -51,15 +51,15 @@ private: * Just store the serialized data. * Its length can very cheaply be computed from the first byte. */ - unsigned char vch[PUBLIC_KEY_SIZE]; + unsigned char vch[SIZE]; //! Compute the length of a pubkey with a given first byte. unsigned int static GetLen(unsigned char chHeader) { if (chHeader == 2 || chHeader == 3) - return COMPRESSED_PUBLIC_KEY_SIZE; + return COMPRESSED_SIZE; if (chHeader == 4 || chHeader == 6 || chHeader == 7) - return PUBLIC_KEY_SIZE; + return SIZE; return 0; } @@ -140,7 +140,7 @@ public: void Unserialize(Stream& s) { unsigned int len = ::ReadCompactSize(s); - if (len <= PUBLIC_KEY_SIZE) { + if (len <= SIZE) { s.read((char*)vch, len); } else { // invalid pubkey, skip available data @@ -179,7 +179,7 @@ public: //! Check whether this is a compressed public key. bool IsCompressed() const { - return size() == COMPRESSED_PUBLIC_KEY_SIZE; + return size() == COMPRESSED_SIZE; } /** diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp index 48201b420e..b6c6984b10 100644 --- a/src/qt/bantablemodel.cpp +++ b/src/qt/bantablemodel.cpp @@ -8,7 +8,7 @@ #include <net_types.h> // For banmap_t #include <qt/clientmodel.h> -#include <algorithm> +#include <utility> #include <QDebug> #include <QList> diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 676c15ea43..0021c3dbc7 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -135,7 +135,7 @@ BitcoinCore::BitcoinCore(interfaces::Node& node) : void BitcoinCore::handleRunawayException(const std::exception *e) { PrintExceptionContinue(e, "Runaway exception"); - Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings("gui"))); + Q_EMIT runawayException(QString::fromStdString(m_node.getWarnings())); } void BitcoinCore::initialize() @@ -589,10 +589,10 @@ int GuiMain(int argc, char* argv[]) } } catch (const std::exception& e) { PrintExceptionContinue(&e, "Runaway exception"); - app.handleRunawayException(QString::fromStdString(node->getWarnings("gui"))); + app.handleRunawayException(QString::fromStdString(node->getWarnings())); } catch (...) { PrintExceptionContinue(nullptr, "Runaway exception"); - app.handleRunawayException(QString::fromStdString(node->getWarnings("gui"))); + app.handleRunawayException(QString::fromStdString(node->getWarnings())); } return rv; } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 2aeba6d82c..8444984b27 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -341,6 +341,7 @@ void BitcoinGUI::createActions() m_close_wallet_action->setStatusTip(tr("Close wallet")); m_create_wallet_action = new QAction(tr("Create Wallet..."), this); + m_create_wallet_action->setEnabled(false); m_create_wallet_action->setStatusTip(tr("Create a new wallet")); showHelpMessageAction = new QAction(tr("&Command-line options"), this); @@ -618,6 +619,7 @@ void BitcoinGUI::setWalletController(WalletController* wallet_controller) m_wallet_controller = wallet_controller; + m_create_wallet_action->setEnabled(true); m_open_wallet_action->setEnabled(true); m_open_wallet_action->setMenu(m_open_wallet_menu); diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 5b216b2705..b84587a99b 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -134,7 +134,7 @@ enum BlockSource ClientModel::getBlockSource() const QString ClientModel::getStatusBarWarnings() const { - return QString::fromStdString(m_node.getWarnings("gui")); + return QString::fromStdString(m_node.getWarnings()); } OptionsModel *ClientModel::getOptionsModel() diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index f928f1ca2a..3302dde4ed 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -468,7 +468,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) { CPubKey pubkey; PKHash *pkhash = boost::get<PKHash>(&address); - if (pkhash && model->wallet().getPubKey(CKeyID(*pkhash), pubkey)) + if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, CKeyID(*pkhash), pubkey)) { nBytesInputs += (pubkey.IsCompressed() ? 148 : 180); } diff --git a/src/qt/forms/modaloverlay.ui b/src/qt/forms/modaloverlay.ui index b5a69c578d..da19a6fa2e 100644 --- a/src/qt/forms/modaloverlay.ui +++ b/src/qt/forms/modaloverlay.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>ModalOverlay</class> - <widget class="ModalOverlay" name="ModalOverlay"> + <widget class="QWidget" name="ModalOverlay"> <property name="geometry"> <rect> <x>0</x> @@ -369,14 +369,6 @@ QLabel { color: rgb(40,40,40); }</string> </item> </layout> </widget> - <customwidgets> - <customwidget> - <class>ModalOverlay</class> - <extends>QWidget</extends> - <header>qt/modaloverlay.h</header> - <container>1</container> - </customwidget> - </customwidgets> <resources/> <connections/> </ui> diff --git a/src/qt/forms/openuridialog.ui b/src/qt/forms/openuridialog.ui index 2acec314fd..1b7291ab9d 100644 --- a/src/qt/forms/openuridialog.ui +++ b/src/qt/forms/openuridialog.ui @@ -24,7 +24,11 @@ </widget> </item> <item> - <widget class="QValidatedLineEdit" name="uriEdit"/> + <widget class="QValidatedLineEdit" name="uriEdit"> + <property name="placeholderText"> + <string notr="true">bitcoin:</string> + </property> + </widget> </item> </layout> </item> diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 240a7a7e92..fea759dee0 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -685,6 +685,9 @@ <property name="toolTip"> <string>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</string> </property> + <property name="placeholderText"> + <string notr="true">https://example.com/tx/%s</string> + </property> </widget> </item> </layout> diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui index 0214356eaa..7dbee6d689 100644 --- a/src/qt/forms/receivecoinsdialog.ui +++ b/src/qt/forms/receivecoinsdialog.ui @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>ReceiveCoinsDialog</class> - <widget class="QWidget" name="ReceiveCoinsDialog"> + <widget class="QDialog" name="ReceiveCoinsDialog"> <property name="geometry"> <rect> <x>0</x> @@ -63,7 +63,7 @@ <item row="4" column="2"> <widget class="QLineEdit" name="reqLabel"> <property name="toolTip"> - <string>An optional label to associate with the new receiving address.</string> + <string>An optional label to associate with the new receiving address (used by you to identify an invoice). It is also attached to the payment request.</string> </property> </widget> </item> @@ -93,7 +93,7 @@ <item row="6" column="2"> <widget class="QLineEdit" name="reqMessage"> <property name="toolTip"> - <string>An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Bitcoin network.</string> + <string>An optional message that is attached to the payment request and may be displayed to the sender.</string> </property> </widget> </item> diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 7190d59240..cfd4bf33d4 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -1190,7 +1190,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p <number>3</number> </property> <item> - <widget class="QLabel" name="label"> + <widget class="QLabel" name="labelBalanceName"> <property name="sizePolicy"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <horstretch>0</horstretch> diff --git a/src/qt/forms/sendcoinsentry.ui b/src/qt/forms/sendcoinsentry.ui index 843d909f68..934363af1f 100644 --- a/src/qt/forms/sendcoinsentry.ui +++ b/src/qt/forms/sendcoinsentry.ui @@ -144,6 +144,9 @@ <property name="toolTip"> <string>Enter a label for this address to add it to the list of used addresses</string> </property> + <property name="placeholderText"> + <string>Enter a label for this address to add it to the list of used addresses</string> + </property> </widget> </item> <item row="2" column="0"> diff --git a/src/qt/forms/signverifymessagedialog.ui b/src/qt/forms/signverifymessagedialog.ui index 202edf27d4..f42d19093b 100644 --- a/src/qt/forms/signverifymessagedialog.ui +++ b/src/qt/forms/signverifymessagedialog.ui @@ -121,6 +121,9 @@ </property> <item> <widget class="QLineEdit" name="signatureOut_SM"> + <property name="placeholderText"> + <string>Click "Sign Message" to generate signature</string> + </property> <property name="font"> <font> <italic>true</italic> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index c4e0321f28..843de651e5 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -7,7 +7,7 @@ #include <qt/bitcoinaddressvalidator.h> #include <qt/bitcoinunits.h> #include <qt/qvalidatedlineedit.h> -#include <qt/walletmodel.h> +#include <qt/sendcoinsrecipient.h> #include <base58.h> #include <chainparams.h> @@ -54,10 +54,7 @@ #include <QUrlQuery> #if defined(Q_OS_MAC) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#include <CoreServices/CoreServices.h> #include <QProcess> void ForceActivation(); @@ -691,87 +688,6 @@ bool SetStartOnSystemStartup(bool fAutoStart) return true; } - -#elif defined(Q_OS_MAC) && defined(MAC_OS_X_VERSION_MIN_REQUIRED) && MAC_OS_X_VERSION_MIN_REQUIRED <= 101100 -// based on: https://github.com/Mozketo/LaunchAtLoginController/blob/master/LaunchAtLoginController.m - -LSSharedFileListItemRef findStartupItemInList(CFArrayRef listSnapshot, LSSharedFileListRef list, CFURLRef findUrl) -{ - if (listSnapshot == nullptr) { - return nullptr; - } - - // loop through the list of startup items and try to find the bitcoin app - for(int i = 0; i < CFArrayGetCount(listSnapshot); i++) { - LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(listSnapshot, i); - UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; - CFURLRef currentItemURL = nullptr; - -#if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= 10100 - if(&LSSharedFileListItemCopyResolvedURL) - currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, nullptr); -#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && MAC_OS_X_VERSION_MIN_REQUIRED < 10100 - else - LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, nullptr); -#endif -#else - LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, nullptr); -#endif - - if(currentItemURL) { - if (CFEqual(currentItemURL, findUrl)) { - // found - CFRelease(currentItemURL); - return item; - } - CFRelease(currentItemURL); - } - } - return nullptr; -} - -bool GetStartOnSystemStartup() -{ - CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); - if (bitcoinAppUrl == nullptr) { - return false; - } - - LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr); - CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(loginItems, nullptr); - bool res = (findStartupItemInList(listSnapshot, loginItems, bitcoinAppUrl) != nullptr); - CFRelease(bitcoinAppUrl); - CFRelease(loginItems); - CFRelease(listSnapshot); - return res; -} - -bool SetStartOnSystemStartup(bool fAutoStart) -{ - CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); - if (bitcoinAppUrl == nullptr) { - return false; - } - - LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr); - CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(loginItems, nullptr); - LSSharedFileListItemRef foundItem = findStartupItemInList(listSnapshot, loginItems, bitcoinAppUrl); - - if(fAutoStart && !foundItem) { - // add bitcoin app to startup item list - LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, nullptr, nullptr, bitcoinAppUrl, nullptr, nullptr); - } - else if(!fAutoStart && foundItem) { - // remove item - LSSharedFileListItemRemove(loginItems, foundItem); - } - - CFRelease(bitcoinAppUrl); - CFRelease(loginItems); - CFRelease(listSnapshot); - return true; -} -#pragma GCC diagnostic pop #else bool GetStartOnSystemStartup() { return false; } @@ -815,32 +731,33 @@ QString formatDurationStr(int secs) return strList.join(" "); } +QString serviceFlagToStr(const quint64 mask, const int bit) +{ + switch (ServiceFlags(mask)) { + case NODE_NONE: abort(); // impossible + case NODE_NETWORK: return "NETWORK"; + case NODE_GETUTXO: return "GETUTXO"; + case NODE_BLOOM: return "BLOOM"; + case NODE_WITNESS: return "WITNESS"; + case NODE_NETWORK_LIMITED: return "NETWORK_LIMITED"; + // Not using default, so we get warned when a case is missing + } + if (bit < 8) { + return QString("%1[%2]").arg("UNKNOWN").arg(mask); + } else { + return QString("%1[2^%2]").arg("UNKNOWN").arg(bit); + } +} + QString formatServicesStr(quint64 mask) { QStringList strList; - // Just scan the last 8 bits for now. - for (int i = 0; i < 8; i++) { - uint64_t check = 1 << i; + for (int i = 0; i < 64; i++) { + uint64_t check = 1LL << i; if (mask & check) { - switch (check) - { - case NODE_NETWORK: - strList.append("NETWORK"); - break; - case NODE_GETUTXO: - strList.append("GETUTXO"); - break; - case NODE_BLOOM: - strList.append("BLOOM"); - break; - case NODE_WITNESS: - strList.append("WITNESS"); - break; - default: - strList.append(QString("%1[%2]").arg("UNKNOWN").arg(check)); - } + strList.append(serviceFlagToStr(check, i)); } } diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp index 199804f84d..9dc64bb23a 100644 --- a/src/qt/openuridialog.cpp +++ b/src/qt/openuridialog.cpp @@ -6,7 +6,7 @@ #include <qt/forms/ui_openuridialog.h> #include <qt/guiutil.h> -#include <qt/walletmodel.h> +#include <qt/sendcoinsrecipient.h> #include <QUrl> @@ -15,7 +15,6 @@ OpenURIDialog::OpenURIDialog(QWidget *parent) : ui(new Ui::OpenURIDialog) { ui->setupUi(this); - ui->uriEdit->setPlaceholderText("bitcoin:"); } OpenURIDialog::~OpenURIDialog() diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 57cafaaac0..3fffb8a288 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -71,17 +71,17 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : #ifdef Q_OS_MAC /* remove Window tab on Mac */ ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWindow)); -#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && MAC_OS_X_VERSION_MIN_REQUIRED > 101100 - /* hide launch at startup option if compiled against macOS > 10.11 (removed API) */ + /* hide launch at startup option on macOS */ ui->bitcoinAtStartup->setVisible(false); ui->verticalLayout_Main->removeWidget(ui->bitcoinAtStartup); ui->verticalLayout_Main->removeItem(ui->horizontalSpacer_0_Main); #endif -#endif - /* remove Wallet tab in case of -disablewallet */ + /* remove Wallet tab and 3rd party-URL textbox in case of -disablewallet */ if (!enableWallet) { ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWallet)); + ui->thirdPartyTxUrlsLabel->setVisible(false); + ui->thirdPartyTxUrls->setVisible(false); } /* Display elements init */ @@ -110,8 +110,6 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : ui->lang->addItem(locale.nativeLanguageName() + QString(" (") + langStr + QString(")"), QVariant(langStr)); } } - ui->thirdPartyTxUrls->setPlaceholderText("https://example.com/tx/%s"); - ui->unit->setModel(new BitcoinUnits(this)); /* Widget-to-option mapper */ diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index 8b2533508d..6d4ce4a7e4 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -36,13 +36,17 @@ #include <config/bitcoin-config.h> #endif -#include <qt/walletmodel.h> +#include <qt/sendcoinsrecipient.h> #include <QObject> #include <QString> class OptionsModel; +namespace interfaces { +class Node; +} // namespace interfaces + QT_BEGIN_NAMESPACE class QApplication; class QByteArray; diff --git a/src/qt/peertablemodel.cpp b/src/qt/peertablemodel.cpp index af2a1bb0e5..514ff35bcd 100644 --- a/src/qt/peertablemodel.cpp +++ b/src/qt/peertablemodel.cpp @@ -10,7 +10,7 @@ #include <interfaces/node.h> -#include <algorithm> +#include <utility> #include <QDebug> #include <QList> diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp index e492502002..2674c9b953 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/receiverequestdialog.cpp @@ -8,6 +8,7 @@ #include <qt/bitcoinunits.h> #include <qt/guiutil.h> #include <qt/optionsmodel.h> +#include <qt/walletmodel.h> #include <QClipboard> #include <QPixmap> diff --git a/src/qt/receiverequestdialog.h b/src/qt/receiverequestdialog.h index a6e1a2af16..1bb43ce6d1 100644 --- a/src/qt/receiverequestdialog.h +++ b/src/qt/receiverequestdialog.h @@ -5,10 +5,12 @@ #ifndef BITCOIN_QT_RECEIVEREQUESTDIALOG_H #define BITCOIN_QT_RECEIVEREQUESTDIALOG_H -#include <qt/walletmodel.h> +#include <qt/sendcoinsrecipient.h> #include <QDialog> +class WalletModel; + namespace Ui { class ReceiveRequestDialog; } diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp index 1611ec823c..18fa5f417f 100644 --- a/src/qt/recentrequeststablemodel.cpp +++ b/src/qt/recentrequeststablemodel.cpp @@ -7,12 +7,12 @@ #include <qt/bitcoinunits.h> #include <qt/guiutil.h> #include <qt/optionsmodel.h> +#include <qt/walletmodel.h> #include <clientversion.h> #include <streams.h> -#include <algorithm> - +#include <utility> RecentRequestsTableModel::RecentRequestsTableModel(WalletModel *parent) : QAbstractTableModel(parent), walletModel(parent) @@ -213,10 +213,10 @@ void RecentRequestsTableModel::updateDisplayUnit() updateAmountColumnTitle(); } -bool RecentRequestEntryLessThan::operator()(RecentRequestEntry &left, RecentRequestEntry &right) const +bool RecentRequestEntryLessThan::operator()(const RecentRequestEntry& left, const RecentRequestEntry& right) const { - RecentRequestEntry *pLeft = &left; - RecentRequestEntry *pRight = &right; + const RecentRequestEntry* pLeft = &left; + const RecentRequestEntry* pRight = &right; if (order == Qt::DescendingOrder) std::swap(pLeft, pRight); diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h index 130b709d46..f5085f7268 100644 --- a/src/qt/recentrequeststablemodel.h +++ b/src/qt/recentrequeststablemodel.h @@ -5,12 +5,14 @@ #ifndef BITCOIN_QT_RECENTREQUESTSTABLEMODEL_H #define BITCOIN_QT_RECENTREQUESTSTABLEMODEL_H -#include <qt/walletmodel.h> +#include <qt/sendcoinsrecipient.h> #include <QAbstractTableModel> #include <QStringList> #include <QDateTime> +class WalletModel; + class RecentRequestEntry { public: @@ -43,7 +45,7 @@ class RecentRequestEntryLessThan public: RecentRequestEntryLessThan(int nColumn, Qt::SortOrder fOrder): column(nColumn), order(fOrder) {} - bool operator()(RecentRequestEntry &left, RecentRequestEntry &right) const; + bool operator()(const RecentRequestEntry& left, const RecentRequestEntry& right) const; private: int column; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 8edcca684d..f1ea3e23e5 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -21,11 +21,13 @@ #include <chainparams.h> #include <interfaces/node.h> #include <key_io.h> -#include <wallet/coincontrol.h> -#include <ui_interface.h> -#include <txmempool.h> #include <policy/fees.h> +#include <txmempool.h> +#include <ui_interface.h> +#include <wallet/coincontrol.h> #include <wallet/fees.h> +#include <wallet/psbtwallet.h> +#include <wallet/wallet.h> #include <QFontMetrics> #include <QScrollBar> @@ -186,6 +188,11 @@ void SendCoinsDialog::setModel(WalletModel *_model) // set default rbf checkbox state ui->optInRBF->setCheckState(Qt::Checked); + if (model->privateKeysDisabled()) { + ui->sendButton->setText(tr("Cr&eate Unsigned")); + ui->sendButton->setToolTip(tr("Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME)); + } + // set the smartfee-sliders default value (wallets default conf.target or last stored value) QSettings settings; if (settings.value("nSmartFeeSliderPosition").toInt() != 0) { @@ -305,9 +312,19 @@ void SendCoinsDialog::on_sendButton_clicked() formatted.append(recipientElement); } - QString questionString = tr("Are you sure you want to send?"); + QString questionString; + if (model->privateKeysDisabled()) { + questionString.append(tr("Do you want to draft this transaction?")); + } else { + questionString.append(tr("Are you sure you want to send?")); + } + questionString.append("<br /><span style='font-size:10pt;'>"); - questionString.append(tr("Please, review your transaction.")); + if (model->privateKeysDisabled()) { + questionString.append(tr("Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME)); + } else { + questionString.append(tr("Please, review your transaction.")); + } questionString.append("</span>%1"); if(txFee > 0) @@ -358,8 +375,9 @@ void SendCoinsDialog::on_sendButton_clicked() } else { questionString = questionString.arg("<br /><br />" + formatted.at(0)); } - - SendConfirmationDialog confirmationDialog(tr("Confirm send coins"), questionString, informative_text, detailed_text, SEND_CONFIRM_DELAY, this); + const QString confirmation = model->privateKeysDisabled() ? tr("Confirm transaction proposal") : tr("Confirm send coins"); + const QString confirmButtonText = model->privateKeysDisabled() ? tr("Copy PSBT to clipboard") : tr("Send"); + SendConfirmationDialog confirmationDialog(confirmation, questionString, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this); confirmationDialog.exec(); QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result()); @@ -369,17 +387,35 @@ void SendCoinsDialog::on_sendButton_clicked() return; } - // now send the prepared transaction - WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction); - // process sendStatus and on error generate message shown to user - processSendCoinsReturn(sendStatus); + bool send_failure = false; + if (model->privateKeysDisabled()) { + CMutableTransaction mtx = CMutableTransaction{*(currentTransaction.getWtx())}; + PartiallySignedTransaction psbtx(mtx); + bool complete = false; + const TransactionError err = model->wallet().fillPSBT(psbtx, complete, SIGHASH_ALL, false /* sign */, true /* bip32derivs */); + assert(!complete); + assert(err == TransactionError::OK); + // Serialize the PSBT + CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); + ssTx << psbtx; + GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str()); + Q_EMIT message(tr("PSBT copied"), "Copied to clipboard", CClientUIInterface::MSG_INFORMATION); + } else { + // now send the prepared transaction + WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction); + // process sendStatus and on error generate message shown to user + processSendCoinsReturn(sendStatus); - if (sendStatus.status == WalletModel::OK) - { + if (sendStatus.status == WalletModel::OK) { + Q_EMIT coinsSent(currentTransaction.getWtx()->GetHash()); + } else { + send_failure = true; + } + } + if (!send_failure) { accept(); CoinControlDialog::coinControl()->UnSelectAll(); coinControlUpdateLabels(); - Q_EMIT coinsSent(currentTransaction.getWtx()->GetHash()); } fNewRecipientAllowed = true; } @@ -526,7 +562,12 @@ void SendCoinsDialog::setBalance(const interfaces::WalletBalances& balances) { if(model && model->getOptionsModel()) { - ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balances.balance)); + CAmount balance = balances.balance; + if (model->privateKeysDisabled()) { + balance = balances.watch_only_balance; + ui->labelBalanceName->setText(tr("Watch-only balance:")); + } + ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance)); } } @@ -611,6 +652,9 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry) coin_control = *CoinControlDialog::coinControl(); } + // Include watch-only for wallets without private key + coin_control.fAllowWatchOnly = model->privateKeysDisabled(); + // Calculate available amount to send. CAmount amount = model->wallet().getAvailableBalance(coin_control); for (int i = 0; i < ui->entries->count(); ++i) { @@ -663,6 +707,8 @@ void SendCoinsDialog::updateCoinControlState(CCoinControl& ctrl) // Either custom fee will be used or if not selected, the confirmation target from dropdown box ctrl.m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex()); ctrl.m_signal_bip125_rbf = ui->optInRBF->isChecked(); + // Include watch-only for wallets without private key + ctrl.fAllowWatchOnly = model->privateKeysDisabled(); } void SendCoinsDialog::updateSmartFeeLabel() @@ -870,8 +916,8 @@ void SendCoinsDialog::coinControlUpdateLabels() } } -SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, QWidget* parent) - : QMessageBox(parent), secDelay(_secDelay) +SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, const QString& _confirmButtonText, QWidget* parent) + : QMessageBox(parent), secDelay(_secDelay), confirmButtonText(_confirmButtonText) { setIcon(QMessageBox::Question); setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines). @@ -908,11 +954,11 @@ void SendConfirmationDialog::updateYesButton() if(secDelay > 0) { yesButton->setEnabled(false); - yesButton->setText(tr("Send") + " (" + QString::number(secDelay) + ")"); + yesButton->setText(confirmButtonText + " (" + QString::number(secDelay) + ")"); } else { yesButton->setEnabled(true); - yesButton->setText(tr("Send")); + yesButton->setText(confirmButtonText); } } diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index c6c1816877..ccd8494613 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -108,7 +108,7 @@ class SendConfirmationDialog : public QMessageBox Q_OBJECT public: - SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, QWidget* parent = nullptr); + SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, const QString& confirmText = "Send", QWidget* parent = nullptr); int exec(); private Q_SLOTS: @@ -119,6 +119,7 @@ private: QAbstractButton *yesButton; QTimer countDownTimer; int secDelay; + QString confirmButtonText; }; #endif // BITCOIN_QT_SENDCOINSDIALOG_H diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp index be417655b4..610dfbb85a 100644 --- a/src/qt/sendcoinsentry.cpp +++ b/src/qt/sendcoinsentry.cpp @@ -14,6 +14,7 @@ #include <qt/guiutil.h> #include <qt/optionsmodel.h> #include <qt/platformstyle.h> +#include <qt/walletmodel.h> #include <QApplication> #include <QClipboard> @@ -36,7 +37,6 @@ SendCoinsEntry::SendCoinsEntry(const PlatformStyle *_platformStyle, QWidget *par if (platformStyle->getUseExtraSpacing()) ui->payToLayout->setSpacing(4); - ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book")); // normal bitcoin address field GUIUtil::setupAddressWidget(ui->payTo, this); diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h index 42e2217130..aa69d30f99 100644 --- a/src/qt/sendcoinsentry.h +++ b/src/qt/sendcoinsentry.h @@ -5,13 +5,17 @@ #ifndef BITCOIN_QT_SENDCOINSENTRY_H #define BITCOIN_QT_SENDCOINSENTRY_H -#include <qt/walletmodel.h> +#include <qt/sendcoinsrecipient.h> #include <QStackedWidget> class WalletModel; class PlatformStyle; +namespace interfaces { +class Node; +} // namespace interfaces + namespace Ui { class SendCoinsEntry; } diff --git a/src/qt/sendcoinsrecipient.h b/src/qt/sendcoinsrecipient.h new file mode 100644 index 0000000000..12279fab64 --- /dev/null +++ b/src/qt/sendcoinsrecipient.h @@ -0,0 +1,74 @@ +// Copyright (c) 2011-2019 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_QT_SENDCOINSRECIPIENT_H +#define BITCOIN_QT_SENDCOINSRECIPIENT_H + +#if defined(HAVE_CONFIG_H) +#include <config/bitcoin-config.h> +#endif + +#include <amount.h> +#include <serialize.h> + +#include <string> + +#include <QString> + +class SendCoinsRecipient +{ +public: + explicit SendCoinsRecipient() : amount(0), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) { } + explicit SendCoinsRecipient(const QString &addr, const QString &_label, const CAmount& _amount, const QString &_message): + address(addr), label(_label), amount(_amount), message(_message), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) {} + + // If from an unauthenticated payment request, this is used for storing + // the addresses, e.g. address-A<br />address-B<br />address-C. + // Info: As we don't need to process addresses in here when using + // payment requests, we can abuse it for displaying an address list. + // Todo: This is a hack, should be replaced with a cleaner solution! + QString address; + QString label; + CAmount amount; + // If from a payment request, this is used for storing the memo + QString message; + // Keep the payment request around as a serialized string to ensure + // load/store is lossless. + std::string sPaymentRequest; + // Empty if no authentication or invalid signature/cert/etc. + QString authenticatedMerchant; + + bool fSubtractFeeFromAmount; // memory only + + static const int CURRENT_VERSION = 1; + int nVersion; + + ADD_SERIALIZE_METHODS; + + template <typename Stream, typename Operation> + inline void SerializationOp(Stream& s, Operation ser_action) { + std::string sAddress = address.toStdString(); + std::string sLabel = label.toStdString(); + std::string sMessage = message.toStdString(); + std::string sAuthenticatedMerchant = authenticatedMerchant.toStdString(); + + READWRITE(this->nVersion); + READWRITE(sAddress); + READWRITE(sLabel); + READWRITE(amount); + READWRITE(sMessage); + READWRITE(sPaymentRequest); + READWRITE(sAuthenticatedMerchant); + + if (ser_action.ForRead()) + { + address = QString::fromStdString(sAddress); + label = QString::fromStdString(sLabel); + message = QString::fromStdString(sMessage); + authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant); + } + } +}; + +#endif // BITCOIN_QT_SENDCOINSRECIPIENT_H diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 1d0e1323bc..81e9d33a60 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -35,8 +35,6 @@ SignVerifyMessageDialog::SignVerifyMessageDialog(const PlatformStyle *_platformS ui->verifyMessageButton_VM->setIcon(platformStyle->SingleColorIcon(":/icons/transaction_0")); ui->clearButton_VM->setIcon(platformStyle->SingleColorIcon(":/icons/remove")); - ui->signatureOut_SM->setPlaceholderText(tr("Click \"Sign Message\" to generate signature")); - GUIUtil::setupAddressWidget(ui->addressIn_SM, this); GUIUtil::setupAddressWidget(ui->addressIn_VM, this); @@ -136,7 +134,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() } CKey key; - if (!model->wallet().getPrivKey(CKeyID(*pkhash), key)) + if (!model->wallet().getPrivKey(GetScriptForDestination(destination), CKeyID(*pkhash), key)) { ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setText(tr("Private key for the entered address is not available.")); diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index 971d9f4a7c..374fc9b59b 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -32,7 +32,6 @@ void RPCNestedTests::rpcNestedTests() // do some test setup // could be moved to a more generic place when we add more tests on QT level tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]); - //mempool.setSanityCheck(1.0); TestingSetup test; diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 980de711db..f6d2816ff8 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -134,6 +134,7 @@ void TestGUI(interfaces::Node& node) test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey())); } node.context()->connman = std::move(test.m_node.connman); + node.context()->mempool = std::move(test.m_node.mempool); std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), WalletDatabase::CreateMock()); bool firstRun; wallet->LoadWallet(firstRun); @@ -170,6 +171,16 @@ void TestGUI(interfaces::Node& node) sendCoinsDialog.setModel(&walletModel); transactionView.setModel(&walletModel); + { + // Check balance in send dialog + QLabel* balanceLabel = sendCoinsDialog.findChild<QLabel*>("labelBalance"); + QString balanceText = balanceLabel->text(); + int unit = walletModel.getOptionsModel()->getDisplayUnit(); + CAmount balance = walletModel.wallet().getBalance(); + QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways); + QCOMPARE(balanceText, balanceComparison); + } + // Send two transactions, and verify they are added to transaction list. TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel(); QCOMPARE(transactionTableModel->rowCount({}), 105); diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index 318b0756c7..9a93798aef 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -15,11 +15,12 @@ #include <consensus/consensus.h> #include <interfaces/node.h> +#include <interfaces/wallet.h> #include <key_io.h> -#include <validation.h> +#include <policy/policy.h> #include <script/script.h> #include <util/system.h> -#include <policy/policy.h> +#include <validation.h> #include <wallet/ismine.h> #include <stdint.h> diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 095c98d26f..220e41b383 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -34,14 +34,6 @@ HelpMessageDialog::HelpMessageDialog(interfaces::Node& node, QWidget *parent, bo ui->setupUi(this); QString version = QString{PACKAGE_NAME} + " " + tr("version") + " " + QString::fromStdString(FormatFullVersion()); - /* On x86 add a bit specifier to the version so that users can distinguish between - * 32 and 64 bit builds. On other architectures, 32/64 bit may be more ambiguous. - */ -#if defined(__x86_64__) - version += " " + tr("(%1-bit)").arg(64); -#elif defined(__i386__ ) - version += " " + tr("(%1-bit)").arg(32); -#endif if (about) { diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp index 4c253f8ddd..7413a1f09e 100644 --- a/src/qt/walletcontroller.cpp +++ b/src/qt/walletcontroller.cpp @@ -2,17 +2,18 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <qt/walletcontroller.h> + #include <qt/askpassphrasedialog.h> #include <qt/createwalletdialog.h> #include <qt/guiconstants.h> #include <qt/guiutil.h> -#include <qt/walletcontroller.h> - -#include <wallet/wallet.h> +#include <qt/walletmodel.h> #include <interfaces/handler.h> #include <interfaces/node.h> #include <util/string.h> +#include <wallet/wallet.h> #include <algorithm> diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h index e50dd5c7eb..956245775e 100644 --- a/src/qt/walletcontroller.h +++ b/src/qt/walletcontroller.h @@ -5,7 +5,7 @@ #ifndef BITCOIN_QT_WALLETCONTROLLER_H #define BITCOIN_QT_WALLETCONTROLLER_H -#include <qt/walletmodel.h> +#include <qt/sendcoinsrecipient.h> #include <support/allocators/secure.h> #include <sync.h> @@ -23,10 +23,12 @@ class OptionsModel; class PlatformStyle; +class WalletModel; namespace interfaces { class Handler; class Node; +class Wallet; } // namespace interfaces class AskPassphraseDialog; diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index d7f0617315..4b2b475883 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -61,11 +61,6 @@ void WalletFrame::addWallet(WalletModel *walletModel) walletStack->addWidget(walletView); mapWalletViews[walletModel] = walletView; - // Ensure a walletView is able to show the main window - connect(walletView, &WalletView::showNormalIfMinimized, [this]{ - gui->showNormalIfMinimized(); - }); - connect(walletView, &WalletView::outOfSyncWarningClicked, this, &WalletFrame::outOfSyncWarningClicked); } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 33801d3907..fb92e29f21 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -183,7 +183,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact std::string strFailReason; auto& newTx = transaction.getWtx(); - newTx = m_wallet->createTransaction(vecSend, coinControl, true /* sign */, nChangePosRet, nFeeRequired, strFailReason); + newTx = m_wallet->createTransaction(vecSend, coinControl, !privateKeysDisabled() /* sign */, nChangePosRet, nFeeRequired, strFailReason); transaction.setTransactionFee(nFeeRequired); if (fSubtractFeeFromAmount && newTx) transaction.reassignAmounts(nChangePosRet); diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index a873519a34..8087356f5e 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -9,9 +9,7 @@ #include <config/bitcoin-config.h> #endif -#include <amount.h> #include <key.h> -#include <serialize.h> #include <script/standard.h> #include <qt/walletmodeltransaction.h> @@ -29,6 +27,7 @@ class AddressTableModel; class OptionsModel; class PlatformStyle; class RecentRequestsTableModel; +class SendCoinsRecipient; class TransactionTableModel; class WalletModelTransaction; @@ -47,61 +46,6 @@ QT_BEGIN_NAMESPACE class QTimer; QT_END_NAMESPACE -class SendCoinsRecipient -{ -public: - explicit SendCoinsRecipient() : amount(0), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) { } - explicit SendCoinsRecipient(const QString &addr, const QString &_label, const CAmount& _amount, const QString &_message): - address(addr), label(_label), amount(_amount), message(_message), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) {} - - // If from an unauthenticated payment request, this is used for storing - // the addresses, e.g. address-A<br />address-B<br />address-C. - // Info: As we don't need to process addresses in here when using - // payment requests, we can abuse it for displaying an address list. - // Todo: This is a hack, should be replaced with a cleaner solution! - QString address; - QString label; - CAmount amount; - // If from a payment request, this is used for storing the memo - QString message; - // Keep the payment request around as a serialized string to ensure - // load/store is lossless. - std::string sPaymentRequest; - // Empty if no authentication or invalid signature/cert/etc. - QString authenticatedMerchant; - - bool fSubtractFeeFromAmount; // memory only - - static const int CURRENT_VERSION = 1; - int nVersion; - - ADD_SERIALIZE_METHODS; - - template <typename Stream, typename Operation> - inline void SerializationOp(Stream& s, Operation ser_action) { - std::string sAddress = address.toStdString(); - std::string sLabel = label.toStdString(); - std::string sMessage = message.toStdString(); - std::string sAuthenticatedMerchant = authenticatedMerchant.toStdString(); - - READWRITE(this->nVersion); - READWRITE(sAddress); - READWRITE(sLabel); - READWRITE(amount); - READWRITE(sMessage); - READWRITE(sPaymentRequest); - READWRITE(sAuthenticatedMerchant); - - if (ser_action.ForRead()) - { - address = QString::fromStdString(sAddress); - label = QString::fromStdString(sLabel); - message = QString::fromStdString(sMessage); - authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant); - } - } -}; - /** Interface to Bitcoin wallet from Qt view code. */ class WalletModel : public QObject { diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h index 242ba13897..9e5d285e8c 100644 --- a/src/qt/walletmodeltransaction.h +++ b/src/qt/walletmodeltransaction.h @@ -5,7 +5,8 @@ #ifndef BITCOIN_QT_WALLETMODELTRANSACTION_H #define BITCOIN_QT_WALLETMODELTRANSACTION_H -#include <qt/walletmodel.h> +#include <primitives/transaction.h> +#include <qt/sendcoinsrecipient.h> #include <amount.h> diff --git a/src/qt/walletview.h b/src/qt/walletview.h index e29c4c52f5..8d5a301cdb 100644 --- a/src/qt/walletview.h +++ b/src/qt/walletview.h @@ -115,8 +115,6 @@ public Q_SLOTS: void requestedSyncWarningInfo(); Q_SIGNALS: - /** Signal that we want to show the main window */ - void showNormalIfMinimized(); /** Fired when a message should be reported to the user */ void message(const QString &title, const QString &message, unsigned int style); /** Encryption status of wallet changed */ diff --git a/src/random.cpp b/src/random.cpp index 48d20d7d72..99927fc5d2 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -5,19 +5,23 @@ #include <random.h> +#include <compat/cpuid.h> +#include <crypto/sha256.h> #include <crypto/sha512.h> #include <support/cleanse.h> #ifdef WIN32 #include <compat.h> // for Windows API #include <wincrypt.h> #endif -#include <logging.h> // for LogPrint() -#include <sync.h> // for WAIT_LOCK +#include <logging.h> // for LogPrintf() +#include <sync.h> // for Mutex #include <util/time.h> // for GetTime() #include <stdlib.h> #include <thread> +#include <randomenv.h> + #include <support/allocators/secure.h> #ifndef WIN32 @@ -40,15 +44,6 @@ #include <sys/sysctl.h> #endif - -#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) -#include <cpuid.h> -#endif - -#include <openssl/err.h> -#include <openssl/rand.h> -#include <openssl/conf.h> - [[noreturn]] static void RandFailure() { LogPrintf("Failed to read randomness, aborting\n"); @@ -75,7 +70,7 @@ static inline int64_t GetPerformanceCounter() noexcept #endif } -#if defined(__x86_64__) || defined(__amd64__) || defined(__i386__) +#ifdef HAVE_GETCPUID static bool g_rdrand_supported = false; static bool g_rdseed_supported = false; static constexpr uint32_t CPUID_F1_ECX_RDRAND = 0x40000000; @@ -86,15 +81,6 @@ static_assert(CPUID_F1_ECX_RDRAND == bit_RDRND, "Unexpected value for bit_RDRND" #ifdef bit_RDSEED static_assert(CPUID_F7_EBX_RDSEED == bit_RDSEED, "Unexpected value for bit_RDSEED"); #endif -static void inline GetCPUID(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d) -{ - // We can't use __get_cpuid as it doesn't support subleafs. -#ifdef __GNUC__ - __cpuid_count(leaf, subleaf, a, b, c, d); -#else - __asm__ ("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(leaf), "2"(subleaf)); -#endif -} static void InitHardwareRand() { @@ -193,7 +179,7 @@ static uint64_t GetRdSeed() noexcept /* Access to other hardware random number generators could be added here later, * assuming it is sufficiently fast (in the order of a few hundred CPU cycles). * Slower sources should probably be invoked separately, and/or only from - * RandAddSeedSleep (which is called during idle background operation). + * RandAddPeriodic (which is called once a minute). */ static void InitHardwareRand() {} static void ReportHardwareRand() {} @@ -263,44 +249,6 @@ static void Strengthen(const unsigned char (&seed)[32], int microseconds, CSHA51 memory_cleanse(buffer, sizeof(buffer)); } -static void RandAddSeedPerfmon(CSHA512& hasher) -{ -#ifdef WIN32 - // Don't need this on Linux, OpenSSL automatically uses /dev/urandom - // Seed with the entire set of perfmon data - - // This can take up to 2 seconds, so only do it every 10 minutes - static int64_t nLastPerfmon; - if (GetTime() < nLastPerfmon + 10 * 60) - return; - nLastPerfmon = GetTime(); - - std::vector<unsigned char> vData(250000, 0); - long ret = 0; - unsigned long nSize = 0; - const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data - while (true) { - nSize = vData.size(); - ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, vData.data(), &nSize); - if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize) - break; - vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially - } - RegCloseKey(HKEY_PERFORMANCE_DATA); - if (ret == ERROR_SUCCESS) { - hasher.Write(vData.data(), nSize); - memory_cleanse(vData.data(), nSize); - } else { - // Performance data is only a best-effort attempt at improving the - // situation when the OS randomness (and other sources) aren't - // adequate. As a result, failure to read it is isn't considered critical, - // so we don't call RandFailure(). - // TODO: Add logging when the logger is made functional before global - // constructors have been invoked. - } -#endif -} - #ifndef WIN32 /** Fallback: get 32 bytes of system entropy from /dev/urandom. The most * compatible way to get cryptographic randomness on UNIX-ish platforms. @@ -396,8 +344,6 @@ void GetOSRand(unsigned char *ent32) #endif } -void LockingCallbackOpenSSL(int mode, int i, const char* file, int line); - namespace { class RNGState { @@ -413,31 +359,47 @@ class RNGState { unsigned char m_state[32] GUARDED_BY(m_mutex) = {0}; uint64_t m_counter GUARDED_BY(m_mutex) = 0; bool m_strongly_seeded GUARDED_BY(m_mutex) = false; - std::unique_ptr<Mutex[]> m_mutex_openssl; + + Mutex m_events_mutex; + CSHA256 m_events_hasher GUARDED_BY(m_events_mutex); public: RNGState() noexcept { InitHardwareRand(); + } + + ~RNGState() + { + } - // Init OpenSSL library multithreading support - m_mutex_openssl.reset(new Mutex[CRYPTO_num_locks()]); - CRYPTO_set_locking_callback(LockingCallbackOpenSSL); + void AddEvent(uint32_t event_info) noexcept + { + LOCK(m_events_mutex); - // OpenSSL can optionally load a config file which lists optional loadable modules and engines. - // We don't use them so we don't require the config. However some of our libs may call functions - // which attempt to load the config file, possibly resulting in an exit() or crash if it is missing - // or corrupt. Explicitly tell OpenSSL not to try to load the file. The result for our libs will be - // that the config appears to have been loaded and there are no modules/engines available. - OPENSSL_no_config(); + m_events_hasher.Write((const unsigned char *)&event_info, sizeof(event_info)); + // Get the low four bytes of the performance counter. This translates to roughly the + // subsecond part. + uint32_t perfcounter = (GetPerformanceCounter() & 0xffffffff); + m_events_hasher.Write((const unsigned char*)&perfcounter, sizeof(perfcounter)); } - ~RNGState() + /** + * Feed (the hash of) all events added through AddEvent() to hasher. + */ + void SeedEvents(CSHA512& hasher) noexcept { - // Securely erase the memory used by the OpenSSL PRNG - RAND_cleanup(); - // Shutdown OpenSSL library multithreading support - CRYPTO_set_locking_callback(nullptr); + // We use only SHA256 for the events hashing to get the ASM speedups we have for SHA256, + // since we want it to be fast as network peers may be able to trigger it repeatedly. + LOCK(m_events_mutex); + + unsigned char events_hash[32]; + m_events_hasher.Finalize(events_hash); + hasher.Write(events_hash, 32); + + // Re-initialize the hasher with the finalized state to use later. + m_events_hasher.Reset(); + m_events_hasher.Write(events_hash, 32); } /** Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher. @@ -473,8 +435,6 @@ public: memory_cleanse(buf, 64); return ret; } - - Mutex& GetOpenSSLMutex(int i) { return m_mutex_openssl[i]; } }; RNGState& GetRNGState() noexcept @@ -486,30 +446,9 @@ RNGState& GetRNGState() noexcept } } -void LockingCallbackOpenSSL(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS -{ - RNGState& rng = GetRNGState(); - - if (mode & CRYPTO_LOCK) { - rng.GetOpenSSLMutex(i).lock(); - } else { - rng.GetOpenSSLMutex(i).unlock(); - } -} - /* A note on the use of noexcept in the seeding functions below: * - * None of the RNG code should ever throw any exception, with the sole exception - * of MilliSleep in SeedSleep, which can (and does) support interruptions which - * cause a boost::thread_interrupted to be thrown. - * - * This means that SeedSleep, and all functions that invoke it are throwing. - * However, we know that GetRandBytes() and GetStrongRandBytes() never trigger - * this sleeping logic, so they are noexcept. The same is true for all the - * GetRand*() functions that use GetRandBytes() indirectly. - * - * TODO: After moving away from interruptible boost-based thread management, - * everything can become noexcept here. + * None of the RNG code should ever throw any exception. */ static void SeedTimestamp(CSHA512& hasher) noexcept @@ -533,7 +472,7 @@ static void SeedFast(CSHA512& hasher) noexcept SeedTimestamp(hasher); } -static void SeedSlow(CSHA512& hasher) noexcept +static void SeedSlow(CSHA512& hasher, RNGState& rng) noexcept { unsigned char buffer[32]; @@ -544,9 +483,8 @@ static void SeedSlow(CSHA512& hasher) noexcept GetOSRand(buffer); hasher.Write(buffer, sizeof(buffer)); - // OpenSSL RNG (for now) - RAND_bytes(buffer, sizeof(buffer)); - hasher.Write(buffer, sizeof(buffer)); + // Add the events hasher into the mix + rng.SeedEvents(hasher); // High-precision timestamp. // @@ -556,22 +494,16 @@ static void SeedSlow(CSHA512& hasher) noexcept } /** Extract entropy from rng, strengthen it, and feed it into hasher. */ -static void SeedStrengthen(CSHA512& hasher, RNGState& rng) noexcept +static void SeedStrengthen(CSHA512& hasher, RNGState& rng, int microseconds) noexcept { - static std::atomic<int64_t> last_strengthen{0}; - int64_t last_time = last_strengthen.load(); - int64_t current_time = GetTimeMicros(); - if (current_time > last_time + 60000000) { // Only run once a minute - // Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher. - unsigned char strengthen_seed[32]; - rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false); - // Strengthen it for 10ms (100ms on first run), and feed it into hasher. - Strengthen(strengthen_seed, last_time == 0 ? 100000 : 10000, hasher); - last_strengthen = current_time; - } + // Generate 32 bytes of entropy from the RNG, and a copy of the entropy already in hasher. + unsigned char strengthen_seed[32]; + rng.MixExtract(strengthen_seed, sizeof(strengthen_seed), CSHA512(hasher), false); + // Strengthen the seed, and feed it into hasher. + Strengthen(strengthen_seed, microseconds, hasher); } -static void SeedSleep(CSHA512& hasher, RNGState& rng) +static void SeedPeriodic(CSHA512& hasher, RNGState& rng) noexcept { // Everything that the 'fast' seeder includes SeedFast(hasher); @@ -579,17 +511,16 @@ static void SeedSleep(CSHA512& hasher, RNGState& rng) // High-precision timestamp SeedTimestamp(hasher); - // Sleep for 1ms - MilliSleep(1); + // Add the events hasher into the mix + rng.SeedEvents(hasher); - // High-precision timestamp after sleeping (as we commit to both the time before and after, this measures the delay) - SeedTimestamp(hasher); + // Dynamic environment data (performance monitoring, ...) + auto old_size = hasher.Size(); + RandAddDynamicEnv(hasher); + LogPrint(BCLog::RAND, "Feeding %i bytes of dynamic environment data into RNG\n", hasher.Size() - old_size); - // Windows performance monitor data (once every 10 minutes) - RandAddSeedPerfmon(hasher); - - // Strengthen every minute - SeedStrengthen(hasher, rng); + // Strengthen for 10 ms + SeedStrengthen(hasher, rng, 10000); } static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept @@ -598,22 +529,27 @@ static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept SeedHardwareSlow(hasher); // Everything that the 'slow' seeder includes. - SeedSlow(hasher); + SeedSlow(hasher, rng); + + // Dynamic environment data (performance monitoring, ...) + auto old_size = hasher.Size(); + RandAddDynamicEnv(hasher); - // Windows performance monitor data. - RandAddSeedPerfmon(hasher); + // Static environment data + RandAddStaticEnv(hasher); + LogPrint(BCLog::RAND, "Feeding %i bytes of environment data into RNG\n", hasher.Size() - old_size); - // Strengthen - SeedStrengthen(hasher, rng); + // Strengthen for 100 ms + SeedStrengthen(hasher, rng, 100000); } enum class RNGLevel { FAST, //!< Automatically called by GetRandBytes SLOW, //!< Automatically called by GetStrongRandBytes - SLEEP, //!< Called by RandAddSeedSleep() + PERIODIC, //!< Called by RandAddPeriodic() }; -static void ProcRand(unsigned char* out, int num, RNGLevel level) +static void ProcRand(unsigned char* out, int num, RNGLevel level) noexcept { // Make sure the RNG is initialized first (as all Seed* function possibly need hwrand to be available). RNGState& rng = GetRNGState(); @@ -626,10 +562,10 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level) SeedFast(hasher); break; case RNGLevel::SLOW: - SeedSlow(hasher); + SeedSlow(hasher, rng); break; - case RNGLevel::SLEEP: - SeedSleep(hasher, rng); + case RNGLevel::PERIODIC: + SeedPeriodic(hasher, rng); break; } @@ -640,19 +576,12 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level) SeedStartup(startup_hasher, rng); rng.MixExtract(out, num, std::move(startup_hasher), true); } - - // For anything but the 'fast' level, feed the resulting RNG output (after an additional hashing step) back into OpenSSL. - if (level != RNGLevel::FAST) { - unsigned char buf[64]; - CSHA512().Write(out, num).Finalize(buf); - RAND_add(buf, sizeof(buf), num); - memory_cleanse(buf, 64); - } } void GetRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::FAST); } void GetStrongRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::SLOW); } -void RandAddSeedSleep() { ProcRand(nullptr, 0, RNGLevel::SLEEP); } +void RandAddPeriodic() noexcept { ProcRand(nullptr, 0, RNGLevel::PERIODIC); } +void RandAddEvent(const uint32_t event_info) noexcept { GetRNGState().AddEvent(event_info); } bool g_mock_deterministic_tests{false}; @@ -716,7 +645,7 @@ bool Random_SanityCheck() uint64_t start = GetPerformanceCounter(); /* This does not measure the quality of randomness, but it does test that - * OSRandom() overwrites all 32 bytes of the output given a maximum + * GetOSRand() overwrites all 32 bytes of the output given a maximum * number of tries. */ static const ssize_t MAX_TRIES = 1024; diff --git a/src/random.h b/src/random.h index 9d1f751773..e1b105168d 100644 --- a/src/random.h +++ b/src/random.h @@ -35,24 +35,22 @@ * that fast seeding includes, but additionally: * - OS entropy (/dev/urandom, getrandom(), ...). The application will terminate if * this entropy source fails. - * - Bytes from OpenSSL's RNG (which itself may be seeded from various sources) * - Another high-precision timestamp (indirectly committing to a benchmark of all the * previous sources). * These entropy sources are slower, but designed to make sure the RNG state contains * fresh data that is unpredictable to attackers. * - * - RandAddSeedSleep() seeds everything that fast seeding includes, but additionally: - * - A high-precision timestamp before and after sleeping 1ms. - * - (On Windows) Once every 10 minutes, performance monitoring data from the OS. - - - Once every minute, strengthen the entropy for 10 ms using repeated SHA512. - * These just exploit the fact the system is idle to improve the quality of the RNG - * slightly. + * - RandAddPeriodic() seeds everything that fast seeding includes, but additionally: + * - A high-precision timestamp + * - Dynamic environment data (performance monitoring, ...) + * - Strengthen the entropy for 10 ms using repeated SHA512. + * This is run once every minute. * * On first use of the RNG (regardless of what function is called first), all entropy * sources used in the 'slow' seeder are included, but also: * - 256 bits from the hardware RNG (rdseed or rdrand) when available. - * - (On Windows) Performance monitoring data from the OS. - * - (On Windows) Through OpenSSL, the screen contents. + * - Dynamic environment data (performance monitoring, ...) + * - Static environment data * - Strengthen the entropy for 100 ms using repeated SHA512. * * When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and @@ -85,11 +83,19 @@ uint256 GetRandHash() noexcept; void GetStrongRandBytes(unsigned char* buf, int num) noexcept; /** - * Sleep for 1ms, gather entropy from various sources, and feed them to the PRNG state. + * Gather entropy from various expensive sources, and feed them to the PRNG state. * * Thread-safe. */ -void RandAddSeedSleep(); +void RandAddPeriodic() noexcept; + +/** + * Gathers entropy from the low bits of the time at which events occur. Should + * be called with a uint32_t describing the event at the time an event occurs. + * + * Thread-safe. + */ +void RandAddEvent(const uint32_t event_info) noexcept; /** * Fast randomness source. This is seeded once with secure random data, but diff --git a/src/randomenv.cpp b/src/randomenv.cpp new file mode 100644 index 0000000000..6992c720ff --- /dev/null +++ b/src/randomenv.cpp @@ -0,0 +1,518 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined(HAVE_CONFIG_H) +#include <config/bitcoin-config.h> +#endif + +#include <randomenv.h> + +#include <clientversion.h> +#include <compat/cpuid.h> +#include <crypto/sha512.h> +#include <support/cleanse.h> +#include <util/time.h> // for GetTime() +#ifdef WIN32 +#include <compat.h> // for Windows API +#endif + +#include <algorithm> +#include <atomic> +#include <chrono> +#include <climits> +#include <thread> +#include <vector> + +#include <stdint.h> +#include <string.h> +#ifndef WIN32 +#include <sys/types.h> // must go before a number of other headers +#include <fcntl.h> +#include <netinet/in.h> +#include <sys/resource.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/utsname.h> +#include <unistd.h> +#endif +#ifdef __MACH__ +#include <mach/clock.h> +#include <mach/mach.h> +#include <mach/mach_time.h> +#endif +#if HAVE_DECL_GETIFADDRS +#include <ifaddrs.h> +#endif +#if HAVE_SYSCTL +#include <sys/sysctl.h> +#if HAVE_VM_VM_PARAM_H +#include <vm/vm_param.h> +#endif +#if HAVE_SYS_RESOURCES_H +#include <sys/resources.h> +#endif +#if HAVE_SYS_VMMETER_H +#include <sys/vmmeter.h> +#endif +#endif +#ifdef __linux__ +#include <sys/auxv.h> +#endif + +//! Necessary on some platforms +extern char** environ; + +namespace { + +void RandAddSeedPerfmon(CSHA512& hasher) +{ +#ifdef WIN32 + // Seed with the entire set of perfmon data + + // This can take up to 2 seconds, so only do it every 10 minutes + static std::atomic<std::chrono::seconds> last_perfmon{std::chrono::seconds{0}}; + auto last_time = last_perfmon.load(); + auto current_time = GetTime<std::chrono::seconds>(); + if (current_time < last_time + std::chrono::minutes{10}) return; + last_perfmon = current_time; + + std::vector<unsigned char> vData(250000, 0); + long ret = 0; + unsigned long nSize = 0; + const size_t nMaxSize = 10000000; // Bail out at more than 10MB of performance data + while (true) { + nSize = vData.size(); + ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", nullptr, nullptr, vData.data(), &nSize); + if (ret != ERROR_MORE_DATA || vData.size() >= nMaxSize) + break; + vData.resize(std::max((vData.size() * 3) / 2, nMaxSize)); // Grow size of buffer exponentially + } + RegCloseKey(HKEY_PERFORMANCE_DATA); + if (ret == ERROR_SUCCESS) { + hasher.Write(vData.data(), nSize); + memory_cleanse(vData.data(), nSize); + } else { + // Performance data is only a best-effort attempt at improving the + // situation when the OS randomness (and other sources) aren't + // adequate. As a result, failure to read it is isn't considered critical, + // so we don't call RandFailure(). + // TODO: Add logging when the logger is made functional before global + // constructors have been invoked. + } +#endif +} + +/** Helper to easily feed data into a CSHA512. + * + * Note that this does not serialize the passed object (like stream.h's << operators do). + * Its raw memory representation is used directly. + */ +template<typename T> +CSHA512& operator<<(CSHA512& hasher, const T& data) { + static_assert(!std::is_same<typename std::decay<T>::type, char*>::value, "Calling operator<<(CSHA512, char*) is probably not what you want"); + static_assert(!std::is_same<typename std::decay<T>::type, unsigned char*>::value, "Calling operator<<(CSHA512, unsigned char*) is probably not what you want"); + static_assert(!std::is_same<typename std::decay<T>::type, const char*>::value, "Calling operator<<(CSHA512, const char*) is probably not what you want"); + static_assert(!std::is_same<typename std::decay<T>::type, const unsigned char*>::value, "Calling operator<<(CSHA512, const unsigned char*) is probably not what you want"); + hasher.Write((const unsigned char*)&data, sizeof(data)); + return hasher; +} + +#ifndef WIN32 +void AddSockaddr(CSHA512& hasher, const struct sockaddr *addr) +{ + if (addr == nullptr) return; + switch (addr->sa_family) { + case AF_INET: + hasher.Write((const unsigned char*)addr, sizeof(sockaddr_in)); + break; + case AF_INET6: + hasher.Write((const unsigned char*)addr, sizeof(sockaddr_in6)); + break; + default: + hasher.Write((const unsigned char*)&addr->sa_family, sizeof(addr->sa_family)); + } +} + +void AddFile(CSHA512& hasher, const char *path) +{ + struct stat sb = {}; + int f = open(path, O_RDONLY); + size_t total = 0; + if (f != -1) { + unsigned char fbuf[4096]; + int n; + hasher.Write((const unsigned char*)&f, sizeof(f)); + if (fstat(f, &sb) == 0) hasher << sb; + do { + n = read(f, fbuf, sizeof(fbuf)); + if (n > 0) hasher.Write(fbuf, n); + total += n; + /* not bothering with EINTR handling. */ + } while (n == sizeof(fbuf) && total < 1048576); // Read only the first 1 Mbyte + close(f); + } +} + +void AddPath(CSHA512& hasher, const char *path) +{ + struct stat sb = {}; + if (stat(path, &sb) == 0) { + hasher.Write((const unsigned char*)path, strlen(path) + 1); + hasher << sb; + } +} +#endif + +#if HAVE_SYSCTL +template<int... S> +void AddSysctl(CSHA512& hasher) +{ + int CTL[sizeof...(S)] = {S...}; + unsigned char buffer[65536]; + size_t siz = 65536; + int ret = sysctl(CTL, sizeof...(S), buffer, &siz, nullptr, 0); + if (ret == 0 || (ret == -1 && errno == ENOMEM)) { + hasher << sizeof(CTL); + hasher.Write((const unsigned char*)CTL, sizeof(CTL)); + if (siz > sizeof(buffer)) siz = sizeof(buffer); + hasher << siz; + hasher.Write(buffer, siz); + } +} +#endif + +#ifdef HAVE_GETCPUID +void inline AddCPUID(CSHA512& hasher, uint32_t leaf, uint32_t subleaf, uint32_t& ax, uint32_t& bx, uint32_t& cx, uint32_t& dx) +{ + GetCPUID(leaf, subleaf, ax, bx, cx, dx); + hasher << leaf << subleaf << ax << bx << cx << dx; +} + +void AddAllCPUID(CSHA512& hasher) +{ + uint32_t ax, bx, cx, dx; + // Iterate over all standard leaves + AddCPUID(hasher, 0, 0, ax, bx, cx, dx); // Returns max leaf in ax + uint32_t max = ax; + for (uint32_t leaf = 1; leaf <= max && leaf <= 0xFF; ++leaf) { + uint32_t maxsub = 0; + for (uint32_t subleaf = 0; subleaf <= 0xFF; ++subleaf) { + AddCPUID(hasher, leaf, subleaf, ax, bx, cx, dx); + // Iterate subleafs for leaf values 4, 7, 11, 13 + if (leaf == 4) { + if ((ax & 0x1f) == 0) break; + } else if (leaf == 7) { + if (subleaf == 0) maxsub = ax; + if (subleaf == maxsub) break; + } else if (leaf == 11) { + if ((cx & 0xff00) == 0) break; + } else if (leaf == 13) { + if (ax == 0 && bx == 0 && cx == 0 && dx == 0) break; + } else { + // For any other leaf, stop after subleaf 0. + break; + } + } + } + // Iterate over all extended leaves + AddCPUID(hasher, 0x80000000, 0, ax, bx, cx, dx); // Returns max extended leaf in ax + uint32_t ext_max = ax; + for (uint32_t leaf = 0x80000001; leaf <= ext_max && leaf <= 0x800000FF; ++leaf) { + AddCPUID(hasher, leaf, 0, ax, bx, cx, dx); + } +} +#endif +} // namespace + +void RandAddDynamicEnv(CSHA512& hasher) +{ + RandAddSeedPerfmon(hasher); + + // Various clocks +#ifdef WIN32 + FILETIME ftime; + GetSystemTimeAsFileTime(&ftime); + hasher << ftime; +#else +# ifndef __MACH__ + // On non-MacOS systems, use various clock_gettime() calls. + struct timespec ts = {}; +# ifdef CLOCK_MONOTONIC + clock_gettime(CLOCK_MONOTONIC, &ts); + hasher << ts; +# endif +# ifdef CLOCK_REALTIME + clock_gettime(CLOCK_REALTIME, &ts); + hasher << ts; +# endif +# ifdef CLOCK_BOOTTIME + clock_gettime(CLOCK_BOOTTIME, &ts); + hasher << ts; +# endif +# else + // On MacOS use mach_absolute_time (number of CPU ticks since boot) as a replacement for CLOCK_MONOTONIC, + // and clock_get_time for CALENDAR_CLOCK as a replacement for CLOCK_REALTIME. + hasher << mach_absolute_time(); + // From https://gist.github.com/jbenet/1087739 + clock_serv_t cclock; + mach_timespec_t mts = {}; + if (host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock) == KERN_SUCCESS && clock_get_time(cclock, &mts) == KERN_SUCCESS) { + hasher << mts; + mach_port_deallocate(mach_task_self(), cclock); + } +# endif + // gettimeofday is available on all UNIX systems, but only has microsecond precision. + struct timeval tv = {}; + gettimeofday(&tv, nullptr); + hasher << tv; +#endif + // Probably redundant, but also use all the clocks C++11 provides: + hasher << std::chrono::system_clock::now().time_since_epoch().count(); + hasher << std::chrono::steady_clock::now().time_since_epoch().count(); + hasher << std::chrono::high_resolution_clock::now().time_since_epoch().count(); + +#ifndef WIN32 + // Current resource usage. + struct rusage usage = {}; + if (getrusage(RUSAGE_SELF, &usage) == 0) hasher << usage; +#endif + +#ifdef __linux__ + AddFile(hasher, "/proc/diskstats"); + AddFile(hasher, "/proc/vmstat"); + AddFile(hasher, "/proc/schedstat"); + AddFile(hasher, "/proc/zoneinfo"); + AddFile(hasher, "/proc/meminfo"); + AddFile(hasher, "/proc/softirqs"); + AddFile(hasher, "/proc/stat"); + AddFile(hasher, "/proc/self/schedstat"); + AddFile(hasher, "/proc/self/status"); +#endif + +#if HAVE_SYSCTL +# ifdef CTL_KERN +# if defined(KERN_PROC) && defined(KERN_PROC_ALL) + AddSysctl<CTL_KERN, KERN_PROC, KERN_PROC_ALL>(hasher); +# endif +# endif +# ifdef CTL_HW +# ifdef HW_DISKSTATS + AddSysctl<CTL_HW, HW_DISKSTATS>(hasher); +# endif +# endif +# ifdef CTL_VM +# ifdef VM_LOADAVG + AddSysctl<CTL_VM, VM_LOADAVG>(hasher); +# endif +# ifdef VM_TOTAL + AddSysctl<CTL_VM, VM_TOTAL>(hasher); +# endif +# ifdef VM_METER + AddSysctl<CTL_VM, VM_METER>(hasher); +# endif +# endif +#endif + + // Stack and heap location + void* addr = malloc(4097); + hasher << &addr << addr; + free(addr); +} + +void RandAddStaticEnv(CSHA512& hasher) +{ + // Some compile-time static properties + hasher << (CHAR_MIN < 0) << sizeof(void*) << sizeof(long) << sizeof(int); +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) + hasher << __GNUC__ << __GNUC_MINOR__ << __GNUC_PATCHLEVEL__; +#endif +#ifdef _MSC_VER + hasher << _MSC_VER; +#endif + hasher << __cplusplus; +#ifdef _XOPEN_VERSION + hasher << _XOPEN_VERSION; +#endif +#ifdef __VERSION__ + const char* COMPILER_VERSION = __VERSION__; + hasher.Write((const unsigned char*)COMPILER_VERSION, strlen(COMPILER_VERSION) + 1); +#endif + + // Bitcoin client version + hasher << CLIENT_VERSION; + +#ifdef __linux__ + // Information available through getauxval() +# ifdef AT_HWCAP + hasher << getauxval(AT_HWCAP); +# endif +# ifdef AT_HWCAP2 + hasher << getauxval(AT_HWCAP2); +# endif +# ifdef AT_RANDOM + const unsigned char* random_aux = (const unsigned char*)getauxval(AT_RANDOM); + if (random_aux) hasher.Write(random_aux, 16); +# endif +# ifdef AT_PLATFORM + const char* platform_str = (const char*)getauxval(AT_PLATFORM); + if (platform_str) hasher.Write((const unsigned char*)platform_str, strlen(platform_str) + 1); +# endif +# ifdef AT_EXECFN + const char* exec_str = (const char*)getauxval(AT_EXECFN); + if (exec_str) hasher.Write((const unsigned char*)exec_str, strlen(exec_str) + 1); +# endif +#endif // __linux__ + +#ifdef HAVE_GETCPUID + AddAllCPUID(hasher); +#endif + + // Memory locations + hasher << &hasher << &RandAddStaticEnv << &malloc << &errno << &environ; + + // Hostname + char hname[256]; + if (gethostname(hname, 256) == 0) { + hasher.Write((const unsigned char*)hname, strnlen(hname, 256)); + } + +#if HAVE_DECL_GETIFADDRS + // Network interfaces + struct ifaddrs *ifad = NULL; + getifaddrs(&ifad); + struct ifaddrs *ifit = ifad; + while (ifit != NULL) { + hasher.Write((const unsigned char*)&ifit, sizeof(ifit)); + hasher.Write((const unsigned char*)ifit->ifa_name, strlen(ifit->ifa_name) + 1); + hasher.Write((const unsigned char*)&ifit->ifa_flags, sizeof(ifit->ifa_flags)); + AddSockaddr(hasher, ifit->ifa_addr); + AddSockaddr(hasher, ifit->ifa_netmask); + AddSockaddr(hasher, ifit->ifa_dstaddr); + ifit = ifit->ifa_next; + } + freeifaddrs(ifad); +#endif + +#ifndef WIN32 + // UNIX kernel information + struct utsname name; + if (uname(&name) != -1) { + hasher.Write((const unsigned char*)&name.sysname, strlen(name.sysname) + 1); + hasher.Write((const unsigned char*)&name.nodename, strlen(name.nodename) + 1); + hasher.Write((const unsigned char*)&name.release, strlen(name.release) + 1); + hasher.Write((const unsigned char*)&name.version, strlen(name.version) + 1); + hasher.Write((const unsigned char*)&name.machine, strlen(name.machine) + 1); + } + + /* Path and filesystem provided data */ + AddPath(hasher, "/"); + AddPath(hasher, "."); + AddPath(hasher, "/tmp"); + AddPath(hasher, "/home"); + AddPath(hasher, "/proc"); +#ifdef __linux__ + AddFile(hasher, "/proc/cmdline"); + AddFile(hasher, "/proc/cpuinfo"); + AddFile(hasher, "/proc/version"); +#endif + AddFile(hasher, "/etc/passwd"); + AddFile(hasher, "/etc/group"); + AddFile(hasher, "/etc/hosts"); + AddFile(hasher, "/etc/resolv.conf"); + AddFile(hasher, "/etc/timezone"); + AddFile(hasher, "/etc/localtime"); +#endif + + // For MacOS/BSDs, gather data through sysctl instead of /proc. Not all of these + // will exist on every system. +#if HAVE_SYSCTL +# ifdef CTL_HW +# ifdef HW_MACHINE + AddSysctl<CTL_HW, HW_MACHINE>(hasher); +# endif +# ifdef HW_MODEL + AddSysctl<CTL_HW, HW_MODEL>(hasher); +# endif +# ifdef HW_NCPU + AddSysctl<CTL_HW, HW_NCPU>(hasher); +# endif +# ifdef HW_PHYSMEM + AddSysctl<CTL_HW, HW_PHYSMEM>(hasher); +# endif +# ifdef HW_USERMEM + AddSysctl<CTL_HW, HW_USERMEM>(hasher); +# endif +# ifdef HW_MACHINE_ARCH + AddSysctl<CTL_HW, HW_MACHINE_ARCH>(hasher); +# endif +# ifdef HW_REALMEM + AddSysctl<CTL_HW, HW_REALMEM>(hasher); +# endif +# ifdef HW_CPU_FREQ + AddSysctl<CTL_HW, HW_CPU_FREQ>(hasher); +# endif +# ifdef HW_BUS_FREQ + AddSysctl<CTL_HW, HW_BUS_FREQ>(hasher); +# endif +# ifdef HW_CACHELINE + AddSysctl<CTL_HW, HW_CACHELINE>(hasher); +# endif +# endif +# ifdef CTL_KERN +# ifdef KERN_BOOTFILE + AddSysctl<CTL_KERN, KERN_BOOTFILE>(hasher); +# endif +# ifdef KERN_BOOTTIME + AddSysctl<CTL_KERN, KERN_BOOTTIME>(hasher); +# endif +# ifdef KERN_CLOCKRATE + AddSysctl<CTL_KERN, KERN_CLOCKRATE>(hasher); +# endif +# ifdef KERN_HOSTID + AddSysctl<CTL_KERN, KERN_HOSTID>(hasher); +# endif +# ifdef KERN_HOSTUUID + AddSysctl<CTL_KERN, KERN_HOSTUUID>(hasher); +# endif +# ifdef KERN_HOSTNAME + AddSysctl<CTL_KERN, KERN_HOSTNAME>(hasher); +# endif +# ifdef KERN_OSRELDATE + AddSysctl<CTL_KERN, KERN_OSRELDATE>(hasher); +# endif +# ifdef KERN_OSRELEASE + AddSysctl<CTL_KERN, KERN_OSRELEASE>(hasher); +# endif +# ifdef KERN_OSREV + AddSysctl<CTL_KERN, KERN_OSREV>(hasher); +# endif +# ifdef KERN_OSTYPE + AddSysctl<CTL_KERN, KERN_OSTYPE>(hasher); +# endif +# ifdef KERN_POSIX1 + AddSysctl<CTL_KERN, KERN_OSREV>(hasher); +# endif +# ifdef KERN_VERSION + AddSysctl<CTL_KERN, KERN_VERSION>(hasher); +# endif +# endif +#endif + + // Env variables + if (environ) { + for (size_t i = 0; environ[i]; ++i) { + hasher.Write((const unsigned char*)environ[i], strlen(environ[i])); + } + } + + // Process, thread, user, session, group, ... ids. +#ifdef WIN32 + hasher << GetCurrentProcessId() << GetCurrentThreadId(); +#else + hasher << getpid() << getppid() << getsid(0) << getpgid(0) << getuid() << geteuid() << getgid() << getegid(); +#endif + hasher << std::this_thread::get_id(); +} diff --git a/src/randomenv.h b/src/randomenv.h new file mode 100644 index 0000000000..46cea6f6f2 --- /dev/null +++ b/src/randomenv.h @@ -0,0 +1,17 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2019 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_RANDOMENV_H +#define BITCOIN_RANDOMENV_H + +#include <crypto/sha512.h> + +/** Gather non-cryptographic environment data that changes over time. */ +void RandAddDynamicEnv(CSHA512& hasher); + +/** Gather non-cryptographic environment data that does not change over time. */ +void RandAddStaticEnv(CSHA512& hasher); + +#endif diff --git a/src/rest.cpp b/src/rest.cpp index 228c122de3..55756ecfdf 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -8,6 +8,7 @@ #include <core_io.h> #include <httpserver.h> #include <index/txindex.h> +#include <node/context.h> #include <primitives/block.h> #include <primitives/transaction.h> #include <rpc/blockchain.h> @@ -16,6 +17,7 @@ #include <streams.h> #include <sync.h> #include <txmempool.h> +#include <util/check.h> #include <util/strencodings.h> #include <validation.h> #include <version.h> @@ -69,6 +71,24 @@ static bool RESTERR(HTTPRequest* req, enum HTTPStatusCode status, std::string me return false; } +/** + * Get the node context mempool. + * + * Set the HTTP error and return nullptr if node context + * mempool is not found. + * + * @param[in] req the HTTP request + * return pointer to the mempool or nullptr if no mempool found + */ +static CTxMemPool* GetMemPool(HTTPRequest* req) +{ + if (!g_rpc_node || !g_rpc_node->mempool) { + RESTERR(req, HTTP_NOT_FOUND, "Mempool disabled or instance not found"); + return nullptr; + } + return g_rpc_node->mempool; +} + static RetFormat ParseDataFormat(std::string& param, const std::string& strReq) { const std::string::size_type pos = strReq.rfind('.'); @@ -295,12 +315,14 @@ static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart) { if (!CheckWarmup(req)) return false; + const CTxMemPool* mempool = GetMemPool(req); + if (!mempool) return false; std::string param; const RetFormat rf = ParseDataFormat(param, strURIPart); switch (rf) { case RetFormat::JSON: { - UniValue mempoolInfoObject = MempoolInfoToJSON(::mempool); + UniValue mempoolInfoObject = MempoolInfoToJSON(*mempool); std::string strJSON = mempoolInfoObject.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); @@ -315,14 +337,15 @@ static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart) static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPart) { - if (!CheckWarmup(req)) - return false; + if (!CheckWarmup(req)) return false; + const CTxMemPool* mempool = GetMemPool(req); + if (!mempool) return false; std::string param; const RetFormat rf = ParseDataFormat(param, strURIPart); switch (rf) { case RetFormat::JSON: { - UniValue mempoolObject = MempoolToJSON(::mempool, true); + UniValue mempoolObject = MempoolToJSON(*mempool, true); std::string strJSON = mempoolObject.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); @@ -500,11 +523,13 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) }; if (fCheckMemPool) { + const CTxMemPool* mempool = GetMemPool(req); + if (!mempool) return false; // use db+mempool as cache backend in case user likes to query mempool - LOCK2(cs_main, mempool.cs); + LOCK2(cs_main, mempool->cs); CCoinsViewCache& viewChain = ::ChainstateActive().CoinsTip(); - CCoinsViewMemPool viewMempool(&viewChain, mempool); - process_utxos(viewMempool, mempool); + CCoinsViewMemPool viewMempool(&viewChain, *mempool); + process_utxos(viewMempool, *mempool); } else { LOCK(cs_main); // no need to lock mempool! process_utxos(::ChainstateActive().CoinsTip(), CTxMemPool()); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 2f4b4412f5..eb5148eebd 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -15,6 +15,7 @@ #include <hash.h> #include <index/blockfilterindex.h> #include <node/coinstats.h> +#include <node/context.h> #include <node/utxo_snapshot.h> #include <policy/feerate.h> #include <policy/policy.h> @@ -53,6 +54,15 @@ static Mutex cs_blockchange; static std::condition_variable cond_blockchange; static CUpdatedBlock latestblock; +CTxMemPool& EnsureMemPool() +{ + CHECK_NONFATAL(g_rpc_node); + if (!g_rpc_node->mempool) { + throw JSONRPCError(RPC_CLIENT_MEMPOOL_DISABLED, "Mempool disabled or instance not found"); + } + return *g_rpc_node->mempool; +} + /* Calculate the difficulty for a given block index. */ double GetDifficulty(const CBlockIndex* blockindex) @@ -518,7 +528,7 @@ static UniValue getrawmempool(const JSONRPCRequest& request) if (!request.params[0].isNull()) fVerbose = request.params[0].get_bool(); - return MempoolToJSON(::mempool, fVerbose); + return MempoolToJSON(EnsureMemPool(), fVerbose); } static UniValue getmempoolancestors(const JSONRPCRequest& request) @@ -556,6 +566,7 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request) uint256 hash = ParseHashV(request.params[0], "parameter 1"); + const CTxMemPool& mempool = EnsureMemPool(); LOCK(mempool.cs); CTxMemPool::txiter it = mempool.mapTx.find(hash); @@ -581,7 +592,7 @@ static UniValue getmempoolancestors(const JSONRPCRequest& request) const CTxMemPoolEntry &e = *ancestorIt; const uint256& _hash = e.GetTx().GetHash(); UniValue info(UniValue::VOBJ); - entryToJSON(::mempool, info, e); + entryToJSON(mempool, info, e); o.pushKV(_hash.ToString(), info); } return o; @@ -623,6 +634,7 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request) uint256 hash = ParseHashV(request.params[0], "parameter 1"); + const CTxMemPool& mempool = EnsureMemPool(); LOCK(mempool.cs); CTxMemPool::txiter it = mempool.mapTx.find(hash); @@ -648,7 +660,7 @@ static UniValue getmempooldescendants(const JSONRPCRequest& request) const CTxMemPoolEntry &e = *descendantIt; const uint256& _hash = e.GetTx().GetHash(); UniValue info(UniValue::VOBJ); - entryToJSON(::mempool, info, e); + entryToJSON(mempool, info, e); o.pushKV(_hash.ToString(), info); } return o; @@ -675,6 +687,7 @@ static UniValue getmempoolentry(const JSONRPCRequest& request) uint256 hash = ParseHashV(request.params[0], "parameter 1"); + const CTxMemPool& mempool = EnsureMemPool(); LOCK(mempool.cs); CTxMemPool::txiter it = mempool.mapTx.find(hash); @@ -684,7 +697,7 @@ static UniValue getmempoolentry(const JSONRPCRequest& request) const CTxMemPoolEntry &e = *it; UniValue info(UniValue::VOBJ); - entryToJSON(::mempool, info, e); + entryToJSON(mempool, info, e); return info; } @@ -732,8 +745,8 @@ static UniValue getblockheader(const JSONRPCRequest& request) " \"version\" : n, (numeric) The block version\n" " \"versionHex\" : \"00000000\", (string) The block version formatted in hexadecimal\n" " \"merkleroot\" : \"xxxx\", (string) The merkle root\n" - " \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" - " \"mediantime\" : ttt, (numeric) The median block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"time\" : ttt, (numeric) The block time expressed in " + UNIX_EPOCH_TIME + "\n" + " \"mediantime\" : ttt, (numeric) The median block time expressed in " + UNIX_EPOCH_TIME + "\n" " \"nonce\" : n, (numeric) The nonce\n" " \"bits\" : \"1d00ffff\", (string) The bits\n" " \"difficulty\" : x.xxx, (numeric) The difficulty\n" @@ -844,8 +857,8 @@ static UniValue getblock(const JSONRPCRequest& request) " \"transactionid\" (string) The transaction id\n" " ,...\n" " ],\n" - " \"time\" : ttt, (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" - " \"mediantime\" : ttt, (numeric) The median block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"time\" : ttt, (numeric) The block time expressed in " + UNIX_EPOCH_TIME + "\n" + " \"mediantime\" : ttt, (numeric) The median block time expressed in " + UNIX_EPOCH_TIME + "\n" " \"nonce\" : n, (numeric) The nonce\n" " \"bits\" : \"1d00ffff\", (string) The bits\n" " \"difficulty\" : x.xxx, (numeric) The difficulty\n" @@ -911,7 +924,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request) { RPCHelpMan{"pruneblockchain", "", { - {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block height to prune up to. May be set to a discrete height, or a unix timestamp\n" + {"height", RPCArg::Type::NUM, RPCArg::Optional::NO, "The block height to prune up to. May be set to a discrete height, or to a " + UNIX_EPOCH_TIME + "\n" " to prune blocks whose block time is at least 2 hours older than the provided timestamp."}, }, RPCResult{ @@ -1060,6 +1073,7 @@ UniValue gettxout(const JSONRPCRequest& request) CCoinsViewCache* coins_view = &::ChainstateActive().CoinsTip(); if (fMempool) { + const CTxMemPool& mempool = EnsureMemPool(); LOCK(mempool.cs); CCoinsViewMemPool view(coins_view, mempool); if (!view.GetCoin(out, coin) || mempool.isSpent(out)) { @@ -1276,7 +1290,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) BIP9SoftForkDescPushBack(softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY); obj.pushKV("softforks", softforks); - obj.pushKV("warnings", GetWarnings("statusbar")); + obj.pushKV("warnings", GetWarnings(false)); return obj; } @@ -1438,7 +1452,7 @@ static UniValue getmempoolinfo(const JSONRPCRequest& request) }, }.Check(request); - return MempoolInfoToJSON(::mempool); + return MempoolInfoToJSON(EnsureMemPool()); } static UniValue preciousblock(const JSONRPCRequest& request) @@ -1563,7 +1577,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) }, RPCResult{ "{\n" - " \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n" + " \"time\": xxxxx, (numeric) The timestamp for the final block in the window, expressed in " + UNIX_EPOCH_TIME + ".\n" " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" " \"window_final_block_hash\": \"...\", (string) The hash of the final block in the window.\n" " \"window_final_block_height\": xxxxx, (numeric) The height of the final block in the window.\n" @@ -1954,11 +1968,13 @@ static UniValue savemempool(const JSONRPCRequest& request) }, }.Check(request); - if (!::mempool.IsLoaded()) { + const CTxMemPool& mempool = EnsureMemPool(); + + if (!mempool.IsLoaded()) { throw JSONRPCError(RPC_MISC_ERROR, "The mempool was not loaded yet"); } - if (!DumpMempool(::mempool)) { + if (!DumpMempool(mempool)) { throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk"); } @@ -2045,7 +2061,7 @@ UniValue scantxoutset(const JSONRPCRequest& request) " \"start\" for starting a scan\n" " \"abort\" for aborting the current scan (returns true when abort was successful)\n" " \"status\" for progress report (in %) of the current scan"}, - {"scanobjects", RPCArg::Type::ARR, RPCArg::Optional::NO, "Array of scan objects\n" + {"scanobjects", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "Array of scan objects. Required for \"start\" action\n" " Every scan object is either a string descriptor or an object:", { {"descriptor", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "An output descriptor"}, @@ -2105,6 +2121,11 @@ UniValue scantxoutset(const JSONRPCRequest& request) if (!reserver.reserve()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\""); } + + if (request.params.size() < 2) { + throw JSONRPCError(RPC_MISC_ERROR, "scanobjects argument is required for the start action"); + } + std::set<CScript> needles; std::map<CScript, std::string> descriptors; CAmount total_in = 0; diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h index 8a1264f824..ccb3e39722 100644 --- a/src/rpc/blockchain.h +++ b/src/rpc/blockchain.h @@ -52,4 +52,6 @@ void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES], //! direct way to pass in state to RPC methods without globals. extern NodeContext* g_rpc_node; +CTxMemPool& EnsureMemPool(); + #endif diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index ab22155651..9f7f7837d3 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -244,6 +244,7 @@ static UniValue getmininginfo(const JSONRPCRequest& request) }.Check(request); LOCK(cs_main); + const CTxMemPool& mempool = EnsureMemPool(); UniValue obj(UniValue::VOBJ); obj.pushKV("blocks", (int)::ChainActive().Height()); @@ -253,7 +254,7 @@ static UniValue getmininginfo(const JSONRPCRequest& request) obj.pushKV("networkhashps", getnetworkhashps(request)); obj.pushKV("pooledtx", (uint64_t)mempool.size()); obj.pushKV("chain", Params().NetworkIDString()); - obj.pushKV("warnings", GetWarnings("statusbar")); + obj.pushKV("warnings", GetWarnings(false)); return obj; } @@ -290,7 +291,7 @@ static UniValue prioritisetransaction(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Priority is no longer supported, dummy argument to prioritisetransaction must be 0."); } - mempool.PrioritiseTransaction(hash, nAmount); + EnsureMemPool().PrioritiseTransaction(hash, nAmount); return true; } @@ -375,13 +376,11 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) " }\n" " ,...\n" " ],\n" - " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n" - " \"flags\" : \"xx\" (string) key name is to be ignored, and value included in scriptSig\n" - " },\n" + " \"coinbaseaux\" : { ... }, (json object) data that should be included in the coinbase's scriptSig content\n" " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)\n" " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n" " \"target\" : \"xxxx\", (string) The hash target\n" - " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for the next block time, expressed in " + UNIX_EPOCH_TIME + "\n" " \"mutable\" : [ (array of string) list of ways the block template may be changed \n" " \"value\" (string) A way the block template may be changed, e.g. 'time', 'transactions', 'prevblock'\n" " ,...\n" @@ -390,7 +389,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) " \"sigoplimit\" : n, (numeric) limit of sigops in blocks\n" " \"sizelimit\" : n, (numeric) limit of block size\n" " \"weightlimit\" : n, (numeric) limit of block weight\n" - " \"curtime\" : ttt, (numeric) current timestamp in seconds since epoch (Jan 1 1970 GMT)\n" + " \"curtime\" : ttt, (numeric) current timestamp in " + UNIX_EPOCH_TIME + "\n" " \"bits\" : \"xxxxxxxx\", (string) compressed target of next block\n" " \"height\" : n (numeric) The height of the next block\n" "}\n" @@ -478,6 +477,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, PACKAGE_NAME " is in initial sync and waiting for blocks..."); static unsigned int nTransactionsUpdatedLast; + const CTxMemPool& mempool = EnsureMemPool(); if (!lpval.isNull()) { @@ -512,7 +512,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) if (g_best_block_cv.wait_until(lock, checktxtime) == std::cv_status::timeout) { // Timeout: Check transactions for update - // without holding ::mempool.cs to avoid deadlocks + // without holding the mempool lock to avoid deadlocks if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) break; checktxtime += std::chrono::seconds(10); @@ -607,7 +607,6 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) } UniValue aux(UniValue::VOBJ); - aux.pushKV("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())); arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index d73dd6e52d..d967d2bcca 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -343,7 +343,7 @@ static UniValue setmocktime(const JSONRPCRequest& request) RPCHelpMan{"setmocktime", "\nSet the local time to given timestamp (-regtest only)\n", { - {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Unix seconds-since-epoch timestamp\n" + {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, UNIX_EPOCH_TIME + "\n" " Pass 0 to go back to using the system time."}, }, RPCResults{}, diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index f1dcc9b607..5e53fa5f5d 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -89,11 +89,11 @@ static UniValue getpeerinfo(const JSONRPCRequest& request) " ...\n" " ],\n" " \"relaytxes\":true|false, (boolean) Whether peer has asked us to relay transactions to it\n" - " \"lastsend\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last send\n" - " \"lastrecv\": ttt, (numeric) The time in seconds since epoch (Jan 1 1970 GMT) of the last receive\n" + " \"lastsend\": ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the last send\n" + " \"lastrecv\": ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the last receive\n" " \"bytessent\": n, (numeric) The total bytes sent\n" " \"bytesrecv\": n, (numeric) The total bytes received\n" - " \"conntime\": ttt, (numeric) The connection time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"conntime\": ttt, (numeric) The " + UNIX_EPOCH_TIME + " of the connection\n" " \"timeoffset\": ttt, (numeric) The time offset in seconds\n" " \"pingtime\": n, (numeric) ping time (if available)\n" " \"minping\": n, (numeric) minimum observed ping time (if any at all)\n" @@ -522,7 +522,7 @@ static UniValue getnetworkinfo(const JSONRPCRequest& request) } } obj.pushKV("localaddresses", localAddresses); - obj.pushKV("warnings", GetWarnings("statusbar")); + obj.pushKV("warnings", GetWarnings(false)); return obj; } @@ -534,7 +534,7 @@ static UniValue setban(const JSONRPCRequest& request) {"subnet", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)"}, {"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'add' to add an IP/Subnet to the list, 'remove' to remove an IP/Subnet from the list"}, {"bantime", RPCArg::Type::NUM, /* default */ "0", "time in seconds how long (or until when if [absolute] is set) the IP is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)"}, - {"absolute", RPCArg::Type::BOOL, /* default */ "false", "If set, the bantime must be an absolute timestamp in seconds since epoch (Jan 1 1970 GMT)"}, + {"absolute", RPCArg::Type::BOOL, /* default */ "false", "If set, the bantime must be an absolute timestamp expressed in " + UNIX_EPOCH_TIME}, }, RPCResults{}, RPCExamples{ @@ -691,7 +691,7 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request) RPCResult{ "[\n" " {\n" - " \"time\": ttt, (numeric) Timestamp in seconds since epoch (Jan 1 1970 GMT) keeping track of when the node was last seen\n" + " \"time\": ttt, (numeric) The " + UNIX_EPOCH_TIME + " 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" diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index ef6537e4ec..ca779497b9 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -63,6 +63,9 @@ enum RPCErrorCode RPC_CLIENT_INVALID_IP_OR_SUBNET = -30, //!< Invalid IP/Subnet RPC_CLIENT_P2P_DISABLED = -31, //!< No valid connection manager instance found + //! Chain errors + RPC_CLIENT_MEMPOOL_DISABLED = -33, //!< No mempool instance found + //! Wallet errors RPC_WALLET_ERROR = -4, //!< Unspecified problem with wallet (key not found etc.) RPC_WALLET_INSUFFICIENT_FUNDS = -6, //!< Not enough funds in wallet or account diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 983f251d6b..5be7acce1c 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -139,7 +139,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) " ],\n" " \"blockhash\" : \"hash\", (string) the block hash\n" " \"confirmations\" : n, (numeric) The confirmations\n" - " \"blocktime\" : ttt (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"blocktime\" : ttt (numeric) The block time expressed in " + UNIX_EPOCH_TIME + "\n" " \"time\" : ttt, (numeric) Same as \"blocktime\"\n" "}\n" }, @@ -636,6 +636,7 @@ static UniValue combinerawtransaction(const JSONRPCRequest& request) CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); { + const CTxMemPool& mempool = EnsureMemPool(); LOCK(cs_main); LOCK(mempool.cs); CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip(); @@ -758,12 +759,14 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request) for (const CTxIn& txin : mtx.vin) { coins[txin.prevout]; // Create empty map entry keyed by prevout. } - FindCoins(coins); + FindCoins(*g_rpc_node, coins); // Parse the prevtxs array ParsePrevouts(request.params[2], &keystore, coins); - return SignTransaction(mtx, &keystore, coins, request.params[3]); + UniValue result(UniValue::VOBJ); + SignTransaction(mtx, &keystore, coins, request.params[3], result); + return result; } static UniValue sendrawtransaction(const JSONRPCRequest& request) @@ -888,6 +891,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) max_raw_tx_fee_rate = CFeeRate(AmountFromValue(request.params[1])); } + CTxMemPool& mempool = EnsureMemPool(); int64_t virtual_size = GetVirtualTransactionSize(*tx); CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size); @@ -1506,6 +1510,7 @@ UniValue utxoupdatepsbt(const JSONRPCRequest& request) CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); { + const CTxMemPool& mempool = EnsureMemPool(); LOCK2(cs_main, mempool.cs); CCoinsViewCache &viewChain = ::ChainstateActive().CoinsTip(); CCoinsViewMemPool viewMempool(&viewChain, mempool); @@ -1672,6 +1677,7 @@ UniValue analyzepsbt(const JSONRPCRequest& request) " \"estimated_feerate\" : feerate (numeric, optional) Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + "/kB. Shown only if all UTXO slots in the PSBT have been filled.\n" " \"fee\" : fee (numeric, optional) The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled.\n" " \"next\" : \"role\" (string) Role of the next person that this psbt needs to go to\n" + " \"error\" : \"error\" (string) Error message if there is one\n" "}\n" }, RPCExamples { @@ -1724,7 +1730,7 @@ UniValue analyzepsbt(const JSONRPCRequest& request) } inputs_result.push_back(input_univ); } - result.pushKV("inputs", inputs_result); + if (!inputs_result.empty()) result.pushKV("inputs", inputs_result); if (psbta.estimated_vsize != nullopt) { result.pushKV("estimated_vsize", (int)*psbta.estimated_vsize); @@ -1736,6 +1742,9 @@ UniValue analyzepsbt(const JSONRPCRequest& request) result.pushKV("fee", ValueFromAmount(*psbta.fee)); } result.pushKV("next", PSBTRoleName(psbta.next)); + if (!psbta.error.empty()) { + result.pushKV("error", psbta.error); + } return result; } diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp index fe98fff4bb..40334883c5 100644 --- a/src/rpc/rawtransaction_util.cpp +++ b/src/rpc/rawtransaction_util.cpp @@ -268,7 +268,7 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst } } -UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType) +void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result) { int nHashType = ParseSighashString(hashType); @@ -319,12 +319,12 @@ UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keysto } bool fComplete = vErrors.empty(); - UniValue result(UniValue::VOBJ); result.pushKV("hex", EncodeHexTx(CTransaction(mtx))); result.pushKV("complete", fComplete); if (!vErrors.empty()) { + if (result.exists("errors")) { + vErrors.push_backV(result["errors"].getValues()); + } result.pushKV("errors", vErrors); } - - return result; } diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h index 1936998ff3..0b7712b83c 100644 --- a/src/rpc/rawtransaction_util.h +++ b/src/rpc/rawtransaction_util.h @@ -21,9 +21,9 @@ class SigningProvider; * @param keystore Temporary keystore containing signing keys * @param coins Map of unspent outputs * @param hashType The signature hash type - * @returns JSON object with details of signed transaction + * @param result JSON object where signed transaction results accumulate */ -UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType); +void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result); /** * Parse a prevtxs UniValue array and get the map of coins from it diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp index cfa3509c65..78586c22f9 100644 --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -13,6 +13,8 @@ #include <tuple> +const std::string UNIX_EPOCH_TIME = "UNIX epoch time"; + void RPCTypeCheck(const UniValue& params, const std::list<UniValueType>& typesExpected, bool fAllowNull) @@ -131,18 +133,18 @@ CPubKey HexToPubKey(const std::string& hex_in) } // Retrieves a public key for an address from the given FillableSigningProvider -CPubKey AddrToPubKey(FillableSigningProvider* const keystore, const std::string& addr_in) +CPubKey AddrToPubKey(const FillableSigningProvider& keystore, const std::string& addr_in) { CTxDestination dest = DecodeDestination(addr_in); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address: " + addr_in); } - CKeyID key = GetKeyForDestination(*keystore, dest); + CKeyID key = GetKeyForDestination(keystore, dest); if (key.IsNull()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("%s does not refer to a key", addr_in)); } CPubKey vchPubKey; - if (!keystore->GetPubKey(key, vchPubKey)) { + if (!keystore.GetPubKey(key, vchPubKey)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("no full public key for address %s", addr_in)); } if (!vchPubKey.IsFullyValid()) { diff --git a/src/rpc/util.h b/src/rpc/util.h index 221638aa9e..065a992a88 100644 --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -22,6 +22,12 @@ #include <boost/variant.hpp> +/** + * String used to describe UNIX epoch time in documentation, factored out to a + * constant for consistency. + */ +extern const std::string UNIX_EPOCH_TIME; + class FillableSigningProvider; class CPubKey; class CScript; @@ -69,7 +75,7 @@ extern std::string HelpExampleCli(const std::string& methodname, const std::stri extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args); CPubKey HexToPubKey(const std::string& hex_in); -CPubKey AddrToPubKey(FillableSigningProvider* const keystore, const std::string& addr_in); +CPubKey AddrToPubKey(const FillableSigningProvider& keystore, const std::string& addr_in); CTxDestination AddAndGetMultisigDestination(const int required, const std::vector<CPubKey>& pubkeys, OutputType type, FillableSigningProvider& keystore, CScript& script_out); UniValue DescribeAddress(const CTxDestination& dest); diff --git a/src/scheduler.cpp b/src/scheduler.cpp index fdc859b3a0..07a54335ac 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -41,8 +41,6 @@ void CScheduler::serviceQueue() try { if (!shouldStop() && taskQueue.empty()) { reverse_lock<boost::unique_lock<boost::mutex> > rlock(lock); - // Use this chance to get more entropy - RandAddSeedSleep(); } while (!shouldStop() && taskQueue.empty()) { // Wait until there is something to do. diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 13cdd6c61a..32b388b7fa 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -815,8 +815,8 @@ std::unique_ptr<DescriptorImpl> ParseScript(Span<const char>& sp, ParseScriptCon } } if (ctx == ParseScriptContext::P2SH) { - if (script_size + 3 > 520) { - error = strprintf("P2SH script is too large, %d bytes is larger than 520 bytes", script_size + 3); + if (script_size + 3 > MAX_SCRIPT_ELEMENT_SIZE) { + error = strprintf("P2SH script is too large, %d bytes is larger than %d bytes", script_size + 3, MAX_SCRIPT_ELEMENT_SIZE); return nullptr; } } diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 20fae2eebf..ad833bc025 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -61,17 +61,17 @@ static inline void popstack(std::vector<valtype>& stack) } bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) { - if (vchPubKey.size() < CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) { + if (vchPubKey.size() < CPubKey::COMPRESSED_SIZE) { // Non-canonical public key: too short return false; } if (vchPubKey[0] == 0x04) { - if (vchPubKey.size() != CPubKey::PUBLIC_KEY_SIZE) { + if (vchPubKey.size() != CPubKey::SIZE) { // Non-canonical public key: invalid length for uncompressed key return false; } } else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) { - if (vchPubKey.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) { + if (vchPubKey.size() != CPubKey::COMPRESSED_SIZE) { // Non-canonical public key: invalid length for compressed key return false; } @@ -83,7 +83,7 @@ bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) { } bool static IsCompressedPubKey(const valtype &vchPubKey) { - if (vchPubKey.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) { + if (vchPubKey.size() != CPubKey::COMPRESSED_SIZE) { // Non-canonical public key: invalid length for compressed key return false; } diff --git a/src/script/sign.h b/src/script/sign.h index 9d0a5b4d70..4c2403f83f 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -101,7 +101,7 @@ template<typename Stream> 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) { + if (key.size() != CPubKey::SIZE + 1 && key.size() != CPubKey::COMPRESSED_SIZE + 1) { throw std::ios_base::failure("Size of key was not the expected size for the type BIP32 keypath"); } // Read in the pubkey from key diff --git a/src/script/standard.cpp b/src/script/standard.cpp index fc6898f444..144bdcff98 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -44,12 +44,12 @@ const char* GetTxnOutputType(txnouttype t) static bool MatchPayToPubkey(const CScript& script, valtype& pubkey) { - if (script.size() == CPubKey::PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) { - pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::PUBLIC_KEY_SIZE + 1); + if (script.size() == CPubKey::SIZE + 2 && script[0] == CPubKey::SIZE && script.back() == OP_CHECKSIG) { + pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::SIZE + 1); return CPubKey::ValidSize(pubkey); } - if (script.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) { - pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1); + if (script.size() == CPubKey::COMPRESSED_SIZE + 2 && script[0] == CPubKey::COMPRESSED_SIZE && script.back() == OP_CHECKSIG) { + pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_SIZE + 1); return CPubKey::ValidSize(pubkey); } return false; diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp index 5c2050e4a2..85e3351e72 100644 --- a/src/support/lockedpool.cpp +++ b/src/support/lockedpool.cpp @@ -23,6 +23,10 @@ #endif #include <algorithm> +#ifdef ARENA_DEBUG +#include <iomanip> +#include <iostream> +#endif LockedPoolManager* LockedPoolManager::_instance = nullptr; std::once_flag LockedPoolManager::init_flag; @@ -137,7 +141,7 @@ Arena::Stats Arena::stats() const } #ifdef ARENA_DEBUG -static void printchunk(char* base, size_t sz, bool used) { +static void printchunk(void* base, size_t sz, bool used) { std::cout << "0x" << std::hex << std::setw(16) << std::setfill('0') << base << " 0x" << std::hex << std::setw(16) << std::setfill('0') << sz << @@ -149,7 +153,7 @@ void Arena::walk() const printchunk(chunk.first, chunk.second, true); std::cout << std::endl; for (const auto& chunk: chunks_free) - printchunk(chunk.first, chunk.second, false); + printchunk(chunk.first, chunk.second->first, false); std::cout << std::endl; } #endif diff --git a/src/test/base32_tests.cpp b/src/test/base32_tests.cpp index bd6ece935b..690368b177 100644 --- a/src/test/base32_tests.cpp +++ b/src/test/base32_tests.cpp @@ -20,6 +20,17 @@ BOOST_AUTO_TEST_CASE(base32_testvectors) std::string strDec = DecodeBase32(vstrOut[i]); BOOST_CHECK_EQUAL(strDec, vstrIn[i]); } + + // Decoding strings with embedded NUL characters should fail + bool failure; + (void)DecodeBase32(std::string("invalid", 7), &failure); + BOOST_CHECK_EQUAL(failure, true); + (void)DecodeBase32(std::string("AWSX3VPP", 8), &failure); + BOOST_CHECK_EQUAL(failure, false); + (void)DecodeBase32(std::string("AWSX3VPP\0invalid", 16), &failure); + BOOST_CHECK_EQUAL(failure, true); + (void)DecodeBase32(std::string("AWSX3VPPinvalid", 15), &failure); + BOOST_CHECK_EQUAL(failure, true); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index 52301f799a..57559fa687 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -7,6 +7,7 @@ #include <base58.h> #include <test/util/setup_common.h> #include <util/strencodings.h> +#include <util/vector.h> #include <univalue.h> @@ -53,17 +54,45 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58) } std::vector<unsigned char> expected = ParseHex(test[0].get_str()); std::string base58string = test[1].get_str(); - BOOST_CHECK_MESSAGE(DecodeBase58(base58string, result), strTest); + BOOST_CHECK_MESSAGE(DecodeBase58(base58string, result, 256), strTest); BOOST_CHECK_MESSAGE(result.size() == expected.size() && std::equal(result.begin(), result.end(), expected.begin()), strTest); } - BOOST_CHECK(!DecodeBase58("invalid", result)); + BOOST_CHECK(!DecodeBase58("invalid", result, 100)); + BOOST_CHECK(!DecodeBase58(std::string("invalid"), result, 100)); + BOOST_CHECK(!DecodeBase58(std::string("\0invalid", 8), result, 100)); + + BOOST_CHECK(DecodeBase58(std::string("good", 4), result, 100)); + BOOST_CHECK(!DecodeBase58(std::string("bad0IOl", 7), result, 100)); + BOOST_CHECK(!DecodeBase58(std::string("goodbad0IOl", 11), result, 100)); + BOOST_CHECK(!DecodeBase58(std::string("good\0bad0IOl", 12), result, 100)); // check that DecodeBase58 skips whitespace, but still fails with unexpected non-whitespace at the end. - BOOST_CHECK(!DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t a", result)); - BOOST_CHECK( DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t ", result)); + BOOST_CHECK(!DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t a", result, 3)); + BOOST_CHECK( DecodeBase58(" \t\n\v\f\r skip \r\f\v\n\t ", result, 3)); std::vector<unsigned char> expected = ParseHex("971a55"); BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); + + BOOST_CHECK(DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh", 21), result, 100)); + BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oi", 21), result, 100)); + BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh0IOl", 25), result, 100)); + BOOST_CHECK(!DecodeBase58Check(std::string("3vQB7B6MrGQZaxCuFg4oh\00IOl", 26), result, 100)); +} + +BOOST_AUTO_TEST_CASE(base58_random_encode_decode) +{ + for (int n = 0; n < 1000; ++n) { + unsigned int len = 1 + InsecureRandBits(8); + unsigned int zeroes = InsecureRandBool() ? InsecureRandRange(len + 1) : 0; + auto data = Cat(std::vector<unsigned char>(zeroes, '\000'), g_insecure_rand_ctx.randbytes(len - zeroes)); + auto encoded = EncodeBase58Check(data); + std::vector<unsigned char> decoded; + auto ok_too_small = DecodeBase58Check(encoded, decoded, InsecureRandRange(len)); + BOOST_CHECK(!ok_too_small); + auto ok = DecodeBase58Check(encoded, decoded, len + InsecureRandRange(257 - len)); + BOOST_CHECK(ok); + BOOST_CHECK(data == decoded); + } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/base64_tests.cpp b/src/test/base64_tests.cpp index a5fed55504..94df4d1955 100644 --- a/src/test/base64_tests.cpp +++ b/src/test/base64_tests.cpp @@ -20,6 +20,17 @@ BOOST_AUTO_TEST_CASE(base64_testvectors) std::string strDec = DecodeBase64(strEnc); BOOST_CHECK_EQUAL(strDec, vstrIn[i]); } + + // Decoding strings with embedded NUL characters should fail + bool failure; + (void)DecodeBase64(std::string("invalid", 7), &failure); + BOOST_CHECK_EQUAL(failure, true); + (void)DecodeBase64(std::string("nQB/pZw=", 8), &failure); + BOOST_CHECK_EQUAL(failure, false); + (void)DecodeBase64(std::string("nQB/pZw=\0invalid", 16), &failure); + BOOST_CHECK_EQUAL(failure, true); + (void)DecodeBase64(std::string("nQB/pZw=invalid", 15), &failure); + BOOST_CHECK_EQUAL(failure, true); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp index 6745bb9015..482fe3772c 100644 --- a/src/test/checkqueue_tests.cpp +++ b/src/test/checkqueue_tests.cpp @@ -17,6 +17,7 @@ #include <condition_variable> #include <unordered_set> +#include <utility> BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, TestingSetup) diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 591a317d17..2deb0c5bfc 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -186,14 +186,15 @@ static void TestHKDF_SHA256_32(const std::string &ikm_hex, const std::string &sa BOOST_CHECK(HexStr(out, out + 32) == okm_check_hex); } -static std::string LongTestString() { +static std::string LongTestString() +{ std::string ret; - for (int i=0; i<200000; i++) { - ret += (unsigned char)(i); - ret += (unsigned char)(i >> 4); - ret += (unsigned char)(i >> 8); - ret += (unsigned char)(i >> 12); - ret += (unsigned char)(i >> 16); + for (int i = 0; i < 200000; i++) { + ret += (char)(i); + ret += (char)(i >> 4); + ret += (char)(i >> 8); + ret += (char)(i >> 12); + ret += (char)(i >> 16); } return ret; } diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 57d5b2bb5c..b647c0f70b 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -397,6 +397,18 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering) } } +BOOST_AUTO_TEST_CASE(unicodepath) +{ + // Attempt to create a database with a utf8 character in the path. + // On Windows this test will fail if the directory is created using + // the ANSI CreateDirectoryA call and the code page isn't UTF8. + // It will succeed if the created with CreateDirectoryW. + fs::path ph = GetDataDir() / "test_runner_₿_🏃_20191128_104644"; + CDBWrapper dbw(ph, (1 << 20)); + + fs::path lockPath = ph / "LOCK"; + BOOST_CHECK(boost::filesystem::exists(lockPath)); +} BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/fuzz/base_encode_decode.cpp b/src/test/fuzz/base_encode_decode.cpp new file mode 100644 index 0000000000..cb0fbdf76f --- /dev/null +++ b/src/test/fuzz/base_encode_decode.cpp @@ -0,0 +1,47 @@ +// Copyright (c) 2019 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/fuzz/fuzz.h> + +#include <base58.h> +#include <util/string.h> +#include <util/strencodings.h> + +#include <cassert> +#include <cstdint> +#include <string> +#include <vector> + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + const std::string random_encoded_string(buffer.begin(), buffer.end()); + + std::vector<unsigned char> decoded; + if (DecodeBase58(random_encoded_string, decoded, 100)) { + const std::string encoded_string = EncodeBase58(decoded); + assert(encoded_string == TrimString(encoded_string)); + assert(ToLower(encoded_string) == ToLower(TrimString(random_encoded_string))); + } + + if (DecodeBase58Check(random_encoded_string, decoded, 100)) { + const std::string encoded_string = EncodeBase58Check(decoded); + assert(encoded_string == TrimString(encoded_string)); + assert(ToLower(encoded_string) == ToLower(TrimString(random_encoded_string))); + } + + bool pf_invalid; + std::string decoded_string = DecodeBase32(random_encoded_string, &pf_invalid); + if (!pf_invalid) { + const std::string encoded_string = EncodeBase32(decoded_string); + assert(encoded_string == TrimString(encoded_string)); + assert(ToLower(encoded_string) == ToLower(TrimString(random_encoded_string))); + } + + decoded_string = DecodeBase64(random_encoded_string, &pf_invalid); + if (!pf_invalid) { + const std::string encoded_string = EncodeBase64(decoded_string); + assert(encoded_string == TrimString(encoded_string)); + assert(ToLower(encoded_string) == ToLower(TrimString(random_encoded_string))); + } +} diff --git a/src/test/fuzz/block.cpp b/src/test/fuzz/block.cpp new file mode 100644 index 0000000000..431248de4a --- /dev/null +++ b/src/test/fuzz/block.cpp @@ -0,0 +1,63 @@ +// Copyright (c) 2019 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 <chainparams.h> +#include <consensus/merkle.h> +#include <consensus/validation.h> +#include <core_io.h> +#include <core_memusage.h> +#include <pubkey.h> +#include <primitives/block.h> +#include <streams.h> +#include <test/fuzz/fuzz.h> +#include <validation.h> +#include <version.h> + +#include <cassert> +#include <string> + +void initialize() +{ + const static auto verify_handle = MakeUnique<ECCVerifyHandle>(); + SelectParams(CBaseChainParams::REGTEST); +} + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); + CBlock block; + try { + int nVersion; + ds >> nVersion; + ds.SetVersion(nVersion); + ds >> block; + } catch (const std::ios_base::failure&) { + return; + } + const Consensus::Params& consensus_params = Params().GetConsensus(); + BlockValidationState validation_state_pow_and_merkle; + const bool valid_incl_pow_and_merkle = CheckBlock(block, validation_state_pow_and_merkle, consensus_params, /* fCheckPOW= */ true, /* fCheckMerkleRoot= */ true); + BlockValidationState validation_state_pow; + const bool valid_incl_pow = CheckBlock(block, validation_state_pow, consensus_params, /* fCheckPOW= */ true, /* fCheckMerkleRoot= */ false); + BlockValidationState validation_state_merkle; + const bool valid_incl_merkle = CheckBlock(block, validation_state_merkle, consensus_params, /* fCheckPOW= */ false, /* fCheckMerkleRoot= */ true); + BlockValidationState validation_state_none; + const bool valid_incl_none = CheckBlock(block, validation_state_none, consensus_params, /* fCheckPOW= */ false, /* fCheckMerkleRoot= */ false); + if (valid_incl_pow_and_merkle) { + assert(valid_incl_pow && valid_incl_merkle && valid_incl_none); + } else if (valid_incl_merkle || valid_incl_pow) { + assert(valid_incl_none); + } + (void)block.GetHash(); + (void)block.ToString(); + (void)BlockMerkleRoot(block); + if (!block.vtx.empty()) { + // TODO: Avoid array index out of bounds error in BlockWitnessMerkleRoot + // when block.vtx.empty(). + (void)BlockWitnessMerkleRoot(block); + } + (void)GetBlockWeight(block); + (void)GetWitnessCommitmentIndex(block); + (void)RecursiveDynamicUsage(block); +} diff --git a/src/test/fuzz/descriptor_parse.cpp b/src/test/fuzz/descriptor_parse.cpp index c4c25854fd..47d5038c26 100644 --- a/src/test/fuzz/descriptor_parse.cpp +++ b/src/test/fuzz/descriptor_parse.cpp @@ -3,11 +3,14 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <chainparams.h> +#include <pubkey.h> #include <script/descriptor.h> #include <test/fuzz/fuzz.h> +#include <util/memory.h> void initialize() { + static const auto verify_handle = MakeUnique<ECCVerifyHandle>(); SelectParams(CBaseChainParams::REGTEST); } diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp index bcd8691359..bd05283b78 100644 --- a/src/test/fuzz/deserialize.cpp +++ b/src/test/fuzz/deserialize.cpp @@ -5,18 +5,25 @@ #include <addrdb.h> #include <addrman.h> #include <blockencodings.h> +#include <blockfilter.h> #include <chain.h> #include <coins.h> #include <compressor.h> #include <consensus/merkle.h> +#include <key.h> +#include <merkleblock.h> #include <net.h> #include <primitives/block.h> #include <protocol.h> +#include <psbt.h> #include <pubkey.h> +#include <script/keyorigin.h> #include <streams.h> #include <undo.h> #include <version.h> +#include <exception> +#include <stdexcept> #include <stdint.h> #include <unistd.h> @@ -30,137 +37,186 @@ void initialize() static const auto verify_handle = MakeUnique<ECCVerifyHandle>(); } -void test_one_input(const std::vector<uint8_t>& buffer) +namespace { + +struct invalid_fuzzing_input_exception : public std::exception { +}; + +template <typename T> +CDataStream Serialize(const T& obj) +{ + CDataStream ds(SER_NETWORK, INIT_PROTO_VERSION); + ds << obj; + return ds; +} + +template <typename T> +T Deserialize(CDataStream ds) +{ + T obj; + ds >> obj; + return obj; +} + +template <typename T> +void DeserializeFromFuzzingInput(const std::vector<uint8_t>& buffer, T& obj) { CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); try { - int nVersion; - ds >> nVersion; - ds.SetVersion(nVersion); - } catch (const std::ios_base::failure& e) { - return; + int version; + ds >> version; + ds.SetVersion(version); + } catch (const std::ios_base::failure&) { + throw invalid_fuzzing_input_exception(); + } + try { + ds >> obj; + } catch (const std::ios_base::failure&) { + throw invalid_fuzzing_input_exception(); } + assert(buffer.empty() || !Serialize(obj).empty()); +} + +template <typename T> +void AssertEqualAfterSerializeDeserialize(const T& obj) +{ + assert(Deserialize<T>(Serialize(obj)) == obj); +} -#if BLOCK_DESERIALIZE - try - { - CBlock block; - ds >> block; - } catch (const std::ios_base::failure& e) {return;} +} // namespace + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + try { +#if BLOCK_FILTER_DESERIALIZE + BlockFilter block_filter; + DeserializeFromFuzzingInput(buffer, block_filter); +#elif ADDR_INFO_DESERIALIZE + CAddrInfo addr_info; + DeserializeFromFuzzingInput(buffer, addr_info); +#elif BLOCK_FILE_INFO_DESERIALIZE + CBlockFileInfo block_file_info; + DeserializeFromFuzzingInput(buffer, block_file_info); +#elif BLOCK_HEADER_AND_SHORT_TXIDS_DESERIALIZE + CBlockHeaderAndShortTxIDs block_header_and_short_txids; + DeserializeFromFuzzingInput(buffer, block_header_and_short_txids); +#elif FEE_RATE_DESERIALIZE + CFeeRate fee_rate; + DeserializeFromFuzzingInput(buffer, fee_rate); + AssertEqualAfterSerializeDeserialize(fee_rate); +#elif MERKLE_BLOCK_DESERIALIZE + CMerkleBlock merkle_block; + DeserializeFromFuzzingInput(buffer, merkle_block); +#elif OUT_POINT_DESERIALIZE + COutPoint out_point; + DeserializeFromFuzzingInput(buffer, out_point); + AssertEqualAfterSerializeDeserialize(out_point); +#elif PARTIAL_MERKLE_TREE_DESERIALIZE + CPartialMerkleTree partial_merkle_tree; + DeserializeFromFuzzingInput(buffer, partial_merkle_tree); +#elif PUB_KEY_DESERIALIZE + CPubKey pub_key; + DeserializeFromFuzzingInput(buffer, pub_key); + // TODO: The following equivalence should hold for CPubKey? Fix. + // AssertEqualAfterSerializeDeserialize(pub_key); +#elif SCRIPT_DESERIALIZE + CScript script; + DeserializeFromFuzzingInput(buffer, script); +#elif SUB_NET_DESERIALIZE + CSubNet sub_net; + DeserializeFromFuzzingInput(buffer, sub_net); + AssertEqualAfterSerializeDeserialize(sub_net); +#elif TX_IN_DESERIALIZE + CTxIn tx_in; + DeserializeFromFuzzingInput(buffer, tx_in); + AssertEqualAfterSerializeDeserialize(tx_in); +#elif FLAT_FILE_POS_DESERIALIZE + FlatFilePos flat_file_pos; + DeserializeFromFuzzingInput(buffer, flat_file_pos); + AssertEqualAfterSerializeDeserialize(flat_file_pos); +#elif KEY_ORIGIN_INFO_DESERIALIZE + KeyOriginInfo key_origin_info; + DeserializeFromFuzzingInput(buffer, key_origin_info); + AssertEqualAfterSerializeDeserialize(key_origin_info); +#elif PARTIALLY_SIGNED_TRANSACTION_DESERIALIZE + PartiallySignedTransaction partially_signed_transaction; + DeserializeFromFuzzingInput(buffer, partially_signed_transaction); +#elif PREFILLED_TRANSACTION_DESERIALIZE + PrefilledTransaction prefilled_transaction; + DeserializeFromFuzzingInput(buffer, prefilled_transaction); +#elif PSBT_INPUT_DESERIALIZE + PSBTInput psbt_input; + DeserializeFromFuzzingInput(buffer, psbt_input); +#elif PSBT_OUTPUT_DESERIALIZE + PSBTOutput psbt_output; + DeserializeFromFuzzingInput(buffer, psbt_output); +#elif BLOCK_DESERIALIZE + CBlock block; + DeserializeFromFuzzingInput(buffer, block); #elif BLOCKLOCATOR_DESERIALIZE - try - { - CBlockLocator bl; - ds >> bl; - } catch (const std::ios_base::failure& e) {return;} + CBlockLocator bl; + DeserializeFromFuzzingInput(buffer, bl); #elif BLOCKMERKLEROOT - try - { - CBlock block; - ds >> block; - bool mutated; - BlockMerkleRoot(block, &mutated); - } catch (const std::ios_base::failure& e) {return;} + CBlock block; + DeserializeFromFuzzingInput(buffer, block); + bool mutated; + BlockMerkleRoot(block, &mutated); #elif ADDRMAN_DESERIALIZE - try - { - CAddrMan am; - ds >> am; - } catch (const std::ios_base::failure& e) {return;} + CAddrMan am; + DeserializeFromFuzzingInput(buffer, am); #elif BLOCKHEADER_DESERIALIZE - try - { - CBlockHeader bh; - ds >> bh; - } catch (const std::ios_base::failure& e) {return;} + CBlockHeader bh; + DeserializeFromFuzzingInput(buffer, bh); #elif BANENTRY_DESERIALIZE - try - { - CBanEntry be; - ds >> be; - } catch (const std::ios_base::failure& e) {return;} + CBanEntry be; + DeserializeFromFuzzingInput(buffer, be); #elif TXUNDO_DESERIALIZE - try - { - CTxUndo tu; - ds >> tu; - } catch (const std::ios_base::failure& e) {return;} + CTxUndo tu; + DeserializeFromFuzzingInput(buffer, tu); #elif BLOCKUNDO_DESERIALIZE - try - { - CBlockUndo bu; - ds >> bu; - } catch (const std::ios_base::failure& e) {return;} + CBlockUndo bu; + DeserializeFromFuzzingInput(buffer, bu); #elif COINS_DESERIALIZE - try - { - Coin coin; - ds >> coin; - } catch (const std::ios_base::failure& e) {return;} + Coin coin; + DeserializeFromFuzzingInput(buffer, coin); #elif NETADDR_DESERIALIZE - try - { - CNetAddr na; - ds >> na; - } catch (const std::ios_base::failure& e) {return;} + CNetAddr na; + DeserializeFromFuzzingInput(buffer, na); + AssertEqualAfterSerializeDeserialize(na); #elif SERVICE_DESERIALIZE - try - { - CService s; - ds >> s; - } catch (const std::ios_base::failure& e) {return;} + CService s; + DeserializeFromFuzzingInput(buffer, s); + AssertEqualAfterSerializeDeserialize(s); #elif MESSAGEHEADER_DESERIALIZE - CMessageHeader::MessageStartChars pchMessageStart = {0x00, 0x00, 0x00, 0x00}; - try - { - CMessageHeader mh(pchMessageStart); - ds >> mh; - if (!mh.IsValid(pchMessageStart)) {return;} - } catch (const std::ios_base::failure& e) {return;} + const CMessageHeader::MessageStartChars pchMessageStart = {0x00, 0x00, 0x00, 0x00}; + CMessageHeader mh(pchMessageStart); + DeserializeFromFuzzingInput(buffer, mh); + (void)mh.IsValid(pchMessageStart); #elif ADDRESS_DESERIALIZE - try - { - CAddress a; - ds >> a; - } catch (const std::ios_base::failure& e) {return;} + CAddress a; + DeserializeFromFuzzingInput(buffer, a); #elif INV_DESERIALIZE - try - { - CInv i; - ds >> i; - } catch (const std::ios_base::failure& e) {return;} + CInv i; + DeserializeFromFuzzingInput(buffer, i); #elif BLOOMFILTER_DESERIALIZE - try - { - CBloomFilter bf; - ds >> bf; - } catch (const std::ios_base::failure& e) {return;} + CBloomFilter bf; + DeserializeFromFuzzingInput(buffer, bf); #elif DISKBLOCKINDEX_DESERIALIZE - try - { - CDiskBlockIndex dbi; - ds >> dbi; - } catch (const std::ios_base::failure& e) {return;} + CDiskBlockIndex dbi; + DeserializeFromFuzzingInput(buffer, dbi); #elif TXOUTCOMPRESSOR_DESERIALIZE - CTxOut to; - CTxOutCompressor toc(to); - try - { - ds >> toc; - } catch (const std::ios_base::failure& e) {return;} + CTxOut to; + CTxOutCompressor toc(to); + DeserializeFromFuzzingInput(buffer, toc); #elif BLOCKTRANSACTIONS_DESERIALIZE - try - { - BlockTransactions bt; - ds >> bt; - } catch (const std::ios_base::failure& e) {return;} + BlockTransactions bt; + DeserializeFromFuzzingInput(buffer, bt); #elif BLOCKTRANSACTIONSREQUEST_DESERIALIZE - try - { - BlockTransactionsRequest btr; - ds >> btr; - } catch (const std::ios_base::failure& e) {return;} + BlockTransactionsRequest btr; + DeserializeFromFuzzingInput(buffer, btr); #else #error Need at least one fuzz target to compile #endif + } catch (const invalid_fuzzing_input_exception&) { + } } diff --git a/src/test/fuzz/hex.cpp b/src/test/fuzz/hex.cpp new file mode 100644 index 0000000000..54693180be --- /dev/null +++ b/src/test/fuzz/hex.cpp @@ -0,0 +1,22 @@ +// Copyright (c) 2019 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/fuzz/fuzz.h> + +#include <util/strencodings.h> + +#include <cassert> +#include <cstdint> +#include <string> +#include <vector> + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + const std::string random_hex_string(buffer.begin(), buffer.end()); + const std::vector<unsigned char> data = ParseHex(random_hex_string); + const std::string hex_data = HexStr(data); + if (IsHex(random_hex_string)) { + assert(ToLower(random_hex_string) == hex_data); + } +} diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp new file mode 100644 index 0000000000..723938bcdb --- /dev/null +++ b/src/test/fuzz/integer.cpp @@ -0,0 +1,127 @@ +// Copyright (c) 2019 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 <arith_uint256.h> +#include <compressor.h> +#include <consensus/merkle.h> +#include <core_io.h> +#include <crypto/common.h> +#include <crypto/siphash.h> +#include <key_io.h> +#include <memusage.h> +#include <netbase.h> +#include <policy/settings.h> +#include <pow.h> +#include <pubkey.h> +#include <rpc/util.h> +#include <script/signingprovider.h> +#include <script/standard.h> +#include <serialize.h> +#include <test/fuzz/FuzzedDataProvider.h> +#include <test/fuzz/fuzz.h> +#include <uint256.h> +#include <util/strencodings.h> +#include <util/system.h> +#include <util/time.h> + +#include <cassert> +#include <limits> +#include <vector> + +void initialize() +{ + SelectParams(CBaseChainParams::REGTEST); +} + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + if (buffer.size() < sizeof(uint256) + sizeof(uint160)) { + return; + } + FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); + const uint256 u256(fuzzed_data_provider.ConsumeBytes<unsigned char>(sizeof(uint256))); + const uint160 u160(fuzzed_data_provider.ConsumeBytes<unsigned char>(sizeof(uint160))); + const uint64_t u64 = fuzzed_data_provider.ConsumeIntegral<uint64_t>(); + const int64_t i64 = fuzzed_data_provider.ConsumeIntegral<int64_t>(); + const uint32_t u32 = fuzzed_data_provider.ConsumeIntegral<uint32_t>(); + const int32_t i32 = fuzzed_data_provider.ConsumeIntegral<int32_t>(); + const uint16_t u16 = fuzzed_data_provider.ConsumeIntegral<uint16_t>(); + const int16_t i16 = fuzzed_data_provider.ConsumeIntegral<int16_t>(); + const uint8_t u8 = fuzzed_data_provider.ConsumeIntegral<uint8_t>(); + const int8_t i8 = fuzzed_data_provider.ConsumeIntegral<int8_t>(); + // We cannot assume a specific value of std::is_signed<char>::value: + // ConsumeIntegral<char>() instead of casting from {u,}int8_t. + const char ch = fuzzed_data_provider.ConsumeIntegral<char>(); + + const Consensus::Params& consensus_params = Params().GetConsensus(); + (void)CheckProofOfWork(u256, u32, consensus_params); + (void)CompressAmount(u64); + static const uint256 u256_min(uint256S("0000000000000000000000000000000000000000000000000000000000000000")); + static const uint256 u256_max(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); + const std::vector<uint256> v256{u256, u256_min, u256_max}; + (void)ComputeMerkleRoot(v256); + (void)CountBits(u64); + (void)DecompressAmount(u64); + (void)FormatISO8601Date(i64); + (void)FormatISO8601DateTime(i64); + (void)GetSizeOfCompactSize(u64); + (void)GetSpecialScriptSize(u32); + // (void)GetVirtualTransactionSize(i64, i64); // function defined only for a subset of int64_t inputs + // (void)GetVirtualTransactionSize(i64, i64, u32); // function defined only for a subset of int64_t/uint32_t inputs + (void)HexDigit(ch); + (void)i64tostr(i64); + (void)IsDigit(ch); + (void)IsSpace(ch); + (void)IsSwitchChar(ch); + (void)itostr(i32); + (void)memusage::DynamicUsage(ch); + (void)memusage::DynamicUsage(i16); + (void)memusage::DynamicUsage(i32); + (void)memusage::DynamicUsage(i64); + (void)memusage::DynamicUsage(i8); + (void)memusage::DynamicUsage(u16); + (void)memusage::DynamicUsage(u32); + (void)memusage::DynamicUsage(u64); + (void)memusage::DynamicUsage(u8); + const unsigned char uch = static_cast<unsigned char>(u8); + (void)memusage::DynamicUsage(uch); + (void)MillisToTimeval(i64); + const double d = ser_uint64_to_double(u64); + assert(ser_double_to_uint64(d) == u64); + const float f = ser_uint32_to_float(u32); + assert(ser_float_to_uint32(f) == u32); + (void)SighashToStr(uch); + (void)SipHashUint256(u64, u64, u256); + (void)SipHashUint256Extra(u64, u64, u256, u32); + (void)ToLower(ch); + + const arith_uint256 au256 = UintToArith256(u256); + assert(ArithToUint256(au256) == u256); + assert(uint256S(au256.GetHex()) == u256); + (void)au256.bits(); + (void)au256.GetCompact(/* fNegative= */ false); + (void)au256.GetCompact(/* fNegative= */ true); + (void)au256.getdouble(); + (void)au256.GetHex(); + (void)au256.GetLow64(); + (void)au256.size(); + (void)au256.ToString(); + + const CKeyID key_id{u160}; + const CScriptID script_id{u160}; + // CTxDestination = CNoDestination ∪ PKHash ∪ ScriptHash ∪ WitnessV0ScriptHash ∪ WitnessV0KeyHash ∪ WitnessUnknown + const PKHash pk_hash{u160}; + const ScriptHash script_hash{u160}; + const WitnessV0KeyHash witness_v0_key_hash{u160}; + const WitnessV0ScriptHash witness_v0_script_hash{u256}; + const std::vector<CTxDestination> destinations{pk_hash, script_hash, witness_v0_key_hash, witness_v0_script_hash}; + const SigningProvider store; + for (const CTxDestination& destination : destinations) { + (void)DescribeAddress(destination); + (void)EncodeDestination(destination); + (void)GetKeyForDestination(store, destination); + (void)GetScriptForDestination(destination); + (void)IsValidDestination(destination); + } +} diff --git a/src/test/fuzz/parse_hd_keypath.cpp b/src/test/fuzz/parse_hd_keypath.cpp new file mode 100644 index 0000000000..9a23f4b2d4 --- /dev/null +++ b/src/test/fuzz/parse_hd_keypath.cpp @@ -0,0 +1,13 @@ +// Copyright (c) 2009-2019 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/fuzz/fuzz.h> +#include <util/bip32.h> + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + const std::string keypath_str(buffer.begin(), buffer.end()); + std::vector<uint32_t> keypath; + (void)ParseHDKeypath(keypath_str, keypath); +} diff --git a/src/test/fuzz/parse_numbers.cpp b/src/test/fuzz/parse_numbers.cpp new file mode 100644 index 0000000000..59f89dc9fb --- /dev/null +++ b/src/test/fuzz/parse_numbers.cpp @@ -0,0 +1,35 @@ +// Copyright (c) 2009-2019 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/fuzz/fuzz.h> +#include <util/moneystr.h> +#include <util/strencodings.h> + +#include <string> + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + const std::string random_string(buffer.begin(), buffer.end()); + + CAmount amount; + (void)ParseMoney(random_string, amount); + + double d; + (void)ParseDouble(random_string, &d); + + int32_t i32; + (void)ParseInt32(random_string, &i32); + (void)atoi(random_string); + + uint32_t u32; + (void)ParseUInt32(random_string, &u32); + + int64_t i64; + (void)atoi64(random_string); + (void)ParseFixedPoint(random_string, 3, &i64); + (void)ParseInt64(random_string, &i64); + + uint64_t u64; + (void)ParseUInt64(random_string, &u64); +} diff --git a/src/test/fuzz/parse_script.cpp b/src/test/fuzz/parse_script.cpp new file mode 100644 index 0000000000..21ac1aecf3 --- /dev/null +++ b/src/test/fuzz/parse_script.cpp @@ -0,0 +1,16 @@ +// Copyright (c) 2009-2019 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 <core_io.h> +#include <script/script.h> +#include <test/fuzz/fuzz.h> + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + const std::string script_string(buffer.begin(), buffer.end()); + try { + (void)ParseScript(script_string); + } catch (const std::runtime_error&) { + } +} diff --git a/src/test/fuzz/parse_univalue.cpp b/src/test/fuzz/parse_univalue.cpp new file mode 100644 index 0000000000..3ad112dbad --- /dev/null +++ b/src/test/fuzz/parse_univalue.cpp @@ -0,0 +1,91 @@ +// Copyright (c) 2009-2019 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 <chainparams.h> +#include <core_io.h> +#include <rpc/client.h> +#include <rpc/util.h> +#include <test/fuzz/fuzz.h> +#include <util/memory.h> + +#include <limits> +#include <string> + +void initialize() +{ + static const auto verify_handle = MakeUnique<ECCVerifyHandle>(); + SelectParams(CBaseChainParams::REGTEST); +} + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + const std::string random_string(buffer.begin(), buffer.end()); + bool valid = true; + const UniValue univalue = [&] { + try { + return ParseNonRFCJSONValue(random_string); + } catch (const std::runtime_error&) { + valid = false; + return NullUniValue; + } + }(); + if (!valid) { + return; + } + try { + (void)ParseHashO(univalue, "A"); + (void)ParseHashO(univalue, random_string); + } catch (const UniValue&) { + } catch (const std::runtime_error&) { + } + try { + (void)ParseHashV(univalue, "A"); + (void)ParseHashV(univalue, random_string); + } catch (const UniValue&) { + } catch (const std::runtime_error&) { + } + try { + (void)ParseHexO(univalue, "A"); + (void)ParseHexO(univalue, random_string); + } catch (const UniValue&) { + } catch (const std::runtime_error&) { + } + try { + (void)ParseHexUV(univalue, "A"); + (void)ParseHexUV(univalue, random_string); + } catch (const UniValue&) { + } catch (const std::runtime_error&) { + } + try { + (void)ParseHexV(univalue, "A"); + (void)ParseHexV(univalue, random_string); + } catch (const UniValue&) { + } catch (const std::runtime_error&) { + } + try { + (void)ParseSighashString(univalue); + } catch (const std::runtime_error&) { + } + try { + (void)AmountFromValue(univalue); + } catch (const UniValue&) { + } catch (const std::runtime_error&) { + } + try { + FlatSigningProvider provider; + (void)EvalDescriptorStringOrObject(univalue, provider); + } catch (const UniValue&) { + } catch (const std::runtime_error&) { + } + try { + (void)ParseConfirmTarget(univalue, std::numeric_limits<unsigned int>::max()); + } catch (const UniValue&) { + } catch (const std::runtime_error&) { + } + try { + (void)ParseDescriptorRange(univalue); + } catch (const UniValue&) { + } catch (const std::runtime_error&) { + } +} diff --git a/src/test/fuzz/psbt.cpp b/src/test/fuzz/psbt.cpp new file mode 100644 index 0000000000..1ce28f9a6d --- /dev/null +++ b/src/test/fuzz/psbt.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2019 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/fuzz/fuzz.h> + +#include <node/psbt.h> +#include <optional.h> +#include <psbt.h> +#include <pubkey.h> +#include <script/script.h> +#include <streams.h> +#include <util/memory.h> +#include <version.h> + +#include <cstdint> +#include <string> +#include <vector> + +void initialize() +{ + static const auto verify_handle = MakeUnique<ECCVerifyHandle>(); +} + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + PartiallySignedTransaction psbt_mut; + const std::string raw_psbt{buffer.begin(), buffer.end()}; + std::string error; + if (!DecodeRawPSBT(psbt_mut, raw_psbt, error)) { + return; + } + const PartiallySignedTransaction psbt = psbt_mut; + + const PSBTAnalysis analysis = AnalyzePSBT(psbt); + (void)PSBTRoleName(analysis.next); + for (const PSBTInputAnalysis& input_analysis : analysis.inputs) { + (void)PSBTRoleName(input_analysis.next); + } + + (void)psbt.IsNull(); + (void)psbt.IsSane(); + + Optional<CMutableTransaction> tx = psbt.tx; + if (tx) { + const CMutableTransaction& mtx = *tx; + const PartiallySignedTransaction psbt_from_tx{mtx}; + } + + for (const PSBTInput& input : psbt.inputs) { + (void)PSBTInputSigned(input); + (void)input.IsNull(); + (void)input.IsSane(); + } + + for (const PSBTOutput& output : psbt.outputs) { + (void)output.IsNull(); + } + + for (size_t i = 0; i < psbt.tx->vin.size(); ++i) { + CTxOut tx_out; + if (psbt.GetInputUTXO(tx_out, i)) { + (void)tx_out.IsNull(); + (void)tx_out.ToString(); + } + } + + psbt_mut = psbt; + (void)FinalizePSBT(psbt_mut); + + psbt_mut = psbt; + CMutableTransaction result; + if (FinalizeAndExtractPSBT(psbt_mut, result)) { + const PartiallySignedTransaction psbt_from_tx{result}; + } + + psbt_mut = psbt; + (void)psbt_mut.Merge(psbt); +} diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp index 76b230ef3c..fefafda36b 100644 --- a/src/test/fuzz/transaction.cpp +++ b/src/test/fuzz/transaction.cpp @@ -26,19 +26,31 @@ void test_one_input(const std::vector<uint8_t>& buffer) int nVersion; ds >> nVersion; ds.SetVersion(nVersion); - } catch (const std::ios_base::failure& e) { + } catch (const std::ios_base::failure&) { return; } - bool valid = true; + bool valid_tx = true; const CTransaction tx = [&] { try { return CTransaction(deserialize, ds); - } catch (const std::ios_base::failure& e) { - valid = false; + } catch (const std::ios_base::failure&) { + valid_tx = false; return CTransaction(); } }(); - if (!valid) { + bool valid_mutable_tx = true; + CDataStream ds_mtx(buffer, SER_NETWORK, INIT_PROTO_VERSION); + CMutableTransaction mutable_tx; + try { + int nVersion; + ds_mtx >> nVersion; + ds_mtx.SetVersion(nVersion); + ds_mtx >> mutable_tx; + } catch (const std::ios_base::failure&) { + valid_mutable_tx = false; + } + assert(valid_tx == valid_mutable_tx); + if (!valid_tx) { return; } diff --git a/src/test/fuzz/tx_in.cpp b/src/test/fuzz/tx_in.cpp new file mode 100644 index 0000000000..8e116537d1 --- /dev/null +++ b/src/test/fuzz/tx_in.cpp @@ -0,0 +1,33 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <consensus/validation.h> +#include <core_memusage.h> +#include <policy/policy.h> +#include <primitives/transaction.h> +#include <streams.h> +#include <test/fuzz/fuzz.h> +#include <version.h> + +#include <cassert> + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); + CTxIn tx_in; + try { + int version; + ds >> version; + ds.SetVersion(version); + ds >> tx_in; + } catch (const std::ios_base::failure&) { + return; + } + + (void)GetTransactionInputWeight(tx_in); + (void)GetVirtualTransactionInputSize(tx_in); + (void)RecursiveDynamicUsage(tx_in); + + (void)tx_in.ToString(); +} diff --git a/src/test/fuzz/tx_out.cpp b/src/test/fuzz/tx_out.cpp new file mode 100644 index 0000000000..aa1338d5ba --- /dev/null +++ b/src/test/fuzz/tx_out.cpp @@ -0,0 +1,35 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <consensus/validation.h> +#include <core_memusage.h> +#include <policy/policy.h> +#include <primitives/transaction.h> +#include <streams.h> +#include <test/fuzz/fuzz.h> +#include <version.h> + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + CDataStream ds(buffer, SER_NETWORK, INIT_PROTO_VERSION); + CTxOut tx_out; + try { + int version; + ds >> version; + ds.SetVersion(version); + ds >> tx_out; + } catch (const std::ios_base::failure&) { + return; + } + + const CFeeRate dust_relay_fee{DUST_RELAY_TX_FEE}; + (void)GetDustThreshold(tx_out, dust_relay_fee); + (void)IsDust(tx_out, dust_relay_fee); + (void)RecursiveDynamicUsage(tx_out); + + (void)tx_out.ToString(); + (void)tx_out.IsNull(); + tx_out.SetNull(); + assert(tx_out.IsNull()); +} diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 6ed7350ea2..d79a598bf1 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -23,7 +23,17 @@ #include <boost/test/unit_test.hpp> -BOOST_FIXTURE_TEST_SUITE(miner_tests, TestingSetup) +namespace miner_tests { +struct MinerTestingSetup : public TestingSetup { + void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs); + bool TestSequenceLocks(const CTransaction& tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs) + { + return CheckSequenceLocks(*m_node.mempool, tx, flags); + } +}; +} // namespace miner_tests + +BOOST_FIXTURE_TEST_SUITE(miner_tests, MinerTestingSetup) // BOOST_CHECK_EXCEPTION predicates to check the specific validation error class HasReason { @@ -89,16 +99,10 @@ static CBlockIndex CreateBlockIndex(int nHeight) EXCLUSIVE_LOCKS_REQUIRED(cs_mai return index; } -static bool TestSequenceLocks(const CTransaction &tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main) -{ - LOCK(::mempool.cs); - return CheckSequenceLocks(::mempool, tx, flags); -} - // Test suite for ancestor feerate transaction selection. // Implemented as an additional function, rather than a separate test case, // to allow reusing the blockchain created in CreateNewBlock_validity. -static void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs) +void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) { // Test the ancestor feerate transaction selection. TestMemPoolEntryHelper entry; @@ -114,19 +118,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(entry.Fee(1000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + m_node.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(entry.Fee(10000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + m_node.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(entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + m_node.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); @@ -137,7 +141,7 @@ 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(entry.Fee(0).FromTx(tx)); + m_node.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 @@ -147,7 +151,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(entry.Fee(feeToUse).FromTx(tx)); + m_node.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) { @@ -158,10 +162,10 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript& // Test that packages above the min relay fee do get included, even if one // of the transactions is below the min relay fee // Remove the low fee transaction and replace with a higher fee transaction - mempool.removeRecursive(CTransaction(tx), MemPoolRemovalReason::REPLACED); + m_node.mempool->removeRecursive(CTransaction(tx), MemPoolRemovalReason::REPLACED); tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee hashLowFeeTx = tx.GetHash(); - mempool.addUnchecked(entry.Fee(feeToUse+2).FromTx(tx)); + m_node.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); @@ -174,7 +178,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(entry.Fee(0).SpendsCoinbase(true).FromTx(tx)); + m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx)); // This tx can't be mined by itself tx.vin[0].prevout.hash = hashFreeTx2; @@ -182,7 +186,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(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx)); + m_node.mempool->addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx)); pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); // Verify that this tx isn't selected. @@ -195,7 +199,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(entry.Fee(10000).FromTx(tx)); + m_node.mempool->addUnchecked(entry.Fee(10000).FromTx(tx)); pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2); } @@ -252,7 +256,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) } LOCK(cs_main); - LOCK(::mempool.cs); + LOCK(m_node.mempool->cs); // Just to make sure we can still make simple blocks BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); @@ -276,12 +280,12 @@ 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(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); + m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops")); - mempool.clear(); + m_node.mempool->clear(); tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vout[0].nValue = BLOCKSUBSIDY; @@ -291,11 +295,11 @@ 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(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx)); + m_node.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)); - mempool.clear(); + m_node.mempool->clear(); // block size > limit tx.vin[0].scriptSig = CScript(); @@ -311,24 +315,24 @@ 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(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); + m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); - mempool.clear(); + m_node.mempool->clear(); - // orphan in mempool, template creation fails + // orphan in *m_node.mempool, template creation fails hash = tx.GetHash(); - mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx)); + m_node.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(); + m_node.mempool->clear(); // child with higher feerate than parent tx.vin[0].scriptSig = CScript() << OP_1; tx.vin[0].prevout.hash = txFirst[1]->GetHash(); tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE; hash = tx.GetHash(); - mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + m_node.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; @@ -336,34 +340,34 @@ 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(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + m_node.mempool->addUnchecked(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); - mempool.clear(); + m_node.mempool->clear(); - // coinbase in mempool, template creation fails + // coinbase in *m_node.mempool, template creation fails tx.vin.resize(1); tx.vin[0].prevout.SetNull(); tx.vin[0].scriptSig = CScript() << OP_0 << OP_1; tx.vout[0].nValue = 0; hash = tx.GetHash(); // give it a fee so it'll get mined - mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + m_node.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(); + m_node.mempool->clear(); - // double spend txn pair in mempool, template creation fails + // double spend txn pair in *m_node.mempool, template creation fails tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].scriptSig = CScript() << OP_1; tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE; tx.vout[0].scriptPubKey = CScript() << OP_1; hash = tx.GetHash(); - mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); - mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + m_node.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(); + m_node.mempool->clear(); // subsidy changing int nHeight = ::ChainActive().Height(); @@ -392,7 +396,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) } BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); - // invalid p2sh txn in mempool, template creation fails + // invalid p2sh txn in *m_node.mempool, template creation fails tx.vin[0].prevout.hash = txFirst[0]->GetHash(); tx.vin[0].prevout.n = 0; tx.vin[0].scriptSig = CScript() << OP_1; @@ -400,15 +404,15 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) script = CScript() << OP_0; tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script)); hash = tx.GetHash(); - mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + m_node.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(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); + m_node.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(); + m_node.mempool->clear(); // Delete the dummy blocks again. while (::ChainActive().Tip()->nHeight > nHeight) { @@ -439,7 +443,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].scriptPubKey = CScript() << OP_1; tx.nLockTime = 0; hash = tx.GetHash(); - mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); + m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, &prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 2))); // Sequence locks pass on 2nd block @@ -449,7 +453,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(entry.Time(GetTime()).FromTx(tx)); + m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx)); BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail @@ -465,7 +469,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) prevheights[0] = baseheight + 3; tx.nLockTime = ::ChainActive().Tip()->nHeight + 1; hash = tx.GetHash(); - mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx)); + m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx)); BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block @@ -476,7 +480,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) prevheights.resize(1); prevheights[0] = baseheight + 4; hash = tx.GetHash(); - mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx)); + m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx)); BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later @@ -513,7 +517,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) ::ChainActive().Tip()->nHeight--; SetMockTime(0); - mempool.clear(); + m_node.mempool->clear(); TestPackageSelection(chainparams, scriptPubKey, txFirst); diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index 78c11ff202..481dedc356 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -54,6 +54,8 @@ BOOST_AUTO_TEST_CASE(netbase_properties) BOOST_CHECK(ResolveIP("10.0.0.1").IsRFC1918()); BOOST_CHECK(ResolveIP("192.168.1.1").IsRFC1918()); BOOST_CHECK(ResolveIP("172.31.255.255").IsRFC1918()); + BOOST_CHECK(ResolveIP("198.18.0.0").IsRFC2544()); + BOOST_CHECK(ResolveIP("198.19.255.255").IsRFC2544()); BOOST_CHECK(ResolveIP("2001:0DB8::").IsRFC3849()); BOOST_CHECK(ResolveIP("169.254.1.1").IsRFC3927()); BOOST_CHECK(ResolveIP("2002::1").IsRFC3964()); diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp index 52dd22de7e..84a3980b19 100644 --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -112,14 +112,10 @@ BOOST_AUTO_TEST_CASE(rpc_rawsign) std::string notsigned = r.get_str(); std::string privkey1 = "\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\""; std::string privkey2 = "\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\""; - NodeContext node; - node.chain = interfaces::MakeChain(node); - g_rpc_node = &node; r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" [] "+prevout); BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == false); r = CallRPC(std::string("signrawtransactionwithkey ")+notsigned+" ["+privkey1+","+privkey2+"] "+prevout); BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true); - g_rpc_node = nullptr; } BOOST_AUTO_TEST_CASE(rpc_createraw_op_return) diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp index 7e30fbcf68..45644834a5 100644 --- a/src/test/settings_tests.cpp +++ b/src/test/settings_tests.cpp @@ -4,8 +4,9 @@ #include <util/settings.h> -#include <test/util.h> #include <test/util/setup_common.h> +#include <test/util/str.h> + #include <boost/test/unit_test.hpp> #include <univalue.h> diff --git a/src/test/timedata_tests.cpp b/src/test/timedata_tests.cpp index 4721151197..19bd0d142f 100644 --- a/src/test/timedata_tests.cpp +++ b/src/test/timedata_tests.cpp @@ -65,7 +65,7 @@ BOOST_AUTO_TEST_CASE(addtimedata) MultiAddTimeData(1, DEFAULT_MAX_TIME_ADJUSTMENT + 1); //filter size 5 } - BOOST_CHECK(GetWarnings("gui").find("clock is wrong") != std::string::npos); + BOOST_CHECK(GetWarnings(true).find("clock is wrong") != std::string::npos); // nTimeOffset is not changed if the median of offsets exceeds DEFAULT_MAX_TIME_ADJUSTMENT BOOST_CHECK_EQUAL(GetTimeOffset(), 0); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 2d55554acb..0939803953 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -713,6 +713,29 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) t.vout[0].nValue = nDustThreshold; BOOST_CHECK(IsStandardTx(CTransaction(t), reason)); + // Disallowed nVersion + t.nVersion = -1; + reason.clear(); + BOOST_CHECK(!IsStandardTx(CTransaction(t), reason)); + BOOST_CHECK_EQUAL(reason, "version"); + + t.nVersion = 0; + reason.clear(); + BOOST_CHECK(!IsStandardTx(CTransaction(t), reason)); + BOOST_CHECK_EQUAL(reason, "version"); + + t.nVersion = 3; + reason.clear(); + BOOST_CHECK(!IsStandardTx(CTransaction(t), reason)); + BOOST_CHECK_EQUAL(reason, "version"); + + // Allowed nVersion + t.nVersion = 1; + BOOST_CHECK(IsStandardTx(CTransaction(t), reason)); + + t.nVersion = 2; + BOOST_CHECK(IsStandardTx(CTransaction(t), reason)); + // Check dust with odd relay fee to verify rounding: // nDustThreshold = 182 * 3702 / 1000 dustRelayFee = CFeeRate(3702); @@ -784,6 +807,30 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) reason.clear(); BOOST_CHECK(!IsStandardTx(CTransaction(t), reason)); BOOST_CHECK_EQUAL(reason, "multi-op-return"); + + // Check large scriptSig (non-standard if size is >1650 bytes) + t.vout.resize(1); + t.vout[0].nValue = MAX_MONEY; + t.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey())); + // OP_PUSHDATA2 with len (3 bytes) + data (1647 bytes) = 1650 bytes + t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(1647, 0); // 1650 + BOOST_CHECK(IsStandardTx(CTransaction(t), reason)); + + t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(1648, 0); // 1651 + reason.clear(); + BOOST_CHECK(!IsStandardTx(CTransaction(t), reason)); + BOOST_CHECK_EQUAL(reason, "scriptsig-size"); + + // Check bare multisig (standard if policy flag fIsBareMultisigStd is set) + fIsBareMultisigStd = true; + t.vout[0].scriptPubKey = GetScriptForMultisig(1, {key.GetPubKey()}); // simple 1-of-1 + t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(65, 0); + BOOST_CHECK(IsStandardTx(CTransaction(t), reason)); + + fIsBareMultisigStd = false; + reason.clear(); + BOOST_CHECK(!IsStandardTx(CTransaction(t), reason)); + BOOST_CHECK_EQUAL(reason, "bare-multisig"); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp index 245c03d774..cace75f093 100644 --- a/src/test/txvalidation_tests.cpp +++ b/src/test/txvalidation_tests.cpp @@ -34,17 +34,17 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup) LOCK(cs_main); - unsigned int initialPoolSize = mempool.size(); + unsigned int initialPoolSize = m_node.mempool->size(); BOOST_CHECK_EQUAL( false, - AcceptToMemoryPool(mempool, state, MakeTransactionRef(coinbaseTx), + AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(coinbaseTx), nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */)); // Check that the transaction hasn't been added to mempool. - BOOST_CHECK_EQUAL(mempool.size(), initialPoolSize); + BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize); // Check that the validation state reflects the unsuccessful attempt. BOOST_CHECK(state.IsInvalid()); diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index a5bc15bb9f..67f45c4ed4 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -17,16 +17,6 @@ bool CheckInputs(const CTransaction& tx, TxValidationState &state, const CCoinsV BOOST_AUTO_TEST_SUITE(tx_validationcache_tests) -static bool -ToMemPool(const CMutableTransaction& tx) -{ - LOCK(cs_main); - - TxValidationState state; - return AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx), - nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */); -} - BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) { // Make sure skipping validation of transactions that were @@ -35,6 +25,14 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG; + const auto ToMemPool = [this](const CMutableTransaction& tx) { + LOCK(cs_main); + + TxValidationState state; + return AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(tx), + nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */); + }; + // Create a double-spend of mature coinbase txn: std::vector<CMutableTransaction> spends; spends.resize(2); @@ -72,7 +70,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) LOCK(cs_main); BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash()); } - mempool.clear(); + m_node.mempool->clear(); // Test 3: ... and should be rejected if spend2 is in the memory pool BOOST_CHECK(ToMemPool(spends[1])); @@ -81,9 +79,9 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) LOCK(cs_main); BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash()); } - mempool.clear(); + m_node.mempool->clear(); - // Final sanity test: first spend in mempool, second in block, that's OK: + // Final sanity test: first spend in *m_node.mempool, second in block, that's OK: std::vector<CMutableTransaction> oneSpend; oneSpend.push_back(spends[0]); BOOST_CHECK(ToMemPool(spends[1])); @@ -94,7 +92,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) } // spends[1] should have been removed from the mempool when the // block with spends[0] is accepted: - BOOST_CHECK_EQUAL(mempool.size(), 0U); + BOOST_CHECK_EQUAL(m_node.mempool->size(), 0U); } // Run CheckInputs (using CoinsTip()) on the given transaction, for all script diff --git a/src/test/util.h b/src/test/util.h deleted file mode 100644 index f90cb0d623..0000000000 --- a/src/test/util.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2019 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_UTIL_H -#define BITCOIN_TEST_UTIL_H - -#include <memory> -#include <string> - -class CBlock; -class CScript; -class CTxIn; -class CWallet; - -// Constants // - -extern const std::string ADDRESS_BCRT1_UNSPENDABLE; - -// Lower-level utils // - -/** Returns the generated coin */ -CTxIn MineBlock(const CScript& coinbase_scriptPubKey); -/** Prepare a block to be mined */ -std::shared_ptr<CBlock> PrepareBlock(const CScript& coinbase_scriptPubKey); - - -// RPC-like // - -/** Import the address to the wallet */ -void importaddress(CWallet& wallet, const std::string& address); -/** Returns a new address from the wallet */ -std::string getnewaddress(CWallet& w); -/** Returns the generated coin */ -CTxIn generatetoaddress(const std::string& address); - -/** - * Increment a string. Useful to enumerate all fixed length strings with - * characters in [min_char, max_char]. - */ -template <typename CharType, size_t StringLength> -bool NextString(CharType (&string)[StringLength], CharType min_char, CharType max_char) -{ - for (CharType& elem : string) { - bool has_next = elem != max_char; - elem = elem < min_char || elem >= max_char ? min_char : CharType(elem + 1); - if (has_next) return true; - } - return false; -} - -/** - * Iterate over string values and call function for each string without - * successive duplicate characters. - */ -template <typename CharType, size_t StringLength, typename Fn> -void ForEachNoDup(CharType (&string)[StringLength], CharType min_char, CharType max_char, Fn&& fn) { - for (bool has_next = true; has_next; has_next = NextString(string, min_char, max_char)) { - int prev = -1; - bool skip_string = false; - for (CharType c : string) { - if (c == prev) skip_string = true; - if (skip_string || c < min_char || c > max_char) break; - prev = c; - } - if (!skip_string) fn(); - } -} - -#endif // BITCOIN_TEST_UTIL_H diff --git a/src/test/util.cpp b/src/test/util/mining.cpp index ed031270f2..30f0f5d7e6 100644 --- a/src/test/util.cpp +++ b/src/test/util/mining.cpp @@ -2,48 +2,15 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <test/util.h> +#include <test/util/mining.h> #include <chainparams.h> #include <consensus/merkle.h> #include <key_io.h> #include <miner.h> -#include <outputtype.h> #include <pow.h> #include <script/standard.h> #include <validation.h> -#include <validationinterface.h> -#ifdef ENABLE_WALLET -#include <wallet/wallet.h> -#endif - -const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj"; - -#ifdef ENABLE_WALLET -std::string getnewaddress(CWallet& w) -{ - constexpr auto output_type = OutputType::BECH32; - CTxDestination dest; - std::string error; - if (!w.GetNewDestination(output_type, "", dest, error)) assert(false); - - return EncodeDestination(dest); -} - -void importaddress(CWallet& wallet, const std::string& address) -{ - auto spk_man = wallet.GetLegacyScriptPubKeyMan(); - LOCK(wallet.cs_wallet); - AssertLockHeld(spk_man->cs_wallet); - const auto dest = DecodeDestination(address); - assert(IsValidDestination(dest)); - const auto script = GetScriptForDestination(dest); - wallet.MarkDirty(); - assert(!spk_man->HaveWatchOnly(script)); - if (!spk_man->AddWatchOnly(script, 0 /* nCreateTime */)) assert(false); - wallet.SetAddressBook(dest, /* label */ "", "receive"); -} -#endif // ENABLE_WALLET CTxIn generatetoaddress(const std::string& address) { diff --git a/src/test/util/mining.h b/src/test/util/mining.h new file mode 100644 index 0000000000..afe4de684f --- /dev/null +++ b/src/test/util/mining.h @@ -0,0 +1,24 @@ +// Copyright (c) 2019 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_UTIL_MINING_H +#define BITCOIN_TEST_UTIL_MINING_H + +#include <memory> +#include <string> + +class CBlock; +class CScript; +class CTxIn; + +/** Returns the generated coin */ +CTxIn MineBlock(const CScript& coinbase_scriptPubKey); + +/** Prepare a block to be mined */ +std::shared_ptr<CBlock> PrepareBlock(const CScript& coinbase_scriptPubKey); + +/** RPC-like helper function, returns the generated coin */ +CTxIn generatetoaddress(const std::string& address); + +#endif // BITCOIN_TEST_UTIL_MINING_H diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 0c6ecdf69d..86c355fdcd 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -107,7 +107,6 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha threadGroup.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler)); GetMainSignals().RegisterBackgroundSignalScheduler(scheduler); - mempool.setSanityCheck(1.0); pblocktree.reset(new CBlockTreeDB(1 << 20, true)); g_chainstate = MakeUnique<CChainState>(); ::ChainstateActive().InitCoinsDB( @@ -131,6 +130,8 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha } g_parallel_script_checks = true; + m_node.mempool = &::mempool; + m_node.mempool->setSanityCheck(1.0); m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME); m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests. } @@ -144,6 +145,7 @@ TestingSetup::~TestingSetup() g_rpc_node = nullptr; m_node.connman.reset(); m_node.banman.reset(); + m_node.mempool = nullptr; UnloadBlockIndex(); g_chainstate.reset(); pblocktree.reset(); diff --git a/src/test/util/str.h b/src/test/util/str.h index 63629501e8..ef94692df0 100644 --- a/src/test/util/str.h +++ b/src/test/util/str.h @@ -9,4 +9,37 @@ bool CaseInsensitiveEqual(const std::string& s1, const std::string& s2); +/** + * Increment a string. Useful to enumerate all fixed length strings with + * characters in [min_char, max_char]. + */ +template <typename CharType, size_t StringLength> +bool NextString(CharType (&string)[StringLength], CharType min_char, CharType max_char) +{ + for (CharType& elem : string) { + bool has_next = elem != max_char; + elem = elem < min_char || elem >= max_char ? min_char : CharType(elem + 1); + if (has_next) return true; + } + return false; +} + +/** + * Iterate over string values and call function for each string without + * successive duplicate characters. + */ +template <typename CharType, size_t StringLength, typename Fn> +void ForEachNoDup(CharType (&string)[StringLength], CharType min_char, CharType max_char, Fn&& fn) { + for (bool has_next = true; has_next; has_next = NextString(string, min_char, max_char)) { + int prev = -1; + bool skip_string = false; + for (CharType c : string) { + if (c == prev) skip_string = true; + if (skip_string || c < min_char || c > max_char) break; + prev = c; + } + if (!skip_string) fn(); + } +} + #endif // BITCOIN_TEST_UTIL_STR_H diff --git a/src/test/util/wallet.cpp b/src/test/util/wallet.cpp new file mode 100644 index 0000000000..226d2df6e4 --- /dev/null +++ b/src/test/util/wallet.cpp @@ -0,0 +1,40 @@ +// Copyright (c) 2019 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/util/wallet.h> + +#include <key_io.h> +#include <outputtype.h> +#include <script/standard.h> +#ifdef ENABLE_WALLET +#include <wallet/wallet.h> +#endif + +const std::string ADDRESS_BCRT1_UNSPENDABLE = "bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj"; + +#ifdef ENABLE_WALLET +std::string getnewaddress(CWallet& w) +{ + constexpr auto output_type = OutputType::BECH32; + CTxDestination dest; + std::string error; + if (!w.GetNewDestination(output_type, "", dest, error)) assert(false); + + return EncodeDestination(dest); +} + +void importaddress(CWallet& wallet, const std::string& address) +{ + auto spk_man = wallet.GetLegacyScriptPubKeyMan(); + LOCK(wallet.cs_wallet); + AssertLockHeld(spk_man->cs_wallet); + const auto dest = DecodeDestination(address); + assert(IsValidDestination(dest)); + const auto script = GetScriptForDestination(dest); + wallet.MarkDirty(); + assert(!spk_man->HaveWatchOnly(script)); + if (!spk_man->AddWatchOnly(script, 0 /* nCreateTime */)) assert(false); + wallet.SetAddressBook(dest, /* label */ "", "receive"); +} +#endif // ENABLE_WALLET diff --git a/src/test/util/wallet.h b/src/test/util/wallet.h new file mode 100644 index 0000000000..565ef1756a --- /dev/null +++ b/src/test/util/wallet.h @@ -0,0 +1,24 @@ +// Copyright (c) 2019 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_UTIL_WALLET_H +#define BITCOIN_TEST_UTIL_WALLET_H + +#include <string> + +class CWallet; + +// Constants // + +extern const std::string ADDRESS_BCRT1_UNSPENDABLE; + +// RPC-like // + +/** Import the address to the wallet */ +void importaddress(CWallet& wallet, const std::string& address); +/** Returns a new address from the wallet */ +std::string getnewaddress(CWallet& w); + + +#endif // BITCOIN_TEST_UTIL_WALLET_H diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 9b8f5254de..86ea56ff36 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -8,7 +8,7 @@ #include <optional.h> #include <sync.h> #include <test/util/setup_common.h> -#include <test/util.h> +#include <test/util/str.h> #include <util/moneystr.h> #include <util/strencodings.h> #include <util/string.h> @@ -1177,6 +1177,11 @@ BOOST_AUTO_TEST_CASE(util_ParseMoney) // Parsing negative amounts must fail BOOST_CHECK(!ParseMoney("-1", ret)); + + // Parsing strings with embedded NUL characters should fail + BOOST_CHECK(!ParseMoney(std::string("\0-1", 3), ret)); + BOOST_CHECK(!ParseMoney(std::string("\01", 2), ret)); + BOOST_CHECK(!ParseMoney(std::string("1\0", 2), ret)); } BOOST_AUTO_TEST_CASE(util_IsHex) diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp index b7cf82906a..1fa48b325c 100644 --- a/src/test/validation_block_tests.cpp +++ b/src/test/validation_block_tests.cpp @@ -279,7 +279,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg) std::list<CTransactionRef> plTxnReplaced; for (const auto& tx : txs) { BOOST_REQUIRE(AcceptToMemoryPool( - ::mempool, + *m_node.mempool, state, tx, &plTxnReplaced, @@ -290,8 +290,8 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg) // Check that all txs are in the pool { - LOCK(::mempool.cs); - BOOST_CHECK_EQUAL(::mempool.mapTx.size(), txs.size()); + LOCK(m_node.mempool->cs); + BOOST_CHECK_EQUAL(m_node.mempool->mapTx.size(), txs.size()); } // Run a thread that simulates an RPC caller that is polling while @@ -301,8 +301,8 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg) // the transactions invalidated by the reorg, or none of them, and // not some intermediate amount. while (true) { - LOCK(::mempool.cs); - if (::mempool.mapTx.size() == 0) { + LOCK(m_node.mempool->cs); + if (m_node.mempool->mapTx.size() == 0) { // We are done with the reorg break; } @@ -311,7 +311,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg) // be atomic. So the caller assumes that the returned mempool // is consistent. That is, it has all txs that were there // before the reorg. - assert(::mempool.mapTx.size() == txs.size()); + assert(m_node.mempool->mapTx.size() == txs.size()); continue; } LOCK(cs_main); diff --git a/src/tinyformat.h b/src/tinyformat.h index 182f518a0b..be63f2d5d8 100644 --- a/src/tinyformat.h +++ b/src/tinyformat.h @@ -33,6 +33,7 @@ // // * Type safety and extensibility for user defined types. // * C99 printf() compatibility, to the extent possible using std::ostream +// * POSIX extension for positional arguments // * Simplicity and minimalism. A single header file to include and distribute // with your projects. // * Augment rather than replace the standard stream formatting mechanism @@ -42,7 +43,7 @@ // Main interface example usage // ---------------------------- // -// To print a date to std::cout: +// To print a date to std::cout for American usage: // // std::string weekday = "Wednesday"; // const char* month = "July"; @@ -52,6 +53,14 @@ // // tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min); // +// POSIX extension for positional arguments is available. +// The ability to rearrange formatting arguments is an important feature +// for localization because the word order may vary in different languages. +// +// Previous example for German usage. Arguments are reordered: +// +// tfm::printf("%1$s, %3$d. %2$s, %4$d:%5$.2d\n", weekday, month, day, hour, min); +// // The strange types here emphasize the type safety of the interface; it is // possible to print a std::string using the "%s" conversion, and a // size_t using the "%d" conversion. A similar result could be achieved @@ -133,12 +142,17 @@ namespace tfm = tinyformat; //------------------------------------------------------------------------------ // Implementation details. #include <algorithm> -#include <cassert> #include <iostream> #include <sstream> -#include <stdexcept> +#include <stdexcept> // Added for Bitcoin Core + +#ifndef TINYFORMAT_ASSERT +# include <cassert> +# define TINYFORMAT_ASSERT(cond) assert(cond) +#endif #ifndef TINYFORMAT_ERROR +# include <cassert> # define TINYFORMAT_ERROR(reason) assert(0 && reason) #endif @@ -149,13 +163,13 @@ namespace tfm = tinyformat; #endif #if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201 -// std::showpos is broken on old libstdc++ as provided with OSX. See +// std::showpos is broken on old libstdc++ as provided with macOS. See // http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html # define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND #endif #ifdef __APPLE__ -// Workaround OSX linker warning: Xcode uses different default symbol +// Workaround macOS linker warning: Xcode uses different default symbol // visibilities for static libs vs executables (see issue #25) # define TINYFORMAT_HIDDEN __attribute__((visibility("hidden"))) #else @@ -164,6 +178,7 @@ namespace tfm = tinyformat; namespace tinyformat { +// Added for Bitcoin Core class format_error: public std::runtime_error { public: @@ -218,7 +233,7 @@ template<int n> struct is_wchar<wchar_t[n]> {}; template<typename T, typename fmtT, bool convertible = is_convertible<T, fmtT>::value> struct formatValueAsType { - static void invoke(std::ostream& /*out*/, const T& /*value*/) { assert(0); } + static void invoke(std::ostream& /*out*/, const T& /*value*/) { TINYFORMAT_ASSERT(0); } }; // Specialized version for types that can actually be converted to fmtT, as // indicated by the "convertible" template parameter. @@ -240,8 +255,7 @@ struct formatZeroIntegerWorkaround<T,true> { static bool invoke(std::ostream& out, const T& value) { - if (static_cast<int>(value) == 0 && out.flags() & std::ios::showpos) - { + if (static_cast<int>(value) == 0 && out.flags() & std::ios::showpos) { out << "+0"; return true; } @@ -282,7 +296,7 @@ inline void formatTruncated(std::ostream& out, const T& value, int ntrunc) inline void formatTruncated(std::ostream& out, type* value, int ntrunc) \ { \ std::streamsize len = 0; \ - while(len < ntrunc && value[len] != 0) \ + while (len < ntrunc && value[len] != 0) \ ++len; \ out.write(value, len); \ } @@ -328,15 +342,14 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, // could otherwise lead to a crash when printing a dangling (const char*). const bool canConvertToChar = detail::is_convertible<T,char>::value; const bool canConvertToVoidPtr = detail::is_convertible<T, const void*>::value; - if(canConvertToChar && *(fmtEnd-1) == 'c') + if (canConvertToChar && *(fmtEnd-1) == 'c') detail::formatValueAsType<T, char>::invoke(out, value); - else if(canConvertToVoidPtr && *(fmtEnd-1) == 'p') + else if (canConvertToVoidPtr && *(fmtEnd-1) == 'p') detail::formatValueAsType<T, const void*>::invoke(out, value); #ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND - else if(detail::formatZeroIntegerWorkaround<T>::invoke(out, value)) /**/; + else if (detail::formatZeroIntegerWorkaround<T>::invoke(out, value)) /**/; #endif - else if(ntrunc >= 0) - { + else if (ntrunc >= 0) { // Take care not to overread C strings in truncating conversions like // "%.4s" where at most 4 characters may be read. detail::formatTruncated(out, value, ntrunc); @@ -351,8 +364,7 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \ const char* fmtEnd, int /**/, charType value) \ { \ - switch(*(fmtEnd-1)) \ - { \ + switch (*(fmtEnd-1)) { \ case 'u': case 'd': case 'i': case 'o': case 'X': case 'x': \ out << static_cast<int>(value); break; \ default: \ @@ -490,19 +502,19 @@ namespace detail { // Type-opaque holder for an argument to format(), with associated actions on // the type held as explicit function pointers. This allows FormatArg's for -// each argument to be allocated as a homogenous array inside FormatList +// each argument to be allocated as a homogeneous array inside FormatList // whereas a naive implementation based on inheritance does not. class FormatArg { public: FormatArg() - : m_value(nullptr), - m_formatImpl(nullptr), - m_toIntImpl(nullptr) - { } + : m_value(NULL), + m_formatImpl(NULL), + m_toIntImpl(NULL) + { } template<typename T> - explicit FormatArg(const T& value) + FormatArg(const T& value) : m_value(static_cast<const void*>(&value)), m_formatImpl(&formatImpl<T>), m_toIntImpl(&toIntImpl<T>) @@ -511,15 +523,15 @@ class FormatArg void format(std::ostream& out, const char* fmtBegin, const char* fmtEnd, int ntrunc) const { - assert(m_value); - assert(m_formatImpl); + TINYFORMAT_ASSERT(m_value); + TINYFORMAT_ASSERT(m_formatImpl); m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value); } int toInt() const { - assert(m_value); - assert(m_toIntImpl); + TINYFORMAT_ASSERT(m_value); + TINYFORMAT_ASSERT(m_toIntImpl); return m_toIntImpl(m_value); } @@ -549,36 +561,68 @@ class FormatArg inline int parseIntAndAdvance(const char*& c) { int i = 0; - for(;*c >= '0' && *c <= '9'; ++c) + for (;*c >= '0' && *c <= '9'; ++c) i = 10*i + (*c - '0'); return i; } -// Print literal part of format string and return next format spec -// position. +// Parse width or precision `n` from format string pointer `c`, and advance it +// to the next character. If an indirection is requested with `*`, the argument +// is read from `args[argIndex]` and `argIndex` is incremented (or read +// from `args[n]` in positional mode). Returns true if one or more +// characters were read. +inline bool parseWidthOrPrecision(int& n, const char*& c, bool positionalMode, + const detail::FormatArg* args, + int& argIndex, int numArgs) +{ + if (*c >= '0' && *c <= '9') { + n = parseIntAndAdvance(c); + } + else if (*c == '*') { + ++c; + n = 0; + if (positionalMode) { + int pos = parseIntAndAdvance(c) - 1; + if (*c != '$') + TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one"); + if (pos >= 0 && pos < numArgs) + n = args[pos].toInt(); + else + TINYFORMAT_ERROR("tinyformat: Positional argument out of range"); + ++c; + } + else { + if (argIndex < numArgs) + n = args[argIndex++].toInt(); + else + TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width or precision"); + } + } + else { + return false; + } + return true; +} + +// Print literal part of format string and return next format spec position. // -// Skips over any occurrences of '%%', printing a literal '%' to the -// output. The position of the first % character of the next -// nontrivial format spec is returned, or the end of string. +// Skips over any occurrences of '%%', printing a literal '%' to the output. +// The position of the first % character of the next nontrivial format spec is +// returned, or the end of string. inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt) { const char* c = fmt; - for(;; ++c) - { - switch(*c) - { - case '\0': - out.write(fmt, c - fmt); + for (;; ++c) { + if (*c == '\0') { + out.write(fmt, c - fmt); + return c; + } + else if (*c == '%') { + out.write(fmt, c - fmt); + if (*(c+1) != '%') return c; - case '%': - out.write(fmt, c - fmt); - if(*(c+1) != '%') - return c; - // for "%%", tack trailing % onto next literal section. - fmt = ++c; - break; - default: - break; + // for "%%", tack trailing % onto next literal section. + fmt = ++c; } } } @@ -587,23 +631,43 @@ inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt) // Parse a format string and set the stream state accordingly. // // The format mini-language recognized here is meant to be the one from C99, -// with the form "%[flags][width][.precision][length]type". +// with the form "%[flags][width][.precision][length]type" with POSIX +// positional arguments extension. +// +// POSIX positional arguments extension: +// Conversions can be applied to the nth argument after the format in +// the argument list, rather than to the next unused argument. In this case, +// the conversion specifier character % (see below) is replaced by the sequence +// "%n$", where n is a decimal integer in the range [1,{NL_ARGMAX}], +// giving the position of the argument in the argument list. This feature +// provides for the definition of format strings that select arguments +// in an order appropriate to specific languages. +// +// The format can contain either numbered argument conversion specifications +// (that is, "%n$" and "*m$"), or unnumbered argument conversion specifications +// (that is, % and * ), but not both. The only exception to this is that %% +// can be mixed with the "%n$" form. The results of mixing numbered and +// unnumbered argument specifications in a format string are undefined. +// When numbered argument specifications are used, specifying the Nth argument +// requires that all the leading arguments, from the first to the (N-1)th, +// are specified in the format string. +// +// In format strings containing the "%n$" form of conversion specification, +// numbered arguments in the argument list can be referenced from the format +// string as many times as required. // // Formatting options which can't be natively represented using the ostream // state are returned in spacePadPositive (for space padded positive numbers) // and ntrunc (for truncating conversions). argIndex is incremented if // necessary to pull out variable width and precision. The function returns a // pointer to the character after the end of the current format spec. -inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositive, +inline const char* streamStateFromFormat(std::ostream& out, bool& positionalMode, + bool& spacePadPositive, int& ntrunc, const char* fmtStart, - const detail::FormatArg* formatters, - int& argIndex, int numFormatters) + const detail::FormatArg* args, + int& argIndex, int numArgs) { - if(*fmtStart != '%') - { - TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string"); - return fmtStart; - } + TINYFORMAT_ASSERT(*fmtStart == '%'); // Reset stream state to defaults. out.width(0); out.precision(6); @@ -616,100 +680,113 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi bool widthSet = false; int widthExtra = 0; const char* c = fmtStart + 1; - // 1) Parse flags - for(;; ++c) - { - switch(*c) - { - case '#': - out.setf(std::ios::showpoint | std::ios::showbase); - continue; - case '0': - // overridden by left alignment ('-' flag) - if(!(out.flags() & std::ios::left)) - { - // Use internal padding so that numeric values are - // formatted correctly, eg -00010 rather than 000-10 - out.fill('0'); - out.setf(std::ios::internal, std::ios::adjustfield); - } - continue; - case '-': - out.fill(' '); - out.setf(std::ios::left, std::ios::adjustfield); - continue; - case ' ': - // overridden by show positive sign, '+' flag. - if(!(out.flags() & std::ios::showpos)) - spacePadPositive = true; - continue; - case '+': - out.setf(std::ios::showpos); - spacePadPositive = false; - widthExtra = 1; - continue; - default: - break; + + // 1) Parse an argument index (if followed by '$') or a width possibly + // preceded with '0' flag. + if (*c >= '0' && *c <= '9') { + const char tmpc = *c; + int value = parseIntAndAdvance(c); + if (*c == '$') { + // value is an argument index + if (value > 0 && value <= numArgs) + argIndex = value - 1; + else + TINYFORMAT_ERROR("tinyformat: Positional argument out of range"); + ++c; + positionalMode = true; + } + else if (positionalMode) { + TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one"); + } + else { + if (tmpc == '0') { + // Use internal padding so that numeric values are + // formatted correctly, eg -00010 rather than 000-10 + out.fill('0'); + out.setf(std::ios::internal, std::ios::adjustfield); + } + if (value != 0) { + // Nonzero value means that we parsed width. + widthSet = true; + out.width(value); + } } - break; } - // 2) Parse width - if(*c >= '0' && *c <= '9') - { - widthSet = true; - out.width(parseIntAndAdvance(c)); + else if (positionalMode) { + TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one"); } - if(*c == '*') - { - widthSet = true; + // 2) Parse flags and width if we did not do it in previous step. + if (!widthSet) { + // Parse flags + for (;; ++c) { + switch (*c) { + case '#': + out.setf(std::ios::showpoint | std::ios::showbase); + continue; + case '0': + // overridden by left alignment ('-' flag) + if (!(out.flags() & std::ios::left)) { + // Use internal padding so that numeric values are + // formatted correctly, eg -00010 rather than 000-10 + out.fill('0'); + out.setf(std::ios::internal, std::ios::adjustfield); + } + continue; + case '-': + out.fill(' '); + out.setf(std::ios::left, std::ios::adjustfield); + continue; + case ' ': + // overridden by show positive sign, '+' flag. + if (!(out.flags() & std::ios::showpos)) + spacePadPositive = true; + continue; + case '+': + out.setf(std::ios::showpos); + spacePadPositive = false; + widthExtra = 1; + continue; + default: + break; + } + break; + } + // Parse width int width = 0; - if(argIndex < numFormatters) - width = formatters[argIndex++].toInt(); - else - TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width"); - if(width < 0) - { - // negative widths correspond to '-' flag set - out.fill(' '); - out.setf(std::ios::left, std::ios::adjustfield); - width = -width; + widthSet = parseWidthOrPrecision(width, c, positionalMode, + args, argIndex, numArgs); + if (widthSet) { + if (width < 0) { + // negative widths correspond to '-' flag set + out.fill(' '); + out.setf(std::ios::left, std::ios::adjustfield); + width = -width; + } + out.width(width); } - out.width(width); - ++c; } // 3) Parse precision - if(*c == '.') - { + if (*c == '.') { ++c; int precision = 0; - if(*c == '*') - { - ++c; - if(argIndex < numFormatters) - precision = formatters[argIndex++].toInt(); - else - TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable precision"); - } - else - { - if(*c >= '0' && *c <= '9') - precision = parseIntAndAdvance(c); - else if(*c == '-') // negative precisions ignored, treated as zero. - parseIntAndAdvance(++c); - } - out.precision(precision); - precisionSet = true; + parseWidthOrPrecision(precision, c, positionalMode, + args, argIndex, numArgs); + // Presence of `.` indicates precision set, unless the inferred value + // was negative in which case the default is used. + precisionSet = precision >= 0; + if (precisionSet) + out.precision(precision); } // 4) Ignore any C99 length modifier - while(*c == 'l' || *c == 'h' || *c == 'L' || - *c == 'j' || *c == 'z' || *c == 't') + while (*c == 'l' || *c == 'h' || *c == 'L' || + *c == 'j' || *c == 'z' || *c == 't') { ++c; + } // 5) We're up to the conversion specifier character. // Set stream flags based on conversion specifier (thanks to the // boost::format class for forging the way here). bool intConversion = false; - switch(*c) - { + switch (*c) { case 'u': case 'd': case 'i': out.setf(std::ios::dec, std::ios::basefield); intConversion = true; @@ -738,6 +815,18 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi case 'f': out.setf(std::ios::fixed, std::ios::floatfield); break; + case 'A': + out.setf(std::ios::uppercase); + // Falls through + case 'a': +# ifdef _MSC_VER + // Workaround https://developercommunity.visualstudio.com/content/problem/520472/hexfloat-stream-output-does-not-ignore-precision-a.html + // by always setting maximum precision on MSVC to avoid precision + // loss for doubles. + out.precision(13); +# endif + out.setf(std::ios::fixed | std::ios::scientific, std::ios::floatfield); + break; case 'G': out.setf(std::ios::uppercase); // Falls through @@ -746,17 +835,13 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi // As in boost::format, let stream decide float format. out.flags(out.flags() & ~std::ios::floatfield); break; - case 'a': case 'A': - TINYFORMAT_ERROR("tinyformat: the %a and %A conversion specs " - "are not supported"); - break; case 'c': // Handled as special case inside formatValue() break; case 's': - if(precisionSet) + if (precisionSet) ntrunc = static_cast<int>(out.precision()); - // Make %s print booleans as "true" and "false" + // Make %s print Booleans as "true" and "false" out.setf(std::ios::boolalpha); break; case 'n': @@ -770,8 +855,7 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi default: break; } - if(intConversion && precisionSet && !widthSet) - { + if (intConversion && precisionSet && !widthSet) { // "precision" for integers gives the minimum number of digits (to be // padded with zeros on the left). This isn't really supported by the // iostreams, but we can approximately simulate it with the width if @@ -786,8 +870,8 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi //------------------------------------------------------------------------------ inline void formatImpl(std::ostream& out, const char* fmt, - const detail::FormatArg* formatters, - int numFormatters) + const detail::FormatArg* args, + int numArgs) { // Saved stream state std::streamsize origWidth = out.width(); @@ -795,26 +879,34 @@ inline void formatImpl(std::ostream& out, const char* fmt, std::ios::fmtflags origFlags = out.flags(); char origFill = out.fill(); - for (int argIndex = 0; argIndex < numFormatters; ++argIndex) - { - // Parse the format string + // "Positional mode" means all format specs should be of the form "%n$..." + // with `n` an integer. We detect this in `streamStateFromFormat`. + bool positionalMode = false; + int argIndex = 0; + while (true) { fmt = printFormatStringLiteral(out, fmt); + if (*fmt == '\0') { + if (!positionalMode && argIndex < numArgs) { + TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string"); + } + break; + } bool spacePadPositive = false; int ntrunc = -1; - const char* fmtEnd = streamStateFromFormat(out, spacePadPositive, ntrunc, fmt, - formatters, argIndex, numFormatters); - if (argIndex >= numFormatters) - { - // Check args remain after reading any variable width/precision - TINYFORMAT_ERROR("tinyformat: Not enough format arguments"); + const char* fmtEnd = streamStateFromFormat(out, positionalMode, spacePadPositive, ntrunc, fmt, + args, argIndex, numArgs); + // NB: argIndex may be incremented by reading variable width/precision + // in `streamStateFromFormat`, so do the bounds check here. + if (argIndex >= numArgs) { + TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string"); return; } - const FormatArg& arg = formatters[argIndex]; + const FormatArg& arg = args[argIndex]; // Format the arg into the stream. - if(!spacePadPositive) + if (!spacePadPositive) { arg.format(out, fmt, fmtEnd, ntrunc); - else - { + } + else { // The following is a special case with no direct correspondence // between stream formatting and the printf() behaviour. Simulate // it crudely by formatting into a temporary string stream and @@ -824,18 +916,17 @@ inline void formatImpl(std::ostream& out, const char* fmt, tmpStream.setf(std::ios::showpos); arg.format(tmpStream, fmt, fmtEnd, ntrunc); std::string result = tmpStream.str(); // allocates... yuck. - for(size_t i = 0, iend = result.size(); i < iend; ++i) - if(result[i] == '+') result[i] = ' '; + for (size_t i = 0, iend = result.size(); i < iend; ++i) { + if (result[i] == '+') + result[i] = ' '; + } out << result; } + if (!positionalMode) + ++argIndex; fmt = fmtEnd; } - // Print remaining part of format string. - fmt = printFormatStringLiteral(out, fmt); - if(*fmt != '\0') - TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string"); - // Restore stream state out.width(origWidth); out.precision(origPrecision); @@ -855,14 +946,14 @@ inline void formatImpl(std::ostream& out, const char* fmt, class FormatList { public: - FormatList(detail::FormatArg* formatters, int N) - : m_formatters(formatters), m_N(N) { } + FormatList(detail::FormatArg* args, int N) + : m_args(args), m_N(N) { } friend void vformat(std::ostream& out, const char* fmt, const FormatList& list); private: - const detail::FormatArg* m_formatters; + const detail::FormatArg* m_args; int m_N; }; @@ -879,29 +970,33 @@ class FormatListN : public FormatList public: #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES template<typename... Args> - explicit FormatListN(const Args&... args) + FormatListN(const Args&... args) : FormatList(&m_formatterStore[0], N), m_formatterStore { FormatArg(args)... } { static_assert(sizeof...(args) == N, "Number of args must be N"); } #else // C++98 version void init(int) {} -# define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \ - \ - template<TINYFORMAT_ARGTYPES(n)> \ - explicit FormatListN(TINYFORMAT_VARARGS(n)) \ - : FormatList(&m_formatterStore[0], n) \ - { assert(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \ - \ - template<TINYFORMAT_ARGTYPES(n)> \ - void init(int i, TINYFORMAT_VARARGS(n)) \ - { \ - m_formatterStore[i] = FormatArg(v1); \ - init(i+1 TINYFORMAT_PASSARGS_TAIL(n)); \ +# define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \ + \ + template<TINYFORMAT_ARGTYPES(n)> \ + FormatListN(TINYFORMAT_VARARGS(n)) \ + : FormatList(&m_formatterStore[0], n) \ + { TINYFORMAT_ASSERT(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \ + \ + template<TINYFORMAT_ARGTYPES(n)> \ + void init(int i, TINYFORMAT_VARARGS(n)) \ + { \ + m_formatterStore[i] = FormatArg(v1); \ + init(i+1 TINYFORMAT_PASSARGS_TAIL(n)); \ } TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR) # undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR #endif + FormatListN(const FormatListN& other) + : FormatList(&m_formatterStore[0], N) + { std::copy(&other.m_formatterStore[0], &other.m_formatterStore[N], + &m_formatterStore[0]); } private: FormatArg m_formatterStore[N]; @@ -956,7 +1051,7 @@ TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST) /// list of format arguments is held in a single function argument. inline void vformat(std::ostream& out, const char* fmt, FormatListRef list) { - detail::formatImpl(out, fmt, list.m_formatters, list.m_N); + detail::formatImpl(out, fmt, list.m_args, list.m_N); } @@ -993,6 +1088,7 @@ void printfln(const char* fmt, const Args&... args) std::cout << '\n'; } + #else // C++98 version inline void format(std::ostream& out, const char* fmt) @@ -1063,6 +1159,7 @@ std::string format(const std::string &fmt, const Args&... args) } // namespace tinyformat +// Added for Bitcoin Core: /** Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for details) */ #define strprintf tfm::format diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 08f935c24f..3c967a04c0 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -17,6 +17,7 @@ #include <util/system.h> #include <util/moneystr.h> #include <util/time.h> +#include <validationinterface.h> CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee, int64_t _nTime, unsigned int _entryHeight, @@ -403,7 +404,12 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason) { - NotifyEntryRemoved(it->GetSharedTx(), reason); + CTransactionRef ptx = it->GetSharedTx(); + NotifyEntryRemoved(ptx, reason); + if (reason != MemPoolRemovalReason::BLOCK && reason != MemPoolRemovalReason::CONFLICT) { + GetMainSignals().TransactionRemovedFromMempool(ptx); + } + const uint256 hash = it->GetTx().GetHash(); for (const CTxIn& txin : it->GetTx().vin) mapNextTx.erase(txin.prevout); diff --git a/src/util/moneystr.cpp b/src/util/moneystr.cpp index ba5a12e58c..3e75a2e3e9 100644 --- a/src/util/moneystr.cpp +++ b/src/util/moneystr.cpp @@ -7,6 +7,7 @@ #include <tinyformat.h> #include <util/strencodings.h> +#include <util/string.h> std::string FormatMoney(const CAmount& n) { @@ -32,6 +33,9 @@ std::string FormatMoney(const CAmount& n) bool ParseMoney(const std::string& str, CAmount& nRet) { + if (!ValidAsCString(str)) { + return false; + } return ParseMoney(str.c_str(), nRet); } diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index 46042f5634..31719cd975 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -4,6 +4,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <util/strencodings.h> +#include <util/string.h> #include <tinyformat.h> @@ -190,6 +191,12 @@ std::vector<unsigned char> DecodeBase64(const char* p, bool* pf_invalid) std::string DecodeBase64(const std::string& str, bool* pf_invalid) { + if (!ValidAsCString(str)) { + if (pf_invalid) { + *pf_invalid = true; + } + return {}; + } std::vector<unsigned char> vchRet = DecodeBase64(str.c_str(), pf_invalid); return std::string((const char*)vchRet.data(), vchRet.size()); } @@ -259,6 +266,12 @@ std::vector<unsigned char> DecodeBase32(const char* p, bool* pf_invalid) std::string DecodeBase32(const std::string& str, bool* pf_invalid) { + if (!ValidAsCString(str)) { + if (pf_invalid) { + *pf_invalid = true; + } + return {}; + } std::vector<unsigned char> vchRet = DecodeBase32(str.c_str(), pf_invalid); return std::string((const char*)vchRet.data(), vchRet.size()); } @@ -269,7 +282,7 @@ NODISCARD static bool ParsePrechecks(const std::string& str) return false; if (str.size() >= 1 && (IsSpace(str[0]) || IsSpace(str[str.size()-1]))) // No padding allowed return false; - if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed + if (!ValidAsCString(str)) // No embedded NUL characters allowed return false; return true; } diff --git a/src/util/string.h b/src/util/string.h index 76a83a4949..3db8fc8b98 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -5,9 +5,22 @@ #ifndef BITCOIN_UTIL_STRING_H #define BITCOIN_UTIL_STRING_H +#include <attributes.h> + +#include <cstring> #include <string> #include <vector> +NODISCARD inline std::string TrimString(const std::string& str, const std::string& pattern = " \f\n\r\t\v") +{ + std::string::size_type front = str.find_first_not_of(pattern); + if (front == std::string::npos) { + return std::string(); + } + std::string::size_type end = str.find_last_not_of(pattern); + return str.substr(front, end - front + 1); +} + /** * Join a list of items * @@ -31,4 +44,12 @@ inline std::string Join(const std::vector<std::string>& list, const std::string& return Join(list, separator, [](const std::string& i) { return i; }); } +/** + * Check if a string does not contain any embedded NUL (\0) characters + */ +NODISCARD inline bool ValidAsCString(const std::string& str) noexcept +{ + return str.size() == strlen(str.c_str()); +} + #endif // BITCOIN_UTIL_STRENCODINGS_H diff --git a/src/util/system.cpp b/src/util/system.cpp index b6a026b02a..d99a87a9f2 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -7,6 +7,7 @@ #include <chainparamsbase.h> #include <util/strencodings.h> +#include <util/string.h> #include <util/translation.h> @@ -659,16 +660,6 @@ fs::path GetConfigFile(const std::string& confPath) return AbsPathForConfigVal(fs::path(confPath), false); } -static std::string TrimString(const std::string& str, const std::string& pattern) -{ - std::string::size_type front = str.find_first_not_of(pattern); - if (front == std::string::npos) { - return std::string(); - } - std::string::size_type end = str.find_last_not_of(pattern); - return str.substr(front, end - front + 1); -} - static bool GetConfigOptions(std::istream& stream, const std::string& filepath, std::string& error, std::vector<std::pair<std::string, std::string>>& options, std::list<SectionInfo>& sections) { std::string str, prefix; @@ -1124,17 +1115,13 @@ fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific) return fs::absolute(path, GetDataDir(net_specific)); } -int ScheduleBatchPriority() +void ScheduleBatchPriority() { #ifdef SCHED_BATCH const static sched_param param{}; - if (int ret = pthread_setschedparam(pthread_self(), SCHED_BATCH, ¶m)) { + if (pthread_setschedparam(pthread_self(), SCHED_BATCH, ¶m) != 0) { LogPrintf("Failed to pthread_setschedparam: %s\n", strerror(errno)); - return ret; } - return 0; -#else - return 1; #endif } diff --git a/src/util/system.h b/src/util/system.h index 633560d70c..394adb9555 100644 --- a/src/util/system.h +++ b/src/util/system.h @@ -386,10 +386,8 @@ std::string CopyrightHolders(const std::string& strPrefix); * On platforms that support it, tell the kernel the calling thread is * CPU-intensive and non-interactive. See SCHED_BATCH in sched(7) for details. * - * @return The return value of sched_setschedule(), or 1 on systems without - * sched_setschedule(). */ -int ScheduleBatchPriority(); +void ScheduleBatchPriority(); namespace util { diff --git a/src/validation.cpp b/src/validation.cpp index cc7687ae05..8f5d333170 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -129,9 +129,6 @@ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); CBlockPolicyEstimator feeEstimator; CTxMemPool mempool(&feeEstimator); -/** Constant stuff for coinbase transactions we create: */ -CScript COINBASE_FLAGS; - // Internal stuff namespace { CBlockIndex* pindexBestInvalid = nullptr; @@ -5089,7 +5086,7 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin fTxTotal = pindex->nChainTx + (nNow - pindex->GetBlockTime()) * data.dTxRate; } - return pindex->nChainTx / fTxTotal; + return std::min<double>(pindex->nChainTx / fTxTotal, 1.0); } class CMainCleanup diff --git a/src/validation.h b/src/validation.h index 31721cfbf7..54f97e7213 100644 --- a/src/validation.h +++ b/src/validation.h @@ -23,7 +23,6 @@ #include <versionbits.h> #include <serialize.h> -#include <algorithm> #include <atomic> #include <map> #include <memory> @@ -137,7 +136,6 @@ struct BlockHasher size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); } }; -extern CScript COINBASE_FLAGS; extern CCriticalSection cs_main; extern CBlockPolicyEstimator feeEstimator; extern CTxMemPool mempool; diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 663308bae9..6c0f4d5edd 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -7,9 +7,9 @@ #include <primitives/block.h> #include <scheduler.h> -#include <txmempool.h> #include <future> +#include <unordered_map> #include <utility> #include <boost/signals2/signal.hpp> @@ -46,11 +46,6 @@ struct MainSignalsInstance { static CMainSignals g_signals; -// This map has to a separate global instead of a member of MainSignalsInstance, -// because RegisterWithMempoolSignals is currently called before RegisterBackgroundSignalScheduler, -// so MainSignalsInstance hasn't been created yet. -static std::unordered_map<CTxMemPool*, boost::signals2::scoped_connection> g_connNotifyEntryRemoved; - void CMainSignals::RegisterBackgroundSignalScheduler(CScheduler& scheduler) { assert(!m_internals); m_internals.reset(new MainSignalsInstance(&scheduler)); @@ -71,17 +66,6 @@ size_t CMainSignals::CallbacksPending() { return m_internals->m_schedulerClient.CallbacksPending(); } -void CMainSignals::RegisterWithMempoolSignals(CTxMemPool& pool) { - g_connNotifyEntryRemoved.emplace(std::piecewise_construct, - std::forward_as_tuple(&pool), - std::forward_as_tuple(pool.NotifyEntryRemoved.connect(std::bind(&CMainSignals::MempoolEntryRemoved, this, std::placeholders::_1, std::placeholders::_2))) - ); -} - -void CMainSignals::UnregisterWithMempoolSignals(CTxMemPool& pool) { - g_connNotifyEntryRemoved.erase(&pool); -} - CMainSignals& GetMainSignals() { return g_signals; @@ -126,13 +110,6 @@ void SyncWithValidationInterfaceQueue() { promise.get_future().wait(); } -void CMainSignals::MempoolEntryRemoved(CTransactionRef ptx, MemPoolRemovalReason reason) { - if (reason != MemPoolRemovalReason::BLOCK && reason != MemPoolRemovalReason::CONFLICT) { - m_internals->m_schedulerClient.AddToProcessQueue([ptx, this] { - m_internals->TransactionRemovedFromMempool(ptx); - }); - } -} void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { // Dependencies exist that require UpdatedBlockTip events to be delivered in the order in which @@ -150,6 +127,12 @@ void CMainSignals::TransactionAddedToMempool(const CTransactionRef &ptx) { }); } +void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef &ptx) { + m_internals->m_schedulerClient.AddToProcessQueue([ptx, this] { + m_internals->TransactionRemovedFromMempool(ptx); + }); +} + void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>>& pvtxConflicted) { m_internals->m_schedulerClient.AddToProcessQueue([pblock, pindex, pvtxConflicted, this] { m_internals->BlockConnected(pblock, pindex, *pvtxConflicted); diff --git a/src/validationinterface.h b/src/validationinterface.h index 6a8059a6a0..63aad8661f 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -21,8 +21,6 @@ class CConnman; class CValidationInterface; class uint256; class CScheduler; -class CTxMemPool; -enum class MemPoolRemovalReason; // These functions dispatch to one or all registered wallets @@ -158,8 +156,6 @@ private: friend void ::UnregisterAllValidationInterfaces(); friend void ::CallFunctionInValidationInterfaceQueue(std::function<void ()> func); - void MempoolEntryRemoved(CTransactionRef tx, MemPoolRemovalReason reason); - public: /** Register a CScheduler to give callbacks which should run in the background (may only be called once) */ void RegisterBackgroundSignalScheduler(CScheduler& scheduler); @@ -170,13 +166,10 @@ public: size_t CallbacksPending(); - /** Register with mempool to call TransactionRemovedFromMempool callbacks */ - void RegisterWithMempoolSignals(CTxMemPool& pool); - /** Unregister with mempool */ - void UnregisterWithMempoolSignals(CTxMemPool& pool); void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload); void TransactionAddedToMempool(const CTransactionRef &); + void TransactionRemovedFromMempool(const CTransactionRef &); void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>> &); void BlockDisconnected(const std::shared_ptr<const CBlock> &, const CBlockIndex* pindex); void ChainStateFlushed(const CBlockLocator &); diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h index fca4b75c45..2893d0ab3d 100644 --- a/src/wallet/coincontrol.h +++ b/src/wallet/coincontrol.h @@ -6,14 +6,18 @@ #define BITCOIN_WALLET_COINCONTROL_H #include <optional.h> +#include <outputtype.h> #include <policy/feerate.h> #include <policy/fees.h> #include <primitives/transaction.h> -#include <wallet/wallet.h> +#include <script/standard.h> const int DEFAULT_MIN_DEPTH = 0; const int DEFAULT_MAX_DEPTH = 9999999; +//! Default for -avoidpartialspends +static constexpr bool DEFAULT_AVOIDPARTIALSPENDS = false; + /** Coin Control Features. */ class CCoinControl { diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 8f0b495ac4..36588eb7d1 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -108,12 +108,11 @@ static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wt return feebumper::Result::OK; } -static CFeeRate EstimateFeeRate(const CWallet& wallet, const CWalletTx& wtx, CCoinControl& coin_control, CAmount& old_fee) +static CFeeRate EstimateFeeRate(const CWallet& wallet, const CWalletTx& wtx, const CAmount old_fee, CCoinControl& coin_control) { // Get the fee rate of the original transaction. This is calculated from // the tx fee/vsize, so it may have been rounded down. Add 1 satoshi to the // result. - old_fee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut(); int64_t txSize = GetVirtualTransactionSize(*(wtx.tx)); CFeeRate feerate(old_fee, txSize); feerate += CFeeRate(1); @@ -309,6 +308,8 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo } } + old_fee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut(); + if (coin_control.m_feerate) { // The user provided a feeRate argument. // We calculate this here to avoid compiler warning on the cs_wallet lock @@ -319,7 +320,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo } } else { // The user did not provide a feeRate argument - new_coin_control.m_feerate = EstimateFeeRate(wallet, wtx, new_coin_control, old_fee); + new_coin_control.m_feerate = EstimateFeeRate(wallet, wtx, old_fee, new_coin_control); } // Fill in required inputs we are double-spending(all of them) diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index a8b3df1f2e..dd0d2ffbd7 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -8,9 +8,11 @@ #include <net.h> #include <node/context.h> #include <outputtype.h> +#include <ui_interface.h> #include <util/moneystr.h> #include <util/system.h> #include <util/translation.h> +#include <wallet/coincontrol.h> #include <wallet/wallet.h> #include <walletinitinterface.h> @@ -58,7 +60,7 @@ void WalletInit::AddWalletOptions() const gArgs.AddArg("-upgradewallet", "Upgrade wallet to latest format on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); gArgs.AddArg("-wallet=<path>", "Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET); gArgs.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); - gArgs.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); + gArgs.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET); #if HAVE_SYSTEM gArgs.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); #endif diff --git a/src/wallet/psbtwallet.cpp b/src/wallet/psbtwallet.cpp index aa13cacca4..96c1ad8d3f 100644 --- a/src/wallet/psbtwallet.cpp +++ b/src/wallet/psbtwallet.cpp @@ -39,12 +39,35 @@ TransactionError FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& ps return TransactionError::SIGHASH_MISMATCH; } - complete &= SignPSBTInput(HidingSigningProvider(pwallet->GetSigningProvider(), !sign, !bip32derivs), psbtx, i, sighash_type); + // Get the scriptPubKey to know which SigningProvider to use + CScript script; + if (!input.witness_utxo.IsNull()) { + script = input.witness_utxo.scriptPubKey; + } else if (input.non_witness_utxo) { + script = input.non_witness_utxo->vout[txin.prevout.n].scriptPubKey; + } else { + // There's no UTXO so we can just skip this now + complete = false; + continue; + } + SignatureData sigdata; + input.FillSignatureData(sigdata); + const SigningProvider* provider = pwallet->GetSigningProvider(script, sigdata); + if (!provider) { + complete = false; + continue; + } + + complete &= SignPSBTInput(HidingSigningProvider(provider, !sign, !bip32derivs), psbtx, i, sighash_type); } // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) { - UpdatePSBTOutput(HidingSigningProvider(pwallet->GetSigningProvider(), true, !bip32derivs), psbtx, i); + const CTxOut& out = psbtx.tx->vout.at(i); + const SigningProvider* provider = pwallet->GetSigningProvider(out.scriptPubKey); + if (provider) { + UpdatePSBTOutput(HidingSigningProvider(provider, true, !bip32derivs), psbtx, i); + } } return TransactionError::OK; diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index d95481500d..633ac1b16d 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -1286,7 +1286,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) {"scriptPubKey", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of scriptPubKey (string for script, json for address). Should not be provided if using a descriptor", /* oneline_description */ "", {"\"<script>\" | { \"address\":\"<address>\" }", "string / json"} }, - {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Creation time of the key in seconds since epoch (Jan 1 1970 GMT),\n" + {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, "Creation time of the key expressed in " + UNIX_EPOCH_TIME + ",\n" " or the string \"now\" to substitute the current synced blockchain time. The timestamp of the oldest\n" " key will determine how far back blockchain rescans need to begin for missing wallet transactions.\n" " \"now\" can be specified to bypass scanning, for keys which are known to never have been used, and\n" diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 00742aed7a..63cbe02b17 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -560,7 +560,11 @@ static UniValue signmessage(const JSONRPCRequest& request) throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); } - const SigningProvider* provider = pwallet->GetSigningProvider(); + CScript script_pub_key = GetScriptForDestination(*pkhash); + const SigningProvider* provider = pwallet->GetSigningProvider(script_pub_key); + if (!provider) { + throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); + } CKey key; CKeyID keyID(*pkhash); @@ -947,7 +951,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request) } RPCHelpMan{"addmultisigaddress", - "\nAdd a nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n" + "\nAdd an nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n" "Each key is a Bitcoin address or hex-encoded public key.\n" "This functionality is only intended for use with non-watchonly addresses.\n" "See `importaddress` for watchonly p2sh address support.\n" @@ -994,7 +998,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request) if (IsHex(keys_or_addrs[i].get_str()) && (keys_or_addrs[i].get_str().length() == 66 || keys_or_addrs[i].get_str().length() == 130)) { pubkeys.push_back(HexToPubKey(keys_or_addrs[i].get_str())); } else { - pubkeys.push_back(AddrToPubKey(&spk_man, keys_or_addrs[i].get_str())); + pubkeys.push_back(AddrToPubKey(spk_man, keys_or_addrs[i].get_str())); } } @@ -1370,14 +1374,14 @@ static const std::string TransactionDescriptionString() " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction.\n" " \"blockheight\": n, (numeric) The block height containing the transaction.\n" " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it.\n" - " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n" + " \"blocktime\": xxx, (numeric) The block time expressed in " + UNIX_EPOCH_TIME + ".\n" " \"txid\": \"transactionid\", (string) The transaction id.\n" " \"walletconflicts\": [ (array) Conflicting transaction ids.\n" " \"txid\", (string) The transaction id.\n" " ...\n" " ],\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).\n" + " \"time\": xxx, (numeric) The transaction time expressed in " + UNIX_EPOCH_TIME + ".\n" + " \"timereceived\": xxx, (numeric) The time received expressed in " + UNIX_EPOCH_TIME + ".\n" " \"comment\": \"...\", (string) If a comment is associated with the transaction, only present if not empty.\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"; @@ -2424,10 +2428,10 @@ static UniValue getwalletinfo(const JSONRPCRequest& request) " \"unconfirmed_balance\": xxx, (numeric) DEPRECATED. Identical to getbalances().mine.untrusted_pending\n" " \"immature_balance\": xxxxxx, (numeric) DEPRECATED. Identical to getbalances().mine.immature\n" " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n" - " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n" + " \"keypoololdest\": xxxxxx, (numeric) the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool\n" " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated (only counts external keys)\n" " \"keypoolsize_hd_internal\": xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\n" - " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" + " \"unlocked_until\": ttt, (numeric) the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked\n" " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n" " \"hdseedid\": \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n" " \"private_keys_enabled\": true|false (boolean) false if privatekeys are disabled for this wallet (enforced watch-only wallet)\n" @@ -2940,34 +2944,36 @@ static UniValue listunspent(const JSONRPCRequest& request) entry.pushKV("label", i->second.name); } - const SigningProvider* provider = pwallet->GetSigningProvider(); - if (scriptPubKey.IsPayToScriptHash()) { - const CScriptID& hash = CScriptID(boost::get<ScriptHash>(address)); - CScript redeemScript; - if (provider->GetCScript(hash, redeemScript)) { - entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())); - // Now check if the redeemScript is actually a P2WSH script - CTxDestination witness_destination; - if (redeemScript.IsPayToWitnessScriptHash()) { - bool extracted = ExtractDestination(redeemScript, witness_destination); - CHECK_NONFATAL(extracted); - // Also return the witness script - const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(witness_destination); - CScriptID id; - CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin()); - CScript witnessScript; - if (provider->GetCScript(id, witnessScript)) { - entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end())); + const SigningProvider* provider = pwallet->GetSigningProvider(scriptPubKey); + if (provider) { + if (scriptPubKey.IsPayToScriptHash()) { + const CScriptID& hash = CScriptID(boost::get<ScriptHash>(address)); + CScript redeemScript; + if (provider->GetCScript(hash, redeemScript)) { + entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())); + // Now check if the redeemScript is actually a P2WSH script + CTxDestination witness_destination; + if (redeemScript.IsPayToWitnessScriptHash()) { + bool extracted = ExtractDestination(redeemScript, witness_destination); + CHECK_NONFATAL(extracted); + // Also return the witness script + const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(witness_destination); + CScriptID id; + CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin()); + CScript witnessScript; + if (provider->GetCScript(id, witnessScript)) { + entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end())); + } } } - } - } else if (scriptPubKey.IsPayToWitnessScriptHash()) { - const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(address); - CScriptID id; - CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin()); - CScript witnessScript; - if (provider->GetCScript(id, witnessScript)) { - entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end())); + } else if (scriptPubKey.IsPayToWitnessScriptHash()) { + const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(address); + CScriptID id; + CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin()); + CScript witnessScript; + if (provider->GetCScript(id, witnessScript)) { + entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end())); + } } } } @@ -2978,8 +2984,11 @@ static UniValue listunspent(const JSONRPCRequest& request) entry.pushKV("spendable", out.fSpendable); entry.pushKV("solvable", out.fSolvable); if (out.fSolvable) { - auto descriptor = InferDescriptor(scriptPubKey, *pwallet->GetLegacyScriptPubKeyMan()); - entry.pushKV("desc", descriptor->ToString()); + const SigningProvider* provider = pwallet->GetSigningProvider(scriptPubKey); + if (provider) { + auto descriptor = InferDescriptor(scriptPubKey, *provider); + entry.pushKV("desc", descriptor->ToString()); + } } if (avoid_reuse) entry.pushKV("reused", reused); entry.pushKV("safe", out.fSafe); @@ -3288,7 +3297,23 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request) // Parse the prevtxs array ParsePrevouts(request.params[1], nullptr, coins); - return SignTransaction(mtx, &*pwallet->GetLegacyScriptPubKeyMan(), coins, request.params[2]); + std::set<const SigningProvider*> providers; + for (const std::pair<COutPoint, Coin> coin_pair : coins) { + const SigningProvider* provider = pwallet->GetSigningProvider(coin_pair.second.out.scriptPubKey); + if (provider) { + providers.insert(std::move(provider)); + } + } + if (providers.size() == 0) { + // When there are no available providers, use DUMMY_SIGNING_PROVIDER so we can check if the tx is complete + providers.insert(&DUMMY_SIGNING_PROVIDER); + } + + UniValue result(UniValue::VOBJ); + for (const SigningProvider* provider : providers) { + SignTransaction(mtx, provider, coins, request.params[2], result); + } + return result; } static UniValue bumpfee(const JSONRPCRequest& request) @@ -3653,9 +3678,10 @@ static UniValue DescribeWalletAddress(CWallet* pwallet, const CTxDestination& de { UniValue ret(UniValue::VOBJ); UniValue detail = DescribeAddress(dest); + CScript script = GetScriptForDestination(dest); const SigningProvider* provider = nullptr; if (pwallet) { - provider = pwallet->GetSigningProvider(); + provider = pwallet->GetSigningProvider(script); } ret.pushKVs(detail); ret.pushKVs(boost::apply_visitor(DescribeWalletAddressVisitor(provider), dest)); @@ -3683,52 +3709,57 @@ UniValue getaddressinfo(const JSONRPCRequest& request) } RPCHelpMan{"getaddressinfo", - "\nReturn information about the given bitcoin address. Some information requires the address\n" - "to be in the wallet.\n", + "\nReturn information about the given bitcoin address.\n" + "Some of the information will only be present if the address is in the active wallet.\n", { - {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to get the information of."}, + {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for which to get information."}, }, RPCResult{ "{\n" - " \"address\" : \"address\", (string) The bitcoin address validated\n" - " \"scriptPubKey\" : \"hex\", (string) The hex-encoded scriptPubKey generated by the address\n" - " \"ismine\" : true|false, (boolean) If the address is yours or not\n" - " \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n" - " \"solvable\" : true|false, (boolean) Whether we know how to spend coins sent to this address, ignoring the possible lack of private keys\n" - " \"desc\" : \"desc\", (string, optional) A descriptor for spending coins sent to this address (only when solvable)\n" - " \"isscript\" : true|false, (boolean) If the key is a script\n" - " \"ischange\" : true|false, (boolean) If the address was used for change output\n" - " \"iswitness\" : true|false, (boolean) If the address is a witness address\n" - " \"witness_version\" : version (numeric, optional) The version number of the witness program\n" - " \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program\n" - " \"script\" : \"type\" (string, optional) The output script type. Only if \"isscript\" is true and the redeemscript is known. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash, witness_unknown\n" - " \"hex\" : \"hex\", (string, optional) The redeemscript for the p2sh address\n" - " \"pubkeys\" (string, optional) Array of pubkeys associated with the known redeemscript (only if \"script\" is \"multisig\")\n" + " \"address\" : \"address\", (string) The bitcoin address validated.\n" + " \"scriptPubKey\" : \"hex\", (string) The hex-encoded scriptPubKey generated by the address.\n" + " \"ismine\" : true|false, (boolean) If the address is yours.\n" + " \"iswatchonly\" : true|false, (boolean) If the address is watchonly.\n" + " \"solvable\" : true|false, (boolean) If we know how to spend coins sent to this address, ignoring the possible lack of private keys.\n" + " \"desc\" : \"desc\", (string, optional) A descriptor for spending coins sent to this address (only when solvable).\n" + " \"isscript\" : true|false, (boolean) If the key is a script.\n" + " \"ischange\" : true|false, (boolean) If the address was used for change output.\n" + " \"iswitness\" : true|false, (boolean) If the address is a witness address.\n" + " \"witness_version\" : version (numeric, optional) The version number of the witness program.\n" + " \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program.\n" + " \"script\" : \"type\" (string, optional) The output script type. Only if isscript is true and the redeemscript is known. Possible\n" + " types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n" + " witness_v0_scripthash, witness_unknown.\n" + " \"hex\" : \"hex\", (string, optional) The redeemscript for the p2sh address.\n" + " \"pubkeys\" (array, optional) Array of pubkeys associated with the known redeemscript (only if script is multisig).\n" " [\n" - " \"pubkey\"\n" + " \"pubkey\" (string)\n" " ,...\n" " ]\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\").\n" - " \"iscompressed\" : true|false, (boolean, optional) If the pubkey is compressed\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" - " \"hdmasterfingerprint\" : \"<hash160>\" (string, optional) The fingperint of the master key.\n" - " \"labels\" (object) Array of labels associated with the address.\n" + " \"sigsrequired\" : xxxxx (numeric, optional) The 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. Includes all\n" + " getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath,\n" + " hdseedid) and relation to the wallet (ismine, iswatchonly).\n" + " \"iscompressed\" : true|false, (boolean, optional) If the pubkey is compressed.\n" + " \"label\" : \"label\" (string) The label associated with the address. Defaults to \"\". Equivalent to the name field in the labels array.\n" + " \"timestamp\" : timestamp, (number, optional) The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + ".\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" + " \"hdmasterfingerprint\" : \"<hash160>\" (string, optional) The fingerprint of the master key.\n" + " \"labels\" (object) An array of labels associated with the address. Currently limited to one label but returned\n" + " as an array to keep the API stable if multiple labels are enabled in the future.\n" " [\n" " { (json object of label data)\n" - " \"name\": \"labelname\" (string) The label\n" - " \"purpose\": \"string\" (string) Purpose of address (\"send\" for sending address, \"receive\" for receiving address)\n" + " \"name\": \"label name\" (string) The label name. Defaults to \"\". Equivalent to the label field above.\n" + " \"purpose\": \"purpose\" (string) The purpose of the associated address (send or receive).\n" " },...\n" " ]\n" "}\n" }, RPCExamples{ - HelpExampleCli("getaddressinfo", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") - + HelpExampleRpc("getaddressinfo", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") + HelpExampleCli("getaddressinfo", "\"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl\"") + + HelpExampleRpc("getaddressinfo", "\"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl\"") }, }.Check(request); @@ -3747,24 +3778,40 @@ UniValue getaddressinfo(const JSONRPCRequest& request) CScript scriptPubKey = GetScriptForDestination(dest); ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end())); - const SigningProvider* provider = pwallet->GetSigningProvider(); + + const SigningProvider* provider = pwallet->GetSigningProvider(scriptPubKey); isminetype mine = pwallet->IsMine(dest); ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE)); - bool solvable = IsSolvable(*provider, scriptPubKey); + + bool solvable = provider && IsSolvable(*provider, scriptPubKey); ret.pushKV("solvable", solvable); + if (solvable) { ret.pushKV("desc", InferDescriptor(scriptPubKey, *provider)->ToString()); } + ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY)); + + // Return DescribeWalletAddress fields. + // Always returned: isscript, ischange, iswitness. + // Optional: witness_version, witness_program, script, hex, pubkeys (array), + // sigsrequired, pubkey, embedded, iscompressed. UniValue detail = DescribeWalletAddress(pwallet, dest); ret.pushKVs(detail); + + // Return label field if existing. Currently only one label can be + // associated with an address, so the label should be equivalent to the + // value of the name key/value pair in the labels hash array below. if (pwallet->mapAddressBook.count(dest)) { ret.pushKV("label", pwallet->mapAddressBook[dest].name); } + ret.pushKV("ischange", pwallet->IsChange(scriptPubKey)); - ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(); + // Fetch KeyMetadata, if present, for the timestamp, hdkeypath, hdseedid, + // and hdmasterfingerprint fields. + ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(scriptPubKey); if (spk_man) { if (const CKeyMetadata* meta = spk_man->GetMetadata(dest)) { ret.pushKV("timestamp", meta->nCreateTime); @@ -3776,9 +3823,11 @@ UniValue getaddressinfo(const JSONRPCRequest& request) } } - // Currently only one label can be associated with an address, return an array - // so the API remains stable if we allow multiple labels to be associated with - // an address. + // Return a labels array containing a hash of key/value pairs for the label + // name and address purpose. The name value is equivalent to the label field + // above. Currently only one label can be associated with an address, but we + // return an array so the API remains stable if we allow multiple labels to + // be associated with an address in the future. UniValue labels(UniValue::VARR); std::map<CTxDestination, CAddressBookData>::iterator mi = pwallet->mapAddressBook.find(dest); if (mi != pwallet->mapAddressBook.end()) { diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 3eaaf3786c..be8a71da97 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -14,11 +14,10 @@ bool LegacyScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error) { error.clear(); - TopUp(); // Generate a new key that is added to wallet CPubKey new_key; - if (!GetKeyFromPool(new_key)) { + if (!GetKeyFromPool(new_key, type)) { error = "Error: Keypool ran out, please call keypoolrefill first"; return false; } @@ -202,12 +201,11 @@ isminetype LegacyScriptPubKeyMan::IsMine(const CScript& script) const assert(false); } -bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys) +bool LegacyScriptPubKeyMan::CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys) { { LOCK(cs_KeyStore); - if (!SetCrypted()) - return false; + assert(mapKeys.empty()); bool keyPass = mapCryptedKeys.empty(); // Always pass when there are no encrypted keys bool keyFail = false; @@ -217,7 +215,7 @@ bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys) const CPubKey &vchPubKey = (*mi).second.first; const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; CKey key; - if (!DecryptKey(vMasterKeyIn, vchCryptedSecret, vchPubKey, key)) + if (!DecryptKey(master_key, vchCryptedSecret, vchPubKey, key)) { keyFail = true; break; @@ -233,53 +231,55 @@ bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys) } if (keyFail || (!keyPass && !accept_no_keys)) return false; - vMasterKey = vMasterKeyIn; fDecryptionThoroughlyChecked = true; } - NotifyStatusChanged(this); return true; } -bool LegacyScriptPubKeyMan::EncryptKeys(CKeyingMaterial& vMasterKeyIn) +bool LegacyScriptPubKeyMan::Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) { + AssertLockHeld(cs_wallet); LOCK(cs_KeyStore); - if (!mapCryptedKeys.empty() || IsCrypted()) + encrypted_batch = batch; + if (!mapCryptedKeys.empty()) { + encrypted_batch = nullptr; return false; + } - fUseCrypto = true; - for (const KeyMap::value_type& mKey : mapKeys) + KeyMap keys_to_encrypt; + keys_to_encrypt.swap(mapKeys); // Clear mapKeys so AddCryptedKeyInner will succeed. + for (const KeyMap::value_type& mKey : keys_to_encrypt) { const CKey &key = mKey.second; CPubKey vchPubKey = key.GetPubKey(); CKeyingMaterial vchSecret(key.begin(), key.end()); std::vector<unsigned char> vchCryptedSecret; - if (!EncryptSecret(vMasterKeyIn, vchSecret, vchPubKey.GetHash(), vchCryptedSecret)) + if (!EncryptSecret(master_key, vchSecret, vchPubKey.GetHash(), vchCryptedSecret)) { + encrypted_batch = nullptr; return false; - if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) + } + if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) { + encrypted_batch = nullptr; return false; + } } - mapKeys.clear(); + encrypted_batch = nullptr; return true; } -bool LegacyScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) +bool LegacyScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool) { + if (!CanGetAddresses(internal)) { + return false; + } + if (!ReserveKeyFromKeyPool(index, keypool, internal)) { return false; } + address = GetDestinationForKey(keypool.vchPubKey, type); return true; } -void LegacyScriptPubKeyMan::KeepDestination(int64_t index) -{ - KeepKey(index); -} - -void LegacyScriptPubKeyMan::ReturnDestination(int64_t index, bool internal, const CPubKey& pubkey) -{ - ReturnKey(index, internal, pubkey); -} - void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script) { AssertLockHeld(cs_wallet); @@ -460,7 +460,7 @@ size_t LegacyScriptPubKeyMan::KeypoolCountExternalKeys() unsigned int LegacyScriptPubKeyMan::GetKeyPoolSize() const { AssertLockHeld(cs_wallet); - return setInternalKeyPool.size() + setExternalKeyPool.size(); + return setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(); } int64_t LegacyScriptPubKeyMan::GetTimeFirstKey() const @@ -548,7 +548,7 @@ bool LegacyScriptPubKeyMan::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& s RemoveWatchOnly(script); } - if (!IsCrypted()) { + if (!m_storage.HasEncryptionKeys()) { return batch.WriteKey(pubkey, secret.GetPrivKey(), mapKeyMetadata[pubkey.GetID()]); @@ -589,7 +589,7 @@ void LegacyScriptPubKeyMan::LoadScriptMetadata(const CScriptID& script_id, const bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey) { LOCK(cs_KeyStore); - if (!IsCrypted()) { + if (!m_storage.HasEncryptionKeys()) { return FillableSigningProvider::AddKeyPubKey(key, pubkey); } @@ -599,7 +599,7 @@ bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pu std::vector<unsigned char> vchCryptedSecret; CKeyingMaterial vchSecret(key.begin(), key.end()); - if (!EncryptSecret(vMasterKey, vchSecret, pubkey.GetHash(), vchCryptedSecret)) { + if (!EncryptSecret(m_storage.GetEncryptionKey(), vchSecret, pubkey.GetHash(), vchCryptedSecret)) { return false; } @@ -617,9 +617,7 @@ bool LegacyScriptPubKeyMan::LoadCryptedKey(const CPubKey &vchPubKey, const std:: bool LegacyScriptPubKeyMan::AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { LOCK(cs_KeyStore); - if (!SetCrypted()) { - return false; - } + assert(mapKeys.empty()); mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret); ImplicitlyLearnRelatedKeyScripts(vchPubKey); @@ -746,7 +744,7 @@ void LegacyScriptPubKeyMan::SetHDChain(const CHDChain& chain, bool memonly) bool LegacyScriptPubKeyMan::HaveKey(const CKeyID &address) const { LOCK(cs_KeyStore); - if (!IsCrypted()) { + if (!m_storage.HasEncryptionKeys()) { return FillableSigningProvider::HaveKey(address); } return mapCryptedKeys.count(address) > 0; @@ -755,7 +753,7 @@ bool LegacyScriptPubKeyMan::HaveKey(const CKeyID &address) const bool LegacyScriptPubKeyMan::GetKey(const CKeyID &address, CKey& keyOut) const { LOCK(cs_KeyStore); - if (!IsCrypted()) { + if (!m_storage.HasEncryptionKeys()) { return FillableSigningProvider::GetKey(address, keyOut); } @@ -764,7 +762,7 @@ bool LegacyScriptPubKeyMan::GetKey(const CKeyID &address, CKey& keyOut) const { const CPubKey &vchPubKey = (*mi).second.first; const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; - return DecryptKey(vMasterKey, vchCryptedSecret, vchPubKey, keyOut); + return DecryptKey(m_storage.GetEncryptionKey(), vchCryptedSecret, vchPubKey, keyOut); } return false; } @@ -802,7 +800,7 @@ bool LegacyScriptPubKeyMan::GetWatchPubKey(const CKeyID &address, CPubKey &pubke bool LegacyScriptPubKeyMan::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const { LOCK(cs_KeyStore); - if (!IsCrypted()) { + if (!m_storage.HasEncryptionKeys()) { if (!FillableSigningProvider::GetPubKey(address, vchPubKeyOut)) { return GetWatchPubKey(address, vchPubKeyOut); } @@ -1092,15 +1090,20 @@ void LegacyScriptPubKeyMan::AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const m_pool_key_to_index[pubkey.GetID()] = index; } -void LegacyScriptPubKeyMan::KeepKey(int64_t nIndex) +void LegacyScriptPubKeyMan::KeepDestination(int64_t nIndex, const OutputType& type) { // Remove from key pool WalletBatch batch(m_storage.GetDatabase()); batch.ErasePool(nIndex); + CPubKey pubkey; + bool have_pk = GetPubKey(m_index_to_reserved_key.at(nIndex), pubkey); + assert(have_pk); + LearnRelatedScripts(pubkey, type); + m_index_to_reserved_key.erase(nIndex); WalletLogPrintf("keypool keep %d\n", nIndex); } -void LegacyScriptPubKeyMan::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey) +void LegacyScriptPubKeyMan::ReturnDestination(int64_t nIndex, bool fInternal, const CTxDestination&) { // Return to key pool { @@ -1112,13 +1115,15 @@ void LegacyScriptPubKeyMan::ReturnKey(int64_t nIndex, bool fInternal, const CPub } else { setExternalKeyPool.insert(nIndex); } - m_pool_key_to_index[pubkey.GetID()] = nIndex; + CKeyID& pubkey_id = m_index_to_reserved_key.at(nIndex); + m_pool_key_to_index[pubkey_id] = nIndex; + m_index_to_reserved_key.erase(nIndex); NotifyCanGetAddressesChanged(); } WalletLogPrintf("keypool return %d\n", nIndex); } -bool LegacyScriptPubKeyMan::GetKeyFromPool(CPubKey& result, bool internal) +bool LegacyScriptPubKeyMan::GetKeyFromPool(CPubKey& result, const OutputType type, bool internal) { if (!CanGetAddresses(internal)) { return false; @@ -1134,7 +1139,7 @@ bool LegacyScriptPubKeyMan::GetKeyFromPool(CPubKey& result, bool internal) result = GenerateNewKey(batch, internal); return true; } - KeepKey(nIndex); + KeepDestination(nIndex, type); result = keypool.vchPubKey; } return true; @@ -1147,8 +1152,6 @@ bool LegacyScriptPubKeyMan::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& key { LOCK(cs_wallet); - TopUp(); - bool fReturningInternal = fRequestedInternal; fReturningInternal &= (IsHDEnabled() && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) || m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); bool use_split_keypool = set_pre_split_keypool.empty(); @@ -1179,6 +1182,8 @@ bool LegacyScriptPubKeyMan::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& key throw std::runtime_error(std::string(__func__) + ": keypool entry invalid"); } + assert(m_index_to_reserved_key.count(nIndex) == 0); + m_index_to_reserved_key[nIndex] = keypool.vchPubKey.GetID(); m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); WalletLogPrintf("keypool reserve %d\n", nIndex); } @@ -1379,7 +1384,7 @@ bool LegacyScriptPubKeyMan::ImportScriptPubKeys(const std::set<CScript>& script_ std::set<CKeyID> LegacyScriptPubKeyMan::GetKeys() const { LOCK(cs_KeyStore); - if (!IsCrypted()) { + if (!m_storage.HasEncryptionKeys()) { return FillableSigningProvider::GetKeys(); } std::set<CKeyID> set_address; @@ -1393,13 +1398,8 @@ std::set<CKeyID> LegacyScriptPubKeyMan::GetKeys() const LegacyScriptPubKeyMan::LegacyScriptPubKeyMan(CWallet& wallet) : ScriptPubKeyMan(wallet), m_wallet(wallet), - cs_wallet(wallet.cs_wallet), - vMasterKey(wallet.vMasterKey), - fUseCrypto(wallet.fUseCrypto), - fDecryptionThoroughlyChecked(wallet.fDecryptionThoroughlyChecked) {} + cs_wallet(wallet.cs_wallet) {} -bool LegacyScriptPubKeyMan::SetCrypted() { return m_wallet.SetCrypted(); } -bool LegacyScriptPubKeyMan::IsCrypted() const { return m_wallet.IsCrypted(); } void LegacyScriptPubKeyMan::NotifyWatchonlyChanged(bool fHaveWatchOnly) const { return m_wallet.NotifyWatchonlyChanged(fHaveWatchOnly); } void LegacyScriptPubKeyMan::NotifyCanGetAddressesChanged() const { return m_wallet.NotifyCanGetAddressesChanged(); } template<typename... Params> void LegacyScriptPubKeyMan::WalletLogPrintf(const std::string& fmt, const Params&... parameters) const { return m_wallet.WalletLogPrintf(fmt, parameters...); } diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h index 4f17156792..b78494921c 100644 --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -31,6 +31,8 @@ public: virtual void UnsetBlankWalletFlag(WalletBatch&) = 0; virtual bool CanSupportFeature(enum WalletFeature) const = 0; virtual void SetMinVersion(enum WalletFeature, WalletBatch* = nullptr, bool = false) = 0; + virtual const CKeyingMaterial& GetEncryptionKey() const = 0; + virtual bool HasEncryptionKeys() const = 0; virtual bool IsLocked() const = 0; }; @@ -150,10 +152,18 @@ public: virtual bool GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error) { return false; } virtual isminetype IsMine(const CScript& script) const { return ISMINE_NO; } - virtual bool GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) { return false; } - virtual void KeepDestination(int64_t index) {} - virtual void ReturnDestination(int64_t index, bool internal, const CPubKey& pubkey) {} + //! Check that the given decryption key is valid for this ScriptPubKeyMan, i.e. it decrypts all of the keys handled by it. + virtual bool CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys = false) { return false; } + virtual bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) { return false; } + virtual bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool) { return false; } + virtual void KeepDestination(int64_t index, const OutputType& type) {} + virtual void ReturnDestination(int64_t index, bool internal, const CTxDestination& addr) {} + + /** Fills internal address pool. Use within ScriptPubKeyMan implementations should be used sparingly and only + * when something from the address pool is removed, excluding GetNewDestination and GetReservedDestination. + * External wallet code is primarily responsible for topping up prior to fetching new addresses + */ virtual bool TopUp(unsigned int size = 0) { return false; } //! Mark unused addresses as being used @@ -193,6 +203,9 @@ public: class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProvider { private: + //! keeps track of whether Unlock has run a thorough check before + bool fDecryptionThoroughlyChecked = false; + using WatchOnlySet = std::set<CScript>; using WatchKeyMap = std::map<CKeyID, CPubKey>; @@ -246,9 +259,11 @@ private: std::set<int64_t> set_pre_split_keypool GUARDED_BY(cs_wallet); int64_t m_max_keypool_index GUARDED_BY(cs_wallet) = 0; std::map<CKeyID, int64_t> m_pool_key_to_index; + // Tracks keypool indexes to CKeyIDs of keys that have been taken out of the keypool but may be returned to it + std::map<int64_t, CKeyID> m_index_to_reserved_key; //! Fetches a key from the keypool - bool GetKeyFromPool(CPubKey &key, bool internal = false); + bool GetKeyFromPool(CPubKey &key, const OutputType type, bool internal = false); /** * Reserves a key from the keypool and sets nIndex to its index @@ -266,19 +281,16 @@ private: */ bool ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal); - void KeepKey(int64_t nIndex); - void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey); - public: bool GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error) override; isminetype IsMine(const CScript& script) const override; - //! will encrypt previously unencrypted keys - bool EncryptKeys(CKeyingMaterial& vMasterKeyIn); + bool CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys = false) override; + bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) override; - bool GetReservedDestination(const OutputType type, bool internal, int64_t& index, CKeyPool& keypool) override; - void KeepDestination(int64_t index) override; - void ReturnDestination(int64_t index, bool internal, const CPubKey& pubkey) override; + bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool) override; + void KeepDestination(int64_t index, const OutputType& type) override; + void ReturnDestination(int64_t index, bool internal, const CTxDestination&) override; bool TopUp(unsigned int size = 0) override; @@ -404,16 +416,11 @@ public: friend class CWallet; friend class ReserveDestination; LegacyScriptPubKeyMan(CWallet& wallet); - bool SetCrypted(); - bool IsCrypted() const; void NotifyWatchonlyChanged(bool fHaveWatchOnly) const; void NotifyCanGetAddressesChanged() const; template<typename... Params> void WalletLogPrintf(const std::string& fmt, const Params&... parameters) const; CWallet& m_wallet; CCriticalSection& cs_wallet; - CKeyingMaterial& vMasterKey GUARDED_BY(cs_KeyStore); - std::atomic<bool>& fUseCrypto; - bool& fDecryptionThoroughlyChecked; }; #endif // BITCOIN_WALLET_SCRIPTPUBKEYMAN_H diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp index bc068f1499..7e9f88f6b7 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -55,7 +55,7 @@ static void add_coin(const CAmount& nValue, int nInput, CoinSet& set) set.emplace(MakeTransactionRef(tx), nInput); } -static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0) +static void add_coin(CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false) { balance += nValue; static int nextLockTime = 0; @@ -63,21 +63,31 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa tx.nLockTime = nextLockTime++; // so all transactions get different hashes tx.vout.resize(nInput + 1); tx.vout[nInput].nValue = nValue; + if (spendable) { + CTxDestination dest; + std::string error; + assert(wallet.GetNewDestination(OutputType::BECH32, "", dest, error)); + tx.vout[nInput].scriptPubKey = GetScriptForDestination(dest); + } if (fIsFromMe) { // IsFromMe() returns (GetDebit() > 0), and GetDebit() is 0 if vin.empty(), // so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe() tx.vin.resize(1); } - std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx))); + std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&wallet, MakeTransactionRef(std::move(tx))); if (fIsFromMe) { wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1); } COutput output(wtx.get(), nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */); vCoins.push_back(output); - testWallet.AddToWallet(*wtx.get()); + wallet.AddToWallet(*wtx.get()); wtxn.emplace_back(std::move(wtx)); } +static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false) +{ + add_coin(testWallet, nValue, nAge, fIsFromMe, nInput, spendable); +} static void empty_wallet(void) { @@ -252,17 +262,33 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) vCoins.at(0).nInputBytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used)); - // Make sure that we aren't using BnB when there are preset inputs + // Test fees subtracted from output: + empty_wallet(); + add_coin(1 * CENT); + vCoins.at(0).nInputBytes = 40; + BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used)); + coin_selection_params_bnb.m_subtract_fee_outputs = true; + BOOST_CHECK(testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used)); + BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); + + // Make sure that can use BnB when there are preset inputs empty_wallet(); - add_coin(5 * CENT); - add_coin(3 * CENT); - add_coin(2 * CENT); - CCoinControl coin_control; - coin_control.fAllowOtherInputs = true; - coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i)); - BOOST_CHECK(testWallet.SelectCoins(vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb, bnb_used)); - BOOST_CHECK(!bnb_used); - BOOST_CHECK(!coin_selection_params_bnb.use_bnb); + { + std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), WalletDatabase::CreateMock()); + bool firstRun; + wallet->LoadWallet(firstRun); + LOCK(wallet->cs_wallet); + add_coin(*wallet, 5 * CENT, 6 * 24, false, 0, true); + add_coin(*wallet, 3 * CENT, 6 * 24, false, 0, true); + add_coin(*wallet, 2 * CENT, 6 * 24, false, 0, true); + CCoinControl coin_control; + coin_control.fAllowOtherInputs = true; + coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i)); + coin_selection_params_bnb.effective_fee = CFeeRate(0); + BOOST_CHECK(wallet->SelectCoins(vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb, bnb_used)); + BOOST_CHECK(bnb_used); + BOOST_CHECK(coin_selection_params_bnb.use_bnb); + } } BOOST_AUTO_TEST_CASE(knapsack_solver_test) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 2f2931cef1..3954f66267 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -532,8 +532,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) { LOCK(cs_wallet); mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; - assert(!encrypted_batch); - encrypted_batch = new WalletBatch(*database); + WalletBatch* encrypted_batch = new WalletBatch(*database); if (!encrypted_batch->TxnBegin()) { delete encrypted_batch; encrypted_batch = nullptr; @@ -542,7 +541,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) encrypted_batch->WriteMasterKey(nMasterKeyMaxID, kMasterKey); if (auto spk_man = m_spk_man.get()) { - if (!spk_man->EncryptKeys(_vMasterKey)) { + if (!spk_man->Encrypt(_vMasterKey, encrypted_batch)) { encrypted_batch->TxnAbort(); delete encrypted_batch; encrypted_batch = nullptr; @@ -822,7 +821,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) void CWallet::LoadToWallet(CWalletTx& wtxIn) { - // If wallet doesn't have a chain (e.g wallet-tool), lock can't be taken. + // If wallet doesn't have a chain (e.g bitcoin-wallet), lock can't be taken. auto locked_chain = LockChain(); if (locked_chain) { Optional<int> block_height = locked_chain->getBlockHeight(wtxIn.m_confirm.hashBlock); @@ -1363,7 +1362,11 @@ bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig const CScript& scriptPubKey = txout.scriptPubKey; SignatureData sigdata; - const SigningProvider* provider = GetSigningProvider(); + const SigningProvider* provider = GetSigningProvider(scriptPubKey); + if (!provider) { + // We don't know about this scriptpbuKey; + return false; + } if (!ProduceSignature(*provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) { return false; @@ -1444,11 +1447,9 @@ bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScri int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig) { std::vector<CTxOut> txouts; - // 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 (const CTxIn& input : tx.vin) { const auto mi = wallet->mapWallet.find(input.prevout.hash); + // Can not estimate size without knowing the input details if (mi == wallet->mapWallet.end()) { return -1; } @@ -1463,8 +1464,6 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wall { CMutableTransaction txNew(tx); if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) { - // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE) - // implies that we can sign for every input. return -1; } return GetVirtualTransactionSize(CTransaction(txNew)); @@ -2125,7 +2124,7 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector< continue; } - const SigningProvider* provider = GetSigningProvider(); + const SigningProvider* provider = GetSigningProvider(wtx.tx->vout[i].scriptPubKey); bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) : false; bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable)); @@ -2160,7 +2159,7 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins(interfaces::Ch for (const COutput& coin : availableCoins) { CTxDestination address; - if (coin.fSpendable && + if ((coin.fSpendable || (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.fSolvable)) && ExtractDestination(FindNonChangeParentOutput(*coin.tx->tx, coin.i).scriptPubKey, address)) { result[address].emplace_back(std::move(coin)); } @@ -2168,12 +2167,16 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins(interfaces::Ch std::vector<COutPoint> lockedCoins; ListLockedCoins(lockedCoins); + // Include watch-only for wallets without private keys + const bool include_watch_only = IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); + const isminetype is_mine_filter = include_watch_only ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE; for (const COutPoint& output : lockedCoins) { auto it = mapWallet.find(output.hash); if (it != mapWallet.end()) { int depth = it->second.GetDepthInMainChain(); if (depth >= 0 && output.n < it->second.tx->vout.size() && - IsMine(it->second.tx->vout[output.n]) == ISMINE_SPENDABLE) { + IsMine(it->second.tx->vout[output.n]) == is_mine_filter + ) { CTxDestination address; if (ExtractDestination(FindNonChangeParentOutput(*it->second.tx, output.n).scriptPubKey, address)) { result[address].emplace_back( @@ -2234,7 +2237,11 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil if (effective_value > 0) { group.fee += coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes); group.long_term_fee += coin.m_input_bytes < 0 ? 0 : long_term_feerate.GetFee(coin.m_input_bytes); - group.effective_value += effective_value; + if (coin_selection_params.m_subtract_fee_outputs) { + group.effective_value += coin.txout.nValue; + } else { + group.effective_value += effective_value; + } ++it; } else { it = group.Discard(coin); @@ -2260,13 +2267,14 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const { std::vector<COutput> vCoins(vAvailableCoins); + CAmount value_to_select = nTargetValue; + + // Default to bnb was not used. If we use it, we set it later + bnb_used = false; // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) if (coin_control.HasSelected() && !coin_control.fAllowOtherInputs) { - // We didn't use BnB here, so set it to false. - bnb_used = false; - for (const COutput& out : vCoins) { if (!out.fSpendable) @@ -2285,22 +2293,30 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm coin_control.ListSelected(vPresetInputs); for (const COutPoint& outpoint : vPresetInputs) { - // For now, don't use BnB if preset inputs are selected. TODO: Enable this later - bnb_used = false; - coin_selection_params.use_bnb = false; - std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash); if (it != mapWallet.end()) { const CWalletTx& wtx = it->second; // Clearly invalid input, fail - if (wtx.tx->vout.size() <= outpoint.n) + if (wtx.tx->vout.size() <= outpoint.n) { return false; + } // Just to calculate the marginal byte size - nValueFromPresetInputs += wtx.tx->vout[outpoint.n].nValue; - setPresetCoins.insert(CInputCoin(wtx.tx, outpoint.n)); - } else + CInputCoin coin(wtx.tx, outpoint.n, wtx.GetSpendSize(outpoint.n, false)); + nValueFromPresetInputs += coin.txout.nValue; + if (coin.m_input_bytes <= 0) { + return false; // Not solvable, can't estimate size for fee + } + coin.effective_value = coin.txout.nValue - coin_selection_params.effective_fee.GetFee(coin.m_input_bytes); + if (coin_selection_params.use_bnb) { + value_to_select -= coin.effective_value; + } else { + value_to_select -= coin.txout.nValue; + } + setPresetCoins.insert(coin); + } else { return false; // TODO: Allow non-wallet inputs + } } // remove preset inputs from vCoins @@ -2329,14 +2345,14 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm size_t max_descendants = (size_t)std::max<int64_t>(1, limit_descendant_count); bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS); - bool res = nTargetValue <= nValueFromPresetInputs || - SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 6, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) || - SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 1, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) || - (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, 2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || - (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || - (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || - (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || - (m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); + bool res = value_to_select <= 0 || + SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 6, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) || + SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) || + (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || + (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || + (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || + (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) || + (m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)); // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset util::insert(setCoinsRet, setPresetCoins); @@ -2362,8 +2378,9 @@ bool CWallet::SignTransaction(CMutableTransaction& tx) const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue; SignatureData sigdata; - const SigningProvider* provider = GetSigningProvider(); + const SigningProvider* provider = GetSigningProvider(scriptPubKey); if (!provider) { + // We don't know about this scriptpbuKey; return false; } @@ -2519,7 +2536,8 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign) { CAmount nValue = 0; - ReserveDestination reservedest(this); + const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend); + ReserveDestination reservedest(this, change_type); int nChangePosRequest = nChangePosInOut; unsigned int nSubtractFeeFromAmount = 0; for (const auto& recipient : vecSend) @@ -2578,8 +2596,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std return false; } CTxDestination dest; - const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend); - bool ret = reservedest.GetReservedDestination(change_type, dest, true); + bool ret = reservedest.GetReservedDestination(dest, true); if (!ret) { strFailReason = "Keypool ran out, please call keypoolrefill first"; @@ -2602,7 +2619,8 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std // BnB selector is the only selector used when this is true. // That should only happen on the first pass through the loop. - coin_selection_params.use_bnb = nSubtractFeeFromAmount == 0; // If we are doing subtract fee from recipient, then don't use BnB + coin_selection_params.use_bnb = true; + coin_selection_params.m_subtract_fee_outputs = nSubtractFeeFromAmount != 0; // If we are doing subtract fee from recipient, don't use effective values // Start with no fee and loop until there is enough fee while (true) { @@ -2616,7 +2634,9 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std nValueToSelect += nFeeRet; // vouts to the payees - coin_selection_params.tx_noinputs_size = 11; // Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 output count, 1 witness overhead (dummy, flag, stack size) + if (!coin_selection_params.m_subtract_fee_outputs) { + coin_selection_params.tx_noinputs_size = 11; // Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 output count, 1 witness overhead (dummy, flag, stack size) + } for (const auto& recipient : vecSend) { CTxOut txout(recipient.nAmount, recipient.scriptPubKey); @@ -2633,7 +2653,9 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std } } // Include the fee cost for outputs. Note this is only used for BnB right now - coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION); + if (!coin_selection_params.m_subtract_fee_outputs) { + coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION); + } if (IsDust(txout, chain().relayDustFee())) { @@ -2652,7 +2674,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std } // Choose coins to use - bool bnb_used; + bool bnb_used = false; if (pick_new_inputs) { nValueIn = 0; setCoins.clear(); @@ -2825,7 +2847,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std const CScript& scriptPubKey = coin.txout.scriptPubKey; SignatureData sigdata; - const SigningProvider* provider = GetSigningProvider(); + const SigningProvider* provider = GetSigningProvider(scriptPubKey); if (!provider || !ProduceSignature(*provider, MutableTransactionSignatureCreator(&txNew, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata)) { strFailReason = _("Signing transaction failed").translated; @@ -2921,7 +2943,7 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { // Even if we don't use this lock in this function, we want to preserve // lock order in LoadToWallet if query of chain state is needed to know - // tx status. If lock can't be taken (e.g wallet-tool), tx confirmation + // tx status. If lock can't be taken (e.g bitcoin-wallet), tx confirmation // status may be not reliable. auto locked_chain = LockChain(); LOCK(cs_wallet); @@ -3083,6 +3105,7 @@ bool CWallet::GetNewDestination(const OutputType type, const std::string label, bool result = false; auto spk_man = m_spk_man.get(); if (spk_man) { + spk_man->TopUp(); result = spk_man->GetNewDestination(type, dest, error); } if (result) { @@ -3096,10 +3119,8 @@ bool CWallet::GetNewChangeDestination(const OutputType type, CTxDestination& des { error.clear(); - m_spk_man->TopUp(); - - ReserveDestination reservedest(this); - if (!reservedest.GetReservedDestination(type, dest, true)) { + ReserveDestination reservedest(this, type); + if (!reservedest.GetReservedDestination(dest, true)) { error = "Error: Keypool ran out, please call keypoolrefill first"; return false; } @@ -3265,49 +3286,43 @@ std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) co return result; } -bool ReserveDestination::GetReservedDestination(const OutputType type, CTxDestination& dest, bool internal) +bool ReserveDestination::GetReservedDestination(CTxDestination& dest, bool internal) { m_spk_man = pwallet->GetLegacyScriptPubKeyMan(); if (!m_spk_man) { return false; } - if (!pwallet->CanGetAddresses(internal)) { - return false; - } if (nIndex == -1) { + m_spk_man->TopUp(); + CKeyPool keypool; - if (!m_spk_man->GetReservedDestination(type, internal, nIndex, keypool)) { + if (!m_spk_man->GetReservedDestination(type, internal, address, nIndex, keypool)) { return false; } - vchPubKey = keypool.vchPubKey; fInternal = keypool.fInternal; } - assert(vchPubKey.IsValid()); - m_spk_man->LearnRelatedScripts(vchPubKey, type); - address = GetDestinationForKey(vchPubKey, type); dest = address; return true; } void ReserveDestination::KeepDestination() { - if (nIndex != -1) - m_spk_man->KeepDestination(nIndex); + if (nIndex != -1) { + m_spk_man->KeepDestination(nIndex, type); + } nIndex = -1; - vchPubKey = CPubKey(); address = CNoDestination(); } void ReserveDestination::ReturnDestination() { if (nIndex != -1) { - m_spk_man->ReturnDestination(nIndex, fInternal, vchPubKey); + m_spk_man->ReturnDestination(nIndex, fInternal, address); } nIndex = -1; - vchPubKey = CPubKey(); address = CNoDestination(); } @@ -3651,9 +3666,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, } if (auto spk_man = walletInstance->m_spk_man.get()) { - std::string error; if (!spk_man->Upgrade(prev_version, error)) { - chain.initError(error); return nullptr; } } @@ -3990,15 +4003,9 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu return groups; } -bool CWallet::SetCrypted() +bool CWallet::IsCrypted() const { - LOCK(cs_KeyStore); - if (fUseCrypto) - return true; - if (!mapKeys.empty()) - return false; - fUseCrypto = true; - return true; + return HasEncryptionKeys(); } bool CWallet::IsLocked() const @@ -4012,7 +4019,7 @@ bool CWallet::IsLocked() const bool CWallet::Lock() { - if (!SetCrypted()) + if (!IsCrypted()) return false; { @@ -4024,12 +4031,32 @@ bool CWallet::Lock() return true; } -ScriptPubKeyMan* CWallet::GetScriptPubKeyMan() const +bool CWallet::Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys) +{ + { + LOCK(cs_KeyStore); + if (m_spk_man) { + if (!m_spk_man->CheckDecryptionKey(vMasterKeyIn, accept_no_keys)) { + return false; + } + } + vMasterKey = vMasterKeyIn; + } + NotifyStatusChanged(this); + return true; +} + +ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const CScript& script) const +{ + return m_spk_man.get(); +} + +const SigningProvider* CWallet::GetSigningProvider(const CScript& script) const { return m_spk_man.get(); } -const SigningProvider* CWallet::GetSigningProvider() const +const SigningProvider* CWallet::GetSigningProvider(const CScript& script, SignatureData& sigdata) const { return m_spk_man.get(); } @@ -4038,3 +4065,13 @@ LegacyScriptPubKeyMan* CWallet::GetLegacyScriptPubKeyMan() const { return m_spk_man.get(); } + +const CKeyingMaterial& CWallet::GetEncryptionKey() const +{ + return vMasterKey; +} + +bool CWallet::HasEncryptionKeys() const +{ + return !mapMasterKeys.empty(); +} diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index a6b9c0131a..c4511601de 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -71,8 +71,6 @@ static const CAmount WALLET_INCREMENTAL_RELAY_FEE = 5000; static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true; //! Default for -walletrejectlongchains static const bool DEFAULT_WALLET_REJECT_LONG_CHAINS = false; -//! Default for -avoidpartialspends -static const bool DEFAULT_AVOIDPARTIALSPENDS = false; //! -txconfirmtarget default static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 6; //! -walletrbf default @@ -140,12 +138,12 @@ class ReserveDestination { protected: //! The wallet to reserve from - CWallet* pwallet; - LegacyScriptPubKeyMan* m_spk_man{nullptr}; + CWallet* const pwallet; + //! The ScriptPubKeyMan to reserve from. Based on type when GetReservedDestination is called + ScriptPubKeyMan* m_spk_man{nullptr}; + OutputType const type; //! The index of the address's key in the keypool int64_t nIndex{-1}; - //! The public key for the address - CPubKey vchPubKey; //! The destination CTxDestination address; //! Whether this is from the internal (change output) keypool @@ -153,10 +151,9 @@ protected: public: //! Construct a ReserveDestination object. This does NOT reserve an address yet - explicit ReserveDestination(CWallet* pwalletIn) - { - pwallet = pwalletIn; - } + explicit ReserveDestination(CWallet* pwallet, OutputType type) + : pwallet(pwallet) + , type(type) { } ReserveDestination(const ReserveDestination&) = delete; ReserveDestination& operator=(const ReserveDestination&) = delete; @@ -168,7 +165,7 @@ public: } //! Reserve an address - bool GetReservedDestination(const OutputType type, CTxDestination& pubkey, bool internal); + bool GetReservedDestination(CTxDestination& pubkey, bool internal); //! Return reserved address void ReturnDestination(); //! Keep the address. Do not return it's key to the keypool when this object goes out of scope @@ -584,6 +581,8 @@ struct CoinSelectionParams size_t change_spend_size = 0; CFeeRate effective_fee = CFeeRate(0); size_t tx_noinputs_size = 0; + //! Indicate that we are subtracting the fee from outputs + bool m_subtract_fee_outputs = false; CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_fee, size_t tx_noinputs_size) : use_bnb(use_bnb), change_output_size(change_output_size), change_spend_size(change_spend_size), effective_fee(effective_fee), tx_noinputs_size(tx_noinputs_size) {} CoinSelectionParams() {} @@ -598,14 +597,7 @@ class CWallet final : public WalletStorage, private interfaces::Chain::Notificat private: CKeyingMaterial vMasterKey GUARDED_BY(cs_KeyStore); - //! if fUseCrypto is true, mapKeys must be empty - //! if fUseCrypto is false, vMasterKey must be empty - std::atomic<bool> fUseCrypto; - - //! keeps track of whether Unlock has run a thorough check before - bool fDecryptionThoroughlyChecked; - bool SetCrypted(); bool Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys = false); std::atomic<bool> fAbortRescan{false}; @@ -735,9 +727,7 @@ public: /** Construct wallet with specified name and database implementation. */ CWallet(interfaces::Chain* chain, const WalletLocation& location, std::unique_ptr<WalletDatabase> database) - : fUseCrypto(false), - fDecryptionThoroughlyChecked(false), - m_chain(chain), + : m_chain(chain), m_location(location), database(std::move(database)) { @@ -747,11 +737,9 @@ public: { // Should not have slots connected at this point. assert(NotifyUnload.empty()); - delete encrypted_batch; - encrypted_batch = nullptr; } - bool IsCrypted() const { return fUseCrypto; } + bool IsCrypted() const; bool IsLocked() const override; bool Lock(); @@ -1128,10 +1116,18 @@ public: LogPrintf(("%s " + fmt).c_str(), GetDisplayName(), parameters...); }; - ScriptPubKeyMan* GetScriptPubKeyMan() const; - const SigningProvider* GetSigningProvider() const; + //! Get the ScriptPubKeyMan for a script + ScriptPubKeyMan* GetScriptPubKeyMan(const CScript& script) const; + + //! Get the SigningProvider for a script + const SigningProvider* GetSigningProvider(const CScript& script) const; + const SigningProvider* GetSigningProvider(const CScript& script, SignatureData& sigdata) const; + LegacyScriptPubKeyMan* GetLegacyScriptPubKeyMan() const; + const CKeyingMaterial& GetEncryptionKey() const override; + bool HasEncryptionKeys() const override; + // Temporary LegacyScriptPubKeyMan accessors and aliases. friend class LegacyScriptPubKeyMan; std::unique_ptr<LegacyScriptPubKeyMan> m_spk_man = MakeUnique<LegacyScriptPubKeyMan>(*this); @@ -1141,8 +1137,6 @@ public: LegacyScriptPubKeyMan::CryptedKeyMap& mapCryptedKeys GUARDED_BY(cs_KeyStore) = m_spk_man->mapCryptedKeys; LegacyScriptPubKeyMan::WatchOnlySet& setWatchOnly GUARDED_BY(cs_KeyStore) = m_spk_man->setWatchOnly; LegacyScriptPubKeyMan::WatchKeyMap& mapWatchKeys GUARDED_BY(cs_KeyStore) = m_spk_man->mapWatchKeys; - WalletBatch*& encrypted_batch GUARDED_BY(cs_wallet) = m_spk_man->encrypted_batch; - using CryptedKeyMap = LegacyScriptPubKeyMan::CryptedKeyMap; /** Get last block processed height */ int GetLastBlockHeight() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) diff --git a/src/warnings.cpp b/src/warnings.cpp index 35d2033ba8..8df77096f1 100644 --- a/src/warnings.cpp +++ b/src/warnings.cpp @@ -38,41 +38,34 @@ void SetfLargeWorkInvalidChainFound(bool flag) fLargeWorkInvalidChainFound = flag; } -std::string GetWarnings(const std::string& strFor) +std::string GetWarnings(bool verbose) { - std::string strStatusBar; - std::string strGUI; - const std::string uiAlertSeperator = "<hr />"; + std::string warnings_concise; + std::string warnings_verbose; + const std::string warning_separator = "<hr />"; LOCK(cs_warnings); + // Pre-release build warning if (!CLIENT_VERSION_IS_RELEASE) { - strStatusBar = "This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"; - strGUI = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications").translated; + warnings_concise = "This is a pre-release test build - use at your own risk - do not use for mining or merchant applications"; + warnings_verbose = _("This is a pre-release test build - use at your own risk - do not use for mining or merchant applications").translated; } // Misc warnings like out of disk space and clock is wrong - if (strMiscWarning != "") - { - strStatusBar = strMiscWarning; - strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + strMiscWarning; + if (strMiscWarning != "") { + warnings_concise = strMiscWarning; + warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + strMiscWarning; } - if (fLargeWorkForkFound) - { - strStatusBar = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."; - strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.").translated; - } - else if (fLargeWorkInvalidChainFound) - { - strStatusBar = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."; - strGUI += (strGUI.empty() ? "" : uiAlertSeperator) + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.").translated; + if (fLargeWorkForkFound) { + warnings_concise = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."; + warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.").translated; + } else if (fLargeWorkInvalidChainFound) { + warnings_concise = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."; + warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.").translated; } - if (strFor == "gui") - return strGUI; - else if (strFor == "statusbar") - return strStatusBar; - assert(!"GetWarnings(): invalid parameter"); - return "error"; + if (verbose) return warnings_verbose; + else return warnings_concise; } diff --git a/src/warnings.h b/src/warnings.h index e6701ebd9e..58e5e4cd19 100644 --- a/src/warnings.h +++ b/src/warnings.h @@ -13,11 +13,11 @@ void SetfLargeWorkForkFound(bool flag); bool GetfLargeWorkForkFound(); void SetfLargeWorkInvalidChainFound(bool flag); /** Format a string that describes several potential problems detected by the core. - * @param[in] strFor can have the following values: - * - "statusbar": get the most important warning - * - "gui": get all warnings, translated (where possible) for GUI, separated by <hr /> - * @returns the warning string selected by strFor + * @param[in] verbose bool + * - if true, get all warnings, translated (where possible), separated by <hr /> + * - if false, get the most important warning + * @returns the warning string */ -std::string GetWarnings(const std::string& strFor); +std::string GetWarnings(bool verbose); #endif // BITCOIN_WARNINGS_H diff --git a/test/README.md b/test/README.md index 24a9389fac..c3e4ae9ad2 100644 --- a/test/README.md +++ b/test/README.md @@ -258,6 +258,7 @@ Use the `-v` option for verbose output. |-----------|:----------:|:-------------------------------------------:|-------------- | [`lint-python.sh`](lint/lint-python.sh) | [flake8](https://gitlab.com/pycqa/flake8) | [3.7.8](https://github.com/bitcoin/bitcoin/pull/15257) | `pip3 install flake8==3.7.8` | [`lint-shell.sh`](lint/lint-shell.sh) | [ShellCheck](https://github.com/koalaman/shellcheck) | [0.6.0](https://github.com/bitcoin/bitcoin/pull/15166) | [details...](https://github.com/koalaman/shellcheck#installing) +| [`lint-shell.sh`](lint/lint-shell.sh) | [yq](https://github.com/kislyuk/yq) | default | `pip3 install yq` | [`lint-spelling.sh`](lint/lint-spelling.sh) | [codespell](https://github.com/codespell-project/codespell) | [1.15.0](https://github.com/bitcoin/bitcoin/pull/16186) | `pip3 install codespell==1.15.0` Please be aware that on Linux distributions all dependencies are usually available as packages, but could be outdated. diff --git a/test/config.ini.in b/test/config.ini.in index 060c553da2..9687206ee1 100644 --- a/test/config.ini.in +++ b/test/config.ini.in @@ -16,6 +16,7 @@ RPCAUTH=@abs_top_srcdir@/share/rpcauth/rpcauth.py # 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_CLI_TRUE@ENABLE_CLI=true +@BUILD_BITCOIN_WALLET_TRUE@ENABLE_WALLET_TOOL=true @BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=true @ENABLE_FUZZ_TRUE@ENABLE_FUZZ=true @ENABLE_ZMQ_TRUE@ENABLE_ZMQ=true diff --git a/test/functional/create_cache.py b/test/functional/create_cache.py index edf16fa47e..16a791177b 100755 --- a/test/functional/create_cache.py +++ b/test/functional/create_cache.py @@ -16,7 +16,6 @@ class CreateCache(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 0 - self.supports_cli = True def setup_network(self): pass diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py index b997c76025..801209b1c0 100755 --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -13,6 +13,7 @@ class ConfArgsTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 + self.supports_cli = False def test_config_file_parser(self): # Assume node is stopped diff --git a/test/functional/feature_csv_activation.py b/test/functional/feature_csv_activation.py index 6bd321992a..c2b4de54f2 100755 --- a/test/functional/feature_csv_activation.py +++ b/test/functional/feature_csv_activation.py @@ -139,6 +139,7 @@ class BIP68_112_113Test(BitcoinTestFramework): self.num_nodes = 1 self.setup_clean_chain = True self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=4', '-addresstype=legacy']] + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/feature_dbcrash.py b/test/functional/feature_dbcrash.py index 6bd6bb5b8c..5bbdb8cda1 100755 --- a/test/functional/feature_dbcrash.py +++ b/test/functional/feature_dbcrash.py @@ -51,6 +51,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework): self.num_nodes = 4 self.setup_clean_chain = False self.rpc_timeout = 480 + self.supports_cli = False # Set -maxmempool=0 to turn off mempool memory sharing with dbcache # Set -rpcservertimeout=900 to reduce socket disconnects in this diff --git a/test/functional/feature_loadblock.py b/test/functional/feature_loadblock.py index bf2a4ff61f..bd4b271ca7 100755 --- a/test/functional/feature_loadblock.py +++ b/test/functional/feature_loadblock.py @@ -26,6 +26,7 @@ class LoadblockTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 + self.supports_cli = False def run_test(self): self.nodes[1].setnetworkactive(state=False) diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py index 180ea0e51d..42e2b5bff2 100755 --- a/test/functional/feature_maxuploadtarget.py +++ b/test/functional/feature_maxuploadtarget.py @@ -36,6 +36,7 @@ class MaxUploadTest(BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [["-maxuploadtarget=800", "-acceptnonstdtxn=1"]] + self.supports_cli = False # Cache for utxos, as the listunspent may take a long time later in the test self.utxo_cache = [] diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py index 51523f13e7..e1e0f00530 100755 --- a/test/functional/feature_pruning.py +++ b/test/functional/feature_pruning.py @@ -78,6 +78,7 @@ class PruneTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 6 + self.supports_cli = False # Create nodes 0 and 1 to mine. # Create node 2 to test pruning. diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py index e7afbd0272..9e578f0026 100755 --- a/test/functional/feature_rbf.py +++ b/test/functional/feature_rbf.py @@ -76,6 +76,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): "-limitdescendantsize=101", ], ] + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/feature_shutdown.py b/test/functional/feature_shutdown.py index 5084cb1322..9485db9864 100755 --- a/test/functional/feature_shutdown.py +++ b/test/functional/feature_shutdown.py @@ -17,6 +17,7 @@ class ShutdownTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 + self.supports_cli = False def run_test(self): node = get_rpc_proxy(self.nodes[0].url, 1, timeout=600, coveragedir=self.nodes[0].coverage_dir) diff --git a/test/functional/interface_bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py index 0a378c5ef5..f04a58cd19 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_cli() + def run_test(self): """Main test logic""" @@ -56,7 +59,6 @@ class TestBitcoinCli(BitcoinTestFramework): blockchain_info = self.nodes[0].getblockchaininfo() assert_equal(cli_get_info['version'], network_info['version']) - assert_equal(cli_get_info['protocolversion'], network_info['protocolversion']) assert_equal(cli_get_info['blocks'], blockchain_info['blocks']) assert_equal(cli_get_info['timeoffset'], network_info['timeoffset']) assert_equal(cli_get_info['connections'], network_info['connections']) @@ -64,9 +66,7 @@ class TestBitcoinCli(BitcoinTestFramework): assert_equal(cli_get_info['difficulty'], blockchain_info['difficulty']) assert_equal(cli_get_info['chain'], blockchain_info['chain']) if self.is_wallet_compiled(): - assert_equal(cli_get_info['walletversion'], wallet_info['walletversion']) assert_equal(cli_get_info['balance'], wallet_info['balance']) - assert_equal(cli_get_info['keypoololdest'], wallet_info['keypoololdest']) assert_equal(cli_get_info['keypoolsize'], wallet_info['keypoolsize']) assert_equal(cli_get_info['paytxfee'], wallet_info['paytxfee']) assert_equal(cli_get_info['relayfee'], network_info['relayfee']) diff --git a/test/functional/interface_http.py b/test/functional/interface_http.py index bb868d7115..d007490f80 100755 --- a/test/functional/interface_http.py +++ b/test/functional/interface_http.py @@ -13,6 +13,7 @@ import urllib.parse class HTTPBasicsTest (BitcoinTestFramework): def set_test_params(self): self.num_nodes = 3 + self.supports_cli = False def setup_network(self): self.setup_nodes() diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py index a036dfc790..e73ec90819 100755 --- a/test/functional/interface_rest.py +++ b/test/functional/interface_rest.py @@ -44,6 +44,7 @@ class RESTTest (BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 2 self.extra_args = [["-rest"], []] + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() @@ -151,7 +152,7 @@ class RESTTest (BitcoinTestFramework): bin_response = self.test_rest_request("/getutxos", http_method='POST', req_type=ReqType.BIN, body=bin_request, ret_type=RetType.BYTES) output = BytesIO(bin_response) - chain_height, = unpack("i", output.read(4)) + chain_height, = unpack("<i", output.read(4)) response_hash = output.read(32)[::-1].hex() assert_equal(bb_hash, response_hash) # check if getutxo's chaintip during calculation was fine diff --git a/test/functional/interface_rpc.py b/test/functional/interface_rpc.py index e99fa22646..e460db39f8 100755 --- a/test/functional/interface_rpc.py +++ b/test/functional/interface_rpc.py @@ -22,6 +22,7 @@ class RPCInterfaceTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True + self.supports_cli = False def test_getrpcinfo(self): self.log.info("Testing getrpcinfo...") diff --git a/test/functional/mempool_accept.py b/test/functional/mempool_accept.py index c466b120f2..9cb6b03ee0 100755 --- a/test/functional/mempool_accept.py +++ b/test/functional/mempool_accept.py @@ -37,6 +37,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework): self.extra_args = [[ '-txindex', ]] * self.num_nodes + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() @@ -267,6 +268,12 @@ class MempoolAcceptanceTest(BitcoinTestFramework): rawtxs=[tx.serialize().hex()], ) tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) + tx.vin[0].scriptSig = CScript([b'a' * 1648]) # Some too large scriptSig (>1650 bytes) + self.check_mempool_result( + result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'scriptsig-size'}], + rawtxs=[tx.serialize().hex()], + ) + tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_reference))) output_p2sh_burn = CTxOut(nValue=540, scriptPubKey=CScript([OP_HASH160, hash160(b'burn'), OP_EQUAL])) num_scripts = 100000 // len(output_p2sh_burn.serialize()) # Use enough outputs to make the tx too large for our policy tx.vout = [output_p2sh_burn] * num_scripts diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py index edf2069933..39035f7cb1 100755 --- a/test/functional/mempool_limit.py +++ b/test/functional/mempool_limit.py @@ -18,6 +18,7 @@ class MempoolLimitTest(BitcoinTestFramework): "-maxmempool=5", "-spendzeroconfchange=0", ]] + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/mining_basic.py b/test/functional/mining_basic.py index f9231614ce..bfeaa76c74 100755 --- a/test/functional/mining_basic.py +++ b/test/functional/mining_basic.py @@ -43,6 +43,7 @@ class MiningTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True + self.supports_cli = False def mine_chain(self): self.log.info('Create some old blocks') diff --git a/test/functional/mining_getblocktemplate_longpoll.py b/test/functional/mining_getblocktemplate_longpoll.py index 445ec124ce..6d0b241e57 100755 --- a/test/functional/mining_getblocktemplate_longpoll.py +++ b/test/functional/mining_getblocktemplate_longpoll.py @@ -27,6 +27,7 @@ class LongpollThread(threading.Thread): class GetBlockTemplateLPTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/mining_prioritisetransaction.py b/test/functional/mining_prioritisetransaction.py index 7e05a8e6c8..1426fdaacb 100755 --- a/test/functional/mining_prioritisetransaction.py +++ b/test/functional/mining_prioritisetransaction.py @@ -18,6 +18,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework): "-printpriority=1", "-acceptnonstdtxn=1", ]] * self.num_nodes + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/p2p_disconnect_ban.py b/test/functional/p2p_disconnect_ban.py index 23dea4b729..1b64b65b82 100755 --- a/test/functional/p2p_disconnect_ban.py +++ b/test/functional/p2p_disconnect_ban.py @@ -16,6 +16,7 @@ from test_framework.util import ( class DisconnectBanTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 + self.supports_cli = False def run_test(self): self.log.info("Connect nodes both way") diff --git a/test/functional/p2p_invalid_messages.py b/test/functional/p2p_invalid_messages.py index f0ceb8e6a3..20864881c1 100755 --- a/test/functional/p2p_invalid_messages.py +++ b/test/functional/p2p_invalid_messages.py @@ -4,7 +4,6 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test node responses to invalid network messages.""" import asyncio -import os import struct import sys @@ -66,27 +65,21 @@ class InvalidMessagesTest(BitcoinTestFramework): msg_at_size = msg_unrecognized(str_data="b" * valid_data_limit) assert len(msg_at_size.serialize()) == msg_limit - increase_allowed = 0.5 - if [s for s in os.environ.get("BITCOIN_CONFIG", "").split(" ") if "--with-sanitizers" in s and "address" in s]: - increase_allowed = 3.5 - with node.assert_memory_usage_stable(increase_allowed=increase_allowed): - self.log.info( - "Sending a bunch of large, junk messages to test " - "memory exhaustion. May take a bit...") - - # Run a bunch of times to test for memory exhaustion. - for _ in range(80): - node.p2p.send_message(msg_at_size) - - # Check that, even though the node is being hammered by nonsense from one - # connection, it can still service other peers in a timely way. - for _ in range(20): - conn2.sync_with_ping(timeout=2) - - # Peer 1, despite serving up a bunch of nonsense, should still be connected. - self.log.info("Waiting for node to drop junk messages.") - node.p2p.sync_with_ping(timeout=320) - assert node.p2p.is_connected + self.log.info("Sending a bunch of large, junk messages to test memory exhaustion. May take a bit...") + + # Run a bunch of times to test for memory exhaustion. + for _ in range(80): + node.p2p.send_message(msg_at_size) + + # Check that, even though the node is being hammered by nonsense from one + # connection, it can still service other peers in a timely way. + for _ in range(20): + conn2.sync_with_ping(timeout=2) + + # Peer 1, despite serving up a bunch of nonsense, should still be connected. + self.log.info("Waiting for node to drop junk messages.") + node.p2p.sync_with_ping(timeout=320) + assert node.p2p.is_connected # # 1. diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index 0c7edbf434..297fa88fbe 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -191,6 +191,7 @@ class SegWitTest(BitcoinTestFramework): ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0", "-segwitheight={}".format(SEGWIT_HEIGHT)], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=1", "-segwitheight=-1"] ] + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() @@ -1116,7 +1117,7 @@ class SegWitTest(BitcoinTestFramework): MAX_PROGRAM_LENGTH = 10000 # This program is 19 max pushes (9937 bytes), then 64 more opcode-bytes. - long_witness_program = CScript([b'a' * 520] * 19 + [OP_DROP] * 63 + [OP_TRUE]) + long_witness_program = CScript([b'a' * MAX_SCRIPT_ELEMENT_SIZE] * 19 + [OP_DROP] * 63 + [OP_TRUE]) assert len(long_witness_program) == MAX_PROGRAM_LENGTH + 1 long_witness_hash = sha256(long_witness_program) long_script_pubkey = CScript([OP_0, long_witness_hash]) @@ -1140,7 +1141,7 @@ class SegWitTest(BitcoinTestFramework): 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]) + witness_program = CScript([b'a' * MAX_SCRIPT_ELEMENT_SIZE] * 19 + [OP_DROP] * 62 + [OP_TRUE]) assert len(witness_program) == MAX_PROGRAM_LENGTH witness_hash = sha256(witness_program) script_pubkey = CScript([OP_0, witness_hash]) diff --git a/test/functional/rpc_bind.py b/test/functional/rpc_bind.py index 8979251a26..664a15a5ec 100755 --- a/test/functional/rpc_bind.py +++ b/test/functional/rpc_bind.py @@ -15,6 +15,7 @@ class RPCBindTest(BitcoinTestFramework): self.setup_clean_chain = True self.bind_to_localhost_only = False self.num_nodes = 1 + self.supports_cli = False def setup_network(self): self.add_nodes(self.num_nodes, None) diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 278ce6d911..adf6f1ca4f 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -49,6 +49,7 @@ class BlockchainTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 + self.supports_cli = False def run_test(self): self.mine_chain() diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py index 2a64a29967..aa7f12848c 100755 --- a/test/functional/rpc_createmultisig.py +++ b/test/functional/rpc_createmultisig.py @@ -22,6 +22,7 @@ class RpcCreateMultiSigTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/rpc_deriveaddresses.py b/test/functional/rpc_deriveaddresses.py index 42128d5767..42d7d59d56 100755 --- a/test/functional/rpc_deriveaddresses.py +++ b/test/functional/rpc_deriveaddresses.py @@ -10,7 +10,6 @@ from test_framework.util import assert_equal, assert_raises_rpc_error class DeriveaddressesTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 - self.supports_cli = 1 def run_test(self): assert_raises_rpc_error(-5, "Missing checksum", self.nodes[0].deriveaddresses, "a") diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py index 693051edc0..6f1ae0d3ba 100755 --- a/test/functional/rpc_fundrawtransaction.py +++ b/test/functional/rpc_fundrawtransaction.py @@ -92,6 +92,7 @@ class RawTransactionsTest(BitcoinTestFramework): self.test_option_feerate() self.test_address_reuse() self.test_option_subtract_fee_from_outputs() + self.test_subtract_fee_with_presets() def test_change_position(self): """Ensure setting changePosition in fundraw with an exact match is handled properly.""" @@ -741,5 +742,17 @@ class RawTransactionsTest(BitcoinTestFramework): # The total subtracted from the outputs is equal to the fee. assert_equal(share[0] + share[2] + share[3], result[0]['fee']) + def test_subtract_fee_with_presets(self): + self.log.info("Test fundrawtxn subtract fee from outputs with preset inputs that are sufficient") + + addr = self.nodes[0].getnewaddress() + txid = self.nodes[0].sendtoaddress(addr, 10) + vout = find_vout_for_address(self.nodes[0], txid, addr) + + rawtx = self.nodes[0].createrawtransaction([{'txid': txid, 'vout': vout}], [{self.nodes[0].getnewaddress(): 5}]) + fundedtx = self.nodes[0].fundrawtransaction(rawtx, {'subtractFeeFromOutputs': [0]}) + signedtx = self.nodes[0].signrawtransactionwithwallet(fundedtx['hex']) + self.nodes[0].sendrawtransaction(signedtx['hex']) + if __name__ == '__main__': RawTransactionsTest().main() diff --git a/test/functional/rpc_getblockstats.py b/test/functional/rpc_getblockstats.py index efab69ac26..57794ae973 100755 --- a/test/functional/rpc_getblockstats.py +++ b/test/functional/rpc_getblockstats.py @@ -33,6 +33,7 @@ class GetblockstatsTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True + self.supports_cli = False def get_stats(self): return [self.nodes[0].getblockstats(hash_or_height=self.start_height + i) for i in range(self.max_stat_pos+1)] diff --git a/test/functional/rpc_help.py b/test/functional/rpc_help.py index 78d6e78aed..ec8ea5697a 100755 --- a/test/functional/rpc_help.py +++ b/test/functional/rpc_help.py @@ -13,6 +13,7 @@ import os class HelpRpcTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 + self.supports_cli = False def run_test(self): self.test_categories() diff --git a/test/functional/rpc_misc.py b/test/functional/rpc_misc.py index 3da9f05ca5..c8517d719e 100755 --- a/test/functional/rpc_misc.py +++ b/test/functional/rpc_misc.py @@ -19,6 +19,7 @@ from test_framework.authproxy import JSONRPCException class RpcMiscTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 + self.supports_cli = False def run_test(self): node = self.nodes[0] diff --git a/test/functional/rpc_named_arguments.py b/test/functional/rpc_named_arguments.py index ecac9c2f82..41b9312969 100755 --- a/test/functional/rpc_named_arguments.py +++ b/test/functional/rpc_named_arguments.py @@ -13,6 +13,7 @@ from test_framework.util import ( class NamedArgumentTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 + self.supports_cli = False def run_test(self): node = self.nodes[0] diff --git a/test/functional/rpc_net.py b/test/functional/rpc_net.py index 615f9abbe0..368debf35c 100755 --- a/test/functional/rpc_net.py +++ b/test/functional/rpc_net.py @@ -45,6 +45,7 @@ class NetTest(BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 2 self.extra_args = [["-minrelaytxfee=0.00001000"],["-minrelaytxfee=0.00000500"]] + self.supports_cli = False def run_test(self): self.log.info('Connect nodes both way') diff --git a/test/functional/rpc_preciousblock.py b/test/functional/rpc_preciousblock.py index 0663ffdf5b..8386e47411 100755 --- a/test/functional/rpc_preciousblock.py +++ b/test/functional/rpc_preciousblock.py @@ -36,6 +36,7 @@ class PreciousTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 + self.supports_cli = False def setup_network(self): self.setup_nodes() diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py index 61572654e0..2cc9650cb2 100755 --- a/test/functional/rpc_psbt.py +++ b/test/functional/rpc_psbt.py @@ -32,6 +32,7 @@ class PSBTTest(BitcoinTestFramework): ["-walletrbf=0"], [] ] + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() @@ -416,5 +417,10 @@ class PSBTTest(BitcoinTestFramework): analyzed = self.nodes[0].analyzepsbt(signed) assert analyzed['inputs'][0]['has_utxo'] and analyzed['inputs'][0]['is_final'] and analyzed['next'] == 'extractor' + self.log.info("PSBT spending unspendable outputs should have error message and Creator as next") + analysis = self.nodes[0].analyzepsbt('cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWAEHYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFv8/wADXYP/7//////8JxOh0LR2HAI8AAAAAAAEBIADC6wsAAAAAF2oUt/X69ELjeX2nTof+fZ10l+OyAokDAQcJAwEHEAABAACAAAEBIADC6wsAAAAAF2oUt/X69ELjeX2nTof+fZ10l+OyAokDAQcJAwEHENkMak8AAAAA') + assert_equal(analysis['next'], 'creator') + assert_equal(analysis['error'], 'PSBT is not valid. Input 0 spends unspendable output') + if __name__ == '__main__': PSBTTest().main() diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py index 4ee46d5f53..14cad3d1b8 100755 --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -53,6 +53,7 @@ class RawTransactionsTest(BitcoinTestFramework): ["-txindex"], ["-txindex"], ] + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py index 9f94d11a93..f31e4f43bd 100755 --- a/test/functional/rpc_scantxoutset.py +++ b/test/functional/rpc_scantxoutset.py @@ -116,5 +116,12 @@ class ScantxoutsetTest(BitcoinTestFramework): assert_equal(descriptors(self.nodes[0].scantxoutset("start", [ "combo(tprv8ZgxMBicQKsPd7Uf69XL1XwhmjHopUGep8GuEiJDZmbQz6o58LninorQAfcKZWARbtRtfnLcJ5MQ2AtHcQJCCRUcMRvmDUjyEmNUWwx8UbK/1/1/0)"])), ["pkh([0c5f9a1e/1/1/0]03e1c5b6e650966971d7e71ef2674f80222752740fc1dfd63bbbd220d2da9bd0fb)#cxmct4w8"]) assert_equal(descriptors(self.nodes[0].scantxoutset("start", [ {"desc": "combo(tpubD6NzVbkrYhZ4WaWSyoBvQwbpLkojyoTZPRsgXELWz3Popb3qkjcJyJUGLnL4qHHoQvao8ESaAstxYSnhyswJ76uZPStJRJCTKvosUCJZL5B/1/1/*)", "range": 1500}])), ['pkh([0c5f9a1e/1/1/0]03e1c5b6e650966971d7e71ef2674f80222752740fc1dfd63bbbd220d2da9bd0fb)#cxmct4w8', 'pkh([0c5f9a1e/1/1/1500]03832901c250025da2aebae2bfb38d5c703a57ab66ad477f9c578bfbcd78abca6f)#vchwd07g', 'pkh([0c5f9a1e/1/1/1]030d820fc9e8211c4169be8530efbc632775d8286167afd178caaf1089b77daba7)#z2t3ypsa']) + # Check that status and abort don't need second arg + assert_equal(self.nodes[0].scantxoutset("status"), None) + assert_equal(self.nodes[0].scantxoutset("abort"), False) + + # Check that second arg is needed for start + assert_raises_rpc_error(-1, "scanobjects argument is required for the start action", self.nodes[0].scantxoutset, "start") + if __name__ == '__main__': ScantxoutsetTest().main() diff --git a/test/functional/rpc_users.py b/test/functional/rpc_users.py index 8bbb3c04fa..3541f96ab7 100755 --- a/test/functional/rpc_users.py +++ b/test/functional/rpc_users.py @@ -35,6 +35,7 @@ def call_with_auth(node, user, password): class HTTPBasicsTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 + self.supports_cli = False def setup_chain(self): super().setup_chain() diff --git a/test/functional/rpc_whitelist.py b/test/functional/rpc_whitelist.py new file mode 100644 index 0000000000..219132410b --- /dev/null +++ b/test/functional/rpc_whitelist.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017-2019 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +""" +A test for RPC users with restricted permissions +""" +from test_framework.test_framework import BitcoinTestFramework +import os +from test_framework.util import ( + get_datadir_path, + assert_equal, + str_to_b64str +) +import http.client +import urllib.parse + +def rpccall(node, user, method): + url = urllib.parse.urlparse(node.url) + headers = {"Authorization": "Basic " + str_to_b64str('{}:{}'.format(user[0], user[3]))} + conn = http.client.HTTPConnection(url.hostname, url.port) + conn.connect() + conn.request('POST', '/', '{"method": "' + method + '"}', headers) + resp = conn.getresponse() + conn.close() + return resp + + +class RPCWhitelistTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + + def setup_chain(self): + super().setup_chain() + # 0 => Username + # 1 => Password (Hashed) + # 2 => Permissions + # 3 => Password Plaintext + self.users = [ + ["user1", "50358aa884c841648e0700b073c32b2e$b73e95fff0748cc0b517859d2ca47d9bac1aa78231f3e48fa9222b612bd2083e", "getbestblockhash,getblockcount,", "12345"], + ["user2", "8650ba41296f62092377a38547f361de$4620db7ba063ef4e2f7249853e9f3c5c3592a9619a759e3e6f1c63f2e22f1d21", "getblockcount", "54321"] + ] + # For exceptions + self.strange_users = [ + # Test empty + ["strangedude", "62d67dffec03836edd698314f1b2be62$c2fb4be29bb0e3646298661123cf2d8629640979cabc268ef05ea613ab54068d", ":", "s7R4nG3R7H1nGZ"], + ["strangedude2", "575c012c7fe4b1e83b9d809412da3ef7$09f448d0acfc19924dd62ecb96004d3c2d4b91f471030dfe43c6ea64a8f658c1", "", "s7R4nG3R7H1nGZ"], + # Test trailing comma + ["strangedude3", "23189c561b5975a56f4cf94030495d61$3a2f6aac26351e2257428550a553c4c1979594e36675bbd3db692442387728c0", ":getblockcount,", "s7R4nG3R7H1nGZ"], + # Test overwrite + ["strangedude4", "990c895760a70df83949e8278665e19a$8f0906f20431ff24cb9e7f5b5041e4943bdf2a5c02a19ef4960dcf45e72cde1c", ":getblockcount, getbestblockhash", "s7R4nG3R7H1nGZ"], + ["strangedude4", "990c895760a70df83949e8278665e19a$8f0906f20431ff24cb9e7f5b5041e4943bdf2a5c02a19ef4960dcf45e72cde1c", ":getblockcount", "s7R4nG3R7H1nGZ"], + # Testing the same permission twice + ["strangedude5", "d12c6e962d47a454f962eb41225e6ec8$2dd39635b155536d3c1a2e95d05feff87d5ba55f2d5ff975e6e997a836b717c9", ":getblockcount,getblockcount", "s7R4nG3R7H1nGZ"] + ] + # These commands shouldn't be allowed for any user to test failures + self.never_allowed = ["getnetworkinfo"] + with open(os.path.join(get_datadir_path(self.options.tmpdir, 0), "bitcoin.conf"), 'a', encoding='utf8') as f: + f.write("\nrpcwhitelistdefault=0\n") + for user in self.users: + f.write("rpcauth=" + user[0] + ":" + user[1] + "\n") + f.write("rpcwhitelist=" + user[0] + ":" + user[2] + "\n") + # Special cases + for strangedude in self.strange_users: + f.write("rpcauth=" + strangedude[0] + ":" + strangedude[1] + "\n") + f.write("rpcwhitelist=" + strangedude[0] + strangedude[2] + "\n") + + + def run_test(self): + for user in self.users: + permissions = user[2].replace(" ", "").split(",") + # Pop all empty items + i = 0 + while i < len(permissions): + if permissions[i] == '': + permissions.pop(i) + + i += 1 + for permission in permissions: + self.log.info("[" + user[0] + "]: Testing a permitted permission (" + permission + ")") + assert_equal(200, rpccall(self.nodes[0], user, permission).status) + for permission in self.never_allowed: + self.log.info("[" + user[0] + "]: Testing a non permitted permission (" + permission + ")") + assert_equal(403, rpccall(self.nodes[0], user, permission).status) + # Now test the strange users + for permission in self.never_allowed: + self.log.info("Strange test 1") + assert_equal(403, rpccall(self.nodes[0], self.strange_users[0], permission).status) + for permission in self.never_allowed: + self.log.info("Strange test 2") + assert_equal(403, rpccall(self.nodes[0], self.strange_users[1], permission).status) + self.log.info("Strange test 3") + assert_equal(200, rpccall(self.nodes[0], self.strange_users[2], "getblockcount").status) + self.log.info("Strange test 4") + assert_equal(403, rpccall(self.nodes[0], self.strange_users[3], "getbestblockhash").status) + self.log.info("Strange test 5") + assert_equal(200, rpccall(self.nodes[0], self.strange_users[4], "getblockcount").status) + +if __name__ == "__main__": + RPCWhitelistTest().main() diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index c56c0d06ff..da92c6325a 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -12,6 +12,7 @@ import os import pdb import random import shutil +import subprocess import sys import tempfile import time @@ -96,7 +97,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): self.nodes = [] self.network_thread = None self.rpc_timeout = 60 # Wait for up to 60 seconds for the RPC server to respond - self.supports_cli = False + self.supports_cli = True self.bind_to_localhost_only = True self.set_test_params() self.parse_args() @@ -121,6 +122,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): except KeyError: self.log.exception("Key error") self.success = TestStatus.FAILED + except subprocess.CalledProcessError as e: + self.log.exception("Called Process failed with '{}'".format(e.output)) + self.success = TestStatus.FAILED except Exception: self.log.exception("Unexpected exception caught during testing") self.success = TestStatus.FAILED @@ -157,6 +161,8 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): help="use bitcoin-cli instead of RPC for all commands") parser.add_argument("--perf", dest="perf", default=False, action="store_true", help="profile running nodes with perf for the duration of the test") + parser.add_argument("--valgrind", dest="valgrind", default=False, action="store_true", + help="run nodes under the valgrind memory error detector: expect at least a ~10x slowdown, valgrind 3.14 or later required") parser.add_argument("--randomseed", type=int, help="set a random seed for deterministically reproducing a previous test run") self.add_options(parser) @@ -394,6 +400,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): extra_args=extra_args[i], use_cli=self.options.usecli, start_perf=self.options.perf, + use_valgrind=self.options.valgrind, )) def start_node(self, i, *args, **kwargs): @@ -596,6 +603,11 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): if not self.is_wallet_compiled(): raise SkipTest("wallet has not been compiled.") + def skip_if_no_wallet_tool(self): + """Skip the running test if bitcoin-wallet has not been compiled.""" + if not self.is_wallet_tool_compiled(): + raise SkipTest("bitcoin-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(): @@ -609,6 +621,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): """Checks whether the wallet module was compiled.""" return self.config["components"].getboolean("ENABLE_WALLET") + def is_wallet_tool_compiled(self): + """Checks whether bitcoin-wallet was compiled.""" + return self.config["components"].getboolean("ENABLE_WALLET_TOOL") + def is_zmq_compiled(self): """Checks whether the zmq module was compiled.""" return self.config["components"].getboolean("ENABLE_ZMQ") diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index d8bfdfd040..e5c77ae5fa 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -30,6 +30,7 @@ from .util import ( rpc_url, wait_until, p2p_port, + EncodeDecimal, ) BITCOIND_PROC_WAIT_TIMEOUT = 60 @@ -59,7 +60,7 @@ class TestNode(): To make things easier for the test writer, any unrecognised messages will be dispatched to the RPC connection.""" - def __init__(self, i, datadir, *, chain, rpchost, timewait, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False): + def __init__(self, i, datadir, *, chain, rpchost, timewait, bitcoind, bitcoin_cli, coverage_dir, cwd, extra_conf=None, extra_args=None, use_cli=False, start_perf=False, use_valgrind=False): """ Kwargs: start_perf (bool): If True, begin profiling the node with `perf` as soon as @@ -96,6 +97,15 @@ class TestNode(): "-debugexclude=leveldb", "-uacomment=testnode%d" % i, ] + if use_valgrind: + default_suppressions_file = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "..", "..", "..", "contrib", "valgrind.supp") + suppressions_file = os.getenv("VALGRIND_SUPPRESSIONS_FILE", + default_suppressions_file) + self.args = ["valgrind", "--suppressions={}".format(suppressions_file), + "--gen-suppressions=all", "--exit-on-first-error=yes", + "--error-exitcode=1", "--quiet"] + self.args self.cli = TestNodeCLI(bitcoin_cli, self.datadir) self.use_cli = use_cli @@ -135,25 +145,6 @@ class TestNode(): assert len(self.PRIV_KEYS) == MAX_NODES return self.PRIV_KEYS[self.index] - def get_mem_rss_kilobytes(self): - """Get the memory usage (RSS) per `ps`. - - Returns None if `ps` is unavailable. - """ - assert self.running - - try: - return int(subprocess.check_output( - ["ps", "h", "-o", "rss", "{}".format(self.process.pid)], - stderr=subprocess.DEVNULL).split()[-1]) - - # Avoid failing on platforms where ps isn't installed. - # - # We could later use something like `psutils` to work across platforms. - except (FileNotFoundError, subprocess.SubprocessError): - self.log.exception("Unable to get memory usage") - return None - 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) @@ -333,33 +324,6 @@ class TestNode(): self._raise_assertion_error('Expected messages "{}" does not partially match log:\n\n{}\n\n'.format(str(expected_msgs), print_log)) @contextlib.contextmanager - def assert_memory_usage_stable(self, *, increase_allowed=0.03): - """Context manager that allows the user to assert that a node's memory usage (RSS) - hasn't increased beyond some threshold percentage. - - Args: - increase_allowed (float): the fractional increase in memory allowed until failure; - e.g. `0.12` for up to 12% increase allowed. - """ - before_memory_usage = self.get_mem_rss_kilobytes() - - yield - - after_memory_usage = self.get_mem_rss_kilobytes() - - if not (before_memory_usage and after_memory_usage): - self.log.warning("Unable to detect memory usage (RSS) - skipping memory check.") - return - - perc_increase_memory_usage = (after_memory_usage / before_memory_usage) - 1 - - if perc_increase_memory_usage > increase_allowed: - self._raise_assertion_error( - "Memory usage increased over threshold of {:.3f}% from {} to {} ({:.3f}%)".format( - increase_allowed * 100, before_memory_usage, after_memory_usage, - perc_increase_memory_usage * 100)) - - @contextlib.contextmanager def profile_with_perf(self, profile_name): """ Context manager that allows easy profiling of node activity using `perf`. @@ -526,7 +490,7 @@ def arg_to_cli(arg): if isinstance(arg, bool): return str(arg).lower() elif isinstance(arg, dict) or isinstance(arg, list): - return json.dumps(arg) + return json.dumps(arg, default=EncodeDecimal) else: return str(arg) diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 4d7967273a..5bb73aee7e 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -190,6 +190,11 @@ def check_json_precision(): if satoshis != 2000000000000003: raise RuntimeError("JSON encode/decode loses precision") +def EncodeDecimal(o): + if isinstance(o, Decimal): + return str(o) + raise TypeError(repr(o) + " is not JSON serializable") + def count_bytes(hex_string): return len(bytearray.fromhex(hex_string)) diff --git a/test/functional/test_framework/wallet_util.py b/test/functional/test_framework/wallet_util.py index c0dfa4c3f0..3d81a61120 100755 --- a/test/functional/test_framework/wallet_util.py +++ b/test/functional/test_framework/wallet_util.py @@ -88,6 +88,11 @@ def get_multisig(node): p2sh_p2wsh_script=CScript([OP_HASH160, witness_script, OP_EQUAL]).hex(), p2sh_p2wsh_addr=script_to_p2sh_p2wsh(script_code)) +def labels_value(name="", purpose="receive"): + """Generate a getaddressinfo labels array from a name and purpose. + Often used as the value of a labels kwarg for calling test_address below.""" + return [{"name": name, "purpose": purpose}] + def test_address(node, address, **kwargs): """Get address info for `address` and test whether the returned values are as expected.""" addr_info = node.getaddressinfo(address) diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 9b4a5d2030..110733c529 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -136,6 +136,7 @@ BASE_SCRIPTS = [ 'interface_rpc.py', 'rpc_psbt.py', 'rpc_users.py', + 'rpc_whitelist.py', 'feature_proxy.py', 'rpc_signrawtransaction.py', 'wallet_groups.py', @@ -364,9 +365,10 @@ def main(): def run_tests(*, test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=False, args=None, combined_logs_len=0, failfast=False, runs_ci, use_term_control): args = args or [] - # Warn if bitcoind is already running (unix only) + # Warn if bitcoind is already running + # pidof might fail or return an empty string if bitcoind is not running try: - if subprocess.check_output(["pidof", "bitcoind"]) is not None: + if subprocess.check_output(["pidof", "bitcoind"]) not in [b'']: print("%sWARNING!%s There is already a bitcoind process running on this system. Tests may fail unexpectedly due to resource contention!" % (BOLD[1], BOLD[0])) except (OSError, subprocess.SubprocessError): pass diff --git a/test/functional/tool_wallet.py b/test/functional/tool_wallet.py index 355cd7af75..32ef257456 100755 --- a/test/functional/tool_wallet.py +++ b/test/functional/tool_wallet.py @@ -23,6 +23,7 @@ class ToolWalletTest(BitcoinTestFramework): def skip_test_if_missing_module(self): self.skip_if_no_wallet() + self.skip_if_no_wallet_tool() def bitcoin_wallet_process(self, *args): binary = self.config["environment"]["BUILDDIR"] + '/src/bitcoin-wallet' + self.config["environment"]["EXEEXT"] diff --git a/test/functional/wallet_abandonconflict.py b/test/functional/wallet_abandonconflict.py index e86679bc31..1122daaf83 100755 --- a/test/functional/wallet_abandonconflict.py +++ b/test/functional/wallet_abandonconflict.py @@ -18,6 +18,7 @@ from test_framework.util import ( assert_raises_rpc_error, connect_nodes, disconnect_nodes, + wait_until, ) @@ -97,6 +98,7 @@ class AbandonConflictTest(BitcoinTestFramework): # TODO: redo with eviction self.stop_node(0) self.start_node(0, extra_args=["-minrelaytxfee=0.0001"]) + wait_until(lambda: self.nodes[0].getmempoolinfo()['loaded']) # Verify txs no longer in either node's mempool assert_equal(len(self.nodes[0].getrawmempool()), 0) @@ -124,6 +126,8 @@ class AbandonConflictTest(BitcoinTestFramework): # Verify that even with a low min relay fee, the tx is not reaccepted from wallet on startup once abandoned self.stop_node(0) self.start_node(0, extra_args=["-minrelaytxfee=0.00001"]) + wait_until(lambda: self.nodes[0].getmempoolinfo()['loaded']) + assert_equal(len(self.nodes[0].getrawmempool()), 0) assert_equal(self.nodes[0].getbalance(), balance) @@ -144,6 +148,7 @@ class AbandonConflictTest(BitcoinTestFramework): # Remove using high relay fee again self.stop_node(0) self.start_node(0, extra_args=["-minrelaytxfee=0.0001"]) + wait_until(lambda: self.nodes[0].getmempoolinfo()['loaded']) assert_equal(len(self.nodes[0].getrawmempool()), 0) newbalance = self.nodes[0].getbalance() assert_equal(newbalance, balance - Decimal("24.9996")) diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py index 479d67fb66..f08606e622 100755 --- a/test/functional/wallet_address_types.py +++ b/test/functional/wallet_address_types.py @@ -83,6 +83,7 @@ class AddressTypeTest(BitcoinTestFramework): # whitelist all peers to speed up tx relay / mempool sync for args in self.extra_args: args.append("-whitelist=127.0.0.1") + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py index 550037923e..2606afa0e4 100755 --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -15,6 +15,10 @@ from test_framework.util import ( connect_nodes, wait_until, ) +from test_framework.wallet_util import ( + labels_value, + test_address, +) class WalletTest(BitcoinTestFramework): @@ -24,6 +28,7 @@ class WalletTest(BitcoinTestFramework): "-acceptnonstdtxn=1", ]] * self.num_nodes self.setup_clean_chain = True + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() @@ -390,7 +395,7 @@ class WalletTest(BitcoinTestFramework): for label in [u'рыба', u'𝅘𝅥𝅯']: addr = self.nodes[0].getnewaddress() self.nodes[0].setlabel(addr, label) - assert_equal(self.nodes[0].getaddressinfo(addr)['label'], label) + test_address(self.nodes[0], addr, label=label, labels=labels_value(name=label)) assert label in self.nodes[0].listlabels() self.nodes[0].rpc.ensure_ascii = True # restore to default diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py index 95d51adebb..0c08655833 100755 --- a/test/functional/wallet_bumpfee.py +++ b/test/functional/wallet_bumpfee.py @@ -101,7 +101,8 @@ def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address): else: bumped_tx = rbf_node.bumpfee(rbfid) assert_equal(bumped_tx["errors"], []) - assert bumped_tx["fee"] - abs(rbftx["fee"]) > 0 + assert bumped_tx["fee"] > -rbftx["fee"] + assert_equal(bumped_tx["origfee"], -rbftx["fee"]) # check that bumped_tx propagates, original tx was evicted and has a wallet conflict self.sync_mempools((rbf_node, peer_node)) assert bumped_tx["txid"] in rbf_node.getrawmempool() @@ -211,15 +212,15 @@ def test_small_output_with_feerate_succeeds(rbf_node, dest_address): # Make sure additional inputs exist rbf_node.generatetoaddress(101, rbf_node.getnewaddress()) rbfid = spend_one_input(rbf_node, dest_address) - original_input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"] - assert_equal(len(original_input_list), 1) - original_txin = original_input_list[0] + input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"] + assert_equal(len(input_list), 1) + original_txin = input_list[0] # Keep bumping until we out-spend change output tx_fee = 0 while tx_fee < Decimal("0.0005"): - new_input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"] - new_item = list(new_input_list)[0] - assert_equal(len(original_input_list), 1) + input_list = rbf_node.getrawtransaction(rbfid, 1)["vin"] + new_item = list(input_list)[0] + assert_equal(len(input_list), 1) assert_equal(original_txin["txid"], new_item["txid"]) assert_equal(original_txin["vout"], new_item["vout"]) rbfid_new_details = rbf_node.bumpfee(rbfid) diff --git a/test/functional/wallet_createwallet.py b/test/functional/wallet_createwallet.py index e302e499f4..048b3127ff 100755 --- a/test/functional/wallet_createwallet.py +++ b/test/functional/wallet_createwallet.py @@ -15,7 +15,6 @@ class CreateWalletTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = False self.num_nodes = 1 - self.supports_cli = True def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py index fa5d5a8878..575b501f50 100755 --- a/test/functional/wallet_hd.py +++ b/test/functional/wallet_hd.py @@ -20,6 +20,7 @@ class WalletHDTest(BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 2 self.extra_args = [[], ['-keypool=0']] + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/wallet_implicitsegwit.py b/test/functional/wallet_implicitsegwit.py index 379fa6a12f..a8583e2879 100755 --- a/test/functional/wallet_implicitsegwit.py +++ b/test/functional/wallet_implicitsegwit.py @@ -41,6 +41,7 @@ def check_implicit_transactions(implicit_keys, implicit_node): class ImplicitSegwitTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py index 79062a4a29..b8b85b7a19 100755 --- a/test/functional/wallet_import_rescan.py +++ b/test/functional/wallet_import_rescan.py @@ -145,6 +145,7 @@ def get_rand_amount(): class ImportRescanTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 + len(IMPORT_NODES) + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/wallet_import_with_label.py b/test/functional/wallet_import_with_label.py index 2a9051b1e8..e356fce469 100755 --- a/test/functional/wallet_import_with_label.py +++ b/test/functional/wallet_import_with_label.py @@ -11,7 +11,10 @@ with and without a label. """ from test_framework.test_framework import BitcoinTestFramework -from test_framework.wallet_util import test_address +from test_framework.wallet_util import ( + labels_value, + test_address, +) class ImportWithLabel(BitcoinTestFramework): @@ -36,7 +39,8 @@ class ImportWithLabel(BitcoinTestFramework): address, iswatchonly=True, ismine=False, - label=label) + label=label, + labels=labels_value(name=label)) self.log.info( "Import the watch-only address's private key without a " @@ -47,7 +51,8 @@ class ImportWithLabel(BitcoinTestFramework): test_address(self.nodes[1], address, - label=label) + label=label, + labels=labels_value(name=label)) self.log.info( "Test importaddress without label and importprivkey with label." @@ -59,7 +64,8 @@ class ImportWithLabel(BitcoinTestFramework): address2, iswatchonly=True, ismine=False, - label="") + label="", + labels=labels_value()) self.log.info( "Import the watch-only address's private key with a " @@ -71,7 +77,8 @@ class ImportWithLabel(BitcoinTestFramework): test_address(self.nodes[1], address2, - label=label2) + label=label2, + labels=labels_value(name=label2)) self.log.info("Test importaddress with label and importprivkey with label.") self.log.info("Import a watch-only address with a label.") @@ -82,7 +89,8 @@ class ImportWithLabel(BitcoinTestFramework): address3, iswatchonly=True, ismine=False, - label=label3_addr) + label=label3_addr, + labels=labels_value(name=label3_addr)) self.log.info( "Import the watch-only address's private key with a " @@ -94,7 +102,8 @@ class ImportWithLabel(BitcoinTestFramework): test_address(self.nodes[1], address3, - label=label3_priv) + label=label3_priv, + labels=labels_value(name=label3_priv)) self.log.info( "Test importprivkey won't label new dests with the same " @@ -109,6 +118,7 @@ class ImportWithLabel(BitcoinTestFramework): iswatchonly=True, ismine=False, label=label4_addr, + labels=labels_value(name=label4_addr), embedded=None) self.log.info( @@ -123,10 +133,13 @@ class ImportWithLabel(BitcoinTestFramework): test_address(self.nodes[1], embedded_addr, - label="") + label="", + labels=labels_value()) + test_address(self.nodes[1], address4, - label=label4_addr) + label=label4_addr, + labels=labels_value(name=label4_addr)) self.stop_nodes() diff --git a/test/functional/wallet_importmulti.py b/test/functional/wallet_importmulti.py index 23748e5dd7..5febac5998 100755 --- a/test/functional/wallet_importmulti.py +++ b/test/functional/wallet_importmulti.py @@ -29,6 +29,7 @@ from test_framework.util import ( from test_framework.wallet_util import ( get_key, get_multisig, + labels_value, test_address, ) @@ -121,7 +122,7 @@ class ImportMultiTest(BitcoinTestFramework): self.test_importmulti({"scriptPubKey": key.p2pkh_script, "timestamp": "now", "internal": True, - "label": "Example label"}, + "label": "Unsuccessful labelling for internal addresses"}, success=False, error_code=-8, error_message='Internal addresses should not have a label') @@ -550,7 +551,7 @@ class ImportMultiTest(BitcoinTestFramework): self.log.info("Should not import a p2sh-p2wpkh address from descriptor without checksum and private key") self.test_importmulti({"desc": "sh(wpkh(" + key.pubkey + "))", "timestamp": "now", - "label": "Descriptor import test", + "label": "Unsuccessful P2SH-P2WPKH descriptor import", "keys": [key.privkey]}, success=False, error_code=-5, @@ -558,17 +559,19 @@ class ImportMultiTest(BitcoinTestFramework): # Test importing of a P2SH-P2WPKH address via descriptor + private key key = get_key(self.nodes[0]) + p2sh_p2wpkh_label = "Successful P2SH-P2WPKH descriptor import" self.log.info("Should import a p2sh-p2wpkh address from descriptor and private key") self.test_importmulti({"desc": descsum_create("sh(wpkh(" + key.pubkey + "))"), "timestamp": "now", - "label": "Descriptor import test", + "label": p2sh_p2wpkh_label, "keys": [key.privkey]}, success=True) test_address(self.nodes[1], key.p2sh_p2wpkh_addr, solvable=True, ismine=True, - label="Descriptor import test") + label=p2sh_p2wpkh_label, + labels=labels_value(name=p2sh_p2wpkh_label)) # Test ranged descriptor fails if range is not specified xpriv = "tprv8ZgxMBicQKsPeuVhWwi6wuMQGfPKi9Li5GtX35jVNknACgqe3CY4g5xgkfDDJcmtF7o1QnxWDRYw4H5P26PXq7sbcUkEqeR4fg3Kxp2tigg" @@ -628,17 +631,19 @@ class ImportMultiTest(BitcoinTestFramework): # Test importing of a P2PKH address via descriptor key = get_key(self.nodes[0]) + p2pkh_label = "P2PKH descriptor import" self.log.info("Should import a p2pkh address from descriptor") self.test_importmulti({"desc": descsum_create("pkh(" + key.pubkey + ")"), "timestamp": "now", - "label": "Descriptor import test"}, + "label": p2pkh_label}, True, warnings=["Some private keys are missing, outputs will be considered watchonly. If this is intentional, specify the watchonly flag."]) test_address(self.nodes[1], key.p2pkh_addr, solvable=True, ismine=False, - label="Descriptor import test") + label=p2pkh_label, + labels=labels_value(name=p2pkh_label)) # Test import fails if both desc and scriptPubKey are provided key = get_key(self.nodes[0]) @@ -675,7 +680,7 @@ class ImportMultiTest(BitcoinTestFramework): # Import pubkeys with key origin info self.log.info("Addresses should have hd keypath and master key id after import with key origin") pub_addr = self.nodes[1].getnewaddress() - pub_addr = self.nodes[1].getnewaddress() + pub_addr = self.nodes[1].getnewaddress(address_type="bech32") info = self.nodes[1].getaddressinfo(pub_addr) pub = info['pubkey'] pub_keypath = info['hdkeypath'] @@ -693,7 +698,7 @@ class ImportMultiTest(BitcoinTestFramework): assert_equal(pub_import_info['hdkeypath'], pub_keypath) # Import privkeys with key origin info - priv_addr = self.nodes[1].getnewaddress() + priv_addr = self.nodes[1].getnewaddress(address_type="bech32") info = self.nodes[1].getaddressinfo(priv_addr) priv = self.nodes[1].dumpprivkey(priv_addr) priv_keypath = info['hdkeypath'] @@ -742,8 +747,8 @@ class ImportMultiTest(BitcoinTestFramework): self.nodes[1].createwallet(wallet_name="noprivkeys", disable_private_keys=True) wrpc = self.nodes[1].get_wallet_rpc("noprivkeys") - addr1 = self.nodes[0].getnewaddress() - addr2 = self.nodes[0].getnewaddress() + addr1 = self.nodes[0].getnewaddress(address_type="bech32") + addr2 = self.nodes[0].getnewaddress(address_type="bech32") pub1 = self.nodes[0].getaddressinfo(addr1)['pubkey'] pub2 = self.nodes[0].getaddressinfo(addr2)['pubkey'] result = wrpc.importmulti( @@ -761,15 +766,15 @@ class ImportMultiTest(BitcoinTestFramework): assert result[0]['success'] assert result[1]['success'] assert_equal(wrpc.getwalletinfo()["keypoolsize"], 2) - newaddr1 = wrpc.getnewaddress() + newaddr1 = wrpc.getnewaddress(address_type="bech32") assert_equal(addr1, newaddr1) - newaddr2 = wrpc.getnewaddress() + newaddr2 = wrpc.getnewaddress(address_type="bech32") assert_equal(addr2, newaddr2) # Import some public keys to the internal keypool of a no privkey wallet self.log.info("Adding pubkey to internal keypool of disableprivkey wallet") - addr1 = self.nodes[0].getnewaddress() - addr2 = self.nodes[0].getnewaddress() + addr1 = self.nodes[0].getnewaddress(address_type="bech32") + addr2 = self.nodes[0].getnewaddress(address_type="bech32") pub1 = self.nodes[0].getaddressinfo(addr1)['pubkey'] pub2 = self.nodes[0].getaddressinfo(addr2)['pubkey'] result = wrpc.importmulti( @@ -789,15 +794,15 @@ class ImportMultiTest(BitcoinTestFramework): assert result[0]['success'] assert result[1]['success'] assert_equal(wrpc.getwalletinfo()["keypoolsize_hd_internal"], 2) - newaddr1 = wrpc.getrawchangeaddress() + newaddr1 = wrpc.getrawchangeaddress(address_type="bech32") assert_equal(addr1, newaddr1) - newaddr2 = wrpc.getrawchangeaddress() + newaddr2 = wrpc.getrawchangeaddress(address_type="bech32") assert_equal(addr2, newaddr2) # Import a multisig and make sure the keys don't go into the keypool self.log.info('Imported scripts with pubkeys should not have their pubkeys go into the keypool') - addr1 = self.nodes[0].getnewaddress() - addr2 = self.nodes[0].getnewaddress() + addr1 = self.nodes[0].getnewaddress(address_type="bech32") + addr2 = self.nodes[0].getnewaddress(address_type="bech32") pub1 = self.nodes[0].getaddressinfo(addr1)['pubkey'] pub2 = self.nodes[0].getaddressinfo(addr2)['pubkey'] result = wrpc.importmulti( diff --git a/test/functional/wallet_labels.py b/test/functional/wallet_labels.py index b71dae9f40..27371d43bb 100755 --- a/test/functional/wallet_labels.py +++ b/test/functional/wallet_labels.py @@ -13,6 +13,10 @@ from collections import defaultdict from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_rpc_error +from test_framework.wallet_util import ( + labels_value, + test_address, +) class WalletLabelsTest(BitcoinTestFramework): def set_test_params(self): @@ -152,14 +156,14 @@ class Label: def verify(self, node): if self.receive_address is not None: assert self.receive_address in self.addresses - for address in self.addresses: - assert_equal( - node.getaddressinfo(address)['labels'][0], - {"name": self.name, - "purpose": self.purpose[address]}) - assert_equal(node.getaddressinfo(address)['label'], self.name) - + test_address( + node, + address, + label=self.name, + labels=labels_value(name=self.name, purpose=self.purpose[address]) + ) + assert self.name in node.listlabels() assert_equal( node.getaddressesbylabel(self.name), {address: {"purpose": self.purpose[address]} for address in self.addresses}) diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py index 5e94068930..afd473306d 100755 --- a/test/functional/wallet_listreceivedby.py +++ b/test/functional/wallet_listreceivedby.py @@ -11,6 +11,10 @@ from test_framework.util import ( assert_equal, assert_raises_rpc_error, ) +from test_framework.wallet_util import ( + labels_value, + test_address, +) class ReceivedByTest(BitcoinTestFramework): @@ -19,6 +23,7 @@ class ReceivedByTest(BitcoinTestFramework): def skip_test_if_missing_module(self): self.skip_if_no_wallet() + self.skip_if_no_cli() def run_test(self): # Generate block to get out of IBD @@ -126,7 +131,7 @@ class ReceivedByTest(BitcoinTestFramework): # set pre-state label = '' address = self.nodes[1].getnewaddress() - assert_equal(self.nodes[1].getaddressinfo(address)['label'], label) + test_address(self.nodes[1], address, label=label, labels=labels_value(name=label)) received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel() if r["label"] == label][0] balance_by_label = self.nodes[1].getreceivedbylabel(label) diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py index ce0b7e8782..85d900f6cc 100755 --- a/test/functional/wallet_multiwallet.py +++ b/test/functional/wallet_multiwallet.py @@ -24,7 +24,6 @@ class MultiWalletTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 - self.supports_cli = True def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py index 60d7205887..99559090ee 100755 --- a/test/functional/wallet_txn_clone.py +++ b/test/functional/wallet_txn_clone.py @@ -16,6 +16,7 @@ from test_framework.messages import CTransaction, COIN class TxnMallTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 4 + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py index 40eeb4048c..1891cd9190 100755 --- a/test/functional/wallet_txn_doublespend.py +++ b/test/functional/wallet_txn_doublespend.py @@ -16,6 +16,7 @@ from test_framework.util import ( class TxnMallTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 4 + self.supports_cli = False def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/functional/wallet_watchonly.py b/test/functional/wallet_watchonly.py index be8d7714fb..b0c41b2738 100755 --- a/test/functional/wallet_watchonly.py +++ b/test/functional/wallet_watchonly.py @@ -16,7 +16,6 @@ class CreateWalletWatchonlyTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = False self.num_nodes = 1 - self.supports_cli = True def skip_test_if_missing_module(self): self.skip_if_no_wallet() diff --git a/test/fuzz/test_runner.py b/test/fuzz/test_runner.py index f19cf924d2..bbdd047465 100755 --- a/test/fuzz/test_runner.py +++ b/test/fuzz/test_runner.py @@ -12,6 +12,37 @@ import sys import subprocess import logging +# Fuzzers known to lack a seed corpus in https://github.com/bitcoin-core/qa-assets/tree/master/fuzz_seed_corpus +FUZZERS_MISSING_CORPORA = [ + "addr_info_deserialize", + "base_encode_decode", + "block", + "block_file_info_deserialize", + "block_filter_deserialize", + "block_header_and_short_txids_deserialize", + "fee_rate_deserialize", + "flat_file_pos_deserialize", + "hex", + "integer", + "key_origin_info_deserialize", + "merkle_block_deserialize", + "out_point_deserialize", + "parse_hd_keypath", + "parse_numbers", + "parse_script", + "parse_univalue", + "partial_merkle_tree_deserialize", + "partially_signed_transaction_deserialize", + "prefilled_transaction_deserialize", + "psbt_input_deserialize", + "psbt_output_deserialize", + "pub_key_deserialize", + "script_deserialize", + "sub_net_deserialize", + "tx_in", + "tx_in_deserialize", + "tx_out", +] def main(): parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) @@ -78,7 +109,7 @@ def main(): os.path.join(config["environment"]["BUILDDIR"], 'src', 'test', 'fuzz', test_list_selection[0]), '-help=1', ], - timeout=1, + timeout=10, check=True, stderr=subprocess.PIPE, universal_newlines=True, @@ -100,10 +131,14 @@ def main(): def run_once(*, corpus, test_list, build_dir, export_coverage): for t in test_list: + corpus_path = os.path.join(corpus, t) + if t in FUZZERS_MISSING_CORPORA: + os.makedirs(corpus_path, exist_ok=True) args = [ os.path.join(build_dir, 'src', 'test', 'fuzz', t), '-runs=1', - os.path.join(corpus, t), + '-detect_leaks=0', + corpus_path, ] logging.debug('Run {} with args {}'.format(t, args)) result = subprocess.run(args, stderr=subprocess.PIPE, universal_newlines=True) diff --git a/test/lint/lint-circular-dependencies.sh b/test/lint/lint-circular-dependencies.sh index ccd12b5823..1538ad77b1 100755 --- a/test/lint/lint-circular-dependencies.sh +++ b/test/lint/lint-circular-dependencies.sh @@ -18,18 +18,13 @@ EXPECTED_CIRCULAR_DEPENDENCIES=( "qt/bitcoingui -> qt/walletframe -> qt/bitcoingui" "qt/bitcoingui -> qt/walletview -> qt/bitcoingui" "qt/clientmodel -> qt/peertablemodel -> qt/clientmodel" - "qt/paymentserver -> qt/walletmodel -> qt/paymentserver" "qt/recentrequeststablemodel -> qt/walletmodel -> qt/recentrequeststablemodel" "qt/sendcoinsdialog -> qt/walletmodel -> qt/sendcoinsdialog" "qt/transactiontablemodel -> qt/walletmodel -> qt/transactiontablemodel" - "qt/walletmodel -> qt/walletmodeltransaction -> qt/walletmodel" "txmempool -> validation -> txmempool" - "wallet/coincontrol -> wallet/wallet -> wallet/coincontrol" "wallet/fees -> wallet/wallet -> wallet/fees" "wallet/wallet -> wallet/walletdb -> wallet/wallet" "policy/fees -> txmempool -> validation -> policy/fees" - "qt/guiutil -> qt/walletmodel -> qt/optionsmodel -> qt/guiutil" - "txmempool -> validation -> validationinterface -> txmempool" "wallet/scriptpubkeyman -> wallet/wallet -> wallet/scriptpubkeyman" ) diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh index 9a1aa766f7..ea9ea7a58d 100755 --- a/test/lint/lint-locale-dependence.sh +++ b/test/lint/lint-locale-dependence.sh @@ -11,6 +11,7 @@ KNOWN_VIOLATIONS=( "src/qt/rpcconsole.cpp:.*atoi" "src/rest.cpp:.*strtol" "src/test/dbwrapper_tests.cpp:.*snprintf" + "src/test/fuzz/parse_numbers.cpp:.*atoi" "src/torcontrol.cpp:.*atoi" "src/torcontrol.cpp:.*strtol" "src/util/strencodings.cpp:.*atoi" diff --git a/test/lint/lint-shell.sh b/test/lint/lint-shell.sh index 69fc3cf368..63624e3ae0 100755 --- a/test/lint/lint-shell.sh +++ b/test/lint/lint-shell.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Copyright (c) 2018 The Bitcoin Core developers +# Copyright (c) 2018-2019 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. # @@ -16,16 +16,45 @@ 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: disabled=( SC2046 # Quote this to prevent word splitting. SC2086 # Double quote to prevent globbing and word splitting. SC2162 # read without -r will mangle backslashes. ) -shellcheck -e "$(IFS=","; echo "${disabled[*]}")" \ - $(git ls-files -- "*.sh" | grep -vE 'src/(secp256k1|univalue)/') +disabled_gitian=( + SC2094 # Make sure not to read and write the same file in the same pipeline. + SC2129 # Consider using { cmd1; cmd2; } >> file instead of individual redirects. + SC2230 # which is non-standard. Use builtin 'command -v' instead. +) + +EXIT_CODE=0 + +if ! command -v shellcheck > /dev/null; then + echo "Skipping shell linting since shellcheck is not installed." + exit $EXIT_CODE +fi + +EXCLUDE="--exclude=$(IFS=','; echo "${disabled[*]}")" +if ! shellcheck "$EXCLUDE" $(git ls-files -- '*.sh' | grep -vE 'src/(leveldb|secp256k1|univalue)/'); then + EXIT_CODE=1 +fi + +if ! command -v yq > /dev/null; then + echo "Skipping Gitian desriptor scripts checking since yq is not installed." + exit $EXIT_CODE +fi + +EXCLUDE_GITIAN=${EXCLUDE}",$(IFS=','; echo "${disabled_gitian[*]}")" +for descriptor in $(git ls-files -- 'contrib/gitian-descriptors/*.yml') +do + echo + echo "$descriptor" + # Use #!/bin/bash as gitian-builder/bin/gbuild does to complete a script. + SCRIPT=$'#!/bin/bash\n'$(yq -r .script "$descriptor") + if ! echo "$SCRIPT" | shellcheck "$EXCLUDE_GITIAN" -; then + EXIT_CODE=1 + fi +done + +exit $EXIT_CODE diff --git a/test/sanitizer_suppressions/lsan b/test/sanitizer_suppressions/lsan index 90a92a5115..d2cb618d4e 100644 --- a/test/sanitizer_suppressions/lsan +++ b/test/sanitizer_suppressions/lsan @@ -1,5 +1,4 @@ # Suppress warnings triggered in dependencies -leak:libcrypto leak:libqminimal leak:libQt5Core leak:libQt5Gui diff --git a/test/sanitizer_suppressions/ubsan b/test/sanitizer_suppressions/ubsan index e7c690fabe..f5de358bcb 100644 --- a/test/sanitizer_suppressions/ubsan +++ b/test/sanitizer_suppressions/ubsan @@ -42,3 +42,45 @@ unsigned-integer-overflow:stl_bvector.h unsigned-integer-overflow:txmempool.cpp unsigned-integer-overflow:util/strencodings.cpp unsigned-integer-overflow:validation.cpp + +implicit-integer-sign-change:*/include/c++/*/bits/*.h +implicit-integer-sign-change:*/new_allocator.h +implicit-integer-sign-change:/usr/include/boost/date_time/format_date_parser.hpp +implicit-integer-sign-change:arith_uint256.cpp +implicit-integer-sign-change:bech32.cpp +implicit-integer-sign-change:bloom.cpp +implicit-integer-sign-change:chain.* +implicit-integer-sign-change:coins.h +implicit-integer-sign-change:compat/stdin.cpp +implicit-integer-sign-change:compressor.h +implicit-integer-sign-change:crypto/* +implicit-integer-sign-change:key.cpp +implicit-integer-sign-change:noui.cpp +implicit-integer-sign-change:prevector.h +implicit-integer-sign-change:protocol.cpp +implicit-integer-sign-change:script/bitcoinconsensus.cpp +implicit-integer-sign-change:script/interpreter.cpp +implicit-integer-sign-change:serialize.h +implicit-integer-sign-change:test/arith_uint256_tests.cpp +implicit-integer-sign-change:test/coins_tests.cpp +implicit-integer-sign-change:test/pow_tests.cpp +implicit-integer-sign-change:test/prevector_tests.cpp +implicit-integer-sign-change:test/sighash_tests.cpp +implicit-integer-sign-change:test/streams_tests.cpp +implicit-integer-sign-change:test/transaction_tests.cpp +implicit-integer-sign-change:txmempool.cpp +implicit-integer-sign-change:util/strencodings.* +implicit-integer-sign-change:validation.cpp +implicit-integer-sign-change:zmq/zmqpublishnotifier.cpp +implicit-signed-integer-truncation,implicit-integer-sign-change:chain.h +implicit-signed-integer-truncation,implicit-integer-sign-change:test/skiplist_tests.cpp +implicit-signed-integer-truncation:chain.h +implicit-signed-integer-truncation:crypto/* +implicit-signed-integer-truncation:cuckoocache.h +implicit-signed-integer-truncation:leveldb/* +implicit-signed-integer-truncation:streams.h +implicit-signed-integer-truncation:test/arith_uint256_tests.cpp +implicit-signed-integer-truncation:test/skiplist_tests.cpp +implicit-signed-integer-truncation:torcontrol.cpp +implicit-unsigned-integer-truncation:crypto/* +implicit-unsigned-integer-truncation:leveldb/* |