diff options
29 files changed, 340 insertions, 324 deletions
diff --git a/.travis.yml b/.travis.yml index 7cbe0b83f1..75df290187 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,7 +40,7 @@ env: # x86_64 Linux, No wallet - HOST=x86_64-unknown-linux-gnu PACKAGES="python3" DEP_OPTS="NO_WALLET=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" # Cross-Mac - - HOST=x86_64-apple-darwin11 PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports --enable-werror" OSX_SDK=10.11 GOAL="deploy" + - HOST=x86_64-apple-darwin11 PACKAGES="cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev python3-setuptools-git" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports --enable-werror" OSX_SDK=10.11 GOAL="all deploy" before_install: - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") @@ -104,5 +104,5 @@ jobs: - test/lint/lint-all.sh - if [ "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_EVENT_TYPE" = "cron" ]; then while read LINE; do travis_retry gpg --keyserver hkp://subset.pool.sks-keyservers.net --recv-keys $LINE; done < contrib/verify-commits/trusted-keys && - travis_wait 30 contrib/verify-commits/verify-commits.sh; + travis_wait 50 contrib/verify-commits/verify-commits.py; fi diff --git a/configure.ac b/configure.ac index af60b28c71..926d0b8d00 100644 --- a/configure.ac +++ b/configure.ac @@ -243,6 +243,10 @@ 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 + if test "x$CXXFLAGS_overridden" = xno; then + CXXFLAGS="" + fi # Prefer -Og, fall back to -O0 if that is unavailable. AX_CHECK_COMPILE_FLAG( [-Og], @@ -403,25 +407,25 @@ case $host in use_pkgconfig=no TARGET_OS=windows - AC_CHECK_LIB([mingwthrd], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([kernel32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([user32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([gdi32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([comdlg32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([winspool], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([winmm], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([shell32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([comctl32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([ole32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([oleaut32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([uuid], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([rpcrt4], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([advapi32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([ws2_32], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([mswsock], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([shlwapi], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([iphlpapi], [main],, AC_MSG_ERROR(lib missing)) - AC_CHECK_LIB([crypt32], [main],, AC_MSG_ERROR(lib missing)) + AC_CHECK_LIB([mingwthrd], [main],, AC_MSG_ERROR(libmingwthrd missing)) + AC_CHECK_LIB([kernel32], [main],, AC_MSG_ERROR(libkernel32 missing)) + AC_CHECK_LIB([user32], [main],, AC_MSG_ERROR(libuser32 missing)) + AC_CHECK_LIB([gdi32], [main],, AC_MSG_ERROR(libgdi32 missing)) + AC_CHECK_LIB([comdlg32], [main],, AC_MSG_ERROR(libcomdlg32 missing)) + AC_CHECK_LIB([winspool], [main],, AC_MSG_ERROR(libwinspool missing)) + AC_CHECK_LIB([winmm], [main],, AC_MSG_ERROR(libwinmm missing)) + AC_CHECK_LIB([shell32], [main],, AC_MSG_ERROR(libshell32 missing)) + AC_CHECK_LIB([comctl32], [main],, AC_MSG_ERROR(libcomctl32 missing)) + AC_CHECK_LIB([ole32], [main],, AC_MSG_ERROR(libole32 missing)) + AC_CHECK_LIB([oleaut32], [main],, AC_MSG_ERROR(liboleaut32 missing)) + AC_CHECK_LIB([uuid], [main],, AC_MSG_ERROR(libuuid missing)) + AC_CHECK_LIB([rpcrt4], [main],, AC_MSG_ERROR(librpcrt4 missing)) + AC_CHECK_LIB([advapi32], [main],, AC_MSG_ERROR(libadvapi32 missing)) + AC_CHECK_LIB([ws2_32], [main],, AC_MSG_ERROR(libws2_32 missing)) + AC_CHECK_LIB([mswsock], [main],, AC_MSG_ERROR(libmswsock missing)) + AC_CHECK_LIB([shlwapi], [main],, AC_MSG_ERROR(libshlwapi missing)) + 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. @@ -620,7 +624,7 @@ 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. - AC_CHECK_LIB([rt],[clock_gettime],, AC_MSG_ERROR(lib missing)) + 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. @@ -682,7 +686,7 @@ if test x$use_hardening != xno; then case $host in *mingw*) - AC_CHECK_LIB([ssp], [main],, AC_MSG_ERROR(lib missing)) + AC_CHECK_LIB([ssp], [main],, AC_MSG_ERROR(libssp missing)) ;; esac fi diff --git a/contrib/verify-commits/README.md b/contrib/verify-commits/README.md index fa492fdd27..aa805ad1b9 100644 --- a/contrib/verify-commits/README.md +++ b/contrib/verify-commits/README.md @@ -7,18 +7,18 @@ are PGP signed (nearly always merge commits), as well as a script to verify commits against a trusted keys list. -Using verify-commits.sh safely +Using verify-commits.py safely ------------------------------ Remember that you can't use an untrusted script to verify itself. This means -that checking out code, then running `verify-commits.sh` against `HEAD` is -_not_ safe, because the version of `verify-commits.sh` that you just ran could +that checking out code, then running `verify-commits.py` against `HEAD` is +_not_ safe, because the version of `verify-commits.py` that you just ran could be backdoored. Instead, you need to use a trusted version of verify-commits prior to checkout to make sure you're checking out only code signed by trusted keys: git fetch origin && \ - ./contrib/verify-commits/verify-commits.sh origin/master && \ + ./contrib/verify-commits/verify-commits.py origin/master && \ git checkout origin/master Note that the above isn't a good UI/UX yet, and needs significant improvements @@ -42,6 +42,6 @@ said key. In order to avoid bumping the root-of-trust `trusted-git-root` file, individual commits which were signed by such a key can be added to the `allow-revsig-commits` file. That way, the PGP signatures are still verified but no new commits can be signed by any expired/revoked key. To easily build a -list of commits which need to be added, verify-commits.sh can be edited to test +list of commits which need to be added, verify-commits.py can be edited to test each commit with BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG set to both 1 and 0, and those which need it set to 1 printed. diff --git a/contrib/verify-commits/allow-incorrect-sha512-commits b/contrib/verify-commits/allow-incorrect-sha512-commits new file mode 100644 index 0000000000..c572806f26 --- /dev/null +++ b/contrib/verify-commits/allow-incorrect-sha512-commits @@ -0,0 +1,2 @@ +f8feaa4636260b599294c7285bcf1c8b7737f74e +8040ae6fc576e9504186f2ae3ff2c8125de1095c diff --git a/contrib/verify-commits/allow-unclean-merge-commits b/contrib/verify-commits/allow-unclean-merge-commits new file mode 100644 index 0000000000..7aab274b9a --- /dev/null +++ b/contrib/verify-commits/allow-unclean-merge-commits @@ -0,0 +1,4 @@ +6052d509105790a26b3ad5df43dd61e7f1b24a12 +3798e5de334c3deb5f71302b782f6b8fbd5087f1 +326ffed09bfcc209a2efd6a2ebc69edf6bd200b5 +97d83739db0631be5d4ba86af3616014652c00ec diff --git a/contrib/verify-commits/pre-push-hook.sh b/contrib/verify-commits/pre-push-hook.sh index c21febb9e9..1f14635f61 100755 --- a/contrib/verify-commits/pre-push-hook.sh +++ b/contrib/verify-commits/pre-push-hook.sh @@ -12,9 +12,9 @@ while read LINE; do if [ "$4" != "refs/heads/master" ]; then continue fi - if ! ./contrib/verify-commits/verify-commits.sh $3 > /dev/null 2>&1; then + if ! ./contrib/verify-commits/verify-commits.py $3 > /dev/null 2>&1; then echo "ERROR: A commit is not signed, can't push" - ./contrib/verify-commits/verify-commits.sh + ./contrib/verify-commits/verify-commits.py exit 1 fi done < /dev/stdin diff --git a/contrib/verify-commits/verify-commits.py b/contrib/verify-commits/verify-commits.py new file mode 100755 index 0000000000..80f0aa0bf1 --- /dev/null +++ b/contrib/verify-commits/verify-commits.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# Copyright (c) 2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Verify commits against a trusted keys list.""" +import argparse +import hashlib +import os +import subprocess +import sys +import time + +GIT = os.getenv('GIT', 'git') + +def tree_sha512sum(commit='HEAD'): + """Calculate the Tree-sha512 for the commit. + + This is copied from github-merge.py.""" + + # request metadata for entire tree, recursively + files = [] + blob_by_name = {} + for line in subprocess.check_output([GIT, 'ls-tree', '--full-tree', '-r', commit]).splitlines(): + name_sep = line.index(b'\t') + metadata = line[:name_sep].split() # perms, 'blob', blobid + assert metadata[1] == b'blob' + name = line[name_sep + 1:] + files.append(name) + blob_by_name[name] = metadata[2] + + files.sort() + # open connection to git-cat-file in batch mode to request data for all blobs + # this is much faster than launching it per file + p = subprocess.Popen([GIT, 'cat-file', '--batch'], stdout=subprocess.PIPE, stdin=subprocess.PIPE) + overall = hashlib.sha512() + for f in files: + blob = blob_by_name[f] + # request blob + p.stdin.write(blob + b'\n') + p.stdin.flush() + # read header: blob, "blob", size + reply = p.stdout.readline().split() + assert reply[0] == blob and reply[1] == b'blob' + size = int(reply[2]) + # hash the blob data + intern = hashlib.sha512() + ptr = 0 + while ptr < size: + bs = min(65536, size - ptr) + piece = p.stdout.read(bs) + if len(piece) == bs: + intern.update(piece) + else: + raise IOError('Premature EOF reading git cat-file output') + ptr += bs + dig = intern.hexdigest() + assert p.stdout.read(1) == b'\n' # ignore LF that follows blob data + # update overall hash with file hash + overall.update(dig.encode("utf-8")) + overall.update(" ".encode("utf-8")) + overall.update(f) + overall.update("\n".encode("utf-8")) + p.stdin.close() + if p.wait(): + raise IOError('Non-zero return value executing git cat-file') + return overall.hexdigest() + +def main(): + # Parse arguments + parser = argparse.ArgumentParser(usage='%(prog)s [options] [commit id]') + parser.add_argument('--disable-tree-check', action='store_false', dest='verify_tree', help='disable SHA-512 tree check') + parser.add_argument('--clean-merge', type=float, dest='clean_merge', default=float('inf'), help='Only check clean merge after <NUMBER> days ago (default: %(default)s)', metavar='NUMBER') + parser.add_argument('commit', nargs='?', default='HEAD', help='Check clean merge up to commit <commit>') + args = parser.parse_args() + + # get directory of this program and read data files + dirname = os.path.dirname(os.path.abspath(__file__)) + print("Using verify-commits data from " + dirname) + verified_root = open(dirname + "/trusted-git-root", "r").read().splitlines()[0] + verified_sha512_root = open(dirname + "/trusted-sha512-root-commit", "r").read().splitlines()[0] + revsig_allowed = open(dirname + "/allow-revsig-commits", "r").read().splitlines() + unclean_merge_allowed = open(dirname + "/allow-unclean-merge-commits", "r").read().splitlines() + incorrect_sha512_allowed = open(dirname + "/allow-incorrect-sha512-commits", "r").read().splitlines() + + # Set commit and branch and set variables + current_commit = args.commit + if ' ' in current_commit: + print("Commit must not contain spaces", file=sys.stderr) + sys.exit(1) + verify_tree = args.verify_tree + no_sha1 = True + prev_commit = "" + initial_commit = current_commit + branch = subprocess.check_output([GIT, 'show', '-s', '--format=%H', initial_commit], universal_newlines=True).splitlines()[0] + + # Iterate through commits + while True: + if current_commit == verified_root: + print('There is a valid path from "{}" to {} where all commits are signed!'.format(initial_commit, verified_root)) + sys.exit(0) + if current_commit == verified_sha512_root: + if verify_tree: + print("All Tree-SHA512s matched up to {}".format(verified_sha512_root), file=sys.stderr) + verify_tree = False + no_sha1 = False + + os.environ['BITCOIN_VERIFY_COMMITS_ALLOW_SHA1'] = "0" if no_sha1 else "1" + os.environ['BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG'] = "1" if current_commit in revsig_allowed else "0" + + # Check that the commit (and parents) was signed with a trusted key + if subprocess.call([GIT, '-c', 'gpg.program={}/gpg.sh'.format(dirname), 'verify-commit', current_commit], stdout=subprocess.DEVNULL): + if prev_commit != "": + print("No parent of {} was signed with a trusted key!".format(prev_commit), file=sys.stderr) + print("Parents are:", file=sys.stderr) + parents = subprocess.check_output([GIT, 'show', '-s', '--format=format:%P', prev_commit], universal_newlines=True).splitlines()[0].split(' ') + for parent in parents: + subprocess.call([GIT, 'show', '-s', parent], stdout=sys.stderr) + else: + print("{} was not signed with a trusted key!".format(current_commit), file=sys.stderr) + sys.exit(1) + + # Check the Tree-SHA512 + if (verify_tree or prev_commit == "") and current_commit not in incorrect_sha512_allowed: + tree_hash = tree_sha512sum(current_commit) + if ("Tree-SHA512: {}".format(tree_hash)) not in subprocess.check_output([GIT, 'show', '-s', '--format=format:%B', current_commit], universal_newlines=True).splitlines(): + print("Tree-SHA512 did not match for commit " + current_commit, file=sys.stderr) + sys.exit(1) + + # Merge commits should only have two parents + parents = subprocess.check_output([GIT, 'show', '-s', '--format=format:%P', current_commit], universal_newlines=True).splitlines()[0].split(' ') + if len(parents) > 2: + print("Commit {} is an octopus merge".format(current_commit), file=sys.stderr) + sys.exit(1) + + # Check that the merge commit is clean + commit_time = int(subprocess.check_output([GIT, 'show', '-s', '--format=format:%ct', current_commit], universal_newlines=True).splitlines()[0]) + check_merge = commit_time > time.time() - args.clean_merge * 24 * 60 * 60 # Only check commits in clean_merge days + allow_unclean = current_commit in unclean_merge_allowed + if len(parents) == 2 and check_merge and not allow_unclean: + current_tree = subprocess.check_output([GIT, 'show', '--format=%T', current_commit], universal_newlines=True).splitlines()[0] + subprocess.call([GIT, 'checkout', '--force', '--quiet', parents[0]]) + subprocess.call([GIT, 'merge', '--no-ff', '--quiet', parents[1]], stdout=subprocess.DEVNULL) + recreated_tree = subprocess.check_output([GIT, 'show', '--format=format:%T', 'HEAD'], universal_newlines=True).splitlines()[0] + if current_tree != recreated_tree: + print("Merge commit {} is not clean".format(current_commit), file=sys.stderr) + subprocess.call([GIT, 'diff', current_commit]) + subprocess.call([GIT, 'checkout', '--force', '--quiet', branch]) + sys.exit(1) + subprocess.call([GIT, 'checkout', '--force', '--quiet', branch]) + + prev_commit = current_commit + current_commit = parents[0] + +if __name__ == '__main__': + main() diff --git a/contrib/verify-commits/verify-commits.sh b/contrib/verify-commits/verify-commits.sh deleted file mode 100755 index 6415eea4d5..0000000000 --- a/contrib/verify-commits/verify-commits.sh +++ /dev/null @@ -1,153 +0,0 @@ -#!/bin/sh -# Copyright (c) 2014-2016 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -DIR=$(dirname "$0") -[ "/${DIR#/}" != "$DIR" ] && DIR=$(dirname "$(pwd)/$0") - -echo "Using verify-commits data from ${DIR}" - -VERIFIED_ROOT=$(cat "${DIR}/trusted-git-root") -VERIFIED_SHA512_ROOT=$(cat "${DIR}/trusted-sha512-root-commit") -REVSIG_ALLOWED=$(cat "${DIR}/allow-revsig-commits") - -HAVE_GNU_SHA512=1 -[ ! -x "$(which sha512sum)" ] && HAVE_GNU_SHA512=0 - -if [ x"$1" = "x" ]; then - CURRENT_COMMIT="HEAD" -else - CURRENT_COMMIT="$1" -fi - -if [ "${CURRENT_COMMIT#* }" != "$CURRENT_COMMIT" ]; then - echo "Commit must not contain spaces?" > /dev/stderr - exit 1 -fi - -VERIFY_TREE=0 -if [ x"$2" = "x--tree-checks" ]; then - VERIFY_TREE=1 -fi - -NO_SHA1=1 -PREV_COMMIT="" -INITIAL_COMMIT="${CURRENT_COMMIT}" - -BRANCH="$(git rev-parse --abbrev-ref HEAD)" - -while true; do - if [ "$CURRENT_COMMIT" = $VERIFIED_ROOT ]; then - echo "There is a valid path from \"$INITIAL_COMMIT\" to $VERIFIED_ROOT where all commits are signed!" - exit 0 - fi - - if [ "$CURRENT_COMMIT" = $VERIFIED_SHA512_ROOT ]; then - if [ "$VERIFY_TREE" = "1" ]; then - echo "All Tree-SHA512s matched up to $VERIFIED_SHA512_ROOT" > /dev/stderr - fi - VERIFY_TREE=0 - NO_SHA1=0 - fi - - if [ "$NO_SHA1" = "1" ]; then - export BITCOIN_VERIFY_COMMITS_ALLOW_SHA1=0 - else - export BITCOIN_VERIFY_COMMITS_ALLOW_SHA1=1 - fi - - if [ "${REVSIG_ALLOWED#*$CURRENT_COMMIT}" != "$REVSIG_ALLOWED" ]; then - export BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG=1 - else - export BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG=0 - fi - - if ! git -c "gpg.program=${DIR}/gpg.sh" verify-commit "$CURRENT_COMMIT" > /dev/null; then - if [ "$PREV_COMMIT" != "" ]; then - echo "No parent of $PREV_COMMIT was signed with a trusted key!" > /dev/stderr - echo "Parents are:" > /dev/stderr - PARENTS=$(git show -s --format=format:%P $PREV_COMMIT) - for PARENT in $PARENTS; do - git show -s $PARENT > /dev/stderr - done - else - echo "$CURRENT_COMMIT was not signed with a trusted key!" > /dev/stderr - fi - exit 1 - fi - - # We always verify the top of the tree - if [ "$VERIFY_TREE" = 1 -o "$PREV_COMMIT" = "" ]; then - IFS_CACHE="$IFS" - IFS=' -' - for LINE in $(git ls-tree --full-tree -r "$CURRENT_COMMIT"); do - case "$LINE" in - "12"*) - echo "Repo contains symlinks" > /dev/stderr - IFS="$IFS_CACHE" - exit 1 - ;; - esac - done - IFS="$IFS_CACHE" - - FILE_HASHES="" - for FILE in $(git ls-tree --full-tree -r --name-only "$CURRENT_COMMIT" | LC_ALL=C sort); do - if [ "$HAVE_GNU_SHA512" = 1 ]; then - HASH=$(git cat-file blob "$CURRENT_COMMIT":"$FILE" | sha512sum | { read FIRST _; echo $FIRST; } ) - else - HASH=$(git cat-file blob "$CURRENT_COMMIT":"$FILE" | shasum -a 512 | { read FIRST _; echo $FIRST; } ) - fi - [ "$FILE_HASHES" != "" ] && FILE_HASHES="$FILE_HASHES"' -' - FILE_HASHES="$FILE_HASHES$HASH $FILE" - done - - if [ "$HAVE_GNU_SHA512" = 1 ]; then - TREE_HASH="$(echo "$FILE_HASHES" | sha512sum)" - else - TREE_HASH="$(echo "$FILE_HASHES" | shasum -a 512)" - fi - HASH_MATCHES=0 - MSG="$(git show -s --format=format:%B "$CURRENT_COMMIT" | tail -n1)" - - case "$MSG -" in - "Tree-SHA512: $TREE_HASH") - HASH_MATCHES=1;; - esac - - if [ "$HASH_MATCHES" = "0" ]; then - echo "Tree-SHA512 did not match for commit $CURRENT_COMMIT" > /dev/stderr - exit 1 - fi - fi - - PARENTS=$(git show -s --format=format:%P "$CURRENT_COMMIT") - PARENT1=${PARENTS%% *} - PARENT2="" - if [ "x$PARENT1" != "x$PARENTS" ]; then - PARENTX=${PARENTS#* } - PARENT2=${PARENTX%% *} - if [ "x$PARENT2" != "x$PARENTX" ]; then - echo "Commit $CURRENT_COMMIT is an octopus merge" > /dev/stderr - exit 1 - fi - fi - if [ "x$PARENT2" != "x" ]; then - CURRENT_TREE="$(git show --format="%T" "$CURRENT_COMMIT")" - git checkout --force --quiet "$PARENT1" - git merge --no-ff --quiet "$PARENT2" >/dev/null - RECREATED_TREE="$(git show --format="%T" HEAD)" - if [ "$CURRENT_TREE" != "$RECREATED_TREE" ]; then - echo "Merge commit $CURRENT_COMMIT is not clean" > /dev/stderr - git diff "$CURRENT_COMMIT" - git checkout --force --quiet "$BRANCH" - exit 1 - fi - git checkout --force --quiet "$BRANCH" - fi - PREV_COMMIT="$CURRENT_COMMIT" - CURRENT_COMMIT="$PARENT1" -done diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 4821a59f19..2fa91ecb02 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -595,6 +595,12 @@ Source code organization - *Rationale*: Shorter and simpler header files are easier to read, and reduce compile time +- Use only the lowercase alphanumerics (`a-z0-9`), underscore (`_`) and hyphen (`-`) in source code filenames. + + - *Rationale*: `grep`:ing and auto-completing filenames is easier when using a consistent + naming pattern. Potential problems when building on case-insensitive filesystems are + avoided when using only lowercase characters in source code filenames. + - Every `.cpp` and `.h` file should `#include` every header file it directly uses classes, functions or other definitions from, even if those headers are already included indirectly through other headers. diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 32de1582f8..bc2c46f228 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -17,7 +17,7 @@ bench_bench_bitcoin_SOURCES = \ bench/bench.h \ bench/checkblock.cpp \ bench/checkqueue.cpp \ - bench/Examples.cpp \ + bench/examples.cpp \ bench/rollingbloom.cpp \ bench/crypto_hash.cpp \ bench/ccoins_caching.cpp \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 94f4e38768..a4d31795ec 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -46,7 +46,7 @@ BITCOIN_TESTS =\ test/compress_tests.cpp \ test/crypto_tests.cpp \ test/cuckoocache_tests.cpp \ - test/DoS_tests.cpp \ + test/denialofservice_tests.cpp \ test/getarg_tests.cpp \ test/hash_tests.cpp \ test/key_io_tests.cpp \ @@ -71,7 +71,7 @@ BITCOIN_TESTS =\ test/rpc_tests.cpp \ test/sanity_tests.cpp \ test/scheduler_tests.cpp \ - test/script_P2SH_tests.cpp \ + test/script_p2sh_tests.cpp \ test/script_tests.cpp \ test/script_standard_tests.cpp \ test/scriptnum_tests.cpp \ diff --git a/src/bench/Examples.cpp b/src/bench/examples.cpp index b68c9cd156..b68c9cd156 100644 --- a/src/bench/Examples.cpp +++ b/src/bench/examples.cpp diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp index 6ac51d11cd..51824fbe9f 100644 --- a/src/crypto/sha256.cpp +++ b/src/crypto/sha256.cpp @@ -478,7 +478,7 @@ TransformD64Type TransformD64 = sha256::TransformD64; TransformD64Type TransformD64_4way = nullptr; TransformD64Type TransformD64_8way = nullptr; -#if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__)) +#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) { @@ -491,12 +491,14 @@ void inline cpuid(uint32_t leaf, uint32_t subleaf, uint32_t& a, uint32_t& b, uin std::string SHA256AutoDetect() { std::string ret = "standard"; -#if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__)) +#if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__) || defined(__i386__)) uint32_t eax, ebx, ecx, edx; cpuid(1, 0, eax, ebx, ecx, edx); if ((ecx >> 19) & 1) { +#if defined(__x86_64__) || defined(__amd64__) Transform = sha256_sse4::Transform; TransformD64 = TransformD64Wrapper<sha256_sse4::Transform>; +#endif #if defined(ENABLE_SSE41) && !defined(BUILD_BITCOIN_INTERNAL) TransformD64_4way = sha256d64_sse41::Transform_4way; ret = "sse4(1way+4way)"; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index fc05dd2ad2..de456e87f4 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -561,7 +561,7 @@ static void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vec } // namespace // This function is used for testing the stale tip eviction logic, see -// DoS_tests.cpp +// denialofservice_tests.cpp void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) { LOCK(cs_main); diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 5963bf371a..aac3fe5c14 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -54,7 +54,7 @@ bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) return (txout.nValue < GetDustThreshold(txout, dustRelayFeeIn)); } -bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool witnessEnabled) +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) { std::vector<std::vector<unsigned char> > vSolutions; if (!Solver(scriptPubKey, whichType, vSolutions)) @@ -73,13 +73,10 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool w (!fAcceptDatacarrier || scriptPubKey.size() > nMaxDatacarrierBytes)) return false; - else if (!witnessEnabled && (whichType == TX_WITNESS_V0_KEYHASH || whichType == TX_WITNESS_V0_SCRIPTHASH)) - return false; - return whichType != TX_NONSTANDARD && whichType != TX_WITNESS_UNKNOWN; } -bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnessEnabled) +bool IsStandardTx(const CTransaction& tx, std::string& reason) { if (tx.nVersion > CTransaction::MAX_STANDARD_VERSION || tx.nVersion < 1) { reason = "version"; @@ -118,7 +115,7 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes unsigned int nDataOut = 0; txnouttype whichType; for (const CTxOut& txout : tx.vout) { - if (!::IsStandard(txout.scriptPubKey, whichType, witnessEnabled)) { + if (!::IsStandard(txout.scriptPubKey, whichType)) { reason = "scriptpubkey"; return false; } diff --git a/src/policy/policy.h b/src/policy/policy.h index 5ce019df4c..035627bd60 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -79,12 +79,12 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee); bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee); -bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool witnessEnabled = false); +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); /** * Check for standard transaction types * @return True if all outputs (scriptPubKeys) use only standard transaction forms */ -bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnessEnabled = false); +bool IsStandardTx(const CTransaction& tx, std::string& reason); /** * Check for standard transaction types * @param[in] mapInputs Map of previous transactions that have outputs we're spending diff --git a/src/test/DoS_tests.cpp b/src/test/denialofservice_tests.cpp index 1868aed7dd..e5f914ba8a 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -42,7 +42,7 @@ static NodeId id = 0; void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds); -BOOST_FIXTURE_TEST_SUITE(DoS_tests, TestingSetup) +BOOST_FIXTURE_TEST_SUITE(denialofservice_tests, TestingSetup) // Test eviction of an outbound peer whose chain never advances // Mock a node connection, and use mocktime to simulate a peer diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index 4070642537..0264d29455 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -587,9 +587,6 @@ inline CTransactionRef make_tx(std::vector<CAmount>&& output_values, std::vector return MakeTransactionRef(tx); } -#define MK_OUTPUTS(amounts...) std::vector<CAmount>{amounts} -#define MK_INPUTS(txs...) std::vector<CTransactionRef>{txs} -#define MK_INPUT_IDX(idxes...) std::vector<uint32_t>{idxes} BOOST_AUTO_TEST_CASE(MempoolAncestryTests) { @@ -602,7 +599,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests) // // [tx1] // - CTransactionRef tx1 = make_tx(MK_OUTPUTS(10 * COIN)); + CTransactionRef tx1 = make_tx(/* output_values */ {10 * COIN}); pool.addUnchecked(tx1->GetHash(), entry.Fee(10000LL).FromTx(tx1)); // Ancestors / descendants should be 1 / 1 (itself / itself) @@ -614,7 +611,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests) // // [tx1].0 <- [tx2] // - CTransactionRef tx2 = make_tx(MK_OUTPUTS(495 * CENT, 5 * COIN), MK_INPUTS(tx1)); + CTransactionRef tx2 = make_tx(/* output_values */ {495 * CENT, 5 * COIN}, /* inputs */ {tx1}); pool.addUnchecked(tx2->GetHash(), entry.Fee(10000LL).FromTx(tx2)); // Ancestors / descendants should be: @@ -633,7 +630,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests) // // [tx1].0 <- [tx2].0 <- [tx3] // - CTransactionRef tx3 = make_tx(MK_OUTPUTS(290 * CENT, 200 * CENT), MK_INPUTS(tx2)); + CTransactionRef tx3 = make_tx(/* output_values */ {290 * CENT, 200 * CENT}, /* inputs */ {tx2}); pool.addUnchecked(tx3->GetHash(), entry.Fee(10000LL).FromTx(tx3)); // Ancestors / descendants should be: @@ -658,7 +655,7 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests) // | // \---1 <- [tx4] // - CTransactionRef tx4 = make_tx(MK_OUTPUTS(290 * CENT, 250 * CENT), MK_INPUTS(tx2), MK_INPUT_IDX(1)); + CTransactionRef tx4 = make_tx(/* output_values */ {290 * CENT, 250 * CENT}, /* inputs */ {tx2}, /* input_indices */ {1}); pool.addUnchecked(tx4->GetHash(), entry.Fee(10000LL).FromTx(tx4)); // Ancestors / descendants should be: @@ -694,14 +691,14 @@ BOOST_AUTO_TEST_CASE(MempoolAncestryTests) CAmount v = 5 * COIN; for (uint64_t i = 0; i < 5; i++) { CTransactionRef& tyi = *ty[i]; - tyi = make_tx(MK_OUTPUTS(v), i > 0 ? MK_INPUTS(*ty[i-1]) : std::vector<CTransactionRef>()); + tyi = make_tx(/* output_values */ {v}, /* inputs */ i > 0 ? std::vector<CTransactionRef>{*ty[i - 1]} : std::vector<CTransactionRef>{}); v -= 50 * CENT; pool.addUnchecked(tyi->GetHash(), entry.Fee(10000LL).FromTx(tyi)); pool.GetTransactionAncestry(tyi->GetHash(), ancestors, descendants); BOOST_CHECK_EQUAL(ancestors, i+1); BOOST_CHECK_EQUAL(descendants, i+1); } - CTransactionRef ty6 = make_tx(MK_OUTPUTS(5 * COIN), MK_INPUTS(tx3, ty5)); + CTransactionRef ty6 = make_tx(/* output_values */ {5 * COIN}, /* inputs */ {tx3, ty5}); pool.addUnchecked(ty6->GetHash(), entry.Fee(10000LL).FromTx(ty6)); // Ancestors / descendants should be: diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_p2sh_tests.cpp index 63d211dd97..803a673fab 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_p2sh_tests.cpp @@ -46,7 +46,7 @@ Verify(const CScript& scriptSig, const CScript& scriptPubKey, bool fStrict, Scri } -BOOST_FIXTURE_TEST_SUITE(script_P2SH_tests, BasicTestingSetup) +BOOST_FIXTURE_TEST_SUITE(script_p2sh_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(sign) { diff --git a/src/validation.cpp b/src/validation.cpp index 9791d6e2d8..bbf2389d34 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -577,15 +577,9 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool if (tx.IsCoinBase()) return state.DoS(100, false, REJECT_INVALID, "coinbase"); - // Reject transactions with witness before segregated witness activates (override with -prematurewitness) - bool witnessEnabled = IsWitnessEnabled(chainActive.Tip(), chainparams.GetConsensus()); - if (!gArgs.GetBoolArg("-prematurewitness", false) && tx.HasWitness() && !witnessEnabled) { - return state.DoS(0, false, REJECT_NONSTANDARD, "no-witness-yet", true); - } - // Rather not work on nonstandard transactions (unless -testnet/-regtest) std::string reason; - if (fRequireStandard && !IsStandardTx(tx, reason, witnessEnabled)) + if (fRequireStandard && !IsStandardTx(tx, reason)) return state.DoS(0, false, REJECT_NONSTANDARD, reason); // Do not work on transactions that are too small. @@ -4006,7 +4000,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, reportDone = percentageDone/10; } uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone, false); - if (pindex->nHeight < chainActive.Height()-nCheckDepth) + if (pindex->nHeight <= chainActive.Height()-nCheckDepth) break; if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) { // If pruning, only go back as far as we have data. diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 84e91643ba..94c35e2cb1 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1450,13 +1450,6 @@ static UniValue addwitnessaddress(const JSONRPCRequest& request) "Projects should transition to using the address_type argument of getnewaddress, or option -addresstype=[bech32|p2sh-segwit] instead.\n"); } - { - LOCK(cs_main); - if (!IsWitnessEnabled(chainActive.Tip(), Params().GetConsensus()) && !gArgs.GetBoolArg("-walletprematurewitness", false)) { - throw JSONRPCError(RPC_WALLET_ERROR, "Segregated witness not enabled on network"); - } - } - CTxDestination dest = DecodeDestination(request.params[0].get_str()); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); diff --git a/test/functional/combine_logs.py b/test/functional/combine_logs.py index d1bf9206b2..91b6415a7c 100755 --- a/test/functional/combine_logs.py +++ b/test/functional/combine_logs.py @@ -63,7 +63,7 @@ def get_log_events(source, logfile): Log events may be split over multiple lines. We use the timestamp regex match as the marker for a new log event.""" try: - with open(logfile, 'r') as infile: + with open(logfile, 'r', encoding='utf-8') as infile: event = '' timestamp = '' for line in infile: diff --git a/test/functional/feature_nulldummy.py b/test/functional/feature_nulldummy.py index 7db6a03b45..6577d83f5c 100755 --- a/test/functional/feature_nulldummy.py +++ b/test/functional/feature_nulldummy.py @@ -42,7 +42,7 @@ class NULLDUMMYTest(BitcoinTestFramework): self.setup_clean_chain = True # This script tests NULLDUMMY activation, which is part of the 'segwit' deployment, so we go through # normal segwit activation here (and don't use the default always-on behaviour). - self.extra_args = [['-whitelist=127.0.0.1', '-walletprematurewitness', '-vbparams=segwit:0:999999999999', '-addresstype=legacy', "-deprecatedrpc=addwitnessaddress"]] + self.extra_args = [['-whitelist=127.0.0.1', '-vbparams=segwit:0:999999999999', '-addresstype=legacy', "-deprecatedrpc=addwitnessaddress"]] def run_test(self): self.address = self.nodes[0].getnewaddress() diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py index 3622e1834a..b10306b283 100755 --- a/test/functional/feature_segwit.py +++ b/test/functional/feature_segwit.py @@ -42,9 +42,9 @@ class SegWitTest(BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 3 # This test tests SegWit both pre and post-activation, so use the normal BIP9 activation. - self.extra_args = [["-walletprematurewitness", "-rpcserialversion=0", "-vbparams=segwit:0:999999999999", "-addresstype=legacy", "-deprecatedrpc=addwitnessaddress"], - ["-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-rpcserialversion=1", "-vbparams=segwit:0:999999999999", "-addresstype=legacy", "-deprecatedrpc=addwitnessaddress"], - ["-blockversion=536870915", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-vbparams=segwit:0:999999999999", "-addresstype=legacy", "-deprecatedrpc=addwitnessaddress"]] + self.extra_args = [["-rpcserialversion=0", "-vbparams=segwit:0:999999999999", "-addresstype=legacy", "-deprecatedrpc=addwitnessaddress"], + ["-blockversion=4", "-promiscuousmempoolflags=517", "-rpcserialversion=1", "-vbparams=segwit:0:999999999999", "-addresstype=legacy", "-deprecatedrpc=addwitnessaddress"], + ["-blockversion=536870915", "-promiscuousmempoolflags=517", "-vbparams=segwit:0:999999999999", "-addresstype=legacy", "-deprecatedrpc=addwitnessaddress"]] def setup_network(self): super().setup_network() @@ -129,21 +129,6 @@ class SegWitTest(BitcoinTestFramework): self.nodes[0].generate(260) #block 423 sync_blocks(self.nodes) - self.log.info("Verify default node can't accept any witness format txs before fork") - # unsigned, no scriptsig - self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", wit_ids[NODE_0][WIT_V0][0], False) - self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", wit_ids[NODE_0][WIT_V1][0], False) - self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V0][0], False) - self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V1][0], False) - # unsigned with redeem script - self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V0][0], False, witness_script(False, self.pubkey[0])) - self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V1][0], False, witness_script(True, self.pubkey[0])) - # signed - self.fail_accept(self.nodes[0], "no-witness-yet", wit_ids[NODE_0][WIT_V0][0], True) - self.fail_accept(self.nodes[0], "no-witness-yet", wit_ids[NODE_0][WIT_V1][0], True) - self.fail_accept(self.nodes[0], "no-witness-yet", p2sh_ids[NODE_0][WIT_V0][0], True) - self.fail_accept(self.nodes[0], "no-witness-yet", p2sh_ids[NODE_0][WIT_V1][0], True) - self.log.info("Verify witness txs are skipped for mining before the fork") self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][0], True) #block 424 self.skip_mine(self.nodes[2], wit_ids[NODE_2][WIT_V1][0], True) #block 425 @@ -164,6 +149,16 @@ class SegWitTest(BitcoinTestFramework): segwit_tx_list = self.nodes[2].getblock(block[0])["tx"] assert_equal(len(segwit_tx_list), 5) + self.log.info("Verify default node can't accept txs with missing witness") + # unsigned, no scriptsig + self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", wit_ids[NODE_0][WIT_V0][0], False) + self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", wit_ids[NODE_0][WIT_V1][0], False) + self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V0][0], False) + self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V1][0], False) + # unsigned with redeem script + self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V0][0], False, witness_script(False, self.pubkey[0])) + self.fail_accept(self.nodes[0], "mandatory-script-verify-flag", p2sh_ids[NODE_0][WIT_V1][0], False, witness_script(True, self.pubkey[0])) + self.log.info("Verify block and transaction serialization rpcs return differing serializations depending on rpc serialization flag") assert(self.nodes[2].getblock(block[0], False) != self.nodes[0].getblock(block[0], False)) assert(self.nodes[1].getblock(block[0], False) == self.nodes[2].getblock(block[0], False)) diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index e80a764477..940d085e89 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -230,55 +230,9 @@ class SegWitTest(BitcoinTestFramework): sync_blocks(self.nodes) - # Create a p2sh output -- this is so we can pass the standardness - # rules (an anyone-can-spend OP_TRUE would be rejected, if not wrapped - # in P2SH). - p2sh_program = CScript([OP_TRUE]) - p2sh_pubkey = hash160(p2sh_program) - scriptPubKey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL]) - - # Now check that unnecessary witnesses can't be used to blind a node - # to a transaction, eg by violating standardness checks. - tx2 = CTransaction() - tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b"")) - tx2.vout.append(CTxOut(tx.vout[0].nValue-1000, scriptPubKey)) - tx2.rehash() - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, False, True) - self.nodes[0].generate(1) - sync_blocks(self.nodes) - - # We'll add an unnecessary witness to this transaction that would cause - # it to be non-standard, to test that violating policy with a witness before - # segwit activation doesn't blind a node to a transaction. Transactions - # rejected for having a witness before segwit activation shouldn't be added - # to the rejection cache. - tx3 = CTransaction() - tx3.vin.append(CTxIn(COutPoint(tx2.sha256, 0), CScript([p2sh_program]))) - tx3.vout.append(CTxOut(tx2.vout[0].nValue-1000, scriptPubKey)) - tx3.wit.vtxinwit.append(CTxInWitness()) - tx3.wit.vtxinwit[0].scriptWitness.stack = [b'a'*400000] - tx3.rehash() - # Note that this should be rejected for the premature witness reason, - # rather than a policy check, since segwit hasn't activated yet. - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, True, False, b'no-witness-yet') - - # If we send without witness, it should be accepted. - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, False, True) - - # Now create a new anyone-can-spend utxo for the next test. - tx4 = CTransaction() - tx4.vin.append(CTxIn(COutPoint(tx3.sha256, 0), CScript([p2sh_program]))) - tx4.vout.append(CTxOut(tx3.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) - tx4.rehash() - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, False, True) - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx4, False, True) - - self.nodes[0].generate(1) - sync_blocks(self.nodes) - # Update our utxo list; we spent the first entry. self.utxo.pop(0) - self.utxo.append(UTXO(tx4.sha256, 0, tx4.vout[0].nValue)) + self.utxo.append(UTXO(tx.sha256, 0, tx.vout[0].nValue)) # ~6 months after segwit activation, the SCRIPT_VERIFY_WITNESS flag was # backdated so that it applies to all blocks, going back to the genesis @@ -1119,7 +1073,7 @@ class SegWitTest(BitcoinTestFramework): self.old_node.announce_tx_and_wait_for_getdata(block4.vtx[0]) assert(block4.sha256 not in self.old_node.getdataset) - # V0 segwit outputs should be standard after activation, but not before. + # V0 segwit outputs and inputs are always standard. V0 segwit inputs may only be mined after activation, but not before. def test_standardness_v0(self, segwit_activated): self.log.info("Testing standardness of v0 outputs (%s activation)" % ("after" if segwit_activated else "before")) assert(len(self.utxo)) @@ -1148,45 +1102,46 @@ class SegWitTest(BitcoinTestFramework): tx.vin = [CTxIn(COutPoint(p2sh_tx.sha256, 0), CScript([witness_program]))] tx.vout = [CTxOut(p2sh_tx.vout[0].nValue-10000, scriptPubKey)] tx.vout.append(CTxOut(8000, scriptPubKey)) # Might burn this later + tx.vin[0].nSequence = BIP125_SEQUENCE_NUMBER # Just to have the option to bump this tx from the mempool tx.rehash() - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx, with_witness=True, accepted=segwit_activated) + # This is always accepted, since the mempool policy is to consider segwit as always active + # and thus allow segwit outputs + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx, with_witness=True, accepted=True) # Now create something that looks like a P2PKH output. This won't be spendable. scriptPubKey = CScript([OP_0, hash160(witness_hash)]) tx2 = CTransaction() - if segwit_activated: - # if tx was accepted, then we spend the second output. - tx2.vin = [CTxIn(COutPoint(tx.sha256, 1), b"")] - tx2.vout = [CTxOut(7000, scriptPubKey)] - tx2.wit.vtxinwit.append(CTxInWitness()) - tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_program] - else: - # if tx wasn't accepted, we just re-spend the p2sh output we started with. - tx2.vin = [CTxIn(COutPoint(p2sh_tx.sha256, 0), CScript([witness_program]))] - tx2.vout = [CTxOut(p2sh_tx.vout[0].nValue-1000, scriptPubKey)] + # tx was accepted, so we spend the second output. + tx2.vin = [CTxIn(COutPoint(tx.sha256, 1), b"")] + tx2.vout = [CTxOut(7000, scriptPubKey)] + tx2.wit.vtxinwit.append(CTxInWitness()) + tx2.wit.vtxinwit[0].scriptWitness.stack = [witness_program] tx2.rehash() - test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, with_witness=True, accepted=segwit_activated) + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, with_witness=True, accepted=True) # Now update self.utxo for later tests. tx3 = CTransaction() - if segwit_activated: - # tx and tx2 were both accepted. Don't bother trying to reclaim the - # P2PKH output; just send tx's first output back to an anyone-can-spend. - sync_mempools([self.nodes[0], self.nodes[1]]) - tx3.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")] - tx3.vout = [CTxOut(tx.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))] - tx3.wit.vtxinwit.append(CTxInWitness()) - tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program] - tx3.rehash() - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True) - else: - # tx and tx2 didn't go anywhere; just clean up the p2sh_tx output. - tx3.vin = [CTxIn(COutPoint(p2sh_tx.sha256, 0), CScript([witness_program]))] - tx3.vout = [CTxOut(p2sh_tx.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))] + # tx and tx2 were both accepted. Don't bother trying to reclaim the + # P2PKH output; just send tx's first output back to an anyone-can-spend. + sync_mempools([self.nodes[0], self.nodes[1]]) + tx3.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")] + tx3.vout = [CTxOut(tx.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))] + tx3.wit.vtxinwit.append(CTxInWitness()) + tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program] + tx3.rehash() + if not segwit_activated: + # Just check mempool acceptance, but don't add the transaction to the mempool, since witness is disallowed + # in blocks and the tx is impossible to mine right now. + assert_equal(self.nodes[0].testmempoolaccept([bytes_to_hex_str(tx3.serialize_with_witness())]), [{'txid': tx3.hash, 'allowed': True}]) + # Create the same output as tx3, but by replacing tx + tx3_out = tx3.vout[0] + tx3 = tx + tx3.vout = [tx3_out] tx3.rehash() - test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True) + assert_equal(self.nodes[0].testmempoolaccept([bytes_to_hex_str(tx3.serialize_with_witness())]), [{'txid': tx3.hash, 'allowed': True}]) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True) self.nodes[0].generate(1) sync_blocks(self.nodes) @@ -1855,6 +1810,60 @@ class SegWitTest(BitcoinTestFramework): test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) self.utxo.append(UTXO(tx5.sha256, 0, tx5.vout[0].nValue)) + def test_non_standard_witness_blinding(self): + self.log.info("Testing behavior of unnecessary witnesses in transactions does not blind the node for the transaction") + assert (len(self.utxo) > 0) + + # Create a p2sh output -- this is so we can pass the standardness + # rules (an anyone-can-spend OP_TRUE would be rejected, if not wrapped + # in P2SH). + p2sh_program = CScript([OP_TRUE]) + p2sh_pubkey = hash160(p2sh_program) + scriptPubKey = CScript([OP_HASH160, p2sh_pubkey, OP_EQUAL]) + + # Now check that unnecessary witnesses can't be used to blind a node + # to a transaction, eg by violating standardness checks. + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) + tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, scriptPubKey)) + tx.rehash() + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, False, True) + self.nodes[0].generate(1) + sync_blocks(self.nodes) + + # We'll add an unnecessary witness to this transaction that would cause + # it to be non-standard, to test that violating policy with a witness + # doesn't blind a node to a transaction. Transactions + # rejected for having a witness shouldn't be added + # to the rejection cache. + tx2 = CTransaction() + tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), CScript([p2sh_program]))) + tx2.vout.append(CTxOut(tx.vout[0].nValue - 1000, scriptPubKey)) + tx2.wit.vtxinwit.append(CTxInWitness()) + tx2.wit.vtxinwit[0].scriptWitness.stack = [b'a' * 400] + tx2.rehash() + # This will be rejected due to a policy check: + # No witness is allowed, since it is not a witness program but a p2sh program + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, True, False, b'bad-witness-nonstandard') + + # If we send without witness, it should be accepted. + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, False, True) + + # Now create a new anyone-can-spend utxo for the next test. + tx3 = CTransaction() + tx3.vin.append(CTxIn(COutPoint(tx2.sha256, 0), CScript([p2sh_program]))) + tx3.vout.append(CTxOut(tx2.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) + tx3.rehash() + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, False, True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, False, True) + + self.nodes[0].generate(1) + sync_blocks(self.nodes) + + # Update our utxo list; we spent the first entry. + self.utxo.pop(0) + self.utxo.append(UTXO(tx3.sha256, 0, tx3.vout[0].nValue)) + def test_non_standard_witness(self): self.log.info("Testing detection of non-standard P2WSH witness") pad = chr(1).encode('latin-1') @@ -2023,6 +2032,7 @@ class SegWitTest(BitcoinTestFramework): self.test_premature_coinbase_witness_spend() self.test_uncompressed_pubkey() self.test_signature_version_1() + self.test_non_standard_witness_blinding() self.test_non_standard_witness() sync_blocks(self.nodes) self.test_upgrade_after_activation(node_id=2) diff --git a/test/functional/p2p_sendheaders.py b/test/functional/p2p_sendheaders.py index 095cc4b734..7ee8168e2f 100755 --- a/test/functional/p2p_sendheaders.py +++ b/test/functional/p2p_sendheaders.py @@ -288,6 +288,7 @@ class SendHeadersTest(BitcoinTestFramework): # 1. Mine a block; expect inv announcements each time self.log.info("Part 1: headers don't start before sendheaders message...") for i in range(4): + self.log.debug("Part 1.{}: starting...".format(i)) old_tip = tip tip = self.mine_blocks(1) inv_node.check_last_inv_announcement(inv=[tip]) @@ -335,11 +336,13 @@ class SendHeadersTest(BitcoinTestFramework): height = self.nodes[0].getblockcount() + 1 block_time += 10 # Advance far enough ahead for i in range(10): + self.log.debug("Part 2.{}: starting...".format(i)) # Mine i blocks, and alternate announcing either via # inv (of tip) or via headers. After each, new blocks # mined by the node should successfully be announced # with block header, even though the blocks are never requested for j in range(2): + self.log.debug("Part 2.{}.{}: starting...".format(i, j)) blocks = [] for b in range(i + 1): blocks.append(create_block(tip, create_coinbase(height), block_time)) @@ -386,6 +389,7 @@ class SendHeadersTest(BitcoinTestFramework): # PART 3. Headers announcements can stop after large reorg, and resume after # getheaders or inv from peer. for j in range(2): + self.log.debug("Part 3.{}: starting...".format(j)) # First try mining a reorg that can propagate with header announcement new_block_hashes = self.mine_reorg(length=7) tip = new_block_hashes[-1] @@ -412,23 +416,28 @@ class SendHeadersTest(BitcoinTestFramework): test_node.wait_for_block(new_block_hashes[-1]) for i in range(3): + self.log.debug("Part 3.{}.{}: starting...".format(j, i)) + # Mine another block, still should get only an inv tip = self.mine_blocks(1) inv_node.check_last_inv_announcement(inv=[tip]) test_node.check_last_inv_announcement(inv=[tip]) if i == 0: - self.log.debug("Just get the data -- shouldn't cause headers announcements to resume") + # Just get the data -- shouldn't cause headers announcements to resume test_node.send_get_data([tip]) test_node.wait_for_block(tip) elif i == 1: - self.log.debug("Send a getheaders message that shouldn't trigger headers announcements to resume (best header sent will be too old)") + # Send a getheaders message that shouldn't trigger headers announcements + # to resume (best header sent will be too old) test_node.send_get_headers(locator=[fork_point], hashstop=new_block_hashes[1]) test_node.send_get_data([tip]) test_node.wait_for_block(tip) elif i == 2: + # This time, try sending either a getheaders to trigger resumption + # of headers announcements, or mine a new block and inv it, also + # triggering resumption of headers announcements. test_node.send_get_data([tip]) test_node.wait_for_block(tip) - self.log.debug("This time, try sending either a getheaders to trigger resumption of headers announcements, or mine a new block and inv it, also triggering resumption of headers announcements.") if j == 0: test_node.send_get_headers(locator=[tip], hashstop=0) test_node.sync_with_ping() @@ -532,6 +541,7 @@ class SendHeadersTest(BitcoinTestFramework): # First we test that receipt of an unconnecting header doesn't prevent # chain sync. for i in range(10): + self.log.debug("Part 5.{}: starting...".format(i)) test_node.last_message.pop("getdata", None) blocks = [] # Create two more blocks. diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 75a8986793..5c2555c1ff 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -359,7 +359,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): self.log = logging.getLogger('TestFramework') self.log.setLevel(logging.DEBUG) # Create file handler to log all messages - fh = logging.FileHandler(self.options.tmpdir + '/test_framework.log') + fh = logging.FileHandler(self.options.tmpdir + '/test_framework.log', encoding='utf-8') fh.setLevel(logging.DEBUG) # Create console handler to log messages to stderr. By default this logs only error messages, but can be configured with --loglevel. ch = logging.StreamHandler(sys.stdout) diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py index fcc11abce0..f07041706a 100755 --- a/test/functional/wallet_bumpfee.py +++ b/test/functional/wallet_bumpfee.py @@ -31,7 +31,7 @@ class BumpFeeTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True - self.extra_args = [["-prematurewitness", "-walletprematurewitness", "-deprecatedrpc=addwitnessaddress", "-walletrbf={}".format(i)] + self.extra_args = [["-deprecatedrpc=addwitnessaddress", "-walletrbf={}".format(i)] for i in range(self.num_nodes)] def run_test(self): diff --git a/test/lint/check-doc.py b/test/lint/check-doc.py index de5719eb29..89776b2a6b 100755 --- a/test/lint/check-doc.py +++ b/test/lint/check-doc.py @@ -22,7 +22,7 @@ CMD_ROOT_DIR = '`git rev-parse --show-toplevel`/{}'.format(FOLDER_GREP) CMD_GREP_ARGS = r"git grep --perl-regexp '{}' -- {} ':(exclude){}'".format(REGEX_ARG, CMD_ROOT_DIR, FOLDER_TEST) CMD_GREP_DOCS = r"git grep --perl-regexp '{}' {}".format(REGEX_DOC, CMD_ROOT_DIR) # list unsupported, deprecated and duplicate args as they need no documentation -SET_DOC_OPTIONAL = set(['-rpcssl', '-benchmark', '-h', '-help', '-socks', '-tor', '-debugnet', '-whitelistalwaysrelay', '-prematurewitness', '-walletprematurewitness', '-promiscuousmempoolflags', '-blockminsize', '-dbcrashratio', '-forcecompactdb', '-usehd']) +SET_DOC_OPTIONAL = set(['-rpcssl', '-benchmark', '-h', '-help', '-socks', '-tor', '-debugnet', '-whitelistalwaysrelay', '-promiscuousmempoolflags', '-blockminsize', '-dbcrashratio', '-forcecompactdb', '-usehd']) def main(): |