diff options
-rwxr-xr-x | contrib/devtools/security-check.py | 2 | ||||
-rwxr-xr-x | contrib/gitian-build.py | 203 | ||||
-rwxr-xr-x | contrib/gitian-build.sh | 413 | ||||
-rw-r--r-- | contrib/zmq/zmq_sub.py | 2 | ||||
-rw-r--r-- | contrib/zmq/zmq_sub3.4.py | 2 | ||||
-rw-r--r-- | doc/release-process.md | 2 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/bench/bech32.cpp | 1 | ||||
-rw-r--r-- | src/chainparamsbase.cpp | 1 | ||||
-rw-r--r-- | src/interfaces/handler.cpp | 2 | ||||
-rw-r--r-- | src/net.cpp | 3 | ||||
-rw-r--r-- | src/rpc/blockchain.cpp | 289 | ||||
-rw-r--r-- | src/rpc/client.cpp | 1 | ||||
-rw-r--r-- | src/util.h | 11 | ||||
-rw-r--r-- | src/utilmemory.h | 19 | ||||
-rw-r--r-- | src/validation.cpp | 12 | ||||
-rwxr-xr-x | test/functional/p2p_segwit.py | 3 | ||||
-rwxr-xr-x | test/functional/rpc_scantxoutset.py | 48 | ||||
-rwxr-xr-x | test/functional/test_runner.py | 1 |
19 files changed, 578 insertions, 438 deletions
diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py index c9516ef83f..47195f73c8 100755 --- a/contrib/devtools/security-check.py +++ b/contrib/devtools/security-check.py @@ -97,7 +97,7 @@ def check_ELF_RELRO(executable): raise IOError('Error opening file') for line in stdout.splitlines(): tokens = line.split() - if len(tokens)>1 and tokens[1] == '(BIND_NOW)' or (len(tokens)>2 and tokens[1] == '(FLAGS)' and 'BIND_NOW' in tokens[2]): + if len(tokens)>1 and tokens[1] == '(BIND_NOW)' or (len(tokens)>2 and tokens[1] == '(FLAGS)' and 'BIND_NOW' in tokens[2:]): have_bindnow = True return have_gnu_relro and have_bindnow diff --git a/contrib/gitian-build.py b/contrib/gitian-build.py new file mode 100755 index 0000000000..1da9e43896 --- /dev/null +++ b/contrib/gitian-build.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 + +import argparse +import os +import subprocess +import sys + +def setup(): + global args, workdir + programs = ['ruby', 'git', 'apt-cacher-ng', 'make', 'wget'] + if args.kvm: + programs += ['python-vm-builder', 'qemu-kvm', 'qemu-utils'] + elif args.docker: + programs += ['docker.io'] + else: + programs += ['lxc', 'debootstrap'] + subprocess.check_call(['sudo', 'apt-get', 'install', '-qq'] + programs) + if not os.path.isdir('gitian.sigs'): + subprocess.check_call(['git', 'clone', 'https://github.com/bitcoin-core/gitian.sigs.git']) + if not os.path.isdir('bitcoin-detached-sigs'): + subprocess.check_call(['git', 'clone', 'https://github.com/bitcoin-core/bitcoin-detached-sigs.git']) + if not os.path.isdir('gitian-builder'): + subprocess.check_call(['git', 'clone', 'https://github.com/devrandom/gitian-builder.git']) + if not os.path.isdir('bitcoin'): + subprocess.check_call(['git', 'clone', 'https://github.com/bitcoin/bitcoin.git']) + os.chdir('gitian-builder') + make_image_prog = ['bin/make-base-vm', '--suite', 'bionic', '--arch', 'amd64'] + if args.docker: + make_image_prog += ['--docker'] + elif not args.kvm: + make_image_prog += ['--lxc'] + subprocess.check_call(make_image_prog) + os.chdir(workdir) + +def build(): + global args, workdir + + os.makedirs('bitcoin-binaries/' + args.version, exist_ok=True) + print('\nBuilding Dependencies\n') + os.chdir('gitian-builder') + os.makedirs('inputs', exist_ok=True) + + subprocess.check_call(['wget', '-N', '-P', 'inputs', 'http://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz']) + subprocess.check_call(['wget', '-N', '-P', 'inputs', 'https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch']) + subprocess.check_call(['make', '-C', '../bitcoin/depends', 'download', 'SOURCES_PATH=' + os.getcwd() + '/cache/common']) + + if args.linux: + print('\nCompiling ' + args.version + ' Linux') + subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'bitcoin='+args.commit, '--url', 'bitcoin='+args.url, '../bitcoin/contrib/gitian-descriptors/gitian-linux.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-linux', '--destination', '../gitian.sigs/', '../bitcoin/contrib/gitian-descriptors/gitian-linux.yml']) + subprocess.check_call('mv build/out/bitcoin-*.tar.gz build/out/src/bitcoin-*.tar.gz ../bitcoin-binaries/'+args.version, shell=True) + + if args.windows: + print('\nCompiling ' + args.version + ' Windows') + subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'bitcoin='+args.commit, '--url', 'bitcoin='+args.url, '../bitcoin/contrib/gitian-descriptors/gitian-win.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win-unsigned', '--destination', '../gitian.sigs/', '../bitcoin/contrib/gitian-descriptors/gitian-win.yml']) + subprocess.check_call('mv build/out/bitcoin-*-win-unsigned.tar.gz inputs/bitcoin-win-unsigned.tar.gz', shell=True) + subprocess.check_call('mv build/out/bitcoin-*.zip build/out/bitcoin-*.exe ../bitcoin-binaries/'+args.version, shell=True) + + if args.macos: + print('\nCompiling ' + args.version + ' MacOS') + subprocess.check_call(['bin/gbuild', '-j', args.jobs, '-m', args.memory, '--commit', 'bitcoin='+args.commit, '--url', 'bitcoin='+args.url, '../bitcoin/contrib/gitian-descriptors/gitian-osx.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx-unsigned', '--destination', '../gitian.sigs/', '../bitcoin/contrib/gitian-descriptors/gitian-osx.yml']) + subprocess.check_call('mv build/out/bitcoin-*-osx-unsigned.tar.gz inputs/bitcoin-osx-unsigned.tar.gz', shell=True) + subprocess.check_call('mv build/out/bitcoin-*.tar.gz build/out/bitcoin-*.dmg ../bitcoin-binaries/'+args.version, shell=True) + + os.chdir(workdir) + + if args.commit_files: + print('\nCommitting '+args.version+' Unsigned Sigs\n') + os.chdir('gitian.sigs') + subprocess.check_call(['git', 'add', args.version+'-linux/'+args.signer]) + subprocess.check_call(['git', 'add', args.version+'-win-unsigned/'+args.signer]) + subprocess.check_call(['git', 'add', args.version+'-osx-unsigned/'+args.signer]) + subprocess.check_call(['git', 'commit', '-m', 'Add '+args.version+' unsigned sigs for '+args.signer]) + os.chdir(workdir) + +def sign(): + global args, workdir + os.chdir('gitian-builder') + + if args.windows: + print('\nSigning ' + args.version + ' Windows') + subprocess.check_call(['bin/gbuild', '-i', '--commit', 'signature='+args.commit, '../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-win-signed', '--destination', '../gitian.sigs/', '../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml']) + subprocess.check_call('mv build/out/bitcoin-*win64-setup.exe ../bitcoin-binaries/'+args.version, shell=True) + subprocess.check_call('mv build/out/bitcoin-*win32-setup.exe ../bitcoin-binaries/'+args.version, shell=True) + + if args.macos: + print('\nSigning ' + args.version + ' MacOS') + subprocess.check_call(['bin/gbuild', '-i', '--commit', 'signature='+args.commit, '../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml']) + subprocess.check_call(['bin/gsign', '-p', args.sign_prog, '--signer', args.signer, '--release', args.version+'-osx-signed', '--destination', '../gitian.sigs/', '../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml']) + subprocess.check_call('mv build/out/bitcoin-osx-signed.dmg ../bitcoin-binaries/'+args.version+'/bitcoin-'+args.version+'-osx.dmg', shell=True) + + os.chdir(workdir) + + if args.commit_files: + print('\nCommitting '+args.version+' Signed Sigs\n') + os.chdir('gitian.sigs') + subprocess.check_call(['git', 'add', args.version+'-win-signed/'+args.signer]) + subprocess.check_call(['git', 'add', args.version+'-osx-signed/'+args.signer]) + subprocess.check_call(['git', 'commit', '-a', '-m', 'Add '+args.version+' signed binary sigs for '+args.signer]) + os.chdir(workdir) + +def verify(): + global args, workdir + os.chdir('gitian-builder') + + print('\nVerifying v'+args.version+' Linux\n') + subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-linux', '../bitcoin/contrib/gitian-descriptors/gitian-linux.yml']) + print('\nVerifying v'+args.version+' Windows\n') + subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-win-unsigned', '../bitcoin/contrib/gitian-descriptors/gitian-win.yml']) + print('\nVerifying v'+args.version+' MacOS\n') + subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-osx-unsigned', '../bitcoin/contrib/gitian-descriptors/gitian-osx.yml']) + print('\nVerifying v'+args.version+' Signed Windows\n') + subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-win-signed', '../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml']) + print('\nVerifying v'+args.version+' Signed MacOS\n') + subprocess.check_call(['bin/gverify', '-v', '-d', '../gitian.sigs/', '-r', args.version+'-osx-signed', '../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml']) + + os.chdir(workdir) + +def main(): + global args, workdir + + parser = argparse.ArgumentParser(usage='%(prog)s [options] signer version') + parser.add_argument('-c', '--commit', action='store_true', dest='commit', help='Indicate that the version argument is for a commit or branch') + parser.add_argument('-u', '--url', dest='url', default='https://github.com/bitcoin/bitcoin', help='Specify the URL of the repository. Default is %(default)s') + parser.add_argument('-v', '--verify', action='store_true', dest='verify', help='Verify the Gitian build') + parser.add_argument('-b', '--build', action='store_true', dest='build', help='Do a Gitian build') + parser.add_argument('-s', '--sign', action='store_true', dest='sign', help='Make signed binaries for Windows and MacOS') + parser.add_argument('-B', '--buildsign', action='store_true', dest='buildsign', help='Build both signed and unsigned binaries') + parser.add_argument('-o', '--os', dest='os', default='lwm', help='Specify which Operating Systems the build is for. Default is %(default)s. l for Linux, w for Windows, m for MacOS') + parser.add_argument('-j', '--jobs', dest='jobs', default='2', help='Number of processes to use. Default %(default)s') + parser.add_argument('-m', '--memory', dest='memory', default='2000', help='Memory to allocate in MiB. Default %(default)s') + parser.add_argument('-k', '--kvm', action='store_true', dest='kvm', help='Use KVM instead of LXC') + parser.add_argument('-d', '--docker', action='store_true', dest='docker', help='Use Docker instead of LXC') + parser.add_argument('-S', '--setup', action='store_true', dest='setup', help='Set up the Gitian building environment. Uses LXC. If you want to use KVM, use the --kvm option. Only works on Debian-based systems (Ubuntu, Debian)') + parser.add_argument('-D', '--detach-sign', action='store_true', dest='detach_sign', help='Create the assert file for detached signing. Will not commit anything.') + parser.add_argument('-n', '--no-commit', action='store_false', dest='commit_files', help='Do not commit anything to git') + parser.add_argument('signer', help='GPG signer to sign each build assert file') + parser.add_argument('version', help='Version number, commit, or branch to build. If building a commit or branch, the -c option must be specified') + + args = parser.parse_args() + workdir = os.getcwd() + + args.linux = 'l' in args.os + args.windows = 'w' in args.os + args.macos = 'm' in args.os + + if args.buildsign: + args.build=True + args.sign=True + + if args.kvm and args.docker: + raise Exception('Error: cannot have both kvm and docker') + + args.sign_prog = 'true' if args.detach_sign else 'gpg --detach-sign' + + # Set enviroment variable USE_LXC or USE_DOCKER, let gitian-builder know that we use lxc or docker + if args.docker: + os.environ['USE_DOCKER'] = '1' + elif not args.kvm: + os.environ['USE_LXC'] = '1' + + # Disable for MacOS if no SDK found + if args.macos and not os.path.isfile('gitian-builder/inputs/MacOSX10.11.sdk.tar.gz'): + print('Cannot build for MacOS, SDK does not exist. Will build for other OSes') + args.macos = False + + script_name = os.path.basename(sys.argv[0]) + # Signer and version shouldn't be empty + if args.signer == '': + print(script_name+': Missing signer.') + print('Try '+script_name+' --help for more information') + exit(1) + if args.version == '': + print(script_name+': Missing version.') + print('Try '+script_name+' --help for more information') + exit(1) + + # Add leading 'v' for tags + args.commit = ('' if args.commit else 'v') + args.version + print(args.commit) + + if args.setup: + setup() + + os.chdir('bitcoin') + subprocess.check_call(['git', 'fetch']) + subprocess.check_call(['git', 'checkout', args.commit]) + os.chdir(workdir) + + if args.build: + build() + + if args.sign: + sign() + + if args.verify: + verify() + +if __name__ == '__main__': + main() diff --git a/contrib/gitian-build.sh b/contrib/gitian-build.sh deleted file mode 100755 index d873580711..0000000000 --- a/contrib/gitian-build.sh +++ /dev/null @@ -1,413 +0,0 @@ -#!/usr/bin/env bash -# Copyright (c) 2016 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -export LC_ALL=C -# What to do -sign=false -verify=false -build=false - -# Systems to build -linux=true -windows=true -osx=true - -# Other Basic variables -SIGNER= -VERSION= -commit=false -url=https://github.com/bitcoin/bitcoin -proc=2 -mem=2000 -lxc=true -docker=false -osslTarUrl=http://downloads.sourceforge.net/project/osslsigncode/osslsigncode/osslsigncode-1.7.1.tar.gz -osslPatchUrl=https://bitcoincore.org/cfields/osslsigncode-Backports-to-1.7.1.patch -scriptName=$(basename -- "$0") -signProg="gpg --detach-sign" -commitFiles=true - -# Help Message -read -d '' usage <<- EOF -Usage: $scriptName [-c|u|v|b|s|B|o|h|j|m|] signer version - -Run this script from the directory containing the bitcoin, gitian-builder, gitian.sigs, and bitcoin-detached-sigs. - -Arguments: -signer GPG signer to sign each build assert file -version Version number, commit, or branch to build. If building a commit or branch, the -c option must be specified - -Options: --c|--commit Indicate that the version argument is for a commit or branch --u|--url Specify the URL of the repository. Default is https://github.com/bitcoin/bitcoin --v|--verify Verify the Gitian build --b|--build Do a Gitian build --s|--sign Make signed binaries for Windows and Mac OSX --B|--buildsign Build both signed and unsigned binaries --o|--os Specify which Operating Systems the build is for. Default is lwx. l for linux, w for windows, x for osx --j Number of processes to use. Default 2 --m Memory to allocate in MiB. Default 2000 ---kvm Use KVM instead of LXC ---docker Use Docker instead of LXC ---setup Set up the Gitian building environment. Uses LXC. If you want to use KVM, use the --kvm option. Only works on Debian-based systems (Ubuntu, Debian) ---detach-sign Create the assert file for detached signing. Will not commit anything. ---no-commit Do not commit anything to git --h|--help Print this help message -EOF - -# Get options and arguments -while :; do - case $1 in - # Verify - -v|--verify) - verify=true - ;; - # Build - -b|--build) - build=true - ;; - # Sign binaries - -s|--sign) - sign=true - ;; - # Build then Sign - -B|--buildsign) - sign=true - build=true - ;; - # PGP Signer - -S|--signer) - if [ -n "$2" ] - then - SIGNER="$2" - shift - else - echo 'Error: "--signer" requires a non-empty argument.' - exit 1 - fi - ;; - # Operating Systems - -o|--os) - if [ -n "$2" ] - then - linux=false - windows=false - osx=false - if [[ "$2" = *"l"* ]] - then - linux=true - fi - if [[ "$2" = *"w"* ]] - then - windows=true - fi - if [[ "$2" = *"x"* ]] - then - osx=true - fi - shift - else - echo 'Error: "--os" requires an argument containing an l (for linux), w (for windows), or x (for Mac OSX)' - exit 1 - fi - ;; - # Help message - -h|--help) - echo "$usage" - exit 0 - ;; - # Commit or branch - -c|--commit) - commit=true - ;; - # Number of Processes - -j) - if [ -n "$2" ] - then - proc=$2 - shift - else - echo 'Error: "-j" requires an argument' - exit 1 - fi - ;; - # Memory to allocate - -m) - if [ -n "$2" ] - then - mem=$2 - shift - else - echo 'Error: "-m" requires an argument' - exit 1 - fi - ;; - # URL - -u) - if [ -n "$2" ] - then - url=$2 - shift - else - echo 'Error: "-u" requires an argument' - exit 1 - fi - ;; - # kvm - --kvm) - lxc=false - ;; - # docker - --docker) - if [[ $lxc = false ]] - then - echo 'Error: cannot have both kvm and docker' - exit 1 - fi - lxc=false - docker=true - ;; - # Detach sign - --detach-sign) - signProg="true" - commitFiles=false - ;; - # Commit files - --no-commit) - commitFiles=false - ;; - # Setup - --setup) - setup=true - ;; - *) # Default case: If no more options then break out of the loop. - break - esac - shift -done - -# Set up LXC -if [[ $lxc = true ]] -then - export USE_LXC=1 -fi - -# Setup docker -if [[ $docker = true ]] -then - export USE_DOCKER=1 -fi - -# Check for OSX SDK -if [[ ! -e "gitian-builder/inputs/MacOSX10.11.sdk.tar.gz" && $osx == true ]] -then - echo "Cannot build for OSX, SDK does not exist. Will build for other OSes" - osx=false -fi - -# Get signer -if [[ -n "$1" ]] -then - SIGNER="$1" - shift -fi - -# Get version -if [[ -n "$1" ]] -then - VERSION=$1 - COMMIT=$VERSION - shift -fi - -# Check that a signer is specified -if [[ "$SIGNER" == "" ]] -then - echo "$scriptName: Missing signer." - echo "Try $scriptName --help for more information" - exit 1 -fi - -# Check that a version is specified -if [[ $VERSION == "" ]] -then - echo "$scriptName: Missing version." - echo "Try $scriptName --help for more information" - exit 1 -fi - -# Add a "v" if no -c -if [[ $commit = false ]] -then - COMMIT="v${VERSION}" -fi -echo ${COMMIT} - -# Setup build environment -if [[ $setup = true ]] -then - sudo apt-get install ruby apache2 git apt-cacher-ng python-vm-builder qemu-kvm qemu-utils - git clone https://github.com/bitcoin-core/gitian.sigs.git - git clone https://github.com/bitcoin-core/bitcoin-detached-sigs.git - git clone https://github.com/devrandom/gitian-builder.git - pushd ./gitian-builder - if [[ -n "$USE_LXC" ]] - then - sudo apt-get install lxc - bin/make-base-vm --suite bionic --arch amd64 --lxc - elif [[ -n "$USE_DOCKER" ]] - then - sudo apt-get install docker-ce - bin/make-base-vm --suite bionic --arch amd64 --docker - else - bin/make-base-vm --suite bionic --arch amd64 - fi - popd -fi - -# Set up build -pushd ./bitcoin -git fetch -git checkout ${COMMIT} -popd - -# Build -if [[ $build = true ]] -then - # Make output folder - mkdir -p ./bitcoin-binaries/${VERSION} - - # Build Dependencies - echo "" - echo "Building Dependencies" - echo "" - pushd ./gitian-builder - mkdir -p inputs - wget -N -P inputs $osslPatchUrl - wget -N -P inputs $osslTarUrl - make -C ../bitcoin/depends download SOURCES_PATH=`pwd`/cache/common - - # Linux - if [[ $linux = true ]] - then - echo "" - echo "Compiling ${VERSION} Linux" - echo "" - ./bin/gbuild -j ${proc} -m ${mem} --commit bitcoin=${COMMIT} --url bitcoin=${url} ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml - ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-linux --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml - mv build/out/bitcoin-*.tar.gz build/out/src/bitcoin-*.tar.gz ../bitcoin-binaries/${VERSION} - fi - # Windows - if [[ $windows = true ]] - then - echo "" - echo "Compiling ${VERSION} Windows" - echo "" - ./bin/gbuild -j ${proc} -m ${mem} --commit bitcoin=${COMMIT} --url bitcoin=${url} ../bitcoin/contrib/gitian-descriptors/gitian-win.yml - ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-win-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win.yml - mv build/out/bitcoin-*-win-unsigned.tar.gz inputs/bitcoin-win-unsigned.tar.gz - mv build/out/bitcoin-*.zip build/out/bitcoin-*.exe ../bitcoin-binaries/${VERSION} - fi - # Mac OSX - if [[ $osx = true ]] - then - echo "" - echo "Compiling ${VERSION} Mac OSX" - echo "" - ./bin/gbuild -j ${proc} -m ${mem} --commit bitcoin=${COMMIT} --url bitcoin=${url} ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml - ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-osx-unsigned --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml - mv build/out/bitcoin-*-osx-unsigned.tar.gz inputs/bitcoin-osx-unsigned.tar.gz - mv build/out/bitcoin-*.tar.gz build/out/bitcoin-*.dmg ../bitcoin-binaries/${VERSION} - fi - popd - - if [[ $commitFiles = true ]] - then - # Commit to gitian.sigs repo - echo "" - echo "Committing ${VERSION} Unsigned Sigs" - echo "" - pushd gitian.sigs - git add ${VERSION}-linux/"${SIGNER}" - git add ${VERSION}-win-unsigned/"${SIGNER}" - git add ${VERSION}-osx-unsigned/"${SIGNER}" - git commit -a -m "Add ${VERSION} unsigned sigs for ${SIGNER}" - popd - fi -fi - -# Verify the build -if [[ $verify = true ]] -then - # Linux - pushd ./gitian-builder - echo "" - echo "Verifying v${VERSION} Linux" - echo "" - ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-linux ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml - # Windows - echo "" - echo "Verifying v${VERSION} Windows" - echo "" - ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-win-unsigned ../bitcoin/contrib/gitian-descriptors/gitian-win.yml - # Mac OSX - echo "" - echo "Verifying v${VERSION} Mac OSX" - echo "" - ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-unsigned ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml - # Signed Windows - echo "" - echo "Verifying v${VERSION} Signed Windows" - echo "" - ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-signed ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml - # Signed Mac OSX - echo "" - echo "Verifying v${VERSION} Signed Mac OSX" - echo "" - ./bin/gverify -v -d ../gitian.sigs/ -r ${VERSION}-osx-signed ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml - popd -fi - -# Sign binaries -if [[ $sign = true ]] -then - - pushd ./gitian-builder - # Sign Windows - if [[ $windows = true ]] - then - echo "" - echo "Signing ${VERSION} Windows" - echo "" - ./bin/gbuild -i --commit signature=${COMMIT} ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml - ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-win-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml - mv build/out/bitcoin-*win64-setup.exe ../bitcoin-binaries/${VERSION} - mv build/out/bitcoin-*win32-setup.exe ../bitcoin-binaries/${VERSION} - fi - # Sign Mac OSX - if [[ $osx = true ]] - then - echo "" - echo "Signing ${VERSION} Mac OSX" - echo "" - ./bin/gbuild -i --commit signature=${COMMIT} ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml - ./bin/gsign -p "$signProg" --signer "$SIGNER" --release ${VERSION}-osx-signed --destination ../gitian.sigs/ ../bitcoin/contrib/gitian-descriptors/gitian-osx-signer.yml - mv build/out/bitcoin-osx-signed.dmg ../bitcoin-binaries/${VERSION}/bitcoin-${VERSION}-osx.dmg - fi - popd - - if [[ $commitFiles = true ]] - then - # Commit Sigs - pushd gitian.sigs - echo "" - echo "Committing ${VERSION} Signed Sigs" - echo "" - git add ${VERSION}-win-signed/"${SIGNER}" - git add ${VERSION}-osx-signed/"${SIGNER}" - git commit -a -m "Add ${VERSION} signed binary sigs for ${SIGNER}" - popd - fi -fi diff --git a/contrib/zmq/zmq_sub.py b/contrib/zmq/zmq_sub.py index 60768dc59a..fa9e669308 100644 --- a/contrib/zmq/zmq_sub.py +++ b/contrib/zmq/zmq_sub.py @@ -30,7 +30,7 @@ import signal import struct import sys -if not (sys.version_info.major >= 3 and sys.version_info.minor >= 5): +if (sys.version_info.major, sys.version_info.minor) < (3, 5): print("This example only works with Python 3.5 and greater") sys.exit(1) diff --git a/contrib/zmq/zmq_sub3.4.py b/contrib/zmq/zmq_sub3.4.py index 0df843c9a3..d05ecc2623 100644 --- a/contrib/zmq/zmq_sub3.4.py +++ b/contrib/zmq/zmq_sub3.4.py @@ -34,7 +34,7 @@ import signal import struct import sys -if not (sys.version_info.major >= 3 and sys.version_info.minor >= 4): +if (sys.version_info.major, sys.version_info.minor) < (3, 4): print("This example only works with Python 3.4 and greater") sys.exit(1) diff --git a/doc/release-process.md b/doc/release-process.md index 912b620794..f3fc4bdfdc 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -157,7 +157,7 @@ Commit your signature to gitian.sigs: git add ${VERSION}-linux/"${SIGNER}" git add ${VERSION}-win-unsigned/"${SIGNER}" git add ${VERSION}-osx-unsigned/"${SIGNER}" - git commit -a + git commit -m "Add ${VERSION} unsigned sigs for ${SIGNER}" git push # Assuming you can push to the gitian.sigs tree popd diff --git a/src/Makefile.am b/src/Makefile.am index 1dd202085a..2603dbae56 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -179,6 +179,7 @@ BITCOIN_CORE_H = \ ui_interface.h \ undo.h \ util.h \ + utilmemory.h \ utilmoneystr.h \ utiltime.h \ validation.h \ diff --git a/src/bench/bech32.cpp b/src/bench/bech32.cpp index ff655bded0..8b80e17391 100644 --- a/src/bench/bech32.cpp +++ b/src/bench/bech32.cpp @@ -27,7 +27,6 @@ static void Bech32Encode(benchmark::State& state) static void Bech32Decode(benchmark::State& state) { std::string addr = "bc1qkallence7tjawwvy0dwt4twc62qjgaw8f4vlhyd006d99f09"; - std::vector<unsigned char> vch; while (state.KeepRunning()) { bech32::Decode(addr); } diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 787d8d8f6a..da9f3e3209 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -7,6 +7,7 @@ #include <tinyformat.h> #include <util.h> +#include <utilmemory.h> #include <assert.h> diff --git a/src/interfaces/handler.cpp b/src/interfaces/handler.cpp index 8e45faa2a5..80f461f4d3 100644 --- a/src/interfaces/handler.cpp +++ b/src/interfaces/handler.cpp @@ -4,7 +4,7 @@ #include <interfaces/handler.h> -#include <util.h> +#include <utilmemory.h> #include <boost/signals2/connection.hpp> #include <utility> diff --git a/src/net.cpp b/src/net.cpp index e44aa1fdb4..34dc4aec5c 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2254,7 +2254,8 @@ bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<C if (binds.empty() && whiteBinds.empty()) { struct in_addr inaddr_any; inaddr_any.s_addr = INADDR_ANY; - fBound |= Bind(CService((in6_addr)IN6ADDR_ANY_INIT, GetListenPort()), BF_NONE); + struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT; + fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE); fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE); } return fBound; diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index d9d803ac7d..012e3e3ac1 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -6,6 +6,8 @@ #include <rpc/blockchain.h> #include <amount.h> +#include <base58.h> +#include <chain.h> #include <chainparams.h> #include <checkpoints.h> #include <coins.h> @@ -13,6 +15,7 @@ #include <validation.h> #include <core_io.h> #include <index/txindex.h> +#include <key_io.h> #include <policy/feerate.h> #include <policy/policy.h> #include <primitives/transaction.h> @@ -27,6 +30,7 @@ #include <validationinterface.h> #include <warnings.h> +#include <assert.h> #include <stdint.h> #include <univalue.h> @@ -1920,6 +1924,290 @@ static UniValue savemempool(const JSONRPCRequest& request) return NullUniValue; } +//! Search for a given set of pubkey scripts +bool FindScriptPubKey(std::atomic<int>& scan_progress, const std::atomic<bool>& should_abort, int64_t& count, CCoinsViewCursor* cursor, const std::set<CScript>& needles, std::map<COutPoint, Coin>& out_results) { + scan_progress = 0; + count = 0; + while (cursor->Valid()) { + COutPoint key; + Coin coin; + if (!cursor->GetKey(key) || !cursor->GetValue(coin)) return false; + if (++count % 8192 == 0) { + boost::this_thread::interruption_point(); + if (should_abort) { + // allow to abort the scan via the abort reference + return false; + } + } + if (count % 256 == 0) { + // update progress reference every 256 item + uint32_t high = 0x100 * *key.hash.begin() + *(key.hash.begin() + 1); + scan_progress = (int)(high * 100.0 / 65536.0 + 0.5); + } + if (needles.count(coin.out.scriptPubKey)) { + out_results.emplace(key, coin); + } + cursor->Next(); + } + scan_progress = 100; + return true; +} + +/** RAII object to prevent concurrency issue when scanning the txout set */ +static std::mutex g_utxosetscan; +static std::atomic<int> g_scan_progress; +static std::atomic<bool> g_scan_in_progress; +static std::atomic<bool> g_should_abort_scan; +class CoinsViewScanReserver +{ +private: + bool m_could_reserve; +public: + explicit CoinsViewScanReserver() : m_could_reserve(false) {} + + bool reserve() { + assert (!m_could_reserve); + std::lock_guard<std::mutex> lock(g_utxosetscan); + if (g_scan_in_progress) { + return false; + } + g_scan_in_progress = true; + m_could_reserve = true; + return true; + } + + ~CoinsViewScanReserver() { + if (m_could_reserve) { + std::lock_guard<std::mutex> lock(g_utxosetscan); + g_scan_in_progress = false; + } + } +}; + +static const char *g_default_scantxoutset_script_types[] = { "P2PKH", "P2SH_P2WPKH", "P2WPKH" }; + +enum class OutputScriptType { + UNKNOWN, + P2PK, + P2PKH, + P2SH_P2WPKH, + P2WPKH +}; + +static inline OutputScriptType GetOutputScriptTypeFromString(const std::string& outputtype) +{ + if (outputtype == "P2PK") return OutputScriptType::P2PK; + else if (outputtype == "P2PKH") return OutputScriptType::P2PKH; + else if (outputtype == "P2SH_P2WPKH") return OutputScriptType::P2SH_P2WPKH; + else if (outputtype == "P2WPKH") return OutputScriptType::P2WPKH; + else return OutputScriptType::UNKNOWN; +} + +CTxDestination GetDestinationForKey(const CPubKey& key, OutputScriptType type) +{ + switch (type) { + case OutputScriptType::P2PKH: return key.GetID(); + case OutputScriptType::P2SH_P2WPKH: + case OutputScriptType::P2WPKH: { + if (!key.IsCompressed()) return key.GetID(); + CTxDestination witdest = WitnessV0KeyHash(key.GetID()); + if (type == OutputScriptType::P2SH_P2WPKH) { + CScript witprog = GetScriptForDestination(witdest); + return CScriptID(witprog); + } else { + return witdest; + } + } + default: assert(false); + } +} + +UniValue scantxoutset(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) + throw std::runtime_error( + "scantxoutset <action> ( <scanobjects> )\n" + "\nScans the unspent transaction output set for possible entries that matches common scripts of given public keys.\n" + "Using addresses as scanobjects will _not_ detect unspent P2PK txouts\n" + "\nArguments:\n" + "1. \"action\" (string, required) The action to execute\n" + " \"start\" for starting a scan\n" + " \"abort\" for aborting the current scan (returns true when abort was successful)\n" + " \"status\" for progress report (in %) of the current scan\n" + "2. \"scanobjects\" (array, optional) Array of scan objects (only one object type per scan object allowed)\n" + " [\n" + " { \"address\" : \"<address>\" }, (string, optional) Bitcoin address\n" + " { \"script\" : \"<scriptPubKey>\" }, (string, optional) HEX encoded script (scriptPubKey)\n" + " { \"pubkey\" : (object, optional) Public key\n" + " {\n" + " \"pubkey\" : \"<pubkey\">, (string, required) HEX encoded public key\n" + " \"script_types\" : [ ... ], (array, optional) Array of script-types to derive from the pubkey (possible values: \"P2PK\", \"P2PKH\", \"P2SH-P2WPKH\", \"P2WPKH\")\n" + " }\n" + " },\n" + " ]\n" + "\nResult:\n" + "{\n" + " \"unspents\": [\n" + " {\n" + " \"txid\" : \"transactionid\", (string) The transaction id\n" + " \"vout\": n, (numeric) the vout value\n" + " \"scriptPubKey\" : \"script\", (string) the script key\n" + " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " of the unspent output\n" + " \"height\" : n, (numeric) Height of the unspent transaction output\n" + " }\n" + " ,...], \n" + " \"total_amount\" : x.xxx, (numeric) The total amount of all found unspent outputs in " + CURRENCY_UNIT + "\n" + "]\n" + ); + + RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VARR}); + + UniValue result(UniValue::VOBJ); + if (request.params[0].get_str() == "status") { + CoinsViewScanReserver reserver; + if (reserver.reserve()) { + // no scan in progress + return NullUniValue; + } + result.pushKV("progress", g_scan_progress); + return result; + } else if (request.params[0].get_str() == "abort") { + CoinsViewScanReserver reserver; + if (reserver.reserve()) { + // reserve was possible which means no scan was running + return false; + } + // set the abort flag + g_should_abort_scan = true; + return true; + } else if (request.params[0].get_str() == "start") { + CoinsViewScanReserver reserver; + if (!reserver.reserve()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Scan already in progress, use action \"abort\" or \"status\""); + } + std::set<CScript> needles; + CAmount total_in = 0; + + // loop through the scan objects + for (const UniValue& scanobject : request.params[1].get_array().getValues()) { + if (!scanobject.isObject()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid scan object"); + } + UniValue address_uni = find_value(scanobject, "address"); + UniValue pubkey_uni = find_value(scanobject, "pubkey"); + UniValue script_uni = find_value(scanobject, "script"); + + // make sure only one object type is present + if (1 != !address_uni.isNull() + !pubkey_uni.isNull() + !script_uni.isNull()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Only one object type is allowed per scan object"); + } else if (!address_uni.isNull() && !address_uni.isStr()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"address\" must contain a single string as value"); + } else if (!pubkey_uni.isNull() && !pubkey_uni.isObject()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"pubkey\" must contain an object as value"); + } else if (!script_uni.isNull() && !script_uni.isStr()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Scanobject \"script\" must contain a single string as value"); + } else if (address_uni.isStr()) { + // type: address + // decode destination and derive the scriptPubKey + // add the script to the scan containers + CTxDestination dest = DecodeDestination(address_uni.get_str()); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } + CScript script = GetScriptForDestination(dest); + assert(!script.empty()); + needles.insert(script); + } else if (pubkey_uni.isObject()) { + // type: pubkey + // derive script(s) according to the script_type parameter + UniValue script_types_uni = find_value(pubkey_uni, "script_types"); + UniValue pubkeydata_uni = find_value(pubkey_uni, "pubkey"); + + // check the script types and use the default if not provided + if (!script_types_uni.isNull() && !script_types_uni.isArray()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "script_types must be an array"); + } else if (script_types_uni.isNull()) { + // use the default script types + script_types_uni = UniValue(UniValue::VARR); + for (const char *t : g_default_scantxoutset_script_types) { + script_types_uni.push_back(t); + } + } + + // check the acctual pubkey + if (!pubkeydata_uni.isStr() || !IsHex(pubkeydata_uni.get_str())) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Public key must be hex encoded"); + } + CPubKey pubkey(ParseHexV(pubkeydata_uni, "pubkey")); + if (!pubkey.IsFullyValid()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid public key"); + } + + // loop through the script types and derive the script + for (const UniValue& script_type_uni : script_types_uni.get_array().getValues()) { + OutputScriptType script_type = GetOutputScriptTypeFromString(script_type_uni.get_str()); + if (script_type == OutputScriptType::UNKNOWN) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid script type"); + CScript script; + if (script_type == OutputScriptType::P2PK) { + // support legacy P2PK scripts + script << ToByteVector(pubkey) << OP_CHECKSIG; + } else { + script = GetScriptForDestination(GetDestinationForKey(pubkey, script_type)); + } + assert(!script.empty()); + needles.insert(script); + } + } else if (script_uni.isStr()) { + // type: script + // check and add the script to the scan containers (needles array) + CScript script(ParseHexV(script_uni, "script")); + // TODO: check script: max length, has OP, is unspenable etc. + needles.insert(script); + } + } + + // Scan the unspent transaction output set for inputs + UniValue unspents(UniValue::VARR); + std::vector<CTxOut> input_txos; + std::map<COutPoint, Coin> coins; + g_should_abort_scan = false; + g_scan_progress = 0; + int64_t count = 0; + std::unique_ptr<CCoinsViewCursor> pcursor; + { + LOCK(cs_main); + FlushStateToDisk(); + pcursor = std::unique_ptr<CCoinsViewCursor>(pcoinsdbview->Cursor()); + assert(pcursor); + } + bool res = FindScriptPubKey(g_scan_progress, g_should_abort_scan, count, pcursor.get(), needles, coins); + result.pushKV("success", res); + result.pushKV("searched_items", count); + + for (const auto& it : coins) { + const COutPoint& outpoint = it.first; + const Coin& coin = it.second; + const CTxOut& txo = coin.out; + input_txos.push_back(txo); + total_in += txo.nValue; + + UniValue unspent(UniValue::VOBJ); + unspent.pushKV("txid", outpoint.hash.GetHex()); + unspent.pushKV("vout", (int32_t)outpoint.n); + unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey.begin(), txo.scriptPubKey.end())); + unspent.pushKV("amount", ValueFromAmount(txo.nValue)); + unspent.pushKV("height", (int32_t)coin.nHeight); + + unspents.push_back(unspent); + } + result.pushKV("unspents", unspents); + result.pushKV("total_amount", ValueFromAmount(total_in)); + } else { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid command"); + } + return result; +} + static const CRPCCommand commands[] = { // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- @@ -1945,6 +2233,7 @@ static const CRPCCommand commands[] = { "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} }, { "blockchain", "preciousblock", &preciousblock, {"blockhash"} }, + { "blockchain", "scantxoutset", &scantxoutset, {"action", "scanobjects"} }, /* Not shown in help */ { "hidden", "invalidateblock", &invalidateblock, {"blockhash"} }, diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 0f35fd3770..ce608631ff 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -78,6 +78,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "sendmany", 4, "subtractfeefrom" }, { "sendmany", 5 , "replaceable" }, { "sendmany", 6 , "conf_target" }, + { "scantxoutset", 1, "scanobjects" }, { "addmultisigaddress", 0, "nrequired" }, { "addmultisigaddress", 1, "keys" }, { "createmultisig", 0, "nrequired" }, diff --git a/src/util.h b/src/util.h index 8094d72d6b..f8bcc0192c 100644 --- a/src/util.h +++ b/src/util.h @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2017 The Bitcoin Core developers +// Copyright (c) 2009-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. @@ -20,11 +20,11 @@ #include <sync.h> #include <tinyformat.h> #include <utiltime.h> +#include <utilmemory.h> #include <atomic> #include <exception> #include <map> -#include <memory> #include <set> #include <stdint.h> #include <string> @@ -346,13 +346,6 @@ template <typename Callable> void TraceThread(const char* name, Callable func) std::string CopyrightHolders(const std::string& strPrefix); -//! Substitute for C++14 std::make_unique. -template <typename T, typename... Args> -std::unique_ptr<T> MakeUnique(Args&&... args) -{ - return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); -} - /** * On platforms that support it, tell the kernel the calling thread is * CPU-intensive and non-interactive. See SCHED_BATCH in sched(7) for details. diff --git a/src/utilmemory.h b/src/utilmemory.h new file mode 100644 index 0000000000..e71fe92284 --- /dev/null +++ b/src/utilmemory.h @@ -0,0 +1,19 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2018 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_UTILMEMORY_H +#define BITCOIN_UTILMEMORY_H + +#include <memory> +#include <utility> + +//! Substitute for C++14 std::make_unique. +template <typename T, typename... Args> +std::unique_ptr<T> MakeUnique(Args&&... args) +{ + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); +} + +#endif diff --git a/src/validation.cpp b/src/validation.cpp index 811530d40e..a30f1fd0ce 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -806,13 +806,11 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // be increased is also an easy-to-reason about way to prevent // DoS attacks via replacements. // - // The mining code doesn't (currently) take children into - // account (CPFP) so we only consider the feerates of - // transactions being directly replaced, not their indirect - // descendants. While that does mean high feerate children are - // ignored when deciding whether or not to replace, we do - // require the replacement to pay more overall fees too, - // mitigating most cases. + // We only consider the feerates of transactions being directly + // replaced, not their indirect descendants. While that does + // mean high feerate children are ignored when deciding whether + // or not to replace, we do require the replacement to pay more + // overall fees too, mitigating most cases. CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize()); if (newFeeRate <= oldFeeRate) { diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index 801c4b87a0..52f6482d56 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -47,6 +47,7 @@ from test_framework.script import ( CScript, CScriptNum, CScriptOp, + MAX_SCRIPT_ELEMENT_SIZE, OP_0, OP_1, OP_16, @@ -1129,8 +1130,6 @@ class SegWitTest(BitcoinTestFramework): def test_max_witness_push_length(self): """Test that witness stack can only allow up to 520 byte pushes.""" - MAX_SCRIPT_ELEMENT_SIZE = 520 - block = self.build_next_block() witness_program = CScript([OP_DROP, OP_TRUE]) diff --git a/test/functional/rpc_scantxoutset.py b/test/functional/rpc_scantxoutset.py new file mode 100755 index 0000000000..ce5d4da9e7 --- /dev/null +++ b/test/functional/rpc_scantxoutset.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# Copyright (c) 2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the scantxoutset rpc call.""" +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +import shutil +import os + +class ScantxoutsetTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.setup_clean_chain = True + def run_test(self): + self.log.info("Mining blocks...") + self.nodes[0].generate(110) + + addr_P2SH_SEGWIT = self.nodes[0].getnewaddress("", "p2sh-segwit") + pubk1 = self.nodes[0].getaddressinfo(addr_P2SH_SEGWIT)['pubkey'] + addr_LEGACY = self.nodes[0].getnewaddress("", "legacy") + pubk2 = self.nodes[0].getaddressinfo(addr_LEGACY)['pubkey'] + addr_BECH32 = self.nodes[0].getnewaddress("", "bech32") + pubk3 = self.nodes[0].getaddressinfo(addr_BECH32)['pubkey'] + self.nodes[0].sendtoaddress(addr_P2SH_SEGWIT, 1) + self.nodes[0].sendtoaddress(addr_LEGACY, 2) + self.nodes[0].sendtoaddress(addr_BECH32, 3) + self.nodes[0].generate(1) + + self.log.info("Stop node, remove wallet, mine again some blocks...") + self.stop_node(0) + shutil.rmtree(os.path.join(self.nodes[0].datadir, "regtest", 'wallets')) + self.start_node(0) + self.nodes[0].generate(110) + + self.restart_node(0, ['-nowallet']) + self.log.info("Test if we have found the non HD unspent outputs.") + assert_equal(self.nodes[0].scantxoutset("start", [ {"pubkey": {"pubkey": pubk1}}, {"pubkey": {"pubkey": pubk2}}, {"pubkey": {"pubkey": pubk3}}])['total_amount'], 6) + assert_equal(self.nodes[0].scantxoutset("start", [ {"address": addr_P2SH_SEGWIT}, {"address": addr_LEGACY}, {"address": addr_BECH32}])['total_amount'], 6) + assert_equal(self.nodes[0].scantxoutset("start", [ {"address": addr_P2SH_SEGWIT}, {"address": addr_LEGACY}, {"pubkey": {"pubkey": pubk3}} ])['total_amount'], 6) + + self.log.info("Test invalid parameters.") + assert_raises_rpc_error(-8, 'Scanobject "pubkey" must contain an object as value', self.nodes[0].scantxoutset, "start", [ {"pubkey": pubk1}]) #missing pubkey object + assert_raises_rpc_error(-8, 'Scanobject "address" must contain a single string as value', self.nodes[0].scantxoutset, "start", [ {"address": {"address": addr_P2SH_SEGWIT}}]) #invalid object for address object + +if __name__ == '__main__': + ScantxoutsetTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 49833e5dd4..5d039d7bc4 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -142,6 +142,7 @@ BASE_SCRIPTS = [ 'feature_uacomment.py', 'p2p_unrequested_blocks.py', 'feature_includeconf.py', + 'rpc_scantxoutset.py', 'feature_logging.py', 'p2p_node_network_limited.py', 'feature_blocksdir.py', |