diff --git a/.github/workflows/build-src.yml b/.github/workflows/build-src.yml index 9af8b19b2e00..6f9d3279b845 100644 --- a/.github/workflows/build-src.yml +++ b/.github/workflows/build-src.yml @@ -76,7 +76,7 @@ jobs: - name: Build source run: | - CCACHE_SIZE="400M" + CCACHE_MAXSIZE="400M" CACHE_DIR="/cache" mkdir /output BASE_OUTDIR="/output" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 224d7988ecc3..4e9b6c9efa8b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,8 +18,29 @@ concurrency: cancel-in-progress: ${{ github.ref_type != 'tag' }} jobs: + check-skip: + name: Check skip conditions + runs-on: ubuntu-latest + outputs: + skip: ${{ steps.skip-check.outputs.skip }} + steps: + - name: Check skip environment variables + id: skip-check + run: | + if [[ "${{ github.event_name }}" == "push" && "${{ vars.SKIP_ON_PUSH }}" != "" ]]; then + echo "Skipping build on push due to SKIP_ON_PUSH environment variable" + echo "skip=true" >> $GITHUB_OUTPUT + elif [[ "${{ github.event_name }}" == "pull_request_target" && "${{ vars.SKIP_ON_PR }}" != "" ]]; then + echo "Skipping build on pull request due to SKIP_ON_PR environment variable" + echo "skip=true" >> $GITHUB_OUTPUT + else + echo "skip=false" >> $GITHUB_OUTPUT + fi + container: name: Build container + needs: [check-skip] + if: ${{ needs.check-skip.outputs.skip == 'false' }} uses: ./.github/workflows/build-container.yml with: context: ./contrib/containers/ci @@ -28,6 +49,8 @@ jobs: container-slim: name: Build slim container + needs: [check-skip] + if: ${{ needs.check-skip.outputs.skip == 'false' }} uses: ./.github/workflows/build-container.yml with: context: ./contrib/containers/ci diff --git a/.tx/config b/.tx/config index 7699aee5fffe..a9939bf4f9cd 100644 --- a/.tx/config +++ b/.tx/config @@ -1,7 +1,7 @@ [main] host = https://www.transifex.com -[dash.dash_ents] +[o:dash:p:dash:r:dash_ents] file_filter = src/qt/locale/dash_.ts source_file = src/qt/locale/dash_en.xlf source_lang = en diff --git a/ci/dash/build_src.sh b/ci/dash/build_src.sh index 3ebdc910dea2..ad1201e4599d 100755 --- a/ci/dash/build_src.sh +++ b/ci/dash/build_src.sh @@ -27,7 +27,7 @@ if [ "$CHECK_DOC" = 1 ]; then test/lint/all-lint.py fi -ccache --zero-stats --max-size="$CCACHE_SIZE" +ccache --zero-stats if [ -n "$CONFIG_SHELL" ]; then export CONFIG_SHELL="$CONFIG_SHELL" diff --git a/ci/lint/04_install.sh b/ci/lint/04_install.sh index bcd636641f7f..431672ba1fb3 100755 --- a/ci/lint/04_install.sh +++ b/ci/lint/04_install.sh @@ -35,6 +35,7 @@ fi ${CI_RETRY_EXE} pip3 install codespell==2.0.0 ${CI_RETRY_EXE} pip3 install flake8==3.8.3 +${CI_RETRY_EXE} pip3 install lief==0.13.1 ${CI_RETRY_EXE} pip3 install mypy==0.910 ${CI_RETRY_EXE} pip3 install pyzmq==22.3.0 ${CI_RETRY_EXE} pip3 install vulture==2.3 diff --git a/ci/test/00_setup_env.sh b/ci/test/00_setup_env.sh index d4840e9ebfd8..1d9acfb52f53 100755 --- a/ci/test/00_setup_env.sh +++ b/ci/test/00_setup_env.sh @@ -55,7 +55,7 @@ export BOOST_TEST_RANDOM=${BOOST_TEST_RANDOM:-1} export DEBIAN_FRONTEND=noninteractive export HOST_CACHE_DIR=${HOST_CACHE_DIR:-$BASE_ROOT_DIR/ci-cache-$BUILD_TARGET} export CACHE_DIR=${CACHE_DIR:-$HOST_CACHE_DIR} -export CCACHE_SIZE=${CCACHE_SIZE:-100M} +export CCACHE_MAXSIZE=${CCACHE_MAXSIZE:-100M} export CCACHE_TEMPDIR=${CCACHE_TEMPDIR:-/tmp/.ccache-temp} export CCACHE_COMPRESS=${CCACHE_COMPRESS:-1} # The cache dir. @@ -70,7 +70,7 @@ export BASE_OUTDIR=${BASE_OUTDIR:-$BASE_SCRATCH_DIR/out/$HOST} export BASE_BUILD_DIR=${BASE_BUILD_DIR:-$BASE_SCRATCH_DIR/build-ci} export PREVIOUS_RELEASES_DIR=${PREVIOUS_RELEASES_DIR:-$BASE_ROOT_DIR/releases/$HOST} export SDK_URL=${SDK_URL:-https://bitcoincore.org/depends-sources/sdks} -export DOCKER_PACKAGES=${DOCKER_PACKAGES:-build-essential libtool autotools-dev automake pkg-config bsdmainutils curl ca-certificates ccache python3 rsync git procps} +export DOCKER_PACKAGES=${DOCKER_PACKAGES:-build-essential libtool autotools-dev automake pkg-config bsdmainutils curl ca-certificates ccache python3 rsync git procps bison e2fsprogs} export GOAL=${GOAL:-install} export DIR_QA_ASSETS=${DIR_QA_ASSETS:-${BASE_SCRATCH_DIR}/qa-assets} export PATH=${BASE_ROOT_DIR}/ci/retry:$PATH diff --git a/ci/test/00_setup_env_i686_centos.sh b/ci/test/00_setup_env_i686_centos.sh new file mode 100644 index 000000000000..d509c7214101 --- /dev/null +++ b/ci/test/00_setup_env_i686_centos.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# +# Copyright (c) 2020-2022 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=i686-pc-linux-gnu +export CONTAINER_NAME=ci_i686_centos +export CI_IMAGE_NAME_TAG="quay.io/centos/amd64:stream9" +export CI_BASE_PACKAGES="gcc-c++ glibc-devel.x86_64 libstdc++-devel.x86_64 glibc-devel.i686 libstdc++-devel.i686 ccache libtool make git python3 python3-pip which patch lbzip2 xz procps-ng dash rsync coreutils bison util-linux e2fsprogs" +export PIP_PACKAGES="pyzmq" +export GOAL="install" +export NO_WERROR=1 # Suppress error: #warning _FORTIFY_SOURCE > 2 is treated like 2 on this platform [-Werror=cpp] +export BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-reduce-exports" +export CONFIG_SHELL="/bin/dash" diff --git a/ci/test/00_setup_env_mac_native_x86_64.sh b/ci/test/00_setup_env_mac_native_x86_64.sh index 0ac2d62eb6c5..252b95c2178f 100755 --- a/ci/test/00_setup_env_mac_native_x86_64.sh +++ b/ci/test/00_setup_env_mac_native_x86_64.sh @@ -14,6 +14,6 @@ export BITCOIN_CONFIG="--with-gui --enable-reduce-exports --disable-miner --with export CI_OS_NAME="macos" export NO_DEPENDS=1 export OSX_SDK="" -export CCACHE_SIZE=300M +export CCACHE_MAXSIZE=300M export RUN_SECURITY_TESTS="true" diff --git a/ci/test/00_setup_env_native_fuzz_with_valgrind.sh b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh index 1b5728feb84c..f379d8ee7edc 100755 --- a/ci/test/00_setup_env_native_fuzz_with_valgrind.sh +++ b/ci/test/00_setup_env_native_fuzz_with_valgrind.sh @@ -7,7 +7,7 @@ export LC_ALL=C.UTF-8 export CONTAINER_NAME=ci_native_fuzz_valgrind -export PACKAGES="clang llvm python3 libevent-dev bsdmainutils libboost-dev valgrind" +export PACKAGES="clang llvm libclang-rt-dev python3 libevent-dev bsdmainutils libboost-dev valgrind" export NO_DEPENDS=1 export RUN_UNIT_TESTS=false export RUN_FUNCTIONAL_TESTS=false @@ -16,4 +16,4 @@ export FUZZ_TESTS_CONFIG="--valgrind" export GOAL="install" # Temporarily pin dwarf 4, until valgrind can understand clang's dwarf 5 export BITCOIN_CONFIG="--enable-fuzz --with-sanitizers=fuzzer CC=clang-18 CXX=clang++-18 CFLAGS='-gdwarf-4' CXXFLAGS='-gdwarf-4'" -export CCACHE_SIZE=200M +export CCACHE_MAXSIZE=200M diff --git a/ci/test/00_setup_env_native_valgrind.sh b/ci/test/00_setup_env_native_valgrind.sh index d0a217a1f659..d7bf8b2f3550 100755 --- a/ci/test/00_setup_env_native_valgrind.sh +++ b/ci/test/00_setup_env_native_valgrind.sh @@ -6,7 +6,7 @@ export LC_ALL=C.UTF-8 -export PACKAGES="valgrind clang llvm python3-zmq libevent-dev bsdmainutils libboost-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev" +export PACKAGES="valgrind clang llvm libclang-rt-dev python3-zmq libevent-dev bsdmainutils libboost-dev libdb5.3++-dev libminiupnpc-dev libzmq3-dev" export USE_VALGRIND=1 export NO_DEPENDS=1 export TEST_RUNNER_EXTRA="--exclude rpc_bind,feature_bind_extra --timeout-factor=4" # Excluded for now, see https://github.com/bitcoin/bitcoin/issues/17765#issuecomment-602068547 diff --git a/ci/test/04_install.sh b/ci/test/04_install.sh index 3ea6d24e6b86..b2a2c7cab6d8 100755 --- a/ci/test/04_install.sh +++ b/ci/test/04_install.sh @@ -42,7 +42,7 @@ if [ -z "$DANGER_RUN_CI_ON_HOST" ]; then ${CI_RETRY_EXE} docker pull "$DOCKER_NAME_TAG" # shellcheck disable=SC2086 - DOCKER_ID=$(docker run $DOCKER_ADMIN -idt \ + DOCKER_ID=$(docker run $DOCKER_ADMIN --cap-add LINUX_IMMUTABLE -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 \ @@ -97,7 +97,7 @@ if [ "$CI_OS_NAME" == "macos" ]; then echo "Number of CPUs: $(sysctl -n hw.logicalcpu)" else CI_EXEC free -m -h - CI_EXEC echo "Number of CPUs \(nproc\):" \$\(nproc\) + CI_EXEC echo "Number of CPUs (nproc): $(nproc)" CI_EXEC echo "$(lscpu | grep Endian)" fi CI_EXEC echo "Free disk space:" diff --git a/configure.ac b/configure.ac index ac4bd086422d..11f4fe2c215b 100644 --- a/configure.ac +++ b/configure.ac @@ -450,15 +450,6 @@ if test "$enable_werror" = "yes"; then fi ERROR_CXXFLAGS=$CXXFLAG_WERROR - dnl -Warray-bounds cause problems with GCC. Do not treat these warnings as errors. - dnl Suppress -Warray-bounds entirely because of noisy output, currently unhappy with immer implementation. - AX_CHECK_COMPILE_FLAG([-Warray-bounds], [NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-array-bounds"], [], [$CXXFLAG_WERROR], [AC_LANG_SOURCE([ - #if defined(__clang__) || defined(__INTEL_COMPILER) || !defined(__GNUC__) - #error Non-GCC compiler detected, not setting flag - #endif - int main(void) { return 0; } - ])]) - dnl -Wattributes cause problems with some versions of GCC. Do not treat these warnings as errors. AX_CHECK_COMPILE_FLAG([-Wattributes], [NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-error=attributes"], [], [$CXXFLAG_WERROR], [AC_LANG_SOURCE([ #if defined(__clang__) || defined(__INTEL_COMPILER) || !defined(__GNUC__) @@ -478,22 +469,31 @@ if test "$enable_werror" = "yes"; then #endif int main(void) { return 0; } ])]) - - dnl -Wstringop-overread and -Wstringop-overflow are broken in GCC. Suppress warnings entirely to avoid noisy output. - AX_CHECK_COMPILE_FLAG([-Wstringop-overread], [NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-stringop-overread"], [], [$CXXFLAG_WERROR], [AC_LANG_SOURCE([ - #if defined(__clang__) || defined(__INTEL_COMPILER) || !defined(__GNUC__) - #error Non-GCC compiler detected, not setting flag - #endif - int main(void) { return 0; } - ])]) - AX_CHECK_COMPILE_FLAG([-Wstringop-overflow], [NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-stringop-overflow"], [], [$CXXFLAG_WERROR], [AC_LANG_SOURCE([ - #if defined(__clang__) || defined(__INTEL_COMPILER) || !defined(__GNUC__) - #error Non-GCC compiler detected, not setting flag - #endif - int main(void) { return 0; } - ])]) fi +dnl -Warray-bounds cause problems with GCC. Do not treat these warnings as errors. +dnl Suppress -Warray-bounds entirely because of noisy output, currently unhappy with immer implementation. +AX_CHECK_COMPILE_FLAG([-Warray-bounds], [NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-array-bounds"], [], [$CXXFLAG_WERROR], [AC_LANG_SOURCE([ + #if defined(__clang__) || defined(__INTEL_COMPILER) || !defined(__GNUC__) + #error Non-GCC compiler detected, not setting flag + #endif + int main(void) { return 0; } +])]) + +dnl -Wstringop-overread and -Wstringop-overflow are broken in GCC. Suppress warnings entirely to avoid noisy output. +AX_CHECK_COMPILE_FLAG([-Wstringop-overread], [NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-stringop-overread"], [], [$CXXFLAG_WERROR], [AC_LANG_SOURCE([ + #if defined(__clang__) || defined(__INTEL_COMPILER) || !defined(__GNUC__) + #error Non-GCC compiler detected, not setting flag + #endif + int main(void) { return 0; } +])]) +AX_CHECK_COMPILE_FLAG([-Wstringop-overflow], [NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-stringop-overflow"], [], [$CXXFLAG_WERROR], [AC_LANG_SOURCE([ + #if defined(__clang__) || defined(__INTEL_COMPILER) || !defined(__GNUC__) + #error Non-GCC compiler detected, not setting flag + #endif + int main(void) { return 0; } +])]) + AX_CHECK_COMPILE_FLAG([-Wall], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wall"], [], [$CXXFLAG_WERROR]) AX_CHECK_COMPILE_FLAG([-Wextra], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wextra"], [], [$CXXFLAG_WERROR]) AX_CHECK_COMPILE_FLAG([-Wgnu], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wgnu"], [], [$CXXFLAG_WERROR]) diff --git a/contrib/dash-cli.bash-completion b/contrib/dash-cli.bash similarity index 100% rename from contrib/dash-cli.bash-completion rename to contrib/dash-cli.bash diff --git a/contrib/dash-tx.bash-completion b/contrib/dash-tx.bash similarity index 100% rename from contrib/dash-tx.bash-completion rename to contrib/dash-tx.bash diff --git a/contrib/dashd.bash-completion b/contrib/dashd.bash similarity index 100% rename from contrib/dashd.bash-completion rename to contrib/dashd.bash diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py index 2cbb861d57c3..001e46ceeef7 100755 --- a/contrib/devtools/security-check.py +++ b/contrib/devtools/security-check.py @@ -10,7 +10,7 @@ import sys from typing import List -import lief #type:ignore +import lief def check_ELF_RELRO(binary) -> bool: ''' diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py index d6bdb193eb64..a89cd32e83d2 100755 --- a/contrib/devtools/symbol-check.py +++ b/contrib/devtools/symbol-check.py @@ -13,7 +13,7 @@ import sys from typing import Dict, List -import lief #type:ignore +import lief # Debian 11 (Bullseye) EOL: 2026. https://wiki.debian.org/LTS # diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py index 5d9dc169045a..035a6e696877 100755 --- a/contrib/devtools/test-security-check.py +++ b/contrib/devtools/test-security-check.py @@ -5,7 +5,7 @@ ''' Test script for security-check.py ''' -import lief #type:ignore +import lief import os import subprocess from typing import List diff --git a/contrib/testgen/README.md b/contrib/testgen/README.md index 4d9054095571..1efe07d483a0 100644 --- a/contrib/testgen/README.md +++ b/contrib/testgen/README.md @@ -4,5 +4,5 @@ Utilities to generate test vectors for the data-driven Dash tests. Usage: - PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py valid 50 > ../../src/test/data/key_io_valid.json - PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py invalid 50 > ../../src/test/data/key_io_invalid.json + ./gen_key_io_test_vectors.py valid 50 > ../../src/test/data/key_io_valid.json + ./gen_key_io_test_vectors.py invalid 50 > ../../src/test/data/key_io_invalid.json diff --git a/contrib/testgen/base58.py b/contrib/testgen/base58.py deleted file mode 100644 index 87341ccf96dc..000000000000 --- a/contrib/testgen/base58.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright (c) 2012-2020 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -''' -Bitcoin base58 encoding and decoding. - -Based on https://bitcointalk.org/index.php?topic=1026.0 (public domain) -''' -import hashlib - -# for compatibility with following code... -class SHA256: - new = hashlib.sha256 - -if str != bytes: - # Python 3.x - def ord(c): - return c - def chr(n): - return bytes( (n,) ) - -__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' -__b58base = len(__b58chars) -b58chars = __b58chars - -def b58encode(v): - """ encode v, which is a string of bytes, to base58. - """ - long_value = 0 - for (i, c) in enumerate(v[::-1]): - if isinstance(c, str): - c = ord(c) - long_value += (256**i) * c - - result = '' - while long_value >= __b58base: - div, mod = divmod(long_value, __b58base) - result = __b58chars[mod] + result - long_value = div - result = __b58chars[long_value] + result - - # Bitcoin does a little leading-zero-compression: - # leading 0-bytes in the input become leading-1s - nPad = 0 - for c in v: - if c == 0: - nPad += 1 - else: - break - - return (__b58chars[0]*nPad) + result - -def b58decode(v, length = None): - """ decode v into a string of len bytes - """ - long_value = 0 - for i, c in enumerate(v[::-1]): - pos = __b58chars.find(c) - assert pos != -1 - long_value += pos * (__b58base**i) - - result = bytes() - while long_value >= 256: - div, mod = divmod(long_value, 256) - result = chr(mod) + result - long_value = div - result = chr(long_value) + result - - nPad = 0 - for c in v: - if c == __b58chars[0]: - nPad += 1 - continue - break - - result = bytes(nPad) + result - if length is not None and len(result) != length: - return None - - return result - -def checksum(v): - """Return 32-bit checksum based on SHA256""" - return SHA256.new(SHA256.new(v).digest()).digest()[0:4] - -def b58encode_chk(v): - """b58encode a string, with 32-bit checksum""" - return b58encode(v + checksum(v)) - -def b58decode_chk(v): - """decode a base58 string, check and remove checksum""" - result = b58decode(v) - if result is None: - return None - if result[-4:] == checksum(result[:-4]): - return result[:-4] - else: - return None - -def get_bcaddress_version(strAddress): - """ Returns None if strAddress is invalid. Otherwise returns integer version of address. """ - addr = b58decode_chk(strAddress) - if addr is None or len(addr)!=21: - return None - version = addr[0] - return ord(version) - -if __name__ == '__main__': - # Test case (from http://gitorious.org/bitcoin/python-base58.git) - assert get_bcaddress_version('15VjRaDX9zpbA8LVnbrCAFzrVzN7ixHNsC') == 0 - _ohai = 'o hai'.encode('ascii') - _tmp = b58encode(_ohai) - assert _tmp == 'DYB3oMS' - assert b58decode(_tmp, 5) == _ohai - print("Tests passed") diff --git a/contrib/testgen/gen_key_io_test_vectors.py b/contrib/testgen/gen_key_io_test_vectors.py index 94f798081975..ba045ec1c505 100755 --- a/contrib/testgen/gen_key_io_test_vectors.py +++ b/contrib/testgen/gen_key_io_test_vectors.py @@ -1,20 +1,24 @@ #!/usr/bin/env python3 -# Copyright (c) 2012-2021 The Bitcoin Core developers +# Copyright (c) 2012-2022 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. ''' Generate valid and invalid base58 address and private key test vectors. Usage: - PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py valid 50 > ../../src/test/data/key_io_valid.json - PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py invalid 50 > ../../src/test/data/key_io_invalid.json + ./gen_key_io_test_vectors.py valid 50 > ../../src/test/data/key_io_valid.json + ./gen_key_io_test_vectors.py invalid 50 > ../../src/test/data/key_io_invalid.json ''' -# 2012 Wladimir J. van der Laan -# Released under MIT License -import os + from itertools import islice -from base58 import b58encode_chk, b58decode_chk, b58chars +import os import random +import sys + +sys.path.append(os.path.join(os.path.dirname(__file__), '../../test/functional')) + +from test_framework.address import base58_to_byte, byte_to_base58, b58chars # noqa: E402 +from test_framework.script import OP_DUP, OP_EQUAL, OP_EQUALVERIFY, OP_HASH160, OP_CHECKSIG # noqa: E402 # key types PUBKEY_ADDRESS = 76 @@ -28,15 +32,6 @@ PRIVKEY_REGTEST = 239 # script -OP_0 = 0x00 -OP_1 = 0x51 -OP_2 = 0x52 -OP_16 = 0x60 -OP_DUP = 0x76 -OP_EQUAL = 0x87 -OP_EQUALVERIFY = 0x88 -OP_HASH160 = 0xa9 -OP_CHECKSIG = 0xac pubkey_prefix = (OP_DUP, OP_HASH160, 20) pubkey_suffix = (OP_EQUALVERIFY, OP_CHECKSIG) script_prefix = (OP_HASH160, 20) @@ -65,8 +60,10 @@ def is_valid(v): '''Check vector v for validity''' if len(set(v) - set(b58chars)) > 0: return False - result = b58decode_chk(v) - if result is None: + try: + payload, version = base58_to_byte(v) + result = bytes([version]) + payload + except ValueError: # thrown if checksum doesn't match return False for template in templates: prefix = bytearray(template[0]) @@ -83,7 +80,8 @@ def gen_valid_base58_vector(template): suffix = bytearray(template[2]) dst_prefix = bytearray(template[4]) dst_suffix = bytearray(template[5]) - rv = b58encode_chk(prefix + payload + suffix) + assert len(prefix) == 1 + rv = byte_to_base58(payload + suffix, prefix[0]) return rv, dst_prefix + payload + dst_suffix def gen_valid_vectors(): @@ -124,7 +122,8 @@ def gen_invalid_base58_vector(template): else: suffix = bytearray(template[2]) - val = b58encode_chk(prefix + payload + suffix) + assert len(prefix) == 1 + val = byte_to_base58(payload + suffix, prefix[0]) if random.randint(0,10)<1: # line corruption if randbool(): # add random character to end val += random.choice(b58chars) @@ -152,7 +151,6 @@ def gen_invalid_vectors(): yield val, if __name__ == '__main__': - import sys import json iters = {'valid':gen_valid_vectors, 'invalid':gen_invalid_vectors} try: diff --git a/doc/REST-interface.md b/doc/REST-interface.md index 2aa6f5ee8d45..5b25d76fc30f 100644 --- a/doc/REST-interface.md +++ b/doc/REST-interface.md @@ -47,18 +47,24 @@ The HTTP request and response are both handled entirely in-memory. With the /notxdetails/ option JSON response will only contain the transaction hash instead of the complete transaction details. The option only affects the JSON response. #### Blockheaders -`GET /rest/headers//.` +`GET /rest/headers/.?count=` Given a block hash: returns amount of blockheaders in upward direction. Returns empty if the block doesn't exist or it isn't in the active chain. +*Deprecated (but not removed) since 23.0:* +`GET /rest/headers//.` + #### Blockfilter Headers -`GET /rest/blockfilterheaders///.` +`GET /rest/blockfilterheaders//.?count=` Given a block hash: returns amount of blockfilter headers in upward direction for the filter type . Returns empty if the block doesn't exist or it isn't in the active chain. +*Deprecated (but not removed) since 23.0:* +`GET /rest/blockfilterheaders///.` + #### Blockfilters `GET /rest/blockfilter//.` diff --git a/doc/descriptors.md b/doc/descriptors.md index 6a94b867c3f5..fce4f9a323a4 100644 --- a/doc/descriptors.md +++ b/doc/descriptors.md @@ -73,7 +73,8 @@ Descriptors consist of several types of expressions. The top level expression is - Followed by zero or more `/NUM` unhardened and `/NUM'` hardened BIP32 derivation steps. - Optionally followed by a single `/*` or `/*'` final step to denote all (direct) unhardened or hardened children. - The usage of hardened derivation steps requires providing the private key. -- Anywhere a `'` suffix is permitted to denote hardened derivation, the suffix `h` can be used instead. + +(Anywhere a `'` suffix is permitted to denote hardened derivation, the suffix `h` can be used instead.) `ADDR` expressions are any type of supported address: - P2PKH addresses (base58, of the form `X...`). Note that P2PKH addresses in descriptors cannot be used for P2PK outputs (use the `pk` function instead). diff --git a/doc/release-note-26194.md b/doc/release-note-26194.md new file mode 100644 index 000000000000..b72dbf9a238b --- /dev/null +++ b/doc/release-note-26194.md @@ -0,0 +1,4 @@ +Add `next_index` in `listdescriptors` RPC +----------------- + +- Added a new `next_index` field in the response in `listdescriptors` to have the same format as `importdescriptors` diff --git a/doc/release-notes-24098.md b/doc/release-notes-24098.md new file mode 100644 index 000000000000..bc74ef655878 --- /dev/null +++ b/doc/release-notes-24098.md @@ -0,0 +1,20 @@ +Notable changes +=============== + +Updated REST APIs +----------------- + +- The `/headers/` and `/blockfilterheaders/` endpoints have been updated to use + a query parameter instead of path parameter to specify the result count. The + count parameter is now optional, and defaults to 5 for both endpoints. The old + endpoints are still functional, and have no documented behaviour change. + + For `/headers`, use + `GET /rest/headers/.?count=` + instead of + `GET /rest/headers//.` (deprecated) + + For `/blockfilterheaders/`, use + `GET /rest/blockfilterheaders//.?count=` + instead of + `GET /rest/blockfilterheaders///.` (deprecated) diff --git a/doc/release-notes-24629.md b/doc/release-notes-24629.md new file mode 100644 index 000000000000..36ccda440586 --- /dev/null +++ b/doc/release-notes-24629.md @@ -0,0 +1,7 @@ +Updated RPCs +------------ + +- The return value of the `pruneblockchain` method had an off-by-one bug, + returning the height of the block *after* the most recent pruned. This has + been corrected, and it now returns the height of the last pruned block as + documented. diff --git a/doc/release-notes-25158.md b/doc/release-notes-25158.md new file mode 100644 index 000000000000..ce8ab53ddd83 --- /dev/null +++ b/doc/release-notes-25158.md @@ -0,0 +1,6 @@ +RPC Wallet +---------- + +- The `gettransaction`, `listtransactions`, `listsinceblock` RPCs now return + the `abandoned` field for all transactions. Previously, the "abandoned" field + was only returned for sent transactions. (#25158) \ No newline at end of file diff --git a/doc/release-notes-27068.md b/doc/release-notes-27068.md new file mode 100644 index 000000000000..3f5c5dba37bd --- /dev/null +++ b/doc/release-notes-27068.md @@ -0,0 +1,6 @@ +Wallet +------ + +- Wallet passphrases may now contain null characters. + Prior to this change, only characters up to the first + null character were recognized and accepted. (#27068) \ No newline at end of file diff --git a/doc/release-process.md b/doc/release-process.md index 2ebcc1582662..aca1c12e52da 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -71,7 +71,7 @@ Checkout the Dash Core version you'd like to build: pushd ./dash export SIGNER='(your builder key, ie udjinm6, pasta, etc)' export VERSION='(new version, e.g. 20.0.0)' -git fetch "v${VERSION}" +git fetch origin "v${VERSION}" git checkout "v${VERSION}" popd ``` diff --git a/src/Makefile.am b/src/Makefile.am index 1b26de7005fc..ab0d3bf0f665 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -302,6 +302,7 @@ BITCOIN_CORE_H = \ psbt.h \ random.h \ randomenv.h \ + rest.h \ rpc/blockchain.h \ rpc/client.h \ rpc/evo_util.h \ @@ -434,7 +435,7 @@ BITCOIN_CORE_H = \ obj/build.h: FORCE @$(MKDIR_P) $(builddir)/obj - @$(top_srcdir)/share/genbuild.sh "$(abs_top_builddir)/src/obj/build.h" \ + $(AM_V_GEN) $(top_srcdir)/share/genbuild.sh "$(abs_top_builddir)/src/obj/build.h" \ "$(abs_top_srcdir)" libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h @@ -1071,7 +1072,7 @@ clean-local: -rm -rf *.dSYM test/*.dSYM bench/*.dSYM qt/*.dSYM qt/test/*.dSYM .rc.o: - @test -f $(WINDRES) + @test -f $(WINDRES) || (echo "windres $(WINDRES) not found, but is required to compile windows resource files"; exit 1) ## FIXME: How to get the appropriate modulename_CPPFLAGS in here? $(AM_V_GEN) $(WINDRES) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) -DWINDRES_PREPROC -i $< -o $@ @@ -1130,12 +1131,11 @@ endif %.raw.h: %.raw @$(MKDIR_P) $(@D) - @{ \ + $(AM_V_GEN) { \ echo "static unsigned const char $(*F)_raw[] = {" && \ $(HEXDUMP) -v -e '8/1 "0x%02x, "' -e '"\n"' $< | $(SED) -e 's/0x ,//g' && \ echo "};"; \ } > "$@.new" && mv -f "$@.new" "$@" - @echo "Generated $@" include Makefile.minisketch.include diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index bc788ac810f8..a6ffeb1c2107 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -88,6 +88,7 @@ if ENABLE_WALLET bench_bench_dash_SOURCES += bench/coin_selection.cpp bench_bench_dash_SOURCES += bench/wallet_balance.cpp bench_bench_dash_SOURCES += bench/wallet_create.cpp +bench_bench_dash_SOURCES += bench/wallet_loading.cpp bench_bench_dash_LDADD += $(BDB_LIBS) $(SQLITE_LIBS) endif diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index c9f918c76467..862b8ba505dc 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -437,20 +437,24 @@ $(srcdir)/qt/dashstrings.cpp: FORCE @test -n $(XGETTEXT) || echo "xgettext is required for updating translations" $(AM_V_GEN) cd $(srcdir); XGETTEXT=$(XGETTEXT) COPYRIGHT_HOLDERS="$(COPYRIGHT_HOLDERS)" $(PYTHON) ../share/qt/extract_strings_qt.py $(libbitcoin_node_a_SOURCES) $(libbitcoin_wallet_a_SOURCES) $(libbitcoin_common_a_SOURCES) $(libbitcoin_zmq_a_SOURCES) $(libbitcoin_consensus_a_SOURCES) $(libbitcoin_util_a_SOURCES) +# The resulted dash_en.xlf source file should follow Transifex requirements. +# See: https://docs.transifex.com/formats/xliff#how-to-distinguish-between-a-source-file-and-a-translation-file translate: $(srcdir)/qt/dashstrings.cpp $(QT_FORMS_UI) $(QT_FORMS_UI) $(BITCOIN_QT_BASE_CPP) qt/bitcoin.cpp $(BITCOIN_QT_WINDOWS_CPP) $(BITCOIN_QT_WALLET_CPP) $(BITCOIN_QT_H) $(BITCOIN_MM) @test -n $(LUPDATE) || echo "lupdate is required for updating translations" $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LUPDATE) -no-obsolete -I $(srcdir) -locations relative $^ -ts $(srcdir)/qt/locale/dash_en.ts @test -n $(LCONVERT) || echo "lconvert is required for updating translations" - $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LCONVERT) -o $(srcdir)/qt/locale/dash_en.xlf -i $(srcdir)/qt/locale/dash_en.ts + $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LCONVERT) -drop-translations -o $(srcdir)/qt/locale/dash_en.xlf -i $(srcdir)/qt/locale/dash_en.ts + @$(SED) -i.old -e 's|source-language="en" target-language="en"|source-language="en"|' -e '/<\/target>/d' $(srcdir)/qt/locale/dash_en.xlf + @rm -f $(srcdir)/qt/locale/dash_en.xlf.old $(QT_QRC_LOCALE_CPP): $(QT_QRC_LOCALE) $(QT_QM) - @test -f $(RCC) + @test -f $(RCC) || (echo "rcc $(RCC) not found, but is required for generating qrc cpp files"; exit 1) @cp -f $< $(@D)/temp_$( $@ @rm $(@D)/temp_$( $@ CLEAN_QT = $(nodist_qt_libbitcoinqt_a_SOURCES) $(QT_QM) $(QT_FORMS_H) qt/*.gcda qt/*.gcno qt/temp_dash_locale.qrc qt/res/css/colors/* @@ -463,7 +467,7 @@ dash_qt_clean: FORCE dash_qt : qt/dash-qt$(EXEEXT) ui_%.h: %.ui - @test -f $(UIC) + @test -f $(UIC) || (echo "uic $(UIC) not found, but is required for generating ui headers"; exit 1) @$(MKDIR_P) $(@D) $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(UIC) -o $@ $< || (echo "Error creating $@"; false) @@ -474,6 +478,6 @@ moc_%.cpp: %.h $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(MOC) $(DEFAULT_INCLUDES) $(QT_INCLUDES_UNSUPPRESSED) $(MOC_DEFS) $< > $@ %.qm: %.ts - @test -f $(LRELEASE) + @test -f $(LRELEASE) || (echo "lrelease $(LRELEASE) not found, but is required for generating translations"; exit 1) @$(MKDIR_P) $(@D) $(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(LRELEASE) -silent $< -qm $@ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index bed7081dd6d4..b479759a8457 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -121,6 +121,7 @@ BITCOIN_TESTS =\ test/getarg_tests.cpp \ test/governance_validators_tests.cpp \ test/hash_tests.cpp \ + test/httpserver_tests.cpp \ test/i2p_tests.cpp \ test/interfaces_tests.cpp \ test/key_io_tests.cpp \ @@ -156,6 +157,7 @@ BITCOIN_TESTS =\ test/raii_event_tests.cpp \ test/random_tests.cpp \ test/ratecheck_tests.cpp \ + test/rest_tests.cpp \ test/reverselock_tests.cpp \ test/rpc_tests.cpp \ test/sanity_tests.cpp \ @@ -222,6 +224,7 @@ BITCOIN_TESTS += wallet/test/db_tests.cpp endif FUZZ_WALLET_SRC = \ + wallet/test/fuzz/coincontrol.cpp \ wallet/test/fuzz/coinselection.cpp if USE_SQLITE @@ -443,10 +446,9 @@ endif %.json.h: %.json @$(MKDIR_P) $(@D) - @{ \ + $(AM_V_GEN) { \ echo "namespace json_tests{" && \ echo "static unsigned const char $(*F)[] = {" && \ $(HEXDUMP) -v -e '8/1 "0x%02x, "' -e '"\n"' $< | $(SED) -e 's/0x ,//g' && \ echo "};};"; \ } > "$@.new" && mv -f "$@.new" "$@" - @echo "Generated $@" diff --git a/src/bench/util_time.cpp b/src/bench/util_time.cpp index afc733482e8a..c45584988d9a 100644 --- a/src/bench/util_time.cpp +++ b/src/bench/util_time.cpp @@ -32,7 +32,7 @@ static void BenchTimeMillis(benchmark::Bench& bench) static void BenchTimeMillisSys(benchmark::Bench& bench) { bench.run([&] { - (void)GetTimeMillis(); + (void)TicksSinceEpoch(SystemClock::now()); }); } diff --git a/src/bench/wallet_loading.cpp b/src/bench/wallet_loading.cpp new file mode 100644 index 000000000000..7fdacc24529a --- /dev/null +++ b/src/bench/wallet_loading.cpp @@ -0,0 +1,133 @@ +// Copyright (c) 2022 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using wallet::CWallet; +using wallet::DatabaseFormat; +using wallet::DatabaseOptions; +using wallet::ISMINE_SPENDABLE; +using wallet::MakeWalletDatabase; +using wallet::TxStateInactive; +using wallet::WALLET_FLAG_DESCRIPTORS; +using wallet::WalletContext; +using wallet::WalletDatabase; + +static const std::shared_ptr BenchLoadWallet(std::unique_ptr database, WalletContext& context, DatabaseOptions& options) +{ + bilingual_str error; + std::vector warnings; + auto wallet = CWallet::Create(context, "", std::move(database), options.create_flags, error, warnings); + NotifyWalletLoaded(context, wallet); + if (context.chain) { + wallet->postInitProcess(); + } + return wallet; +} + +static void BenchUnloadWallet(std::shared_ptr&& wallet) +{ + SyncWithValidationInterfaceQueue(); + wallet->m_chain_notifications_handler.reset(); + UnloadWallet(std::move(wallet)); +} + +static void AddTx(CWallet& wallet) +{ + bilingual_str error; + CTxDestination dest; + wallet.GetNewDestination("", dest, error); + + CMutableTransaction mtx; + mtx.vout.push_back({COIN, GetScriptForDestination(dest)}); + mtx.vin.push_back(CTxIn()); + + wallet.AddToWallet(MakeTransactionRef(mtx), TxStateInactive{}); +} + +static std::unique_ptr DuplicateMockDatabase(WalletDatabase& database, DatabaseOptions& options) +{ + auto new_database = CreateMockWalletDatabase(options); + + // Get a cursor to the original database + auto batch = database.MakeBatch(); + batch->StartCursor(); + + // Get a batch for the new database + auto new_batch = new_database->MakeBatch(); + + // Read all records from the original database and write them to the new one + while (true) { + CDataStream key(SER_DISK, CLIENT_VERSION); + CDataStream value(SER_DISK, CLIENT_VERSION); + bool complete; + batch->ReadAtCursor(key, value, complete); + if (complete) break; + new_batch->Write(key, value); + } + + return new_database; +} + +static void WalletLoading(benchmark::Bench& bench, bool legacy_wallet) +{ + const auto test_setup = MakeNoLogFileContext(); + test_setup->m_args.ForceSetArg("-unsafesqlitesync", "1"); + + WalletContext context; + context.args = &test_setup->m_args; + context.chain = test_setup->m_node.chain.get(); + + // Setup the wallet + // Loading the wallet will also create it + DatabaseOptions options; + if (legacy_wallet) { + options.require_format = DatabaseFormat::BERKELEY; + } else { + options.create_flags = WALLET_FLAG_DESCRIPTORS; + options.require_format = DatabaseFormat::SQLITE; + } + auto database = CreateMockWalletDatabase(options); + auto wallet = BenchLoadWallet(std::move(database), context, options); + + // Generate a bunch of transactions and addresses to put into the wallet + for (int i = 0; i < 1000; ++i) { + AddTx(*wallet); + } + + database = DuplicateMockDatabase(wallet->GetDatabase(), options); + + // reload the wallet for the actual benchmark + BenchUnloadWallet(std::move(wallet)); + + bench.epochs(5).run([&] { + wallet = BenchLoadWallet(std::move(database), context, options); + + // Cleanup + database = DuplicateMockDatabase(wallet->GetDatabase(), options); + BenchUnloadWallet(std::move(wallet)); + }); +} + +#ifdef USE_BDB +static void WalletLoadingLegacy(benchmark::Bench& bench) { WalletLoading(bench, /*legacy_wallet=*/true); } +BENCHMARK(WalletLoadingLegacy); +#endif + +#ifdef USE_SQLITE +static void WalletLoadingDescriptors(benchmark::Bench& bench) { WalletLoading(bench, /*legacy_wallet=*/false); } +BENCHMARK(WalletLoadingDescriptors); +#endif diff --git a/src/chain.cpp b/src/chain.cpp index 2480047d024f..765c4d55a6ba 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -19,11 +19,9 @@ std::string CBlockIndex::ToString() const pprev, nHeight, hashMerkleRoot.ToString(), GetBlockHash().ToString()); } -void CChain::SetTip(CBlockIndex *pindex) { - if (pindex == nullptr) { - vChain.clear(); - return; - } +void CChain::SetTip(CBlockIndex& block) +{ + CBlockIndex* pindex = █ vChain.resize(pindex->nHeight + 1); while (pindex && vChain[pindex->nHeight] != pindex) { vChain[pindex->nHeight] = pindex; diff --git a/src/chain.h b/src/chain.h index f7fe7584936b..2fdac98e85c0 100644 --- a/src/chain.h +++ b/src/chain.h @@ -470,7 +470,7 @@ class CChain } /** Set/initialize a chain with a given tip. */ - void SetTip(CBlockIndex* pindex); + void SetTip(CBlockIndex& block); /** Return a CBlockLocator that refers to a block in this chain (by default the tip). */ CBlockLocator GetLocator(const CBlockIndex* pindex = nullptr) const; diff --git a/src/crypto/sha256_arm_shani.cpp b/src/crypto/sha256_arm_shani.cpp index 7ffa56be7060..2ea1d9c2acc4 100644 --- a/src/crypto/sha256_arm_shani.cpp +++ b/src/crypto/sha256_arm_shani.cpp @@ -62,7 +62,7 @@ void Transform(uint32_t* s, const unsigned char* chunk, size_t blocks) MSG3 = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(chunk + 48))); chunk += 64; - // Original implemenation preloaded message and constant addition which was 1-3% slower. + // Original implementation preloaded message and constant addition which was 1-3% slower. // Now included as first step in quad round code saving one Q Neon register // "TMP0 = vaddq_u32(MSG0, vld1q_u32(&K[0]));" diff --git a/src/evo/cbtx.cpp b/src/evo/cbtx.cpp index 0ea054809917..af8ea39deb0a 100644 --- a/src/evo/cbtx.cpp +++ b/src/evo/cbtx.cpp @@ -43,23 +43,6 @@ bool CheckCbTx(const CCbTx& cbTx, const CBlockIndex* pindexPrev, TxValidationSta return true; } -bool CheckCbTxMerkleRoots(const CBlock& block, const CCbTx& cbTx, const CBlockIndex* pindex, - const llmq::CQuorumBlockProcessor& quorum_block_processor, BlockValidationState& state) -{ - if (pindex && cbTx.nVersion >= CCbTx::Version::MERKLE_ROOT_QUORUMS) { - uint256 calculatedMerkleRoot; - if (!CalcCbTxMerkleRootQuorums(block, pindex->pprev, quorum_block_processor, calculatedMerkleRoot, state)) { - // pass the state returned by the function above - return false; - } - if (calculatedMerkleRoot != cbTx.merkleRootQuorums) { - return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-quorummerkleroot"); - } - } - - return true; -} - using QcHashMap = std::map>; using QcIndexedHashMap = std::map>; diff --git a/src/evo/cbtx.h b/src/evo/cbtx.h index c9087f5866b0..482d880dd522 100644 --- a/src/evo/cbtx.h +++ b/src/evo/cbtx.h @@ -65,9 +65,6 @@ template<> struct is_serializable_enum : std::true_type {}; bool CheckCbTx(const CCbTx& cbTx, const CBlockIndex* pindexPrev, TxValidationState& state); -// This can only be done after the block has been fully processed, as otherwise we won't have the finished MN list -bool CheckCbTxMerkleRoots(const CBlock& block, const CCbTx& cbTx, const CBlockIndex* pindex, - const llmq::CQuorumBlockProcessor& quorum_block_processor, BlockValidationState& state); bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPrev, const llmq::CQuorumBlockProcessor& quorum_block_processor, uint256& merkleRootRet, BlockValidationState& state); diff --git a/src/evo/mnhftx.h b/src/evo/mnhftx.h index 8de6ef6c5d59..2dc38919cf48 100644 --- a/src/evo/mnhftx.h +++ b/src/evo/mnhftx.h @@ -50,7 +50,7 @@ class MNHFTx [[nodiscard]] UniValue ToJson() const { UniValue obj(UniValue::VOBJ); - obj.pushKV("versionBit", (int)versionBit); + obj.pushKV("versionBit", versionBit); obj.pushKV("quorumHash", quorumHash.ToString()); obj.pushKV("sig", sig.ToString()); return obj; diff --git a/src/evo/specialtxman.cpp b/src/evo/specialtxman.cpp index 162446a14d99..f0141922f460 100644 --- a/src/evo/specialtxman.cpp +++ b/src/evo/specialtxman.cpp @@ -497,7 +497,7 @@ bool CSpecialTxProcessor::ProcessSpecialTxsInBlock(const CBlock& block, const CB static int64_t nTimeQuorum = 0; static int64_t nTimeDMN = 0; static int64_t nTimeMerkleMNL = 0; - static int64_t nTimeMerkle = 0; + static int64_t nTimeMerkleQuorums = 0; static int64_t nTimeCbTxCL = 0; static int64_t nTimeMnehf = 0; static int64_t nTimePayload = 0; @@ -599,62 +599,63 @@ bool CSpecialTxProcessor::ProcessSpecialTxsInBlock(const CBlock& block, const CB } } - int64_t nTime5_1 = GetTimeMicros(); - nTimeDMN += nTime5_1 - nTime5; - - LogPrint(BCLog::BENCHMARK, " - m_dmnman: %.2fms [%.2fs]\n", 0.001 * (nTime5_1 - nTime5), - nTimeDMN * 0.000001); + int64_t nTime6 = GetTimeMicros(); + nTimeDMN += nTime6 - nTime5; + LogPrint(BCLog::BENCHMARK, " - m_dmnman: %.2fms [%.2fs]\n", 0.001 * (nTime6 - nTime5), nTimeDMN * 0.000001); if (opt_cbTx.has_value()) { - uint256 calculatedMerkleRoot; - if (!CalcCbTxMerkleRootMNList(calculatedMerkleRoot, mn_list.to_sml(), state)) { + uint256 calculatedMerkleRootMNL; + if (!CalcCbTxMerkleRootMNList(calculatedMerkleRootMNL, mn_list.to_sml(), state)) { // pass the state returned by the function above return false; } - if (calculatedMerkleRoot != opt_cbTx->merkleRootMNList) { + if (calculatedMerkleRootMNL != opt_cbTx->merkleRootMNList) { return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-mnmerkleroot"); } - int64_t nTime5_2 = GetTimeMicros(); - nTimeMerkleMNL += nTime5_2 - nTime5_1; + int64_t nTime6_1 = GetTimeMicros(); + nTimeMerkleMNL += nTime6_1 - nTime6; LogPrint(BCLog::BENCHMARK, " - CalcCbTxMerkleRootMNList: %.2fms [%.2fs]\n", - 0.001 * (nTime5_2 - nTime5_1), nTimeMerkleMNL * 0.000001); - } + 0.001 * (nTime6_1 - nTime6), nTimeMerkleMNL * 0.000001); - int64_t nTime6 = GetTimeMicros(); - if (opt_cbTx.has_value()) { - if (!CheckCbTxMerkleRoots(block, *opt_cbTx, pindex, m_qblockman, state)) { - // pass the state returned by the function above - return false; + if (opt_cbTx->nVersion >= CCbTx::Version::MERKLE_ROOT_QUORUMS) { + uint256 calculatedMerkleRootQuorums; + if (!CalcCbTxMerkleRootQuorums(block, pindex->pprev, m_qblockman, calculatedMerkleRootQuorums, state)) { + // pass the state returned by the function above + return false; + } + if (calculatedMerkleRootQuorums != opt_cbTx->merkleRootQuorums) { + return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-cbtx-quorummerkleroot"); + } } - } - int64_t nTime7 = GetTimeMicros(); - nTimeMerkle += nTime7 - nTime6; + int64_t nTime6_2 = GetTimeMicros(); + nTimeMerkleQuorums += nTime6_2 - nTime6_1; - LogPrint(BCLog::BENCHMARK, " - CheckCbTxMerkleRoots: %.2fms [%.2fs]\n", 0.001 * (nTime7 - nTime6), - nTimeMerkle * 0.000001); + LogPrint(BCLog::BENCHMARK, " - CalcCbTxMerkleRootQuorums: %.2fms [%.2fs]\n", + 0.001 * (nTime6_2 - nTime6_1), nTimeMerkleQuorums * 0.000001); - if (opt_cbTx.has_value()) { if (!CheckCbTxBestChainlock(*opt_cbTx, pindex, m_clhandler, state)) { // pass the state returned by the function above return false; } + + int64_t nTime6_3 = GetTimeMicros(); + nTimeCbTxCL += nTime6_3 - nTime6_2; + LogPrint(BCLog::BENCHMARK, " - CheckCbTxBestChainlock: %.2fms [%.2fs]\n", + 0.001 * (nTime6_3 - nTime6_2), nTimeCbTxCL * 0.000001); } - int64_t nTime8 = GetTimeMicros(); - nTimeCbTxCL += nTime8 - nTime7; - LogPrint(BCLog::BENCHMARK, " - CheckCbTxBestChainlock: %.2fms [%.2fs]\n", 0.001 * (nTime8 - nTime7), - nTimeCbTxCL * 0.000001); + int64_t nTime7 = GetTimeMicros(); if (!m_mnhfman.ProcessBlock(block, pindex, fJustCheck, state)) { // pass the state returned by the function above return false; } - int64_t nTime9 = GetTimeMicros(); - nTimeMnehf += nTime9 - nTime8; - LogPrint(BCLog::BENCHMARK, " - m_mnhfman: %.2fms [%.2fs]\n", 0.001 * (nTime9 - nTime8), nTimeMnehf * 0.000001); + int64_t nTime8 = GetTimeMicros(); + nTimeMnehf += nTime8 - nTime7; + LogPrint(BCLog::BENCHMARK, " - m_mnhfman: %.2fms [%.2fs]\n", 0.001 * (nTime8 - nTime7), nTimeMnehf * 0.000001); if (DeploymentActiveAfter(pindex, m_consensus_params, Consensus::DEPLOYMENT_V19) && bls::bls_legacy_scheme.load()) { // NOTE: The block next to the activation is the one that is using new rules. diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 51d8c3e0d8c2..dc4d6c5dc0d4 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -21,15 +21,17 @@ #include #include +#include #include #include -#include #include #include -#include +#include #include +#include +#include #include @@ -670,6 +672,37 @@ HTTPRequest::RequestMethod HTTPRequest::GetRequestMethod() const } } +std::optional HTTPRequest::GetQueryParameter(const std::string& key) const +{ + const char* uri{evhttp_request_get_uri(req)}; + + return GetQueryParameterFromUri(uri, key); +} + +std::optional GetQueryParameterFromUri(const char* uri, const std::string& key) +{ + evhttp_uri* uri_parsed{evhttp_uri_parse(uri)}; + const char* query{evhttp_uri_get_query(uri_parsed)}; + std::optional result; + + if (query) { + // Parse the query string into a key-value queue and iterate over it + struct evkeyvalq params_q; + evhttp_parse_query_str(query, ¶ms_q); + + for (struct evkeyval* param{params_q.tqh_first}; param != nullptr; param = param->next.tqe_next) { + if (param->key == key) { + result = param->value; + break; + } + } + evhttp_clear_headers(¶ms_q); + } + evhttp_uri_free(uri_parsed); + + return result; +} + void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler) { LogPrint(BCLog::HTTP, "Registering HTTP handler for %s (exactmatch %d)\n", prefix, exactMatch); diff --git a/src/httpserver.h b/src/httpserver.h index 92c8cf1843a8..5ab3f189273a 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -5,8 +5,9 @@ #ifndef BITCOIN_HTTPSERVER_H #define BITCOIN_HTTPSERVER_H -#include #include +#include +#include static const int DEFAULT_HTTP_THREADS=4; static const int DEFAULT_HTTP_WORKQUEUE=16; @@ -82,6 +83,17 @@ class HTTPRequest */ RequestMethod GetRequestMethod() const; + /** Get the query parameter value from request uri for a specified key, or std::nullopt if the + * key is not found. + * + * If the query string contains duplicate keys, the first value is returned. Many web frameworks + * would instead parse this as an array of values, but this is not (yet) implemented as it is + * currently not needed in any of the endpoints. + * + * @param[in] key represents the query parameter of which the value is returned + */ + std::optional GetQueryParameter(const std::string& key) const; + /** * Get the request header specified by hdr, or an empty string. * Return a pair (isPresent,string). @@ -114,6 +126,20 @@ class HTTPRequest void WriteReply(int nStatus, const std::string& strReply = ""); }; +/** Get the query parameter value from request uri for a specified key, or std::nullopt if the key + * is not found. + * + * If the query string contains duplicate keys, the first value is returned. Many web frameworks + * would instead parse this as an array of values, but this is not (yet) implemented as it is + * currently not needed in any of the endpoints. + * + * Helper function for HTTPRequest::GetQueryParameter. + * + * @param[in] uri is the entire request uri + * @param[in] key represents the query parameter of which the value is returned + */ +std::optional GetQueryParameterFromUri(const char* uri, const std::string& key); + /** Event handler closure. */ class HTTPClosure diff --git a/src/init.cpp b/src/init.cpp index f748a4e93416..15929f489831 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -591,7 +591,6 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-txreconciliation", strprintf("Enable transaction reconciliations per BIP 330 (default: %d)", DEFAULT_TXRECONCILIATION_ENABLE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION); argsman.AddArg("-peertimeout=", strprintf("Specify a p2p connection timeout delay in seconds. After connecting to a peer, wait this amount of time before considering disconnection based on inactivity (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); - argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); // TODO: remove the sentence "Nodes not using ... incoming connections." once the changes from // https://github.com/bitcoin/bitcoin/pull/23542 have become widespread. argsman.AddArg("-port=", strprintf("Listen for connections on . Nodes not using the default ports (default: %u, testnet: %u, regtest: %u) are unlikely to get incoming connections. Not relevant for I2P (see doc/i2p.md).", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); @@ -746,6 +745,8 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine (default: %u)", MAX_OP_RETURN_RELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); + argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, + OptionsCategory::NODE_RELAY); argsman.AddArg("-minrelaytxfee=", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted inbound peers with default permissions. This will relay transactions even if the transactions were already in the mempool. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); @@ -1040,7 +1041,7 @@ namespace { // Variables internal to initialization process only int nMaxConnections; int nUserMaxConnections; int nFD; -ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED | NODE_HEADERS_COMPRESSED); +ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK_LIMITED | NODE_HEADERS_COMPRESSED); int64_t peer_connect_timeout; std::set g_enabled_filter_types; @@ -2220,12 +2221,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } // ********************************************************* Step 10: data directory maintenance - - // if pruning, unset the service bit and perform the initial blockstore prune + // if pruning, perform the initial blockstore prune // after any wallet rescanning has taken place. if (fPruneMode) { - LogPrintf("Unsetting NODE_NETWORK on prune mode\n"); - nLocalServices = ServiceFlags(nLocalServices & ~NODE_NETWORK); if (!fReindex) { LOCK(cs_main); for (CChainState* chainstate : chainman.GetAll()) { @@ -2233,6 +2231,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) chainstate->PruneAndFlush(); } } + } else { + LogPrintf("Setting NODE_NETWORK on non-prune mode\n"); + nLocalServices = ServiceFlags(nLocalServices | NODE_NETWORK); } // As PruneAndFlush can take several minutes, it's possible the user diff --git a/src/instantsend/instantsend.cpp b/src/instantsend/instantsend.cpp index 1e92ea074a41..4a228bc18537 100644 --- a/src/instantsend/instantsend.cpp +++ b/src/instantsend/instantsend.cpp @@ -155,7 +155,7 @@ PeerMsgRet CInstantSendManager::ProcessMessageInstantSendLock(const CNode& pfrom LOCK(cs_timingsTxSeen); if (auto it = timingsTxSeen.find(islock->txid); it != timingsTxSeen.end()) { // This is the normal case where we received the TX before the islock - auto diff = GetTimeMillis() - it->second; + auto diff = TicksSinceEpoch(SystemClock::now()) - it->second; timingsTxSeen.erase(it); return diff; } @@ -551,7 +551,7 @@ void CInstantSendManager::AddNonLockedTx(const CTransactionRef& tx, const CBlock if (ShouldReportISLockTiming()) { LOCK(cs_timingsTxSeen); // Only insert the time the first time we see the tx, as we sometimes try to resign - timingsTxSeen.try_emplace(tx->GetHash(), GetTimeMillis()); + timingsTxSeen.try_emplace(tx->GetHash(), TicksSinceEpoch(SystemClock::now())); } LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s, pindexMined=%s\n", __func__, diff --git a/src/llmq/chainlocks.cpp b/src/llmq/chainlocks.cpp index 8e8917180ae8..155e1117260a 100644 --- a/src/llmq/chainlocks.cpp +++ b/src/llmq/chainlocks.cpp @@ -111,7 +111,7 @@ MessageProcessingResult CChainLocksHandler::ProcessNewChainLock(const NodeId fro { LOCK(cs); - if (!seenChainLocks.emplace(hash, GetTimeMillis()).second) { + if (!seenChainLocks.emplace(hash, TicksSinceEpoch(SystemClock::now())).second) { return {}; } @@ -611,15 +611,15 @@ void CChainLocksHandler::Cleanup() return; } - if (GetTimeMillis() - lastCleanupTime < CLEANUP_INTERVAL) { + if (TicksSinceEpoch(SystemClock::now()) - lastCleanupTime < CLEANUP_INTERVAL) { return; } - lastCleanupTime = GetTimeMillis(); + lastCleanupTime = TicksSinceEpoch(SystemClock::now()); { LOCK(cs); for (auto it = seenChainLocks.begin(); it != seenChainLocks.end(); ) { - if (GetTimeMillis() - it->second >= CLEANUP_SEEN_TIMEOUT) { + if (TicksSinceEpoch(SystemClock::now()) - it->second >= CLEANUP_SEEN_TIMEOUT) { it = seenChainLocks.erase(it); } else { ++it; diff --git a/src/llmq/debug.cpp b/src/llmq/debug.cpp index 0952c3f52192..953ece2b3d08 100644 --- a/src/llmq/debug.cpp +++ b/src/llmq/debug.cpp @@ -34,7 +34,7 @@ UniValue CDKGDebugSessionStatus::ToJson(CDeterministicMNManager& dmnman, CQuorum ret.pushKV("llmqType", ToUnderlying(llmqType)); ret.pushKV("quorumHash", quorumHash.ToString()); - ret.pushKV("quorumHeight", (int)quorumHeight); + ret.pushKV("quorumHeight", quorumHeight); ret.pushKV("phase", ToUnderlying(phase)); ret.pushKV("sentContributions", statusBits.sentContributions); @@ -61,10 +61,10 @@ UniValue CDKGDebugSessionStatus::ToJson(CDeterministicMNManager& dmnman, CQuorum if (detailLevel == 0) { v.count++; } else if (detailLevel == 1) { - v.arr.push_back((int)idx); + v.arr.push_back(idx); } else if (detailLevel == 2) { UniValue a(UniValue::VOBJ); - a.pushKV("memberIndex", (int)idx); + a.pushKV("memberIndex", idx); if (idx < dmnMembers.size()) { a.pushKV("proTxHash", dmnMembers[idx]->proTxHash.ToString()); } diff --git a/src/llmq/dkgsessionhandler.cpp b/src/llmq/dkgsessionhandler.cpp index beb111533e9e..7ef53456c8d4 100644 --- a/src/llmq/dkgsessionhandler.cpp +++ b/src/llmq/dkgsessionhandler.cpp @@ -301,13 +301,13 @@ void CDKGSessionHandler::SleepBeforePhase(QuorumPhase curPhase, double adjustedPhaseSleepTimePerMember = phaseSleepTimePerMember * randomSleepFactor; int64_t sleepTime = (int64_t)(adjustedPhaseSleepTimePerMember * curSession->GetMyMemberIndex().value_or(0)); - int64_t endTime = GetTimeMillis() + sleepTime; + int64_t endTime = TicksSinceEpoch(SystemClock::now()) + sleepTime; int heightTmp{currentHeight.load()}; int heightStart{heightTmp}; LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - starting sleep for %d ms, curPhase=%d\n", __func__, params.name, quorumIndex, sleepTime, ToUnderlying(curPhase)); - while (GetTimeMillis() < endTime) { + while (TicksSinceEpoch(SystemClock::now()) < endTime) { if (stopRequested) { LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex); throw AbortPhaseException(); diff --git a/src/llmq/dkgsessionmgr.cpp b/src/llmq/dkgsessionmgr.cpp index 6386d9487ab4..49ca664a597f 100644 --- a/src/llmq/dkgsessionmgr.cpp +++ b/src/llmq/dkgsessionmgr.cpp @@ -317,6 +317,9 @@ bool CDKGSessionManager::GetVerifiedContributions(Consensus::LLMQType llmqType, if (it == contributionsCache.end()) { CDataStream s(SER_DISK, CLIENT_VERSION); if (!db->ReadDataStream(std::make_tuple(DB_VVEC, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), s)) { + LogPrint(BCLog::LLMQ, "%s -- this node does not have vvec for llmq=%d block=%s protx=%s\n", + __func__, ToUnderlying(llmqType), pQuorumBaseBlockIndex->GetBlockHash().ToString(), + proTxHash.ToString()); return false; } size_t vvec_size = ReadCompactSize(s); @@ -331,7 +334,7 @@ bool CDKGSessionManager::GetVerifiedContributions(Consensus::LLMQType llmqType, CBLSSecretKey skContribution; db->Read(std::make_tuple(DB_SKCONTRIB, llmqType, pQuorumBaseBlockIndex->GetBlockHash(), proTxHash), skContribution); - it = contributionsCache.emplace(cacheKey, ContributionsCacheEntry{GetTimeMillis(), vvecPtr, skContribution}).first; + it = contributionsCache.emplace(cacheKey, ContributionsCacheEntry{TicksSinceEpoch(SystemClock::now()), vvecPtr, skContribution}).first; } memberIndexesRet.emplace_back(i); @@ -378,7 +381,7 @@ bool CDKGSessionManager::GetEncryptedContributions(Consensus::LLMQType llmqType, void CDKGSessionManager::CleanupCache() const { LOCK(contributionsCacheCs); - auto curTime = GetTimeMillis(); + auto curTime = TicksSinceEpoch(SystemClock::now()); for (auto it = contributionsCache.begin(); it != contributionsCache.end(); ) { if (curTime - it->second.entryTime > MAX_CONTRIBUTION_CACHE_TIME) { it = contributionsCache.erase(it); diff --git a/src/llmq/quorums.cpp b/src/llmq/quorums.cpp index 6c1dc379bd4e..86e1f29c6240 100644 --- a/src/llmq/quorums.cpp +++ b/src/llmq/quorums.cpp @@ -923,11 +923,11 @@ void CQuorumManager::StartQuorumDataRecoveryThread(CConnman& connman, const CQuo { assert(m_mn_activeman); - if (pQuorum->fQuorumDataRecoveryThreadRunning) { + bool expected = false; + if (!pQuorum->fQuorumDataRecoveryThreadRunning.compare_exchange_strong(expected, true)) { LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- Already running\n", __func__); return; } - pQuorum->fQuorumDataRecoveryThreadRunning = true; workerPool.push([&connman, pQuorum, pIndex, nDataMaskIn, this](int threadId) { size_t nTries{0}; diff --git a/src/llmq/signing.cpp b/src/llmq/signing.cpp index f543cd399087..b05375c63d62 100644 --- a/src/llmq/signing.cpp +++ b/src/llmq/signing.cpp @@ -656,7 +656,7 @@ void CSigningManager::TruncateRecoveredSig(Consensus::LLMQType llmqType, const u void CSigningManager::Cleanup() { - int64_t now = GetTimeMillis(); + int64_t now = TicksSinceEpoch(SystemClock::now()); if (now - lastCleanupTime < 5000) { return; } @@ -666,7 +666,7 @@ void CSigningManager::Cleanup() db.CleanupOldRecoveredSigs(maxAge); db.CleanupOldVotes(maxAge); - lastCleanupTime = GetTimeMillis(); + lastCleanupTime = TicksSinceEpoch(SystemClock::now()); } void CSigningManager::RegisterRecoveredSigsListener(CRecoveredSigsListener* l) diff --git a/src/llmq/signing_shares.cpp b/src/llmq/signing_shares.cpp index 35aaddbb7fc6..d0b5d3ed8d22 100644 --- a/src/llmq/signing_shares.cpp +++ b/src/llmq/signing_shares.cpp @@ -1488,9 +1488,9 @@ void CSigSharesManager::WorkThreadMain(CConnman& connman, PeerManager& peerman) bool fMoreWork = ProcessPendingSigShares(peerman, connman); SignPendingSigShares(connman, peerman); - if (GetTimeMillis() - lastSendTime > 100) { + if (TicksSinceEpoch(SystemClock::now()) - lastSendTime > 100) { SendMessages(connman); - lastSendTime = GetTimeMillis(); + lastSendTime = TicksSinceEpoch(SystemClock::now()); } Cleanup(connman); diff --git a/src/masternode/sync.cpp b/src/masternode/sync.cpp index c653778fd401..fca0f0cde5cf 100644 --- a/src/masternode/sync.cpp +++ b/src/masternode/sync.cpp @@ -119,7 +119,7 @@ void CMasternodeSync::ProcessTick(const PeerManager& peerman, const CGovernanceM static int nTick = 0; nTick++; - const static int64_t nSyncStart = GetTimeMillis(); + const static int64_t nSyncStart = TicksSinceEpoch(SystemClock::now()); const static std::string strAllow = strprintf("allow-sync-%lld", nSyncStart); // reset the sync process if the last call to this function was more than 60 minutes ago (client was in sleep mode) diff --git a/src/net.cpp b/src/net.cpp index 659841ee5dbd..5c44be1d8307 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2117,9 +2117,9 @@ void CConnman::DisconnectNodes() // 1. vSendMsg must be empty and all messages sent via send(). This is ensured by SocketHandler() // being called before DisconnectNodes and also by the linger time // 2. Internal socket send buffers must be flushed. This is ensured solely by the linger time - pnode->nDisconnectLingerTime = GetTimeMillis() + 100; + pnode->nDisconnectLingerTime = TicksSinceEpoch(SystemClock::now()) + 100; } - if (GetTimeMillis() < pnode->nDisconnectLingerTime) { + if (TicksSinceEpoch(SystemClock::now()) < pnode->nDisconnectLingerTime) { // everything flushed to the kernel? const auto& [to_send, more, _msg_type] = pnode->m_transport->GetBytesToSend(pnode->nSendMsgSize != 0); const bool queue_is_empty{to_send.empty() && !more}; @@ -2638,11 +2638,11 @@ void CConnman::ThreadSocketHandler(CMasternodeSync& mn_sync) // Handle sockets before we do the next round of disconnects. This allows us to flush send buffers one last time // before actually closing sockets. Receiving is however skipped in case a peer is pending to be disconnected SocketHandler(mn_sync); - if (GetTimeMillis() - nLastCleanupNodes > 1000) { + if (TicksSinceEpoch(SystemClock::now()) - nLastCleanupNodes > 1000) { ForEachNode(AllNodes, [&](CNode* pnode) { if (InactivityCheck(*pnode)) pnode->fDisconnect = true; }); - nLastCleanupNodes = GetTimeMillis(); + nLastCleanupNodes = TicksSinceEpoch(SystemClock::now()); } DisconnectNodes(); NotifyNumConnectionsChanged(mn_sync); @@ -3650,9 +3650,9 @@ void CConnman::ThreadMessageHandler() bool fMoreWork = false; bool fSkipSendMessagesForMasternodes = true; - if (GetTimeMillis() - nLastSendMessagesTimeMasternodes >= 100) { + if (TicksSinceEpoch(SystemClock::now()) - nLastSendMessagesTimeMasternodes >= 100) { fSkipSendMessagesForMasternodes = false; - nLastSendMessagesTimeMasternodes = GetTimeMillis(); + nLastSendMessagesTimeMasternodes = TicksSinceEpoch(SystemClock::now()); } // Randomize the order in which we process messages from/to our peers. diff --git a/src/net.h b/src/net.h index 2dfc9d32b08a..957aff1b4fd3 100644 --- a/src/net.h +++ b/src/net.h @@ -1935,7 +1935,6 @@ friend class CNode; */ static constexpr size_t MAX_UNUSED_I2P_SESSIONS_SIZE{10}; - friend struct CConnmanTest; friend struct ConnmanTestMsg; }; diff --git a/src/netbase.cpp b/src/netbase.cpp index 850044f00c3d..95d95f3f31cc 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -320,7 +320,7 @@ enum class IntrRecvError { */ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const Sock& sock) { - int64_t curTime = GetTimeMillis(); + int64_t curTime = TicksSinceEpoch(SystemClock::now()); int64_t endTime = curTime + timeout; while (len > 0 && curTime < endTime) { ssize_t ret = sock.Recv(data, len, 0); // Optimistically try the recv first @@ -345,7 +345,7 @@ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, c } if (interruptSocks5Recv) return IntrRecvError::Interrupted; - curTime = GetTimeMillis(); + curTime = TicksSinceEpoch(SystemClock::now()); } return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout; } diff --git a/src/outputtype.cpp b/src/outputtype.cpp index c2c69dd9b72d..440372c0b5ea 100644 --- a/src/outputtype.cpp +++ b/src/outputtype.cpp @@ -25,9 +25,6 @@ bool ParseOutputType(const std::string& type, OutputType& output_type) if (type == OUTPUT_TYPE_STRING_LEGACY) { output_type = OutputType::LEGACY; return true; - } else if (type == OUTPUT_TYPE_STRING_UNKNOWN) { - output_type = OutputType::UNKNOWN; - return true; } return false; } diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index 829a7660491b..3b9fd117ffe6 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -106,11 +106,10 @@ void AskPassphraseDialog::accept() oldpass.reserve(MAX_PASSPHRASE_SIZE); newpass1.reserve(MAX_PASSPHRASE_SIZE); newpass2.reserve(MAX_PASSPHRASE_SIZE); - // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) - // Alternately, find a way to make this input mlock()'d to begin with. - oldpass.assign(ui->passEdit1->text().toStdString().c_str()); - newpass1.assign(ui->passEdit2->text().toStdString().c_str()); - newpass2.assign(ui->passEdit3->text().toStdString().c_str()); + + oldpass.assign(std::string_view{ui->passEdit1->text().toStdString()}); + newpass1.assign(std::string_view{ui->passEdit2->text().toStdString()}); + newpass2.assign(std::string_view{ui->passEdit3->text().toStdString()}); secureClearPassFields(); @@ -185,8 +184,19 @@ void AskPassphraseDialog::accept() try { if(!model->setWalletLocked(false, oldpass, mode == UnlockMixing)) { - QMessageBox::critical(this, tr("Wallet unlock failed"), - tr("The passphrase entered for the wallet decryption was incorrect.")); + // Check if the passphrase has a null character (see #27067 for details) + if (oldpass.find('\0') == std::string::npos) { + QMessageBox::critical(this, tr("Wallet unlock failed"), + tr("The passphrase entered for the wallet decryption was incorrect.")); + } else { + QMessageBox::critical(this, tr("Wallet unlock failed"), + tr("The passphrase entered for the wallet decryption is incorrect. " + "It contains a null character (ie - a zero byte). " + "If the passphrase was set with a version of this software prior to 23.0, " + "please try again with only the characters up to — but not including — " + "the first null character. If this is successful, please set a new " + "passphrase to avoid this issue in the future.")); + } } else { @@ -207,8 +217,18 @@ void AskPassphraseDialog::accept() } else { - QMessageBox::critical(this, tr("Wallet encryption failed"), - tr("The passphrase entered for the wallet decryption was incorrect.")); + // Check if the old passphrase had a null character (see #27067 for details) + if (oldpass.find('\0') == std::string::npos) { + QMessageBox::critical(this, tr("Passphrase change failed"), + tr("The passphrase entered for the wallet decryption was incorrect.")); + } else { + QMessageBox::critical(this, tr("Passphrase change failed"), + tr("The old passphrase entered for the wallet decryption is incorrect. " + "It contains a null character (ie - a zero byte). " + "If the passphrase was set with a version of this software prior to 23.0, " + "please try again with only the characters up to — but not including — " + "the first null character.")); + } } } else diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 3b99604d8868..e78adbb79884 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -171,6 +171,7 @@ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTrans static bool InitSettings() { + gArgs.EnsureDataDir(); if (!gArgs.GetSettingsPath()) { return true; // Do nothing if settings file disabled. } diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 7b20ce368b97..fb6b90e86ce7 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -263,7 +263,7 @@ void ClientModel::TipChanged(SynchronizationState sync_state, interfaces::BlockT // Throttle GUI notifications about (a) blocks during initial sync, and (b) both blocks and headers during reindex. const bool throttle = (sync_state != SynchronizationState::POST_INIT && !header) || sync_state == SynchronizationState::INIT_REINDEX; - const int64_t now = throttle ? GetTimeMillis() : 0; + const int64_t now = throttle ? TicksSinceEpoch(SystemClock::now()) : 0; int64_t& nLastUpdateNotification = header ? nLastHeaderTipUpdateNotification : nLastBlockTipUpdateNotification; if (throttle && now < nLastUpdateNotification + count_milliseconds(MODEL_UPDATE_DELAY)) { return; diff --git a/src/rest.cpp b/src/rest.cpp index 790b55af47ac..56b4c5861407 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -3,6 +3,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include + #include #include #include @@ -30,6 +32,8 @@ #include #include +#include + #include using node::GetTransaction; @@ -39,21 +43,14 @@ using node::ReadBlockFromDisk; static const size_t MAX_GETUTXOS_OUTPOINTS = 15; //allow a max of 15 outpoints to be queried at once static constexpr unsigned int MAX_REST_HEADERS_RESULTS = 2000; -enum class RetFormat { - UNDEF, - BINARY, - HEX, - JSON, -}; - static const struct { - RetFormat rf; + RESTResponseFormat rf; const char* name; } rf_names[] = { - {RetFormat::UNDEF, ""}, - {RetFormat::BINARY, "bin"}, - {RetFormat::HEX, "hex"}, - {RetFormat::JSON, "json"}, + {RESTResponseFormat::UNDEF, ""}, + {RESTResponseFormat::BINARY, "bin"}, + {RESTResponseFormat::HEX, "hex"}, + {RESTResponseFormat::JSON, "json"}, }; struct CCoin { @@ -157,25 +154,28 @@ static LLMQContext* GetLLMQContext(const CoreContext& context, HTTPRequest* req) return node_context->llmq_ctx.get(); } -static RetFormat ParseDataFormat(std::string& param, const std::string& strReq) +RESTResponseFormat ParseDataFormat(std::string& param, const std::string& strReq) { - const std::string::size_type pos = strReq.rfind('.'); - if (pos == std::string::npos) - { - param = strReq; + // Remove query string (if any, separated with '?') as it should not interfere with + // parsing param and data format + param = strReq.substr(0, strReq.rfind('?')); + const std::string::size_type pos_format{param.rfind('.')}; + + // No format string is found + if (pos_format == std::string::npos) { return rf_names[0].rf; } - param = strReq.substr(0, pos); - const std::string suff(strReq, pos + 1); - + // Match format string to available formats + const std::string suffix(param, pos_format + 1); for (const auto& rf_name : rf_names) { - if (suff == rf_name.name) + if (suffix == rf_name.name) { + param.erase(pos_format); return rf_name.rf; + } } - /* If no suffix is found, return original string. */ - param = strReq; + // If no suffix is found, return RESTResponseFormat::UNDEF and original string without query string return rf_names[0].rf; } @@ -211,18 +211,28 @@ static bool rest_headers(const CoreContext& context, if (!CheckWarmup(req)) return false; std::string param; - const RetFormat rf = ParseDataFormat(param, strURIPart); + const RESTResponseFormat rf = ParseDataFormat(param, strURIPart); std::vector path = SplitString(param, '/'); - if (path.size() != 2) - return RESTERR(req, HTTP_BAD_REQUEST, "No header count specified. Use /rest/headers//.."); - - const auto parsed_count{ToIntegral(path[0])}; + std::string raw_count; + std::string hashStr; + if (path.size() == 2) { + // deprecated path: /rest/headers// + hashStr = path[1]; + raw_count = path[0]; + } else if (path.size() == 1) { + // new path with query parameter: /rest/headers/?count= + hashStr = path[0]; + raw_count = req->GetQueryParameter("count").value_or("5"); + } else { + return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/headers/.?count="); + } + + const auto parsed_count{ToIntegral(raw_count)}; if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) { - return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count is invalid or out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, path[0])); + return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count is invalid or out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, raw_count)); } - std::string hashStr = path[1]; uint256 hash; if (!ParseHashStr(hashStr, hash)) return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + hashStr); @@ -248,7 +258,7 @@ static bool rest_headers(const CoreContext& context, } switch (rf) { - case RetFormat::BINARY: { + case RESTResponseFormat::BINARY: { CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION); for (const CBlockIndex *pindex : headers) { ssHeader << pindex->GetBlockHeader(); @@ -260,7 +270,7 @@ static bool rest_headers(const CoreContext& context, return true; } - case RetFormat::HEX: { + case RESTResponseFormat::HEX: { CDataStream ssHeader(SER_NETWORK, PROTOCOL_VERSION); for (const CBlockIndex *pindex : headers) { ssHeader << pindex->GetBlockHeader(); @@ -271,7 +281,7 @@ static bool rest_headers(const CoreContext& context, req->WriteReply(HTTP_OK, strHex); return true; } - case RetFormat::JSON: { + case RESTResponseFormat::JSON: { const LLMQContext* llmq_ctx = GetLLMQContext(context, req); if (!llmq_ctx) return false; @@ -298,7 +308,7 @@ static bool rest_block(const CoreContext& context, if (!CheckWarmup(req)) return false; std::string hashStr; - const RetFormat rf = ParseDataFormat(hashStr, strURIPart); + const RESTResponseFormat rf = ParseDataFormat(hashStr, strURIPart); uint256 hash; if (!ParseHashStr(hashStr, hash)) @@ -327,7 +337,7 @@ static bool rest_block(const CoreContext& context, } switch (rf) { - case RetFormat::BINARY: { + case RESTResponseFormat::BINARY: { CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); ssBlock << block; std::string binaryBlock = ssBlock.str(); @@ -336,7 +346,7 @@ static bool rest_block(const CoreContext& context, return true; } - case RetFormat::HEX: { + case RESTResponseFormat::HEX: { CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION); ssBlock << block; std::string strHex = HexStr(ssBlock) + "\n"; @@ -345,7 +355,7 @@ static bool rest_block(const CoreContext& context, return true; } - case RetFormat::JSON: { + case RESTResponseFormat::JSON: { const LLMQContext* llmq_ctx = GetLLMQContext(context, req); if (!llmq_ctx) return false; @@ -377,16 +387,31 @@ static bool rest_filter_header(const CoreContext& context, HTTPRequest* req, con if (!CheckWarmup(req)) return false; std::string param; - const RetFormat rf = ParseDataFormat(param, strURIPart); + const RESTResponseFormat rf = ParseDataFormat(param, strURIPart); std::vector uri_parts = SplitString(param, '/'); - if (uri_parts.size() != 3) { - return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilterheaders///"); + std::string raw_count; + std::string raw_blockhash; + if (uri_parts.size() == 3) { + // deprecated path: /rest/blockfilterheaders/// + raw_blockhash = uri_parts[2]; + raw_count = uri_parts[1]; + } else if (uri_parts.size() == 2) { + // new path with query parameter: /rest/blockfilterheaders//?count= + raw_blockhash = uri_parts[1]; + raw_count = req->GetQueryParameter("count").value_or("5"); + } else { + return RESTERR(req, HTTP_BAD_REQUEST, "Invalid URI format. Expected /rest/blockfilterheaders//.?count="); + } + + const auto parsed_count{ToIntegral(raw_count)}; + if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) { + return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count is invalid or out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, raw_count)); } uint256 block_hash; - if (!ParseHashStr(uri_parts[2], block_hash)) { - return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + uri_parts[2]); + if (!ParseHashStr(raw_blockhash, block_hash)) { + return RESTERR(req, HTTP_BAD_REQUEST, "Invalid hash: " + raw_blockhash); } BlockFilterType filtertype; @@ -399,11 +424,6 @@ static bool rest_filter_header(const CoreContext& context, HTTPRequest* req, con return RESTERR(req, HTTP_BAD_REQUEST, "Index is not enabled for filtertype " + uri_parts[0]); } - const auto parsed_count{ToIntegral(uri_parts[1])}; - if (!parsed_count.has_value() || *parsed_count < 1 || *parsed_count > MAX_REST_HEADERS_RESULTS) { - return RESTERR(req, HTTP_BAD_REQUEST, strprintf("Header count is invalid or out of acceptable range (1-%u): %s", MAX_REST_HEADERS_RESULTS, uri_parts[1])); - } - std::vector headers; headers.reserve(*parsed_count); { @@ -442,7 +462,7 @@ static bool rest_filter_header(const CoreContext& context, HTTPRequest* req, con } switch (rf) { - case RetFormat::BINARY: { + case RESTResponseFormat::BINARY: { CDataStream ssHeader{SER_NETWORK, PROTOCOL_VERSION}; for (const uint256& header : filter_headers) { ssHeader << header; @@ -453,7 +473,7 @@ static bool rest_filter_header(const CoreContext& context, HTTPRequest* req, con req->WriteReply(HTTP_OK, binaryHeader); return true; } - case RetFormat::HEX: { + case RESTResponseFormat::HEX: { CDataStream ssHeader{SER_NETWORK, PROTOCOL_VERSION}; for (const uint256& header : filter_headers) { ssHeader << header; @@ -464,7 +484,7 @@ static bool rest_filter_header(const CoreContext& context, HTTPRequest* req, con req->WriteReply(HTTP_OK, strHex); return true; } - case RetFormat::JSON: { + case RESTResponseFormat::JSON: { UniValue jsonHeaders(UniValue::VARR); for (const uint256& header : filter_headers) { jsonHeaders.push_back(header.GetHex()); @@ -486,7 +506,7 @@ static bool rest_block_filter(const CoreContext& context, HTTPRequest* req, cons if (!CheckWarmup(req)) return false; std::string param; - const RetFormat rf = ParseDataFormat(param, strURIPart); + const RESTResponseFormat rf = ParseDataFormat(param, strURIPart); // request is sent over URI scheme /rest/blockfilter/filtertype/blockhash std::vector uri_parts = SplitString(param, '/'); @@ -541,7 +561,7 @@ static bool rest_block_filter(const CoreContext& context, HTTPRequest* req, cons } switch (rf) { - case RetFormat::BINARY: { + case RESTResponseFormat::BINARY: { CDataStream ssResp{SER_NETWORK, PROTOCOL_VERSION}; ssResp << filter; @@ -550,7 +570,7 @@ static bool rest_block_filter(const CoreContext& context, HTTPRequest* req, cons req->WriteReply(HTTP_OK, binaryResp); return true; } - case RetFormat::HEX: { + case RESTResponseFormat::HEX: { CDataStream ssResp{SER_NETWORK, PROTOCOL_VERSION}; ssResp << filter; @@ -559,7 +579,7 @@ static bool rest_block_filter(const CoreContext& context, HTTPRequest* req, cons req->WriteReply(HTTP_OK, strHex); return true; } - case RetFormat::JSON: { + case RESTResponseFormat::JSON: { UniValue ret(UniValue::VOBJ); ret.pushKV("filter", HexStr(filter.GetEncodedFilter())); std::string strJSON = ret.write() + "\n"; @@ -581,10 +601,10 @@ static bool rest_chaininfo(const CoreContext& context, HTTPRequest* req, const s if (!CheckWarmup(req)) return false; std::string param; - const RetFormat rf = ParseDataFormat(param, strURIPart); + const RESTResponseFormat rf = ParseDataFormat(param, strURIPart); switch (rf) { - case RetFormat::JSON: { + case RESTResponseFormat::JSON: { JSONRPCRequest jsonRequest; jsonRequest.context = context; jsonRequest.params = UniValue(UniValue::VARR); @@ -607,10 +627,10 @@ static bool rest_mempool_info(const CoreContext& context, HTTPRequest* req, cons const CTxMemPool* mempool = GetMemPool(context, req); if (!mempool) return false; std::string param; - const RetFormat rf = ParseDataFormat(param, strURIPart); + const RESTResponseFormat rf = ParseDataFormat(param, strURIPart); switch (rf) { - case RetFormat::JSON: { + case RESTResponseFormat::JSON: { const LLMQContext* llmq_ctx = GetLLMQContext(context, req); if (!llmq_ctx) return false; @@ -633,10 +653,10 @@ static bool rest_mempool_contents(const CoreContext& context, HTTPRequest* req, const CTxMemPool* mempool = GetMemPool(context, req); if (!mempool) return false; std::string param; - const RetFormat rf = ParseDataFormat(param, strURIPart); + const RESTResponseFormat rf = ParseDataFormat(param, strURIPart); switch (rf) { - case RetFormat::JSON: { + case RESTResponseFormat::JSON: { const LLMQContext* llmq_ctx = GetLLMQContext(context, req); if (!llmq_ctx) return false; @@ -658,7 +678,7 @@ static bool rest_tx(const CoreContext& context, HTTPRequest* req, const std::str if (!CheckWarmup(req)) return false; std::string hashStr; - const RetFormat rf = ParseDataFormat(hashStr, strURIPart); + const RESTResponseFormat rf = ParseDataFormat(hashStr, strURIPart); uint256 hash; if (!ParseHashStr(hashStr, hash)) @@ -677,7 +697,7 @@ static bool rest_tx(const CoreContext& context, HTTPRequest* req, const std::str } switch (rf) { - case RetFormat::BINARY: { + case RESTResponseFormat::BINARY: { CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << tx; @@ -687,7 +707,7 @@ static bool rest_tx(const CoreContext& context, HTTPRequest* req, const std::str return true; } - case RetFormat::HEX: { + case RESTResponseFormat::HEX: { CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION); ssTx << tx; @@ -697,7 +717,7 @@ static bool rest_tx(const CoreContext& context, HTTPRequest* req, const std::str return true; } - case RetFormat::JSON: { + case RESTResponseFormat::JSON: { UniValue objTx(UniValue::VOBJ); TxToUniv(*tx, /*block_hash=*/hashBlock, /*entry=*/objTx); std::string strJSON = objTx.write() + "\n"; @@ -717,7 +737,7 @@ static bool rest_getutxos(const CoreContext& context, HTTPRequest* req, const st if (!CheckWarmup(req)) return false; std::string param; - const RetFormat rf = ParseDataFormat(param, strURIPart); + const RESTResponseFormat rf = ParseDataFormat(param, strURIPart); std::vector uriParts; if (param.length() > 1) @@ -764,14 +784,14 @@ static bool rest_getutxos(const CoreContext& context, HTTPRequest* req, const st } switch (rf) { - case RetFormat::HEX: { + case RESTResponseFormat::HEX: { // convert hex to bin, continue then with bin part std::vector strRequestV = ParseHex(strRequestMutable); strRequestMutable.assign(strRequestV.begin(), strRequestV.end()); [[fallthrough]]; } - case RetFormat::BINARY: { + case RESTResponseFormat::BINARY: { try { //deserialize only if user sent a request if (strRequestMutable.size() > 0) @@ -791,7 +811,7 @@ static bool rest_getutxos(const CoreContext& context, HTTPRequest* req, const st break; } - case RetFormat::JSON: { + case RESTResponseFormat::JSON: { if (!fInputParsed) return RESTERR(req, HTTP_BAD_REQUEST, "Error: empty request"); break; @@ -845,7 +865,7 @@ static bool rest_getutxos(const CoreContext& context, HTTPRequest* req, const st } switch (rf) { - case RetFormat::BINARY: { + case RESTResponseFormat::BINARY: { // serialize data // use exact same output as mentioned in Bip64 CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION); @@ -857,7 +877,7 @@ static bool rest_getutxos(const CoreContext& context, HTTPRequest* req, const st return true; } - case RetFormat::HEX: { + case RESTResponseFormat::HEX: { CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION); ssGetUTXOResponse << chainman.ActiveChain().Height() << chainman.ActiveChain().Tip()->GetBlockHash() << bitmap << outs; std::string strHex = HexStr(ssGetUTXOResponse) + "\n"; @@ -867,7 +887,7 @@ static bool rest_getutxos(const CoreContext& context, HTTPRequest* req, const st return true; } - case RetFormat::JSON: { + case RESTResponseFormat::JSON: { UniValue objGetUTXOResponse(UniValue::VOBJ); // pack in some essentials @@ -907,7 +927,7 @@ static bool rest_blockhash_by_height(const CoreContext& context, HTTPRequest* re { if (!CheckWarmup(req)) return false; std::string height_str; - const RetFormat rf = ParseDataFormat(height_str, str_uri_part); + const RESTResponseFormat rf = ParseDataFormat(height_str, str_uri_part); int32_t blockheight = -1; // Initialization done only to prevent valgrind false positive, see https://github.com/bitcoin/bitcoin/pull/18785 if (!ParseInt32(height_str, &blockheight) || blockheight < 0) { @@ -927,19 +947,19 @@ static bool rest_blockhash_by_height(const CoreContext& context, HTTPRequest* re pblockindex = active_chain[blockheight]; } switch (rf) { - case RetFormat::BINARY: { + case RESTResponseFormat::BINARY: { CDataStream ss_blockhash(SER_NETWORK, PROTOCOL_VERSION); ss_blockhash << pblockindex->GetBlockHash(); req->WriteHeader("Content-Type", "application/octet-stream"); req->WriteReply(HTTP_OK, ss_blockhash.str()); return true; } - case RetFormat::HEX: { + case RESTResponseFormat::HEX: { req->WriteHeader("Content-Type", "text/plain"); req->WriteReply(HTTP_OK, pblockindex->GetBlockHash().GetHex() + "\n"); return true; } - case RetFormat::JSON: { + case RESTResponseFormat::JSON: { req->WriteHeader("Content-Type", "application/json"); UniValue resp = UniValue(UniValue::VOBJ); resp.pushKV("blockhash", pblockindex->GetBlockHash().GetHex()); diff --git a/src/rest.h b/src/rest.h new file mode 100644 index 000000000000..49b1c333d052 --- /dev/null +++ b/src/rest.h @@ -0,0 +1,28 @@ +// Copyright (c) 2015-2022 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_REST_H +#define BITCOIN_REST_H + +#include + +enum class RESTResponseFormat { + UNDEF, + BINARY, + HEX, + JSON, +}; + +/** + * Parse a URI to get the data format and URI without data format + * and query string. + * + * @param[out] param The strReq without the data format string and + * without the query string (if any). + * @param[in] strReq The URI to be parsed. + * @return RESTResponseFormat that was parsed from the URI. + */ +RESTResponseFormat ParseDataFormat(std::string& param, const std::string& strReq); + +#endif // BITCOIN_REST_H diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index ae6ca2b23ea8..9ae25ef11ba3 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1086,7 +1086,7 @@ static RPCHelpMan pruneblockchain() const CBlockIndex& block{*CHECK_NONFATAL(active_chain.Tip())}; const CBlockIndex* last_block{active_chainstate.m_blockman.GetFirstStoredBlock(block)}; - return static_cast(last_block->nHeight); + return static_cast(last_block->nHeight - 1); }, }; } @@ -1457,7 +1457,7 @@ RPCHelpMan getblockchaininfo() {RPCResult::Type::STR_HEX, "chainwork", "total amount of work in active chain, in hexadecimal"}, {RPCResult::Type::NUM, "size_on_disk", "the estimated size of the block and undo files on disk"}, {RPCResult::Type::BOOL, "pruned", "if the blocks are subject to pruning"}, - {RPCResult::Type::NUM, "pruneheight", /* optional */ true, "lowest-height complete block stored (only present if pruning is enabled)"}, + {RPCResult::Type::NUM, "pruneheight", /* optional */ true, "height of the last block pruned, plus one (only present if pruning is enabled)"}, {RPCResult::Type::BOOL, "automatic_pruning", /* optional */ true, "whether automatic pruning is enabled (only present if pruning is enabled)"}, {RPCResult::Type::NUM, "prune_target_size", /* optional */ true, "the target size used by pruning (only present if automatic pruning is enabled)"}, {RPCResult::Type::OBJ, "softforks", "status of softforks in progress", @@ -2671,6 +2671,12 @@ static RPCHelpMan dumptxoutset() FILE* file{fsbridge::fopen(temppath, "wb")}; CAutoFile afile{file, SER_DISK, CLIENT_VERSION}; + if (afile.IsNull()) { + throw JSONRPCError( + RPC_INVALID_PARAMETER, + "Couldn't open file " + temppath.utf8string() + " for writing."); + } + NodeContext& node = EnsureAnyNodeContext(request.context); UniValue result = CreateUTXOSnapshot( node, node.chainman->ActiveChainstate(), afile, path, temppath); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 69612e171dd1..4b44ab63aa99 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -542,15 +542,15 @@ static RPCHelpMan getaddednodeinfo() static RPCHelpMan getnettotals() { return RPCHelpMan{"getnettotals", - "\nReturns information about network traffic, including bytes in, bytes out,\n" - "and current time.\n", + "Returns information about network traffic, including bytes in, bytes out,\n" + "and current system time.", {}, RPCResult{ RPCResult::Type::OBJ, "", "", { {RPCResult::Type::NUM, "totalbytesrecv", "Total bytes received"}, {RPCResult::Type::NUM, "totalbytessent", "Total bytes sent"}, - {RPCResult::Type::NUM_TIME, "timemillis", "Current " + UNIX_EPOCH_TIME + " in milliseconds"}, + {RPCResult::Type::NUM_TIME, "timemillis", "Current system " + UNIX_EPOCH_TIME + " in milliseconds"}, {RPCResult::Type::OBJ, "uploadtarget", "", { {RPCResult::Type::NUM, "timeframe", "Length of the measuring timeframe in seconds"}, @@ -574,7 +574,7 @@ static RPCHelpMan getnettotals() UniValue obj(UniValue::VOBJ); obj.pushKV("totalbytesrecv", connman.GetTotalBytesRecv()); obj.pushKV("totalbytessent", connman.GetTotalBytesSent()); - obj.pushKV("timemillis", GetTimeMillis()); + obj.pushKV("timemillis", TicksSinceEpoch(SystemClock::now())); UniValue outboundLimit(UniValue::VOBJ); outboundLimit.pushKV("timeframe", count_seconds(connman.GetMaxOutboundTimeframe())); diff --git a/src/support/allocators/secure.h b/src/support/allocators/secure.h index 14676dbe4d8e..46be5f4876f1 100644 --- a/src/support/allocators/secure.h +++ b/src/support/allocators/secure.h @@ -55,6 +55,7 @@ struct secure_allocator { }; // This is exactly like std::string, but with a custom allocator. +// TODO: Consider finding a way to make incoming RPC request.params[i] mlock()ed as well typedef std::basic_string, secure_allocator > SecureString; typedef std::vector > SecureVector; diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp index 766e817a8ab5..b130b3957229 100644 --- a/src/support/lockedpool.cpp +++ b/src/support/lockedpool.cpp @@ -19,6 +19,7 @@ #endif #include +#include #include #ifdef ARENA_DEBUG #include diff --git a/src/support/lockedpool.h b/src/support/lockedpool.h index 1aaa76009d67..794e51cc6baa 100644 --- a/src/support/lockedpool.h +++ b/src/support/lockedpool.h @@ -5,11 +5,11 @@ #ifndef BITCOIN_SUPPORT_LOCKEDPOOL_H #define BITCOIN_SUPPORT_LOCKEDPOOL_H -#include +#include #include #include -#include #include +#include #include /** diff --git a/src/test/blockmanager_tests.cpp b/src/test/blockmanager_tests.cpp index dd7c32cc46c0..432b6cbf21d6 100644 --- a/src/test/blockmanager_tests.cpp +++ b/src/test/blockmanager_tests.cpp @@ -5,9 +5,14 @@ #include #include #include +#include +#include