diff options
439 files changed, 8688 insertions, 5301 deletions
diff --git a/.travis.yml b/.travis.yml index dadac3165e..0de7ca6f75 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,11 +24,13 @@ env: # ARM - HOST=arm-linux-gnueabihf PACKAGES="g++-arm-linux-gnueabihf" DEP_OPTS="NO_QT=1" CHECK_DOC=1 GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" # Win32 - - HOST=i686-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="python3 nsis g++-mingw-w64-i686 wine1.6 bc" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports" + - HOST=i686-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="python3 nsis g++-mingw-w64-i686 wine1.6" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports" +# Qt4 & system libs + - HOST=x86_64-unknown-linux-gnu PACKAGES="python3-zmq qt4-dev-tools libssl-dev libevent-dev bsdmainutils libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-program-options-dev libboost-test-dev libboost-thread-dev libdb5.1++-dev libminiupnpc-dev libzmq3-dev libprotobuf-dev protobuf-compiler libqrencode-dev xvfb" NO_DEPENDS=1 NEED_XVFB=1 RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --with-incompatible-bdb --enable-glibc-back-compat --enable-reduce-exports --with-gui=qt4 CPPFLAGS=-DDEBUG_LOCKORDER" # 32-bit + dash - - HOST=i686-pc-linux-gnu PACKAGES="g++-multilib bc python3-zmq" DEP_OPTS="NO_QT=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++" USE_SHELL="/bin/dash" + - HOST=i686-pc-linux-gnu PACKAGES="g++-multilib python3-zmq" DEP_OPTS="NO_QT=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++" USE_SHELL="/bin/dash" # Win64 - - HOST=x86_64-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine1.6 bc" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports" + - HOST=x86_64-w64-mingw32 DPKG_ADD_ARCH="i386" DEP_OPTS="NO_QT=1" PACKAGES="python3 nsis g++-mingw-w64-x86-64 wine1.6" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports" # x86_64 Linux (uses qt5 dev package instead of depends Qt to speed up build and avoid timeout) - HOST=x86_64-unknown-linux-gnu PACKAGES="python3-zmq qtbase5-dev qttools5-dev-tools protobuf-compiler libdbus-1-dev libharfbuzz-dev" DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1 ALLOW_HOST_PACKAGES=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --with-gui=qt5 --enable-glibc-back-compat --enable-reduce-exports CPPFLAGS=-DDEBUG_LOCKORDER" # x86_64 Linux, No wallet @@ -38,18 +40,23 @@ env: before_install: - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") + - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/pyenv/d' | tr "\n" ":" | sed "s|::|:|g") install: - if [ -n "$DPKG_ADD_ARCH" ]; then sudo dpkg --add-architecture "$DPKG_ADD_ARCH" ; fi - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get update; fi - if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES; fi before_script: - - if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/commit-script-check.sh $TRAVIS_COMMIT_RANGE; fi - - unset CC; unset CXX + - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/commit-script-check.sh $TRAVIS_COMMIT_RANGE; fi - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/check-doc.py; fi + - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/check-rpc-mappings.py .; fi + - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/lint-all.sh; fi + - unset CC; unset CXX - mkdir -p depends/SDKs depends/sdk-sources - if [ -n "$OSX_SDK" -a ! -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then curl --location --fail $SDK_URL/MacOSX${OSX_SDK}.sdk.tar.gz -o depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi - if [ -n "$OSX_SDK" -a -f depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz ]; then tar -C depends/SDKs -xf depends/sdk-sources/MacOSX${OSX_SDK}.sdk.tar.gz; fi - - make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS + - if [ -z "$NO_DEPENDS" ]; then make $MAKEJOBS -C depends HOST=$HOST $DEP_OPTS; fi + # Start xvfb if needed, as documented at https://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-xvfb-to-Run-Tests-That-Require-a-GUI + - if [ "$NEED_XVFB" = 1 ]; then export DISPLAY=:99.0; /sbin/start-stop-daemon --start --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac; fi script: - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then while read LINE; do travis_retry gpg --keyserver hkp://subset.pool.sks-keyservers.net --recv-keys $LINE; done < contrib/verify-commits/trusted-keys; fi - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then git fetch --unshallow; fi @@ -58,7 +65,7 @@ script: - if [ -n "$USE_SHELL" ]; then export CONFIG_SHELL="$USE_SHELL"; fi - OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST - BITCOIN_CONFIG_ALL="--disable-dependency-tracking --prefix=$TRAVIS_BUILD_DIR/depends/$HOST --bindir=$OUTDIR/bin --libdir=$OUTDIR/lib" - - depends/$HOST/native/bin/ccache --max-size=$CCACHE_SIZE + - if [ -z "$NO_DEPENDS" ]; then depends/$HOST/native/bin/ccache --max-size=$CCACHE_SIZE; fi - test -n "$USE_SHELL" && eval '"$USE_SHELL" -c "./autogen.sh"' || ./autogen.sh - mkdir build && cd build - ../configure --cache-file=config.cache $BITCOIN_CONFIG_ALL $BITCOIN_CONFIG || ( cat config.log && false) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4e67af6278..aed6d79542 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,6 +81,10 @@ Examples: Qt: Add feed bump button Trivial: Fix typo in init.cpp +Note that translations should not be submitted as pull requests, please see +[Translation Process](https://github.com/bitcoin/bitcoin/blob/master/doc/translation_process.md) +for more information on helping with translations. + If a pull request is specifically not to be considered for merging (yet) please prefix the title with [WIP] or use [Tasks Lists](https://help.github.com/articles/basic-writing-and-formatting-syntax/#task-lists) in the body of the pull request to indicate tasks are pending. @@ -153,6 +157,14 @@ behaviour of code within the pull request (bugs must be preserved as is). Project maintainers aim for a quick turnaround on refactoring pull requests, so where possible keep them short, uncomplex and easy to verify. +Pull requests that refactor the code should not be made by new contributors. It +requires a certain level of experience to know where the code belongs to and to +understand the full ramification (including rebase effort of open pull requests). + +Trivial pull requests or pull requests that refactor the code with no clear +benefits may be immediately closed by the maintainers to reduce unnecessary +workload on reviewing. + "Decision Making" Process ------------------------- @@ -1,6 +1,7 @@ The MIT License (MIT) Copyright (c) 2009-2017 The Bitcoin Core developers +Copyright (c) 2009-2017 Bitcoin Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile.am b/Makefile.am index 8216b7d608..3b62a10603 100644 --- a/Makefile.am +++ b/Makefile.am @@ -249,6 +249,7 @@ EXTRA_DIST += \ test/util/data/txcreatemultisig3.json \ test/util/data/txcreatemultisig4.hex \ test/util/data/txcreatemultisig4.json \ + test/util/data/txcreatemultisig5.json \ test/util/data/txcreateoutpubkey1.hex \ test/util/data/txcreateoutpubkey1.json \ test/util/data/txcreateoutpubkey2.hex \ diff --git a/configure.ac b/configure.ac index e5ed938947..81c84a8af4 100644 --- a/configure.ac +++ b/configure.ac @@ -177,14 +177,14 @@ AC_ARG_ENABLE([glibc-back-compat], [use_glibc_compat=$enableval], [use_glibc_compat=no]) -AC_ARG_ENABLE([experimental-asm], - [AS_HELP_STRING([--enable-experimental-asm], - [Enable experimental assembly routines (default is no)])], - [experimental_asm=$enableval], - [experimental_asm=no]) - -if test "x$experimental_asm" = xyes; then - AC_DEFINE(EXPERIMENTAL_ASM, 1, [Define this symbol to build in experimental assembly routines]) +AC_ARG_ENABLE([asm], + [AS_HELP_STRING([--enable-asm], + [Enable assembly routines (default is yes)])], + [use_asm=$enableval], + [use_asm=yes]) + +if test "x$use_asm" = xyes; then + AC_DEFINE(USE_ASM, 1, [Define this symbol to build in assembly routines]) fi AC_ARG_WITH([system-univalue], @@ -827,14 +827,14 @@ TEMP_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" AC_MSG_CHECKING([for mismatched boost c++11 scoped enums]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ - #include "boost/config.hpp" - #include "boost/version.hpp" + #include <boost/config.hpp> + #include <boost/version.hpp> #if !defined(BOOST_NO_SCOPED_ENUMS) && !defined(BOOST_NO_CXX11_SCOPED_ENUMS) && BOOST_VERSION < 105700 #define BOOST_NO_SCOPED_ENUMS #define BOOST_NO_CXX11_SCOPED_ENUMS #define CHECK #endif - #include "boost/filesystem.hpp" + #include <boost/filesystem.hpp> ]],[[ #if defined(CHECK) boost::filesystem::copy_file("foo", "bar"); @@ -1179,7 +1179,7 @@ AM_CONDITIONAL([USE_LCOV],[test x$use_lcov = xyes]) AM_CONDITIONAL([GLIBC_BACK_COMPAT],[test x$use_glibc_compat = xyes]) AM_CONDITIONAL([HARDEN],[test x$use_hardening = xyes]) AM_CONDITIONAL([ENABLE_HWCRC32],[test x$enable_hwcrc32 = xyes]) -AM_CONDITIONAL([EXPERIMENTAL_ASM],[test x$experimental_asm = xyes]) +AM_CONDITIONAL([USE_ASM],[test x$use_asm = xyes]) AC_DEFINE(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR, [Major version]) AC_DEFINE(CLIENT_VERSION_MINOR, _CLIENT_VERSION_MINOR, [Minor version]) @@ -1231,6 +1231,7 @@ AC_SUBST(QR_LIBS) AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist test/config.ini]) AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh]) AC_CONFIG_FILES([doc/Doxyfile]) +AC_CONFIG_LINKS([contrib/filter-lcov.py:contrib/filter-lcov.py]) AC_CONFIG_LINKS([test/functional/test_runner.py:test/functional/test_runner.py]) AC_CONFIG_LINKS([test/util/bitcoin-util-test.py:test/util/bitcoin-util-test.py]) @@ -1297,6 +1298,7 @@ echo " with zmq = $use_zmq" echo " with test = $use_tests" echo " with bench = $use_bench" echo " with upnp = $use_upnp" +echo " use asm = $use_asm" echo " debug enabled = $enable_debug" echo " werror = $enable_werror" echo diff --git a/contrib/README.md b/contrib/README.md index 34c4cfc55a..a582a724f7 100644 --- a/contrib/README.md +++ b/contrib/README.md @@ -26,7 +26,7 @@ Contains files used to package bitcoind/bitcoin-qt for Debian-based Linux systems. If you compile bitcoind/bitcoin-qt yourself, there are some useful files here. ### [Gitian-descriptors](/contrib/gitian-descriptors) ### -Notes on getting Gitian builds up and running using KVM. +Files used during the gitian build process. For more information about gitian, see the [the Bitcoin Core documentation repository](https://github.com/bitcoin-core/docs). ### [Gitian-keys](/contrib/gitian-keys) PGP keys used for signing Bitcoin Core [Gitian release](/doc/release-process.md) results. diff --git a/contrib/devtools/check-doc.py b/contrib/devtools/check-doc.py index 3b7a8f9a61..10c3bab03b 100755 --- a/contrib/devtools/check-doc.py +++ b/contrib/devtools/check-doc.py @@ -12,6 +12,7 @@ Author: @MarcoFalke from subprocess import check_output import re +import sys FOLDER_GREP = 'src' FOLDER_TEST = 'src/test/' @@ -21,7 +22,7 @@ CMD_GREP_DOCS = r"egrep -r -I 'HelpMessageOpt\(\"\-[^\"=]+?(=|\")' %s" % (CMD_RO REGEX_ARG = re.compile(r'(?:map(?:Multi)?Args(?:\.count\(|\[)|Get(?:Bool)?Arg\()\"(\-[^\"]+?)\"') REGEX_DOC = re.compile(r'HelpMessageOpt\(\"(\-[^\"=]+?)(?:=|\")') # 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']) +SET_DOC_OPTIONAL = set(['-rpcssl', '-benchmark', '-h', '-help', '-socks', '-tor', '-debugnet', '-whitelistalwaysrelay', '-prematurewitness', '-walletprematurewitness', '-promiscuousmempoolflags', '-blockminsize', '-dbcrashratio', '-forcecompactdb', '-usehd']) def main(): used = check_output(CMD_GREP_ARGS, shell=True) @@ -39,7 +40,7 @@ def main(): print "Args unknown : %s" % len(args_unknown) print args_unknown - exit(len(args_need_doc)) + sys.exit(len(args_need_doc)) if __name__ == "__main__": main() diff --git a/contrib/devtools/check-rpc-mappings.py b/contrib/devtools/check-rpc-mappings.py new file mode 100755 index 0000000000..7e96852c5c --- /dev/null +++ b/contrib/devtools/check-rpc-mappings.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Check RPC argument consistency.""" + +from collections import defaultdict +import os +import re +import sys + +# Source files (relative to root) to scan for dispatch tables +SOURCES = [ + "src/rpc/server.cpp", + "src/rpc/blockchain.cpp", + "src/rpc/mining.cpp", + "src/rpc/misc.cpp", + "src/rpc/net.cpp", + "src/rpc/rawtransaction.cpp", + "src/wallet/rpcwallet.cpp", +] +# Source file (relative to root) containing conversion mapping +SOURCE_CLIENT = 'src/rpc/client.cpp' +# Argument names that should be ignored in consistency checks +IGNORE_DUMMY_ARGS = {'dummy', 'arg0', 'arg1', 'arg2', 'arg3', 'arg4', 'arg5', 'arg6', 'arg7', 'arg8', 'arg9'} + +class RPCCommand: + def __init__(self, name, args): + self.name = name + self.args = args + +class RPCArgument: + def __init__(self, names, idx): + self.names = names + self.idx = idx + self.convert = False + +def parse_string(s): + assert s[0] == '"' + assert s[-1] == '"' + return s[1:-1] + +def process_commands(fname): + """Find and parse dispatch table in implementation file `fname`.""" + cmds = [] + in_rpcs = False + with open(fname, "r") as f: + for line in f: + line = line.rstrip() + if not in_rpcs: + if re.match("static const CRPCCommand .*\[\] =", line): + in_rpcs = True + else: + if line.startswith('};'): + in_rpcs = False + elif '{' in line and '"' in line: + m = re.search('{ *("[^"]*"), *("[^"]*"), *&([^,]*), *{([^}]*)} *},', line) + assert m, 'No match to table expression: %s' % line + name = parse_string(m.group(2)) + args_str = m.group(4).strip() + if args_str: + args = [RPCArgument(parse_string(x.strip()).split('|'), idx) for idx, x in enumerate(args_str.split(','))] + else: + args = [] + cmds.append(RPCCommand(name, args)) + assert not in_rpcs and cmds, "Something went wrong with parsing the C++ file: update the regexps" + return cmds + +def process_mapping(fname): + """Find and parse conversion table in implementation file `fname`.""" + cmds = [] + in_rpcs = False + with open(fname, "r") as f: + for line in f: + line = line.rstrip() + if not in_rpcs: + if line == 'static const CRPCConvertParam vRPCConvertParams[] =': + in_rpcs = True + else: + if line.startswith('};'): + in_rpcs = False + elif '{' in line and '"' in line: + m = re.search('{ *("[^"]*"), *([0-9]+) *, *("[^"]*") *},', line) + assert m, 'No match to table expression: %s' % line + name = parse_string(m.group(1)) + idx = int(m.group(2)) + argname = parse_string(m.group(3)) + cmds.append((name, idx, argname)) + assert not in_rpcs and cmds + return cmds + +def main(): + root = sys.argv[1] + + # Get all commands from dispatch tables + cmds = [] + for fname in SOURCES: + cmds += process_commands(os.path.join(root, fname)) + + cmds_by_name = {} + for cmd in cmds: + cmds_by_name[cmd.name] = cmd + + # Get current convert mapping for client + client = SOURCE_CLIENT + mapping = set(process_mapping(os.path.join(root, client))) + + print('* Checking consistency between dispatch tables and vRPCConvertParams') + + # Check mapping consistency + errors = 0 + for (cmdname, argidx, argname) in mapping: + try: + rargnames = cmds_by_name[cmdname].args[argidx].names + except IndexError: + print('ERROR: %s argument %i (named %s in vRPCConvertParams) is not defined in dispatch table' % (cmdname, argidx, argname)) + errors += 1 + continue + if argname not in rargnames: + print('ERROR: %s argument %i is named %s in vRPCConvertParams but %s in dispatch table' % (cmdname, argidx, argname, rargnames), file=sys.stderr) + errors += 1 + + # Check for conflicts in vRPCConvertParams conversion + # All aliases for an argument must either be present in the + # conversion table, or not. Anything in between means an oversight + # and some aliases won't work. + for cmd in cmds: + for arg in cmd.args: + convert = [((cmd.name, arg.idx, argname) in mapping) for argname in arg.names] + if any(convert) != all(convert): + print('ERROR: %s argument %s has conflicts in vRPCConvertParams conversion specifier %s' % (cmd.name, arg.names, convert)) + errors += 1 + arg.convert = all(convert) + + # Check for conversion difference by argument name. + # It is preferable for API consistency that arguments with the same name + # have the same conversion, so bin by argument name. + all_methods_by_argname = defaultdict(list) + converts_by_argname = defaultdict(list) + for cmd in cmds: + for arg in cmd.args: + for argname in arg.names: + all_methods_by_argname[argname].append(cmd.name) + converts_by_argname[argname].append(arg.convert) + + for argname, convert in converts_by_argname.items(): + if all(convert) != any(convert): + if argname in IGNORE_DUMMY_ARGS: + # these are testing or dummy, don't warn for them + continue + print('WARNING: conversion mismatch for argument named %s (%s)' % + (argname, list(zip(all_methods_by_argname[argname], converts_by_argname[argname])))) + + sys.exit(errors > 0) + + +if __name__ == '__main__': + main() diff --git a/contrib/devtools/github-merge.py b/contrib/devtools/github-merge.py index c664cf81fa..2941d2cb6d 100755 --- a/contrib/devtools/github-merge.py +++ b/contrib/devtools/github-merge.py @@ -20,6 +20,7 @@ from sys import stdin,stdout,stderr import argparse import hashlib import subprocess +import sys import json,codecs try: from urllib.request import Request,urlopen @@ -158,11 +159,11 @@ def main(): if repo is None: print("ERROR: No repository configured. Use this command to set:", file=stderr) print("git config githubmerge.repository <owner>/<repo>", file=stderr) - exit(1) + sys.exit(1) if signingkey is None: print("ERROR: No GPG signing key set. Set one using:",file=stderr) print("git config --global user.signingkey <key>",file=stderr) - exit(1) + sys.exit(1) host_repo = host+":"+repo # shortcut for push/pull target @@ -173,7 +174,7 @@ def main(): # Receive pull information from github info = retrieve_pr_info(repo,pull) if info is None: - exit(1) + sys.exit(1) title = info['title'].strip() body = info['body'].strip() # precedence order for destination branch argument: @@ -194,27 +195,23 @@ def main(): subprocess.check_call([GIT,'checkout','-q',branch]) except subprocess.CalledProcessError as e: print("ERROR: Cannot check out branch %s." % (branch), file=stderr) - exit(3) + sys.exit(3) try: - subprocess.check_call([GIT,'fetch','-q',host_repo,'+refs/pull/'+pull+'/*:refs/heads/pull/'+pull+'/*']) + subprocess.check_call([GIT,'fetch','-q',host_repo,'+refs/pull/'+pull+'/*:refs/heads/pull/'+pull+'/*', + '+refs/heads/'+branch+':refs/heads/'+base_branch]) except subprocess.CalledProcessError as e: - print("ERROR: Cannot find pull request #%s on %s." % (pull,host_repo), file=stderr) - exit(3) + print("ERROR: Cannot find pull request #%s or branch %s on %s." % (pull,branch,host_repo), file=stderr) + sys.exit(3) try: subprocess.check_call([GIT,'log','-q','-1','refs/heads/'+head_branch], stdout=devnull, stderr=stdout) except subprocess.CalledProcessError as e: print("ERROR: Cannot find head of pull request #%s on %s." % (pull,host_repo), file=stderr) - exit(3) + sys.exit(3) try: subprocess.check_call([GIT,'log','-q','-1','refs/heads/'+merge_branch], stdout=devnull, stderr=stdout) except subprocess.CalledProcessError as e: print("ERROR: Cannot find merge of pull request #%s on %s." % (pull,host_repo), file=stderr) - exit(3) - try: - subprocess.check_call([GIT,'fetch','-q',host_repo,'+refs/heads/'+branch+':refs/heads/'+base_branch]) - except subprocess.CalledProcessError as e: - print("ERROR: Cannot find branch %s on %s." % (branch,host_repo), file=stderr) - exit(3) + sys.exit(3) subprocess.check_call([GIT,'checkout','-q',base_branch]) subprocess.call([GIT,'branch','-q','-D',local_merge_branch], stderr=devnull) subprocess.check_call([GIT,'checkout','-q','-b',local_merge_branch]) @@ -236,30 +233,30 @@ def main(): except subprocess.CalledProcessError as e: print("ERROR: Cannot be merged cleanly.",file=stderr) subprocess.check_call([GIT,'merge','--abort']) - exit(4) + sys.exit(4) logmsg = subprocess.check_output([GIT,'log','--pretty=format:%s','-n','1']).decode('utf-8') if logmsg.rstrip() != firstline.rstrip(): print("ERROR: Creating merge failed (already merged?).",file=stderr) - exit(4) + sys.exit(4) symlink_files = get_symlink_files() for f in symlink_files: print("ERROR: File %s was a symlink" % f) if len(symlink_files) > 0: - exit(4) + sys.exit(4) # Put tree SHA512 into the message try: first_sha512 = tree_sha512sum() message += '\n\nTree-SHA512: ' + first_sha512 except subprocess.CalledProcessError as e: - printf("ERROR: Unable to compute tree hash") - exit(4) + print("ERROR: Unable to compute tree hash") + sys.exit(4) try: subprocess.check_call([GIT,'commit','--amend','-m',message.encode('utf-8')]) except subprocess.CalledProcessError as e: - printf("ERROR: Cannot update message.",file=stderr) - exit(4) + print("ERROR: Cannot update message.", file=stderr) + sys.exit(4) print_merge_details(pull, title, branch, base_branch, head_branch) print() @@ -268,7 +265,7 @@ def main(): if testcmd: if subprocess.call(testcmd,shell=True): print("ERROR: Running %s failed." % testcmd,file=stderr) - exit(5) + sys.exit(5) # Show the created merge. diff = subprocess.check_output([GIT,'diff',merge_branch+'..'+local_merge_branch]) @@ -279,7 +276,7 @@ def main(): if reply.lower() == 'ignore': print("Difference with github ignored.",file=stderr) else: - exit(6) + sys.exit(6) else: # Verify the result manually. print("Dropping you on a shell so you can try building/testing the merged source.",file=stderr) @@ -292,7 +289,7 @@ def main(): second_sha512 = tree_sha512sum() if first_sha512 != second_sha512: print("ERROR: Tree hash changed unexpectedly",file=stderr) - exit(8) + sys.exit(8) # Sign the merge commit. print_merge_details(pull, title, branch, base_branch, head_branch) @@ -306,7 +303,7 @@ def main(): print("Error while signing, asking again.",file=stderr) elif reply == 'x': print("Not signing off on merge, exiting.",file=stderr) - exit(1) + sys.exit(1) # Put the result in branch. subprocess.check_call([GIT,'checkout','-q',branch]) @@ -326,7 +323,7 @@ def main(): subprocess.check_call([GIT,'push',host_repo,'refs/heads/'+branch]) break elif reply == 'x': - exit(1) + sys.exit(1) if __name__ == '__main__': main() diff --git a/contrib/devtools/lint-all.sh b/contrib/devtools/lint-all.sh new file mode 100755 index 0000000000..b6d86959c6 --- /dev/null +++ b/contrib/devtools/lint-all.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# This script runs all contrib/devtools/lint-*.sh files, and fails if any exit +# with a non-zero status code. + +set -u + +SCRIPTDIR=$(dirname "${BASH_SOURCE[0]}") +LINTALL=$(basename "${BASH_SOURCE[0]}") + +for f in "${SCRIPTDIR}"/lint-*.sh; do + if [ "$(basename "$f")" != "$LINTALL" ]; then + if ! "$f"; then + echo "^---- failure generated from $f" + exit 1 + fi + fi +done diff --git a/contrib/devtools/lint-whitespace.sh b/contrib/devtools/lint-whitespace.sh new file mode 100755 index 0000000000..989923f31a --- /dev/null +++ b/contrib/devtools/lint-whitespace.sh @@ -0,0 +1,88 @@ +#!/bin/bash +# +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# Check for new lines in diff that introduce trailing whitespace. + +# We can't run this check unless we know the commit range for the PR. +if [ -z "${TRAVIS_COMMIT_RANGE}" ]; then + echo "Cannot run lint-whitespace.sh without commit range. To run locally, use:" + echo "TRAVIS_COMMIT_RANGE='<commit range>' .lint-whitespace.sh" + echo "For example:" + echo "TRAVIS_COMMIT_RANGE='47ba2c3...ee50c9e' .lint-whitespace.sh" + exit 1 +fi + +showdiff() { + if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- "." ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/"; then + echo "Failed to get a diff" + exit 1 + fi +} + +showcodediff() { + if ! git diff -U0 "${TRAVIS_COMMIT_RANGE}" -- *.cpp *.h *.md *.py *.sh ":(exclude)src/leveldb/" ":(exclude)src/secp256k1/" ":(exclude)src/univalue/" ":(exclude)doc/release-notes/"; then + echo "Failed to get a diff" + exit 1 + fi +} + +RET=0 + +# Check if trailing whitespace was found in the diff. +if showdiff | grep -E -q '^\+.*\s+$'; then + echo "This diff appears to have added new lines with trailing whitespace." + echo "The following changes were suspected:" + FILENAME="" + SEEN=0 + while read -r line; do + if [[ "$line" =~ ^diff ]]; then + FILENAME="$line" + SEEN=0 + elif [[ "$line" =~ ^@@ ]]; then + LINENUMBER="$line" + else + if [ "$SEEN" -eq 0 ]; then + # The first time a file is seen with trailing whitespace, we print the + # filename (preceded by a newline). + echo + echo "$FILENAME" + echo "$LINENUMBER" + SEEN=1 + fi + echo "$line" + fi + done < <(showdiff | grep -E '^(diff --git |@@|\+.*\s+$)') + RET=1 +fi + +# Check if tab characters were found in the diff. +if showcodediff | grep -P -q '^\+.*\t'; then + echo "This diff appears to have added new lines with tab characters instead of spaces." + echo "The following changes were suspected:" + FILENAME="" + SEEN=0 + while read -r line; do + if [[ "$line" =~ ^diff ]]; then + FILENAME="$line" + SEEN=0 + elif [[ "$line" =~ ^@@ ]]; then + LINENUMBER="$line" + else + if [ "$SEEN" -eq 0 ]; then + # The first time a file is seen with a tab character, we print the + # filename (preceded by a newline). + echo + echo "$FILENAME" + echo "$LINENUMBER" + SEEN=1 + fi + echo "$line" + fi + done < <(showcodediff | grep -P '^(diff --git |@@|\+.*\t)') + RET=1 +fi + +exit $RET diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py index c90541e271..6eb5667453 100755 --- a/contrib/devtools/security-check.py +++ b/contrib/devtools/security-check.py @@ -212,5 +212,5 @@ if __name__ == '__main__': except IOError: print('%s: cannot open' % filename) retval = 1 - exit(retval) + sys.exit(retval) diff --git a/contrib/devtools/symbol-check.py b/contrib/devtools/symbol-check.py index 8f8685006e..98daa1bd76 100755 --- a/contrib/devtools/symbol-check.py +++ b/contrib/devtools/symbol-check.py @@ -159,6 +159,6 @@ if __name__ == '__main__': print('%s: NEEDED library %s is not allowed' % (filename, library_name.decode('utf-8'))) retval = 1 - exit(retval) + sys.exit(retval) diff --git a/contrib/devtools/update-translations.py b/contrib/devtools/update-translations.py index 2011841005..e1924749d2 100755 --- a/contrib/devtools/update-translations.py +++ b/contrib/devtools/update-translations.py @@ -36,12 +36,12 @@ def check_at_repository_root(): if not os.path.exists('.git'): print('No .git directory found') print('Execute this script at the root of the repository', file=sys.stderr) - exit(1) + sys.exit(1) def fetch_all_translations(): if subprocess.call([TX, 'pull', '-f', '-a']): print('Error while fetching translations', file=sys.stderr) - exit(1) + sys.exit(1) def find_format_specifiers(s): '''Find all format specifiers in a string.''' diff --git a/contrib/gitian-build.sh b/contrib/gitian-build.sh index d94c7f4f8e..8fdec21b0e 100755 --- a/contrib/gitian-build.sh +++ b/contrib/gitian-build.sh @@ -179,8 +179,6 @@ done if [[ $lxc = true ]] then export USE_LXC=1 - export LXC_BRIDGE=lxcbr0 - sudo ifconfig lxcbr0 up 10.0.2.2 fi # Check for OSX SDK diff --git a/contrib/gitian-descriptors/README.md b/contrib/gitian-descriptors/README.md deleted file mode 100644 index d9dbfd3cb3..0000000000 --- a/contrib/gitian-descriptors/README.md +++ /dev/null @@ -1,65 +0,0 @@ -### Gavin's notes on getting Gitian builds up and running using KVM - -These instructions distilled from -[https://help.ubuntu.com/community/KVM/Installation](https://help.ubuntu.com/community/KVM/Installation). - -You need the right hardware: you need a 64-bit-capable CPU with hardware virtualization support (Intel VT-x or AMD-V). Not all modern CPUs support hardware virtualization. - -You probably need to enable hardware virtualization in your machine's BIOS. - -You need to be running a recent version of 64-bit-Ubuntu, and you need to install several prerequisites: - - sudo apt-get install ruby apache2 git apt-cacher-ng python-vm-builder qemu-kvm - -Sanity checks: - - sudo service apt-cacher-ng status # Should return apt-cacher-ng is running - ls -l /dev/kvm # Should show a /dev/kvm device - - -Once you've got the right hardware and software: - - git clone git://github.com/bitcoin/bitcoin.git - git clone git://github.com/devrandom/gitian-builder.git - mkdir gitian-builder/inputs - cd gitian-builder/inputs - - # Create base images - cd gitian-builder - bin/make-base-vm --suite trusty --arch amd64 - cd .. - - # Get inputs (see doc/release-process.md for exact inputs needed and where to get them) - ... - - # For further build instructions see doc/release-process.md - ... - ---------------------- - -`gitian-builder` now also supports building using LXC. See -[help.ubuntu.com](https://help.ubuntu.com/14.04/serverguide/lxc.html) -for how to get LXC up and running under Ubuntu. - -If your main machine is a 64-bit Mac or PC with a few gigabytes of memory -and at least 10 gigabytes of free disk space, you can `gitian-build` using -LXC running inside a virtual machine. - -Here's a description of Gavin's setup on OSX 10.6: - -1. Download and install VirtualBox from [https://www.virtualbox.org/](https://www.virtualbox.org/) - -2. Download the 64-bit Ubuntu Desktop 12.04 LTS .iso CD image from - [http://www.ubuntu.com/](http://www.ubuntu.com/) - -3. Run VirtualBox and create a new virtual machine, using the Ubuntu .iso (see the [VirtualBox documentation](https://www.virtualbox.org/wiki/Documentation) for details). Create it with at least 2 gigabytes of memory and a disk that is at least 20 gigabytes big. - -4. Inside the running Ubuntu desktop, install: - - sudo apt-get install debootstrap lxc ruby apache2 git apt-cacher-ng python-vm-builder - -5. Still inside Ubuntu, tell gitian-builder to use LXC, then follow the "Once you've got the right hardware and software" instructions above: - - export USE_LXC=1 - git clone git://github.com/bitcoin/bitcoin.git - ... etc diff --git a/contrib/gitian-keys/meshcollider-key.pgp b/contrib/gitian-keys/meshcollider-key.pgp new file mode 100644 index 0000000000..20963e7e25 --- /dev/null +++ b/contrib/gitian-keys/meshcollider-key.pgp @@ -0,0 +1,51 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBFlm5UcBEADFhn2Tcfr7gtsLRj9dzHGPoZYjc8Jy7wceqT8918lqaULJKgDW +vkEWCVOHRlrr/h1ugldouTRv3k8cdzhCR9YBakVJ3vBmn73CvHQl57jGRSogyqm5 +hb6IXJkBdualnZVFvCDV37VYeyuSYkJ+DL3c2wEjC2gdQKUsc8ePrJZZEMJVScdD +hoXR/sPnu8P5yHOi56XGJi9395GUmmxJKNucD4HXjSq+7yTTs5GXm4niaKfcKyBy +kIGN4aEeV8sqzkN8JzNH9fc8i8MPDYLW7SGljpLSnIvIsdBRjXXBHwRnfmGEO7lF +sVTyepUUYX3GhLcCNhZjoMkpagVjSpQPj1gylSM4EFkmU2AgK/iEzqB7Ay4WC8EE +E2HrcN0ysjyhuyntFwMa1cze99vtfOIQnVJ8E58AvsOs9+xYz8DkbYntCHDD+Zcv +y200/knT1jJSZMXkiDciLjGSeFFbh6H+VpaFUKjy3G3yJC4BTXwnACga5/WPsgmK ++Y9gpTXRsZ8Op2teiwl8wI85mNF+2QmQw3uvymfojI8YPmjx2LOCbzkFYIJt20nw +iP1QMH3vtk+iSbcnexQlOPh03ZtDp3NbkBvBOy7cOc57Nc6IX7TllZicQj0FUjWq +ctUAU+f5pQuVgS8H3B4XE+Pk1u6/5zX9H0sTi0LzeQ0OdWFcvmZ8mYK5lQARAQAB +tCNNZXNoQ29sbGlkZXIgPGRvYnNvbnNhNjhAZ21haWwuY29tPokCOAQTAQgALAUC +WWblRwkQ0wARbhyHWj0CGwMFCR4TOAACGQEECwcJAwUVCAoCAwQWAAECAABJ2Q/8 +D6FMutVLsz55vwy2FjWojcvSpk+BV50YMGYTCdnXZod7V0dP1iQ5+NMcYfpWgJKM +YbJ2eaWpW2TgsBd12LTjA6BKX3FquN8Y3nKZiknGCLiimDiys0+VuO9ieEH0flhC +olhGysRmPO5clNmZOzn3yiPgUekw6ejLVUEY8vPCbjojSuLZyjctQR3s/9oOMyvm +tldJ0waLi3KSOPEDQ8gXfE0QfDf2eMTdlMkbOHS6BlDIre6P5RZ5IJaLwCdzne+W +aS96CUqVcR3aqil4mG+T+kHf1wF99TZwY+tSXtweGENjc+QGEaR30to+catSc0nz +KQi3dGCH2Y+rc4VHE1S2Id88M38883mHXUeDMqzV9mHwMA50r/jzcLPybrJA1Qhn +ZQNWr8zGilmZfWnf2VyiPqZCIAEEFcwg6uNg9Rwy2N3Q/5+vhAVcVNJamMA/dpHa +hnq8HmZjraPWHL5Q9oL3Ggtc1Jahb8skaUMV26PHkXOxNFhVynghw3ujC3mocKqQ +stmsg+2m5Wf+TZtmbd8geMWcRpuxovYX2ZmeFPWIU+6p9XpwyiPR4mp5hWn/20dQ +YAyN/cQhWjDRU2i/HJB1lVnQIsSVsy3eWUJk4htQNHmk8crYocsXb5hgQ2C+JZ0L +gY2AxoGjqtzKkydTd5GbiCmqqFdW9ngmVerZ6yCbyRK5Ag0EWWblRwEQALdMSVUR +fCXTW2zCiP7g0Aj6yvyi1Wg1zK0CeRRljXKfgFoAI6IGW9QSSpXPmdsnAQOf7L0Q +wTTqwWNhKOLV0IWLenbpgIVwfLMkrwn71q9MBJFHiL+PgZLaRxqF5pmW34ZReUj5 +k55Bg49kB98rfyz9K6qNxKLzY0j/8zsCvCgDMpr7U61xfz8Xo3Ds8bRdaFoH3RWR +wm3ePTe/8Rpm/LWWLlzhnfTpyZCUcOPh5+2yt0twHQ5zlzj7Gp8Il8XNlP6hvfx3 +QGDuFTQ++Utom7T3QLa5E5Yx2iTD7qaNLdpQLZmcHUvdQV0QWSILccEvSJ+vXiE0 +NvlgQIAE1pUuyTGpm97+mBeDC+4PvXUxQqFoOTJiwJxCpIAA0yvloUaZyeT0Toar +mowVOn0JXfbZRFFdxNUXgz9RbzANB+twGJ/ySh3mQz+Mur/1HqnCpHEjy73yOA9e +alN2LNvJt92hMdq+QU7I0bNqUS456h6Ft6mOpqG2y57qpl8ZL/MIvMaw3s45hA6p +7gzi7/TOnoqAkDUPf7lRbYjGgLkcGlimRxyL1SAYKuFgpNnhxk6BNPKdly7MRWF5 +I+oUc5W7HkNefbHw5sdLgYZBQk8JoSwF1K/ES5gvJHWZjCiLAcbyum2W843etfU3 +Qa/3YNt4Gri5zhAoD7U2kAs1ct3hQ6cLmDrxABEBAAGJAjUEGAEIACkFAllm5UcJ +ENMAEW4ch1o9AhsMBQkeEzgABAsHCQMFFQgKAgMEFgABAgAAWWcP/1ErBIqJ+SFZ +bL3YyLB9iObLEAUxNQP8bEV6lI9V0XUBhReasxQrMUFEXsFoFU6i/qlyfQFsBN8J +2QJFJT1pNE+Pleuz4yMuK5Ddcuuyl9ZklfEclmkLpSEwapFMm9IOgaGhucBMpvkC +2FE05oc0dEyTCdt1rBppGXvx2aw1khSiuWU13bWXw4hWfJaYKDKdTQyJLsjKGe0u +qjaR6yHWHbjlchQWKGUWLHomTKG6wZx9k5YbEy5LN7HnyCHos4SiWyaSpXSjCtNn +15i0JdH68fpKAtaGtkUYtoEJIg8qg7u4B6wM70BK2WCZr8T5yWK0c7NrojMIYjEu +HwEA9XPkcF9TF7V1VOZMze1ZOWSNzGOfq1yJf6hpUNrw+B3TbYsqJkuJmVSYoamH +0QBy0sHxlUtsALMnuKIQt8Sp20bJZLwpudXF+ZSRwrjmYc2RMc5AWaBHTGz2IGte +AvH+SOOaRWj+UvhSFZVKVOZHWqErzKG+NfqQzEaEL4h/6QU64h5GLhocYHCiCbFm +X1t01eKoDfOhwQlSlPjpDxxr7yi60ntt1R7DpgwqMNIdyXylxsc4SCvM6NDRXVM1 +DoaPHI7GRuT1w6zEtkCGQoGsy1OBrnjeIy40mzh8L5q8L7n3jNtN/d6YCYDzP/P6 +gb52/WPhR6CJQ2/7y3Uj7u8sPNpb7BGI +=as33 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/contrib/init/org.bitcoin.bitcoind.plist b/contrib/init/org.bitcoin.bitcoind.plist index e94cd4466d..95b5342f1e 100644 --- a/contrib/init/org.bitcoin.bitcoind.plist +++ b/contrib/init/org.bitcoin.bitcoind.plist @@ -7,7 +7,6 @@ <key>ProgramArguments</key> <array> <string>/usr/local/bin/bitcoind</string> - <string>-daemon</string> </array> <key>RunAtLoad</key> <true/> diff --git a/contrib/linearize/example-linearize.cfg b/contrib/linearize/example-linearize.cfg index d019b06b6c..2315898bf1 100644 --- a/contrib/linearize/example-linearize.cfg +++ b/contrib/linearize/example-linearize.cfg @@ -3,9 +3,16 @@ rpcuser=someuser rpcpassword=somepassword #datadir=~/.bitcoin host=127.0.0.1 + +#mainnet default port=8332 + +#testnet default #port=18332 +#regtest default +#port=18443 + # bootstrap.dat hashlist settings (linearize-hashes) max_height=313000 diff --git a/contrib/linearize/linearize-hashes.py b/contrib/linearize/linearize-hashes.py index db8eb7021e..58fec6dddc 100755 --- a/contrib/linearize/linearize-hashes.py +++ b/contrib/linearize/linearize-hashes.py @@ -87,7 +87,7 @@ def get_block_hashes(settings, max_blocks_per_call=10000): for x,resp_obj in enumerate(reply): if rpc.response_is_error(resp_obj): print('JSON-RPC: error at height', height+x, ': ', resp_obj['error'], file=sys.stderr) - exit(1) + sys.exit(1) assert(resp_obj['id'] == x) # assume replies are in-sequence if settings['rev_hash_bytes'] == 'true': resp_obj['result'] = hex_switchEndian(resp_obj['result']) @@ -140,7 +140,7 @@ if __name__ == '__main__': if 'datadir' in settings and not use_userpass: use_datadir = True if not use_userpass and not use_datadir: - print("Missing datadir or username and/or password in cfg file", file=stderr) + print("Missing datadir or username and/or password in cfg file", file=sys.stderr) sys.exit(1) settings['port'] = int(settings['port']) diff --git a/contrib/rpm/bitcoin.spec b/contrib/rpm/bitcoin.spec index cc54fcaf3d..7c4d933ee0 100644 --- a/contrib/rpm/bitcoin.spec +++ b/contrib/rpm/bitcoin.spec @@ -336,6 +336,8 @@ done %{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 8333 %{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 18332 %{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 18333 +%{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 18443 +%{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 18444 %{_sbindir}/fixfiles -R bitcoin-server restore &> /dev/null || : %{_sbindir}/restorecon -R %{_localstatedir}/lib/bitcoin || : fi @@ -355,6 +357,8 @@ if [ $1 -eq 0 ]; then %{_sbindir}/semanage port -d -p tcp 8333 %{_sbindir}/semanage port -d -p tcp 18332 %{_sbindir}/semanage port -d -p tcp 18333 + %{_sbindir}/semanage port -d -p tcp 18443 + %{_sbindir}/semanage port -d -p tcp 18444 for selinuxvariant in %{selinux_variants}; do %{_sbindir}/semodule -s ${selinuxvariant} -r bitcoin &> /dev/null || : done diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py index b0ac92ae03..28068a7523 100755 --- a/contrib/seeds/generate-seeds.py +++ b/contrib/seeds/generate-seeds.py @@ -114,7 +114,7 @@ def process_nodes(g, f, structname, defaultport): def main(): if len(sys.argv)<2: print(('Usage: %s <path_to_nodes_txt>' % sys.argv[0]), file=sys.stderr) - exit(1) + sys.exit(1) g = sys.stdout indir = sys.argv[1] g.write('#ifndef BITCOIN_CHAINPARAMSSEEDS_H\n') diff --git a/contrib/zmq/zmq_sub.py b/contrib/zmq/zmq_sub.py index ea398a27ea..5cc19761d4 100755 --- a/contrib/zmq/zmq_sub.py +++ b/contrib/zmq/zmq_sub.py @@ -32,7 +32,7 @@ import sys if not (sys.version_info.major >= 3 and sys.version_info.minor >= 5): print("This example only works with Python 3.5 and greater") - exit(1) + sys.exit(1) port = 28332 diff --git a/contrib/zmq/zmq_sub3.4.py b/contrib/zmq/zmq_sub3.4.py index 1cb7eec0c0..bfb7ea9eae 100755 --- a/contrib/zmq/zmq_sub3.4.py +++ b/contrib/zmq/zmq_sub3.4.py @@ -36,7 +36,7 @@ import sys if not (sys.version_info.major >= 3 and sys.version_info.minor >= 4): print("This example only works with Python 3.4 and greater") - exit(1) + sys.exit(1) port = 28332 diff --git a/depends/packages/libevent.mk b/depends/packages/libevent.mk index 00231d75d5..5f622f8e6e 100644 --- a/depends/packages/libevent.mk +++ b/depends/packages/libevent.mk @@ -9,7 +9,7 @@ define $(package)_preprocess_cmds endef define $(package)_set_vars - $(package)_config_opts=--disable-shared --disable-openssl --disable-libevent-regress + $(package)_config_opts=--disable-shared --disable-openssl --disable-libevent-regress --disable-samples $(package)_config_opts_release=--disable-debug-mode $(package)_config_opts_linux=--with-pic endef diff --git a/depends/packages/native_ds_store.mk b/depends/packages/native_ds_store.mk index 49f5829ac1..116fa25d38 100644 --- a/depends/packages/native_ds_store.mk +++ b/depends/packages/native_ds_store.mk @@ -1,9 +1,8 @@ package=native_ds_store -$(package)_version=1.1.0 -$(package)_download_path=https://bitbucket.org/al45tair/ds_store/get -$(package)_download_file=v$($(package)_version).tar.bz2 -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=921596764d71d1bbd3297a90ef6d286f718794d667e4f81d91d14053525d64c1 +$(package)_version=1.1.2 +$(package)_download_path=https://github.com/al45tair/ds_store/archive/ +$(package)_file_name=v$($(package)_version).tar.gz +$(package)_sha256_hash=3b3ecb7bf0a5157f5b6010bc3af7c141fb0ad3527084e63336220d22744bc20c $(package)_install_libdir=$(build_prefix)/lib/python/dist-packages $(package)_dependencies=native_biplist diff --git a/depends/packages/native_mac_alias.mk b/depends/packages/native_mac_alias.mk index 85a8a402bf..488ec8b59c 100644 --- a/depends/packages/native_mac_alias.mk +++ b/depends/packages/native_mac_alias.mk @@ -1,14 +1,13 @@ package=native_mac_alias -$(package)_version=1.1.0 -$(package)_download_path=https://bitbucket.org/al45tair/mac_alias/get -$(package)_download_file=v$($(package)_version).tar.bz2 -$(package)_file_name=$(package)-$($(package)_version).tar.bz2 -$(package)_sha256_hash=87ad827e66790028361e43fc754f68ed041a9bdb214cca03c853f079b04fb120 +$(package)_version=2.0.6 +$(package)_download_path=https://github.com/al45tair/mac_alias/archive/ +$(package)_file_name=v$($(package)_version).tar.gz +$(package)_sha256_hash=78a3332d9a597eebf09ae652d38ad1e263b28db5c2e6dd725fad357b03b77371 $(package)_install_libdir=$(build_prefix)/lib/python/dist-packages $(package)_patches=python3.patch define $(package)_preprocess_cmds - patch -p1 < $($(package)_patch_dir)/python3.patch + patch -p1 < $($(package)_patch_dir)/python3.patch endef define $(package)_build_cmds diff --git a/depends/patches/native_mac_alias/python3.patch b/depends/patches/native_mac_alias/python3.patch index 1a32340be5..6f2f5534a2 100644 --- a/depends/patches/native_mac_alias/python3.patch +++ b/depends/patches/native_mac_alias/python3.patch @@ -1,7 +1,7 @@ diff -dur a/mac_alias/alias.py b/mac_alias/alias.py ---- a/mac_alias/alias.py 2015-10-19 12:12:48.000000000 +0200 -+++ b/mac_alias/alias.py 2016-04-03 12:13:12.037159417 +0200 -@@ -243,10 +243,10 @@ +--- a/mac_alias/alias.py ++++ b/mac_alias/alias.py +@@ -258,10 +258,10 @@ alias = Alias() alias.appinfo = appinfo @@ -14,7 +14,7 @@ diff -dur a/mac_alias/alias.py b/mac_alias/alias.py folder_cnid, cnid, crdate, creator_code, type_code) alias.target.levels_from = levels_from -@@ -261,9 +261,9 @@ +@@ -276,9 +276,9 @@ b.read(1) if tag == TAG_CARBON_FOLDER_NAME: @@ -26,7 +26,7 @@ diff -dur a/mac_alias/alias.py b/mac_alias/alias.py value) elif tag == TAG_CARBON_PATH: alias.target.carbon_path = value -@@ -298,9 +298,9 @@ +@@ -313,9 +313,9 @@ alias.target.creation_date \ = mac_epoch + datetime.timedelta(seconds=seconds) elif tag == TAG_POSIX_PATH: @@ -38,23 +38,7 @@ diff -dur a/mac_alias/alias.py b/mac_alias/alias.py elif tag == TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE: alias.volume.disk_image_alias = Alias.from_bytes(value) elif tag == TAG_USER_HOME_LENGTH_PREFIX: -@@ -422,13 +422,13 @@ - # (so doing so is ridiculous, and nothing could rely on it). - b.write(struct.pack(b'>h28pI2shI64pII4s4shhI2s10s', - self.target.kind, -- carbon_volname, voldate, -+ carbon_volname, int(voldate), - self.volume.fs_type, - self.volume.disk_type, - self.target.folder_cnid, - carbon_filename, - self.target.cnid, -- crdate, -+ int(crdate), - self.target.creator_code, - self.target.type_code, - self.target.levels_from, -@@ -449,12 +449,12 @@ +@@ -467,12 +467,12 @@ b.write(struct.pack(b'>hhQhhQ', TAG_HIGH_RES_VOLUME_CREATION_DATE, diff --git a/doc/README.md b/doc/README.md index 275ae67e54..988019869e 100644 --- a/doc/README.md +++ b/doc/README.md @@ -37,6 +37,7 @@ Building --------------------- The following are developer notes on how to build Bitcoin on your native platform. They are not complete guides, but include notes on the necessary libraries, compile flags, etc. +- [Dependencies](dependencies.md) - [OS X Build Notes](build-osx.md) - [Unix Build Notes](build-unix.md) - [Windows Build Notes](build-windows.md) diff --git a/doc/REST-interface.md b/doc/REST-interface.md index 7a17c175bd..f3dc124ece 100644 --- a/doc/REST-interface.md +++ b/doc/REST-interface.md @@ -3,7 +3,8 @@ Unauthenticated REST Interface The REST API can be enabled with the `-rest` option. -The interface runs on the same port as the JSON-RPC interface, by default port 8332 for mainnet and port 18332 for testnet. +The interface runs on the same port as the JSON-RPC interface, by default port 8332 for mainnet, port 18332 for testnet, +and port 18443 for regtest. Supported API ------------- diff --git a/doc/build-openbsd.md b/doc/build-openbsd.md index cd800464b4..4ea8e30b53 100644 --- a/doc/build-openbsd.md +++ b/doc/build-openbsd.md @@ -18,12 +18,12 @@ pkg_add automake # (select highest version, e.g. 1.15) pkg_add python # (select highest version, e.g. 3.5) ``` -The default C++ compiler that comes with OpenBSD 5.9 is g++ 4.2. This version is old (from 2007), and is not able to compile the current version of Bitcoin Core, primarily as it has no C++11 support, but even before there were issues. So here we will be installing a newer compiler. +See [dependencies.md](dependencies.md) for a complete overview. GCC ------- -You can install a newer version of gcc with: +The default C++ compiler that comes with OpenBSD 5.9 is g++ 4.2. This version is old (from 2007), and is not able to compile the current version of Bitcoin Core, primarily as it has no C++11 support, but even before there were issues. So here we will be installing a newer compiler: ```bash pkg_add g++ # (select newest 4.x version, e.g. 4.9.3) diff --git a/doc/build-osx.md b/doc/build-osx.md index 32d7dbd69e..836c856a64 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -16,7 +16,9 @@ Then install [Homebrew](https://brew.sh). Dependencies ---------------------- - brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config protobuf qt libevent + brew install automake berkeley-db4 libtool boost --c++11 miniupnpc openssl pkg-config protobuf python3 qt libevent + +See [dependencies.md](dependencies.md) for a complete overview. If you want to build the disk image with `make deploy` (.dmg / optional), you need RSVG diff --git a/doc/build-unix.md b/doc/build-unix.md index b7eae2a630..8a102abaea 100644 --- a/doc/build-unix.md +++ b/doc/build-unix.md @@ -49,7 +49,7 @@ Optional dependencies: univalue | Utility | JSON parsing and encoding (bundled version will be used unless --with-system-univalue passed to configure) libzmq3 | ZMQ notification | Optional, allows generating ZMQ notifications (requires ZMQ version >= 4.x) -For the versions used in the release, see [release-process.md](release-process.md) under *Fetch and build inputs*. +For the versions used, see [dependencies.md](dependencies.md) Memory Requirements -------------------- @@ -65,7 +65,7 @@ Dependency Build Instructions: Ubuntu & Debian ---------------------------------------------- Build requirements: - sudo apt-get install build-essential libtool autotools-dev automake pkg-config libssl-dev libevent-dev bsdmainutils + sudo apt-get install build-essential libtool autotools-dev automake pkg-config libssl-dev libevent-dev bsdmainutils python3 Options when installing required Boost library files: @@ -131,7 +131,7 @@ Dependency Build Instructions: Fedora ------------------------------------- Build requirements: - sudo dnf install gcc-c++ libtool make autoconf automake openssl-devel libevent-devel boost-devel libdb4-devel libdb4-cxx-devel + sudo dnf install gcc-c++ libtool make autoconf automake openssl-devel libevent-devel boost-devel libdb4-devel libdb4-cxx-devel python3 Optional: diff --git a/doc/build-windows.md b/doc/build-windows.md index ca1a05dfb4..0d96af26a2 100644 --- a/doc/build-windows.md +++ b/doc/build-windows.md @@ -4,10 +4,11 @@ WINDOWS BUILD NOTES Below are some notes on how to build Bitcoin Core for Windows. Most developers use cross-compilation from Ubuntu to build executables for -Windows. This is also used to build the release binaries. +Windows. Cross-compilation is also used to build the release binaries. -Currently only building on Ubuntu Trusty 14.04 is supported. -Other versions are unsupported or known to be broken (e.g. Ubuntu Xenial 16.04). +Currently only building on Ubuntu Trusty 14.04 or Ubuntu Zesty 17.04 or later is supported. +Building on Ubuntu Xenial 16.04 is known to be broken, see extensive discussion in issue [8732](https://github.com/bitcoin/bitcoin/issues/8732). +While it may be possible to do so with work arounds, it's potentially dangerous and not recommended. While there are potentially a number of ways to build on Windows (for example using msys / mingw-w64), using the Windows Subsystem For Linux is the most straightforward. If you are building with @@ -62,6 +63,15 @@ A host toolchain (`build-essential`) is necessary because some dependency packages (such as `protobuf`) need to build host utilities that are used in the build process. +See also: [dependencies.md](dependencies.md). + +If you're building on Ubuntu 17.04 or later, run these two commands, selecting the 'posix' variant for both, +to work around issues with mingw-w64. See issue [8732](https://github.com/bitcoin/bitcoin/issues/8732) for more information. +``` +sudo update-alternatives --config x86_64-w64-mingw32-g++ +sudo update-alternatives --config x86_64-w64-mingw32-gcc +``` + ## Building for 64-bit Windows To build executables for Windows 64-bit, install the following dependencies: @@ -70,6 +80,7 @@ To build executables for Windows 64-bit, install the following dependencies: Then build using: + PATH=$(echo "$PATH" | sed -e 's/:\/mnt.*//g') # strip out problematic Windows %PATH% imported var cd depends make HOST=x86_64-w64-mingw32 cd .. @@ -81,10 +92,11 @@ Then build using: To build executables for Windows 32-bit, install the following dependencies: - sudo apt-get install g++-mingw-w64-i686 mingw-w64-i686-dev + sudo apt-get install g++-mingw-w64-i686 mingw-w64-i686-dev Then build using: + PATH=$(echo "$PATH" | sed -e 's/:\/mnt.*//g') # strip out problematic Windows %PATH% imported var cd depends make HOST=i686-w64-mingw32 cd .. diff --git a/doc/dependencies.md b/doc/dependencies.md new file mode 100644 index 0000000000..964c03ba88 --- /dev/null +++ b/doc/dependencies.md @@ -0,0 +1,31 @@ +Dependencies +============ + +These are the dependencies currently used by Bitcoin Core. You can find instructions for installing them in the `build-*.md` file for your platform. + +| Dependency | Version used | Minimum required | CVEs | Shared | [Bundled Qt library](https://doc.qt.io/qt-5/configure-options.html) | +| --- | --- | --- | --- | --- | --- | +| Berkeley DB | [4.8.30](http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html) | 4.8.x | No | | | +| Boost | [1.64.0](http://www.boost.org/users/download/) | [1.47.0](https://github.com/bitcoin/bitcoin/pull/8920) | No | | | +| ccache | [3.3.4](https://ccache.samba.org/download.html) | | No | | | +| Clang | | [3.3+](http://llvm.org/releases/download.html) (C++11 support) | | | | +| D-Bus | [1.10.18](https://cgit.freedesktop.org/dbus/dbus/tree/NEWS?h=dbus-1.10) | | No | Yes | | +| Expat | [2.2.1](https://libexpat.github.io/) | | No | Yes | | +| fontconfig | [2.12.1](https://www.freedesktop.org/software/fontconfig/release/) | | No | Yes | | +| FreeType | [2.7.1](http://download.savannah.gnu.org/releases/freetype) | | No | | | +| GCC | | [4.7+](https://gcc.gnu.org/) | | | | +| HarfBuzz-NG | | | | | | +| libevent | [2.1.8-stable](https://github.com/libevent/libevent/releases) | 2.0.22 | No | | | +| libjpeg | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L75) | +| libpng | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L74) | +| MiniUPnPc | [2.0.20170509](http://miniupnp.free.fr/files) | | No | | | +| OpenSSL | [1.0.1k](https://www.openssl.org/source) | | Yes | | | +| PCRE | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L76) | +| protobuf | [2.6.3](https://github.com/google/protobuf/releases) | | No | | | +| Python (tests) | | [3.4](https://www.python.org/downloads) | | | | +| qrencode | [3.4.4](https://fukuchi.org/works/qrencode) | | No | | | +| Qt | [5.7.1](https://download.qt.io/official_releases/qt/) | 4.7+ | No | | | +| XCB | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L94) (Linux only) | +| xkbcommon | | | | | [Yes](https://github.com/bitcoin/bitcoin/blob/master/depends/packages/qt.mk#L93) (Linux only) | +| ZeroMQ | [4.1.5](https://github.com/zeromq/libzmq/releases) | | No | | | +| zlib | [1.2.11](http://zlib.net/) | | | | No | diff --git a/doc/developer-notes.md b/doc/developer-notes.md index 2f04d6ee6c..33c6ab9cb3 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -549,6 +549,26 @@ Git and GitHub tips or `git fetch upstream-pull`. Afterwards, you can use `upstream-pull/NUMBER/head` in arguments to `git show`, `git checkout` and anywhere a commit id would be acceptable to see the changes from pull request NUMBER. +Scripted diffs +-------------- + +For reformatting and refactoring commits where the changes can be easily automated using a bash script, we use +scripted-diff commits. The bash script is included in the commit message and our Travis CI job checks that +the result of the script is identical to the commit. This aids reviewers since they can verify that the script +does exactly what it's supposed to do. It is also helpful for rebasing (since the same script can just be re-run +on the new master commit). + +To create a scripted-diff: + +- start the commit message with `scripted-diff:` (and then a description of the diff on the same line) +- in the commit message include the bash script between lines containing just the following text: + - `-BEGIN VERIFY SCRIPT-` + - `-END VERIFY SCRIPT-` + +The scripted-diff is verified by the tool `contrib/devtools/commit-script-check.sh` + +Commit `bb81e173` is an example of a scripted-diff. + RPC interface guidelines -------------------------- @@ -613,9 +633,14 @@ A few guidelines for introducing and reviewing new RPC interfaces: from there. - A RPC method must either be a wallet method or a non-wallet method. Do not - introduce new methods such as `getinfo` and `signrawtransaction` that differ - in behavior based on presence of a wallet. + introduce new methods such as `signrawtransaction` that differ in behavior + based on presence of a wallet. - *Rationale*: as well as complicating the implementation and interfering with the introduction of multi-wallet, wallet and non-wallet code should be separated to avoid introducing circular dependencies between code units. + +- Try to make the RPC response a JSON object. + + - *Rationale*: If a RPC response is not a JSON object then it is harder to avoid API breakage if + new data in the response is needed. diff --git a/doc/files.md b/doc/files.md index 928977143b..3d603445fb 100644 --- a/doc/files.md +++ b/doc/files.md @@ -15,6 +15,7 @@ * wallet.dat: personal wallet (BDB) with keys and transactions * .cookie: session RPC authentication cookie (written at start when cookie authentication is used, deleted on shutdown): since 0.12.0 * onion_private_key: cached Tor hidden service private key for `-listenonion`: since 0.12.0 +* guisettings.ini.bak: backup of former GUI settings after `-resetguisettings` is used Only used in pre-0.8.0 --------------------- diff --git a/doc/gitian-building.md b/doc/gitian-building.md index 636686b391..3a48f4a0b3 100644 --- a/doc/gitian-building.md +++ b/doc/gitian-building.md @@ -1,484 +1,4 @@ Gitian building ================ -*Setup instructions for a Gitian build of Bitcoin Core using a Debian VM or physical system.* - -Gitian is the deterministic build process that is used to build the Bitcoin -Core executables. It provides a way to be reasonably sure that the -executables are really built from the source on GitHub. It also makes sure that -the same, tested dependencies are used and statically built into the executable. - -Multiple developers build the source code by following a specific descriptor -("recipe"), cryptographically sign the result, and upload the resulting signature. -These results are compared and only if they match, the build is accepted and uploaded -to bitcoin.org. - -More independent Gitian builders are needed, which is why this guide exists. -It is preferred you follow these steps yourself instead of using someone else's -VM image to avoid 'contaminating' the build. - -Table of Contents ------------------- - -- [Create a new VirtualBox VM](#create-a-new-virtualbox-vm) -- [Connecting to the VM](#connecting-to-the-vm) -- [Setting up Debian for Gitian building](#setting-up-debian-for-gitian-building) -- [Installing Gitian](#installing-gitian) -- [Setting up the Gitian image](#setting-up-the-gitian-image) -- [Getting and building the inputs](#getting-and-building-the-inputs) -- [Building Bitcoin Core](#building-bitcoin-core) -- [Building an alternative repository](#building-an-alternative-repository) -- [Signing externally](#signing-externally) -- [Uploading signatures](#uploading-signatures) - -Preparing the Gitian builder host ---------------------------------- - -The first step is to prepare the host environment that will be used to perform the Gitian builds. -This guide explains how to set up the environment, and how to start the builds. - -Debian Linux was chosen as the host distribution because it has a lightweight install (in contrast to Ubuntu) and is readily available. -Any kind of virtualization can be used, for example: -- [VirtualBox](https://www.virtualbox.org/) (covered by this guide) -- [KVM](http://www.linux-kvm.org/page/Main_Page) -- [LXC](https://linuxcontainers.org/), see also [Gitian host docker container](https://github.com/gdm85/tenku/tree/master/docker/gitian-bitcoin-host/README.md). - -You can also install Gitian on actual hardware instead of using virtualization. - -Create a new VirtualBox VM ---------------------------- -In the VirtualBox GUI click "New" and choose the following parameters in the wizard: - -![](gitian-building/create_new_vm.png) - -- Type: Linux, Debian (64-bit) - -![](gitian-building/create_vm_memsize.png) - -- Memory Size: at least 3000MB, anything less and the build might not complete. - -![](gitian-building/create_vm_hard_disk.png) - -- Hard Disk: Create a virtual hard disk now - -![](gitian-building/create_vm_hard_disk_file_type.png) - -- Hard Disk file type: Use the default, VDI (VirtualBox Disk Image) - -![](gitian-building/create_vm_storage_physical_hard_disk.png) - -- Storage on physical hard disk: Dynamically Allocated - -![](gitian-building/create_vm_file_location_size.png) - -- File location and size: at least 40GB; as low as 20GB *may* be possible, but better to err on the safe side -- Click `Create` - -After creating the VM, we need to configure it. - -- Click the `Settings` button, then go to `System` tab and `Processor` sub-tab. Increase the number of processors to the number of cores on your machine if you want builds to be faster. - -![](gitian-building/system_settings.png) - -- Go to the `Network` tab. Adapter 1 should be attached to `NAT`. - -![](gitian-building/network_settings.png) - -- Click `Advanced`, then `Port Forwarding`. We want to set up a port through which we can reach the VM to get files in and out. -- Create a new rule by clicking the plus icon. - -![](gitian-building/port_forwarding_rules.png) - -- Set up the new rule the following way: - - Name: `SSH` - - Protocol: `TCP` - - Leave Host IP empty - - Host Port: `22222` - - Leave Guest IP empty - - Guest Port: `22` - -- Click `Ok` twice to save. - -Get the [Debian 8.x net installer](http://cdimage.debian.org/mirror/cdimage/archive/8.5.0/amd64/iso-cd/debian-8.5.0-amd64-netinst.iso) (a more recent minor version should also work, see also [Debian Network installation](https://www.debian.org/CD/netinst/)). -This DVD image can be [validated](https://www.debian.org/CD/verify) using a SHA256 hashing tool, for example on -Unixy OSes by entering the following in a terminal: - - echo "ad4e8c27c561ad8248d5ebc1d36eb172f884057bfeb2c22ead823f59fa8c3dff debian-8.5.0-amd64-netinst.iso" | sha256sum -c - # (must return OK) - -Then start the VM. On the first launch you will be asked for a CD or DVD image. Choose the downloaded ISO. - -![](gitian-building/select_startup_disk.png) - -Installing Debian ------------------- - -This section will explain how to install Debian on the newly created VM. - -- Choose the non-graphical installer. We do not need the graphical environment; it will only increase installation time and disk usage. - -![](gitian-building/debian_install_1_boot_menu.png) - -**Note**: Navigating in the Debian installer: -To keep a setting at the default and proceed, just press `Enter`. -To select a different button, press `Tab`. - -- Choose locale and keyboard settings (doesn't matter, you can just go with the defaults or select your own information) - -![](gitian-building/debian_install_2_select_a_language.png) -![](gitian-building/debian_install_3_select_location.png) -![](gitian-building/debian_install_4_configure_keyboard.png) - -- The VM will detect network settings using DHCP, this should all proceed automatically -- Configure the network: - - Hostname `debian`. - - Leave domain name empty. - -![](gitian-building/debian_install_5_configure_the_network.png) -![](gitian-building/debian_install_6_domain_name.png) - -- Choose a root password and enter it twice (remember it for later) - -![](gitian-building/debian_install_6a_set_up_root_password.png) - -- Name the new user `debian` (the full name doesn't matter, you can leave it empty) -- Set the account username as `debian` - -![](gitian-building/debian_install_7_set_up_user_fullname.png) -![](gitian-building/debian_install_8_set_up_username.png) - -- Choose a user password and enter it twice (remember it for later) - -![](gitian-building/debian_install_9_user_password.png) - -- The installer will set up the clock using a time server; this process should be automatic -- Set up the clock: choose a time zone (depends on the locale settings that you picked earlier; specifics don't matter) - -![](gitian-building/debian_install_10_configure_clock.png) - -- Disk setup - - Partitioning method: Guided - Use the entire disk - -![](gitian-building/debian_install_11_partition_disks.png) - - - Select disk to partition: SCSI1 (0,0,0) - -![](gitian-building/debian_install_12_choose_disk.png) - - - Partition Disks -> *All files in one partition* - -![](gitian-building/all_files_in_one_partition.png) - - - Finish partitioning and write changes to disk -> *Yes* (`Tab`, `Enter` to select the `Yes` button) - -![](gitian-building/debian_install_14_finish.png) -![](gitian-building/debian_install_15_write_changes.png) - -- The base system will be installed, this will take a minute or so -- Choose a mirror (any will do) - -![](gitian-building/debian_install_16_choose_a_mirror.png) - -- Enter proxy information (unless you are on an intranet, leave this empty) - -![](gitian-building/debian_install_18_proxy_settings.png) - -- Wait a bit while 'Select and install software' runs -- Participate in popularity contest -> *No* -- Choose software to install. We need just the base system. -- Make sure only 'SSH server' and 'Standard System Utilities' are checked -- Uncheck 'Debian Desktop Environment' and 'Print Server' - -![](gitian-building/debian_install_19_software_selection.png) - -- Install the GRUB boot loader to the master boot record? -> Yes - -![](gitian-building/debian_install_20_install_grub.png) - -- Device for boot loader installation -> ata-VBOX_HARDDISK - -![](gitian-building/debian_install_21_install_grub_bootloader.png) - -- Installation Complete -> *Continue* -- After installation, the VM will reboot and you will have a working Debian VM. Congratulations! - -![](gitian-building/debian_install_22_finish_installation.png) - - -After Installation -------------------- -The next step in the guide involves logging in as root via SSH. -SSH login for root users is disabled by default, so we'll enable that now. - -Login to the VM using username `root` and the root password you chose earlier. -You'll be presented with a screen similar to this. - -![](gitian-building/debian_root_login.png) - -Type: - -``` -sed -i 's/^PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config -``` -and press enter. Then, -``` -/etc/init.d/ssh restart -``` -and enter to restart SSH. Logout by typing 'logout' and pressing 'enter'. - -Connecting to the VM ----------------------- - -After the VM has booted you can connect to it using SSH, and files can be copied from and to the VM using a SFTP utility. -Connect to `localhost`, port `22222` (or the port configured when installing the VM). -On Windows you can use [putty](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html) and [WinSCP](http://winscp.net/eng/index.php). - -For example, to connect as `root` from a Linux command prompt use - - $ ssh root@localhost -p 22222 - The authenticity of host '[localhost]:22222 ([127.0.0.1]:22222)' can't be established. - RSA key fingerprint is ae:f5:c8:9f:17:c6:c7:1b:c2:1b:12:31:1d:bb:d0:c7. - Are you sure you want to continue connecting (yes/no)? yes - Warning: Permanently added '[localhost]:22222' (RSA) to the list of known hosts. - root@localhost's password: (enter root password configured during install) - - The programs included with the Debian GNU/Linux system are free software; - the exact distribution terms for each program are described in the - individual files in /usr/share/doc/*/copyright. - - Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent - permitted by applicable law. - root@debian:~# - -Replace `root` with `debian` to log in as user. - -Setting up Debian for Gitian building --------------------------------------- - -In this section we will be setting up the Debian installation for Gitian building. - -First we need to log in as `root` to set up dependencies and make sure that our -user can use the sudo command. Type/paste the following in the terminal: - -```bash -apt-get install git ruby sudo apt-cacher-ng qemu-utils debootstrap lxc python-cheetah parted kpartx bridge-utils make ubuntu-archive-keyring curl -adduser debian sudo -``` - -Then set up LXC and the rest with the following, which is a complex jumble of settings and workarounds: - -```bash -# the version of lxc-start in Debian needs to run as root, so make sure -# that the build script can execute it without providing a password -echo "%sudo ALL=NOPASSWD: /usr/bin/lxc-start" > /etc/sudoers.d/gitian-lxc -echo "%sudo ALL=NOPASSWD: /usr/bin/lxc-execute" >> /etc/sudoers.d/gitian-lxc -# make /etc/rc.local script that sets up bridge between guest and host -echo '#!/bin/sh -e' > /etc/rc.local -echo 'brctl addbr br0' >> /etc/rc.local -echo 'ifconfig br0 10.0.3.2/24 up' >> /etc/rc.local -echo 'iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE' >> /etc/rc.local -echo 'echo 1 > /proc/sys/net/ipv4/ip_forward' >> /etc/rc.local -echo 'exit 0' >> /etc/rc.local -# make sure that USE_LXC is always set when logging in as debian, -# and configure LXC IP addresses -echo 'export USE_LXC=1' >> /home/debian/.profile -echo 'export GITIAN_HOST_IP=10.0.3.2' >> /home/debian/.profile -echo 'export LXC_GUEST_IP=10.0.3.5' >> /home/debian/.profile -reboot -``` - -At the end the VM is rebooted to make sure that the changes take effect. The steps in this -section only need to be performed once. - -Installing Gitian ------------------- - -Re-login as the user `debian` that was created during installation. -The rest of the steps in this guide will be performed as that user. - -There is no `python-vm-builder` package in Debian, so we need to install it from source ourselves, - -```bash -wget http://archive.ubuntu.com/ubuntu/pool/universe/v/vm-builder/vm-builder_0.12.4+bzr494.orig.tar.gz -echo "76cbf8c52c391160b2641e7120dbade5afded713afaa6032f733a261f13e6a8e vm-builder_0.12.4+bzr494.orig.tar.gz" | sha256sum -c -# (verification -- must return OK) -tar -zxvf vm-builder_0.12.4+bzr494.orig.tar.gz -cd vm-builder-0.12.4+bzr494 -sudo python setup.py install -cd .. -``` - -**Note**: When sudo asks for a password, enter the password for the user *debian* not for *root*. - -Clone the git repositories for bitcoin and Gitian. - -```bash -git clone https://github.com/devrandom/gitian-builder.git -git clone https://github.com/bitcoin/bitcoin -git clone https://github.com/bitcoin-core/gitian.sigs.git -``` - -Setting up the Gitian image -------------------------- - -Gitian needs a virtual image of the operating system to build in. -Currently this is Ubuntu Trusty x86_64. -This image will be copied and used every time that a build is started to -make sure that the build is deterministic. -Creating the image will take a while, but only has to be done once. - -Execute the following as user `debian`: - -```bash -cd gitian-builder -bin/make-base-vm --lxc --arch amd64 --suite trusty -``` - -There will be a lot of warnings printed during the build of the image. These can be ignored. - -**Note**: When sudo asks for a password, enter the password for the user *debian* not for *root*. - -Getting and building the inputs --------------------------------- - -At this point you have two options, you can either use the automated script (found in [contrib/gitian-build.sh](/contrib/gitian-build.sh)) or you could manually do everything by following this guide. If you're using the automated script, then run it with the "--setup" command. Afterwards, run it with the "--build" command (example: "contrib/gitian-build.sh -b signer 0.13.0"). Otherwise ignore this. - -Follow the instructions in [doc/release-process.md](release-process.md#fetch-and-create-inputs-first-time-or-when-dependency-versions-change) -in the bitcoin repository under 'Fetch and create inputs' to install sources which require -manual intervention. Also optionally follow the next step: 'Seed the Gitian sources cache -and offline git repositories' which will fetch the remaining files required for building -offline. - -Building Bitcoin Core ----------------- - -To build Bitcoin Core (for Linux, OS X and Windows) just follow the steps under 'perform -Gitian builds' in [doc/release-process.md](release-process.md#perform-gitian-builds) in the bitcoin repository. - -This may take some time as it will build all the dependencies needed for each descriptor. -These dependencies will be cached after a successful build to avoid rebuilding them when possible. - -At any time you can check the package installation and build progress with - -```bash -tail -f var/install.log -tail -f var/build.log -``` - -Output from `gbuild` will look something like - - Initialized empty Git repository in /home/debian/gitian-builder/inputs/bitcoin/.git/ - remote: Counting objects: 57959, done. - remote: Total 57959 (delta 0), reused 0 (delta 0), pack-reused 57958 - Receiving objects: 100% (57959/57959), 53.76 MiB | 484.00 KiB/s, done. - Resolving deltas: 100% (41590/41590), done. - From https://github.com/bitcoin/bitcoin - ... (new tags, new branch etc) - --- Building for trusty amd64 --- - Stopping target if it is up - Making a new image copy - stdin: is not a tty - Starting target - Checking if target is up - Preparing build environment - Updating apt-get repository (log in var/install.log) - Installing additional packages (log in var/install.log) - Grabbing package manifest - stdin: is not a tty - Creating build script (var/build-script) - lxc-start: Connection refused - inotify event with no name (mask 32768) - Running build script (log in var/build.log) - -Building an alternative repository ------------------------------------ - -If you want to do a test build of a pull on GitHub it can be useful to point -the Gitian builder at an alternative repository, using the same descriptors -and inputs. - -For example: -```bash -URL=https://github.com/laanwj/bitcoin.git -COMMIT=2014_03_windows_unicode_path -./bin/gbuild --commit bitcoin=${COMMIT} --url bitcoin=${URL} ../bitcoin/contrib/gitian-descriptors/gitian-linux.yml -./bin/gbuild --commit bitcoin=${COMMIT} --url bitcoin=${URL} ../bitcoin/contrib/gitian-descriptors/gitian-win.yml -./bin/gbuild --commit bitcoin=${COMMIT} --url bitcoin=${URL} ../bitcoin/contrib/gitian-descriptors/gitian-osx.yml -``` - -Building fully offline ------------------------ - -For building fully offline including attaching signatures to unsigned builds, the detached-sigs repository -and the bitcoin git repository with the desired tag must both be available locally, and then gbuild must be -told where to find them. It also requires an apt-cacher-ng which is fully-populated but set to offline mode, or -manually disabling gitian-builder's use of apt-get to update the VM build environment. - -To configure apt-cacher-ng as an offline cacher, you will need to first populate its cache with the relevant -files. You must additionally patch target-bin/bootstrap-fixup to set its apt sources to something other than -plain archive.ubuntu.com: us.archive.ubuntu.com works. - -So, if you use LXC: - -```bash -export PATH="$PATH":/path/to/gitian-builder/libexec -export USE_LXC=1 -cd /path/to/gitian-builder -./libexec/make-clean-vm --suite trusty --arch amd64 - -LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root apt-get update -LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root \ - -e DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends -y install \ - $( sed -ne '/^packages:/,/[^-] .*/ {/^- .*/{s/"//g;s/- //;p}}' ../bitcoin/contrib/gitian-descriptors/*|sort|uniq ) -LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root apt-get -q -y purge grub -LXC_ARCH=amd64 LXC_SUITE=trusty on-target -u root -e DEBIAN_FRONTEND=noninteractive apt-get -y dist-upgrade -``` - -And then set offline mode for apt-cacher-ng: - -``` -/etc/apt-cacher-ng/acng.conf -[...] -Offlinemode: 1 -[...] - -service apt-cacher-ng restart -``` - -Then when building, override the remote URLs that gbuild would otherwise pull from the Gitian descriptors:: -```bash - -cd /some/root/path/ -git clone https://github.com/bitcoin-core/bitcoin-detached-sigs.git - -BTCPATH=/some/root/path/bitcoin -SIGPATH=/some/root/path/bitcoin-detached-sigs - -./bin/gbuild --url bitcoin=${BTCPATH},signature=${SIGPATH} ../bitcoin/contrib/gitian-descriptors/gitian-win-signer.yml -``` - -Signing externally -------------------- - -If you want to do the PGP signing on another device, that's also possible; just define `SIGNER` as mentioned -and follow the steps in the build process as normal. - - gpg: skipped "laanwj": secret key not available - -When you execute `gsign` you will get an error from GPG, which can be ignored. Copy the resulting `.assert` files -in `gitian.sigs` to your signing machine and do - -```bash - gpg --detach-sign ${VERSION}-linux/${SIGNER}/bitcoin-linux-build.assert - gpg --detach-sign ${VERSION}-win/${SIGNER}/bitcoin-win-build.assert - gpg --detach-sign ${VERSION}-osx-unsigned/${SIGNER}/bitcoin-osx-build.assert -``` - -This will create the `.sig` files that can be committed together with the `.assert` files to assert your -Gitian build. - -Uploading signatures ---------------------- - -After building and signing you can push your signatures (both the `.assert` and `.assert.sig` files) to the -[bitcoin-core/gitian.sigs](https://github.com/bitcoin-core/gitian.sigs/) repository, or if that's not possible create a pull -request. You can also mail the files to Wladimir (laanwj@gmail.com) and he will commit them. +This file was moved to [the Bitcoin Core documentation repository](https://github.com/bitcoin-core/docs/blob/master/gitian-building.md) at [https://github.com/bitcoin-core/docs](https://github.com/bitcoin-core/docs). diff --git a/doc/gitian-building/all_files_in_one_partition.png b/doc/gitian-building/all_files_in_one_partition.png Binary files differdeleted file mode 100644 index 8cbb0d8adc..0000000000 --- a/doc/gitian-building/all_files_in_one_partition.png +++ /dev/null diff --git a/doc/gitian-building/create_new_vm.png b/doc/gitian-building/create_new_vm.png Binary files differdeleted file mode 100644 index dd22428e17..0000000000 --- a/doc/gitian-building/create_new_vm.png +++ /dev/null diff --git a/doc/gitian-building/create_vm_file_location_size.png b/doc/gitian-building/create_vm_file_location_size.png Binary files differdeleted file mode 100644 index 5f77206b6f..0000000000 --- a/doc/gitian-building/create_vm_file_location_size.png +++ /dev/null diff --git a/doc/gitian-building/create_vm_hard_disk.png b/doc/gitian-building/create_vm_hard_disk.png Binary files differdeleted file mode 100644 index 8e29816fab..0000000000 --- a/doc/gitian-building/create_vm_hard_disk.png +++ /dev/null diff --git a/doc/gitian-building/create_vm_hard_disk_file_type.png b/doc/gitian-building/create_vm_hard_disk_file_type.png Binary files differdeleted file mode 100644 index a157211cf5..0000000000 --- a/doc/gitian-building/create_vm_hard_disk_file_type.png +++ /dev/null diff --git a/doc/gitian-building/create_vm_memsize.png b/doc/gitian-building/create_vm_memsize.png Binary files differdeleted file mode 100644 index 6f42cda73f..0000000000 --- a/doc/gitian-building/create_vm_memsize.png +++ /dev/null diff --git a/doc/gitian-building/create_vm_storage_physical_hard_disk.png b/doc/gitian-building/create_vm_storage_physical_hard_disk.png Binary files differdeleted file mode 100644 index cee16a6c63..0000000000 --- a/doc/gitian-building/create_vm_storage_physical_hard_disk.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_10_configure_clock.png b/doc/gitian-building/debian_install_10_configure_clock.png Binary files differdeleted file mode 100644 index 7cda038ae4..0000000000 --- a/doc/gitian-building/debian_install_10_configure_clock.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_11_partition_disks.png b/doc/gitian-building/debian_install_11_partition_disks.png Binary files differdeleted file mode 100644 index 2a648c517f..0000000000 --- a/doc/gitian-building/debian_install_11_partition_disks.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_12_choose_disk.png b/doc/gitian-building/debian_install_12_choose_disk.png Binary files differdeleted file mode 100644 index 0f3acc498e..0000000000 --- a/doc/gitian-building/debian_install_12_choose_disk.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_14_finish.png b/doc/gitian-building/debian_install_14_finish.png Binary files differdeleted file mode 100644 index c8ef0b37ad..0000000000 --- a/doc/gitian-building/debian_install_14_finish.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_15_write_changes.png b/doc/gitian-building/debian_install_15_write_changes.png Binary files differdeleted file mode 100644 index d8de00dec6..0000000000 --- a/doc/gitian-building/debian_install_15_write_changes.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_16_choose_a_mirror.png b/doc/gitian-building/debian_install_16_choose_a_mirror.png Binary files differdeleted file mode 100644 index 0bd985b38c..0000000000 --- a/doc/gitian-building/debian_install_16_choose_a_mirror.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_18_proxy_settings.png b/doc/gitian-building/debian_install_18_proxy_settings.png Binary files differdeleted file mode 100644 index 2c19919f64..0000000000 --- a/doc/gitian-building/debian_install_18_proxy_settings.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_19_software_selection.png b/doc/gitian-building/debian_install_19_software_selection.png Binary files differdeleted file mode 100644 index 5430456b14..0000000000 --- a/doc/gitian-building/debian_install_19_software_selection.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_1_boot_menu.png b/doc/gitian-building/debian_install_1_boot_menu.png Binary files differdeleted file mode 100644 index 216502e1c6..0000000000 --- a/doc/gitian-building/debian_install_1_boot_menu.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_20_install_grub.png b/doc/gitian-building/debian_install_20_install_grub.png Binary files differdeleted file mode 100644 index d853c15871..0000000000 --- a/doc/gitian-building/debian_install_20_install_grub.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_21_install_grub_bootloader.png b/doc/gitian-building/debian_install_21_install_grub_bootloader.png Binary files differdeleted file mode 100644 index 493ab806a6..0000000000 --- a/doc/gitian-building/debian_install_21_install_grub_bootloader.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_22_finish_installation.png b/doc/gitian-building/debian_install_22_finish_installation.png Binary files differdeleted file mode 100644 index 7c4445585b..0000000000 --- a/doc/gitian-building/debian_install_22_finish_installation.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_2_select_a_language.png b/doc/gitian-building/debian_install_2_select_a_language.png Binary files differdeleted file mode 100644 index 0228ae2c01..0000000000 --- a/doc/gitian-building/debian_install_2_select_a_language.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_3_select_location.png b/doc/gitian-building/debian_install_3_select_location.png Binary files differdeleted file mode 100644 index 7b18fba975..0000000000 --- a/doc/gitian-building/debian_install_3_select_location.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_4_configure_keyboard.png b/doc/gitian-building/debian_install_4_configure_keyboard.png Binary files differdeleted file mode 100644 index 8e46117de4..0000000000 --- a/doc/gitian-building/debian_install_4_configure_keyboard.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_5_configure_the_network.png b/doc/gitian-building/debian_install_5_configure_the_network.png Binary files differdeleted file mode 100644 index 8e3720f243..0000000000 --- a/doc/gitian-building/debian_install_5_configure_the_network.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_6_domain_name.png b/doc/gitian-building/debian_install_6_domain_name.png Binary files differdeleted file mode 100644 index 7a986d92f4..0000000000 --- a/doc/gitian-building/debian_install_6_domain_name.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_6a_set_up_root_password.png b/doc/gitian-building/debian_install_6a_set_up_root_password.png Binary files differdeleted file mode 100644 index dcade11967..0000000000 --- a/doc/gitian-building/debian_install_6a_set_up_root_password.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_7_set_up_user_fullname.png b/doc/gitian-building/debian_install_7_set_up_user_fullname.png Binary files differdeleted file mode 100644 index 6763c6e08a..0000000000 --- a/doc/gitian-building/debian_install_7_set_up_user_fullname.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_8_set_up_username.png b/doc/gitian-building/debian_install_8_set_up_username.png Binary files differdeleted file mode 100644 index bb04de96d2..0000000000 --- a/doc/gitian-building/debian_install_8_set_up_username.png +++ /dev/null diff --git a/doc/gitian-building/debian_install_9_user_password.png b/doc/gitian-building/debian_install_9_user_password.png Binary files differdeleted file mode 100644 index 981f1181d7..0000000000 --- a/doc/gitian-building/debian_install_9_user_password.png +++ /dev/null diff --git a/doc/gitian-building/debian_root_login.png b/doc/gitian-building/debian_root_login.png Binary files differdeleted file mode 100644 index 14cdd5ba5b..0000000000 --- a/doc/gitian-building/debian_root_login.png +++ /dev/null diff --git a/doc/gitian-building/network_settings.png b/doc/gitian-building/network_settings.png Binary files differdeleted file mode 100644 index 9e714fd154..0000000000 --- a/doc/gitian-building/network_settings.png +++ /dev/null diff --git a/doc/gitian-building/port_forwarding_rules.png b/doc/gitian-building/port_forwarding_rules.png Binary files differdeleted file mode 100644 index 9e1fa2af20..0000000000 --- a/doc/gitian-building/port_forwarding_rules.png +++ /dev/null diff --git a/doc/gitian-building/select_startup_disk.png b/doc/gitian-building/select_startup_disk.png Binary files differdeleted file mode 100644 index 59bc093e2c..0000000000 --- a/doc/gitian-building/select_startup_disk.png +++ /dev/null diff --git a/doc/gitian-building/system_settings.png b/doc/gitian-building/system_settings.png Binary files differdeleted file mode 100644 index a5720ef3a3..0000000000 --- a/doc/gitian-building/system_settings.png +++ /dev/null diff --git a/doc/man/bitcoin-cli.1 b/doc/man/bitcoin-cli.1 index 0493241b1e..6787638443 100644 --- a/doc/man/bitcoin-cli.1 +++ b/doc/man/bitcoin-cli.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH BITCOIN-CLI "1" "February 2017" "bitcoin-cli v0.14.99.0" "User Commands" +.TH BITCOIN-CLI "1" "September 2017" "bitcoin-cli v0.15.99.0" "User Commands" .SH NAME -bitcoin-cli \- manual page for bitcoin-cli v0.14.99.0 +bitcoin-cli \- manual page for bitcoin-cli v0.15.99.0 .SH DESCRIPTION -Bitcoin Core RPC client version v0.14.99.0 +Bitcoin Core RPC client version v0.15.99.0 .SS "Usage:" .TP bitcoin\-cli [options] <command> [params] @@ -64,12 +64,29 @@ Password for JSON\-RPC connections .HP \fB\-rpcclienttimeout=\fR<n> .IP -Timeout during HTTP requests (default: 900) +Timeout in seconds during HTTP requests, or 0 for no timeout. (default: +900) +.HP +\fB\-stdinrpcpass\fR +.TP +Read RPC password from standard input as a single line. +When combined +.IP +with \fB\-stdin\fR, the first line from standard input is used for the +RPC password. .HP \fB\-stdin\fR .IP Read extra arguments from standard input, one per line until EOF/Ctrl\-D -(recommended for sensitive information such as passphrases) +(recommended for sensitive information such as passphrases). +When combined with \fB\-stdinrpcpass\fR, the first line from standard +input is used for the RPC password. +.HP +\fB\-rpcwallet=\fR<walletname> +.IP +Send RPC for non\-default wallet on RPC server (argument is wallet +filename in bitcoind directory, required if bitcoind/\-Qt runs +with multiple wallets) .SH COPYRIGHT Copyright (C) 2009-2017 The Bitcoin Core developers diff --git a/doc/man/bitcoin-qt.1 b/doc/man/bitcoin-qt.1 index ce252612e5..ae35d50ac3 100644 --- a/doc/man/bitcoin-qt.1 +++ b/doc/man/bitcoin-qt.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH BITCOIN-QT "1" "February 2017" "bitcoin-qt v0.14.99.0" "User Commands" +.TH BITCOIN-QT "1" "September 2017" "bitcoin-qt v0.15.99.0" "User Commands" .SH NAME -bitcoin-qt \- manual page for bitcoin-qt v0.14.99.0 +bitcoin-qt \- manual page for bitcoin-qt v0.15.99.0 .SH DESCRIPTION -Bitcoin Core version v0.14.99.0 (64\-bit) +Bitcoin Core version v0.15.99.0 (64\-bit) Usage: .IP bitcoin\-qt [command\-line options] @@ -32,9 +32,9 @@ block hash) If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: -00000000000000000013176bf8d7dfeab4e1db31dc93bc311b436e82ab226b90, +0000000000000000003b9ce759c2a087d52abc4266f8f4ebd6d768b89defa50a, testnet: -00000000000128796ee387cf110ccb9d2f36cffaf7f73079c995377c65ac0dcc) +0000000002e9e7b00e1f6dc5123a04aad68dd0f0968d8c7aa45f6640795c37b1) .HP \fB\-conf=\fR<file> .IP @@ -46,7 +46,7 @@ Specify data directory .HP \fB\-dbcache=\fR<n> .IP -Set database cache size in megabytes (4 to 16384, default: 300) +Set database cache size in megabytes (4 to 16384, default: 450) .HP \fB\-loadblock=\fR<file> .IP @@ -65,6 +65,10 @@ Keep the transaction memory pool below <n> megabytes (default: 300) Do not keep transactions in the mempool longer than <n> hours (default: 336) .HP +\fB\-persistmempool\fR +.IP +Whether to save the mempool on shutdown and load on restart (default: 1) +.HP \fB\-blockreconstructionextratxn=\fR<n> .IP Extra transactions to keep in memory for compact block reconstructions @@ -131,8 +135,8 @@ for IPv6 .HP \fB\-connect=\fR<ip> .IP -Connect only to the specified node(s); \fB\-noconnect\fR or \fB\-connect\fR=\fI\,0\/\fR alone to -disable automatic connections +Connect only to the specified node(s); \fB\-connect\fR=\fI\,0\/\fR disables automatic +connections .HP \fB\-discover\fR .IP @@ -146,7 +150,7 @@ Allow DNS lookups for \fB\-addnode\fR, \fB\-seednode\fR and \fB\-connect\fR (def \fB\-dnsseed\fR .IP Query for peer addresses via DNS lookup, if low on addresses (default: 1 -unless \fB\-connect\fR/\-noconnect) +unless \fB\-connect\fR used) .HP \fB\-externalip=\fR<ip> .IP @@ -158,8 +162,7 @@ Always query for peer addresses via DNS lookup (default: 0) .HP \fB\-listen\fR .IP -Accept connections from outside (default: 1 if no \fB\-proxy\fR or -\fB\-connect\fR/\-noconnect) +Accept connections from outside (default: 1 if no \fB\-proxy\fR or \fB\-connect\fR) .HP \fB\-listenonion\fR .IP @@ -214,11 +217,6 @@ Connect through SOCKS5 proxy Randomize credentials for every proxy connection. This enables Tor stream isolation (default: 1) .HP -\fB\-rpcserialversion\fR -.IP -Sets the serialization of raw transaction or block hex returned in -non\-verbose mode, non\-segwit(0) or segwit(1) (default: 1) -.HP \fB\-seednode=\fR<ip> .IP Connect to a node to retrieve peer addresses, and disconnect @@ -253,16 +251,6 @@ times. Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway .HP -\fB\-whitelistrelay\fR -.IP -Accept relayed transactions received from whitelisted peers even when -not relaying transactions (default: 1) -.HP -\fB\-whitelistforcerelay\fR -.IP -Force relay of transactions from whitelisted peers even if they violate -local relay policy (default: 1) -.HP \fB\-maxuploadtarget=\fR<n> .IP Tries to keep outbound traffic under the given target (in MiB per 24h), @@ -276,13 +264,21 @@ Do not load the wallet and disable wallet RPC calls .HP \fB\-keypool=\fR<n> .IP -Set key pool size to <n> (default: 100) +Set key pool size to <n> (default: 1000) .HP \fB\-fallbackfee=\fR<amt> .IP A fee rate (in BTC/kB) that will be used when fee estimation has insufficient data (default: 0.0002) .HP +\fB\-discardfee=\fR<amt> +.IP +The fee rate (in BTC/kB) that indicates your tolerance for discarding +change by adding it to the fee (default: 0.0001). Note: An output +is discarded if it is dust at this rate, but we will always +discard up to the dust relay fee and a discard fee above that is +limited by the fee estimate for the longest target +.HP \fB\-mintxfee=\fR<amt> .IP Fees (in BTC/kB) smaller than this are considered zero fee for @@ -309,11 +305,6 @@ Spend unconfirmed change when sending transactions (default: 1) If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: 6) .HP -\fB\-usehd\fR -.IP -Use hierarchical deterministic key generation (HD) after BIP32. Only has -effect during wallet creation/first start (default: 1) -.HP \fB\-walletrbf\fR .IP Send transactions with full\-RBF opt\-in enabled (default: 0) @@ -370,10 +361,16 @@ Append comment to the user agent string .IP Output debugging information (default: 0, supplying <category> is optional). If <category> is not supplied or if <category> = 1, -output all debugging information.<category> can be: addrman, -alert, bench, cmpctblock, coindb, db, http, libevent, lock, -mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc, -selectcoins, tor, zmq, qt. +output all debugging information. <category> can be: net, tor, +mempool, http, bench, zmq, db, rpc, estimatefee, addrman, +selectcoins, reindex, cmpctblock, rand, prune, proxy, mempoolrej, +libevent, coindb, qt, leveldb. +.HP +\fB\-debugexclude=\fR<category> +.IP +Exclude debugging information for a category. Can be used in conjunction +with \fB\-debug\fR=\fI\,1\/\fR to output debug logs for all categories except one +or more specified categories. .HP \fB\-help\-debug\fR .IP @@ -387,11 +384,6 @@ Include IP addresses in debug output (default: 0) .IP Prepend debug output with timestamp (default: 1) .HP -\fB\-minrelaytxfee=\fR<amt> -.IP -Fees (in BTC/kB) smaller than this are considered zero fee for relaying, -mining and transaction creation (default: 0.00001) -.HP \fB\-maxtxfee=\fR<amt> .IP Maximum total fees (in BTC) to use in a single wallet transaction or raw @@ -431,21 +423,32 @@ Maximum size of data in data carrier transactions we relay and mine \fB\-mempoolreplacement\fR .IP Enable transaction replacement in the memory pool (default: 1) +.HP +\fB\-minrelaytxfee=\fR<amt> +.IP +Fees (in BTC/kB) smaller than this are considered zero fee for relaying, +mining and transaction creation (default: 0.00001) +.HP +\fB\-whitelistrelay\fR +.IP +Accept relayed transactions received from whitelisted peers even when +not relaying transactions (default: 1) +.HP +\fB\-whitelistforcerelay\fR +.IP +Force relay of transactions from whitelisted peers even if they violate +local relay policy (default: 1) .PP Block creation options: .HP \fB\-blockmaxweight=\fR<n> .IP -Set maximum BIP141 block weight (default: 3000000) +Set maximum BIP141 block weight (default: 3996000) .HP \fB\-blockmaxsize=\fR<n> .IP -Set maximum block size in bytes (default: 750000) -.HP -\fB\-blockprioritysize=\fR<n> -.IP -Set maximum size of high\-priority/low\-fee transactions in bytes -(default: 0) +Set maximum BIP141 block weight to this * 4. Deprecated, use +blockmaxweight .HP \fB\-blockmintxfee=\fR<amt> .IP @@ -462,11 +465,14 @@ Accept command line and JSON\-RPC commands .IP Accept public REST requests (default: 0) .HP -\fB\-rpcbind=\fR<addr> +\fB\-rpcbind=\fR<addr>[:port] .IP -Bind to given address to listen for JSON\-RPC connections. Use -[host]:port notation for IPv6. This option can be specified -multiple times (default: bind to all interfaces) +Bind to given address to listen for JSON\-RPC connections. This option is +ignored unless \fB\-rpcallowip\fR is also passed. Port is optional and +overrides \fB\-rpcport\fR. Use [host]:port notation for IPv6. This +option can be specified multiple times (default: 127.0.0.1 and +::1 i.e., localhost, or if \fB\-rpcallowip\fR has been specified, +0.0.0.0 and :: i.e., all addresses) .HP \fB\-rpccookiefile=\fR<loc> .IP @@ -501,6 +507,11 @@ single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times .HP +\fB\-rpcserialversion\fR +.IP +Sets the serialization of raw transaction or block hex returned in +non\-verbose mode, non\-segwit(0) or segwit(1) (default: 1) +.HP \fB\-rpcthreads=\fR<n> .IP Set the number of threads to service RPC calls (default: 4) diff --git a/doc/man/bitcoin-tx.1 b/doc/man/bitcoin-tx.1 index 98adf2f5b1..8b72fbde05 100644 --- a/doc/man/bitcoin-tx.1 +++ b/doc/man/bitcoin-tx.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH BITCOIN-TX "1" "February 2017" "bitcoin-tx v0.14.99.0" "User Commands" +.TH BITCOIN-TX "1" "September 2017" "bitcoin-tx v0.15.99.0" "User Commands" .SH NAME -bitcoin-tx \- manual page for bitcoin-tx v0.14.99.0 +bitcoin-tx \- manual page for bitcoin-tx v0.15.99.0 .SH DESCRIPTION -Bitcoin Core bitcoin\-tx utility version v0.14.99.0 +Bitcoin Core bitcoin\-tx utility version v0.15.99.0 .SS "Usage:" .TP bitcoin\-tx [options] <hex\-tx> [commands] @@ -63,6 +63,11 @@ nversion=N .IP Set TX version to N .IP +replaceable(=N) +.IP +Set RBF opt\-in sequence number for input N (if not provided, opt\-in all +available inputs) +.IP outaddr=VALUE:ADDRESS .IP Add address\-based output to TX diff --git a/doc/man/bitcoind.1 b/doc/man/bitcoind.1 index fb066e0c6f..baf747436f 100644 --- a/doc/man/bitcoind.1 +++ b/doc/man/bitcoind.1 @@ -1,9 +1,9 @@ .\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH BITCOIND "1" "February 2017" "bitcoind v0.14.99.0" "User Commands" +.TH BITCOIND "1" "September 2017" "bitcoind v0.15.99.0" "User Commands" .SH NAME -bitcoind \- manual page for bitcoind v0.14.99.0 +bitcoind \- manual page for bitcoind v0.15.99.0 .SH DESCRIPTION -Bitcoin Core Daemon version v0.14.99.0 +Bitcoin Core Daemon version v0.15.99.0 .SS "Usage:" .TP bitcoind [options] @@ -33,9 +33,9 @@ block hash) If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: -00000000000000000013176bf8d7dfeab4e1db31dc93bc311b436e82ab226b90, +0000000000000000003b9ce759c2a087d52abc4266f8f4ebd6d768b89defa50a, testnet: -00000000000128796ee387cf110ccb9d2f36cffaf7f73079c995377c65ac0dcc) +0000000002e9e7b00e1f6dc5123a04aad68dd0f0968d8c7aa45f6640795c37b1) .HP \fB\-conf=\fR<file> .IP @@ -51,7 +51,7 @@ Specify data directory .HP \fB\-dbcache=\fR<n> .IP -Set database cache size in megabytes (4 to 16384, default: 300) +Set database cache size in megabytes (4 to 16384, default: 450) .HP \fB\-loadblock=\fR<file> .IP @@ -70,6 +70,10 @@ Keep the transaction memory pool below <n> megabytes (default: 300) Do not keep transactions in the mempool longer than <n> hours (default: 336) .HP +\fB\-persistmempool\fR +.IP +Whether to save the mempool on shutdown and load on restart (default: 1) +.HP \fB\-blockreconstructionextratxn=\fR<n> .IP Extra transactions to keep in memory for compact block reconstructions @@ -136,8 +140,8 @@ for IPv6 .HP \fB\-connect=\fR<ip> .IP -Connect only to the specified node(s); \fB\-noconnect\fR or \fB\-connect\fR=\fI\,0\/\fR alone to -disable automatic connections +Connect only to the specified node(s); \fB\-connect\fR=\fI\,0\/\fR disables automatic +connections .HP \fB\-discover\fR .IP @@ -151,7 +155,7 @@ Allow DNS lookups for \fB\-addnode\fR, \fB\-seednode\fR and \fB\-connect\fR (def \fB\-dnsseed\fR .IP Query for peer addresses via DNS lookup, if low on addresses (default: 1 -unless \fB\-connect\fR/\-noconnect) +unless \fB\-connect\fR used) .HP \fB\-externalip=\fR<ip> .IP @@ -163,8 +167,7 @@ Always query for peer addresses via DNS lookup (default: 0) .HP \fB\-listen\fR .IP -Accept connections from outside (default: 1 if no \fB\-proxy\fR or -\fB\-connect\fR/\-noconnect) +Accept connections from outside (default: 1 if no \fB\-proxy\fR or \fB\-connect\fR) .HP \fB\-listenonion\fR .IP @@ -219,11 +222,6 @@ Connect through SOCKS5 proxy Randomize credentials for every proxy connection. This enables Tor stream isolation (default: 1) .HP -\fB\-rpcserialversion\fR -.IP -Sets the serialization of raw transaction or block hex returned in -non\-verbose mode, non\-segwit(0) or segwit(1) (default: 1) -.HP \fB\-seednode=\fR<ip> .IP Connect to a node to retrieve peer addresses, and disconnect @@ -258,16 +256,6 @@ times. Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway .HP -\fB\-whitelistrelay\fR -.IP -Accept relayed transactions received from whitelisted peers even when -not relaying transactions (default: 1) -.HP -\fB\-whitelistforcerelay\fR -.IP -Force relay of transactions from whitelisted peers even if they violate -local relay policy (default: 1) -.HP \fB\-maxuploadtarget=\fR<n> .IP Tries to keep outbound traffic under the given target (in MiB per 24h), @@ -281,13 +269,21 @@ Do not load the wallet and disable wallet RPC calls .HP \fB\-keypool=\fR<n> .IP -Set key pool size to <n> (default: 100) +Set key pool size to <n> (default: 1000) .HP \fB\-fallbackfee=\fR<amt> .IP A fee rate (in BTC/kB) that will be used when fee estimation has insufficient data (default: 0.0002) .HP +\fB\-discardfee=\fR<amt> +.IP +The fee rate (in BTC/kB) that indicates your tolerance for discarding +change by adding it to the fee (default: 0.0001). Note: An output +is discarded if it is dust at this rate, but we will always +discard up to the dust relay fee and a discard fee above that is +limited by the fee estimate for the longest target +.HP \fB\-mintxfee=\fR<amt> .IP Fees (in BTC/kB) smaller than this are considered zero fee for @@ -314,11 +310,6 @@ Spend unconfirmed change when sending transactions (default: 1) If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: 6) .HP -\fB\-usehd\fR -.IP -Use hierarchical deterministic key generation (HD) after BIP32. Only has -effect during wallet creation/first start (default: 1) -.HP \fB\-walletrbf\fR .IP Send transactions with full\-RBF opt\-in enabled (default: 0) @@ -375,10 +366,16 @@ Append comment to the user agent string .IP Output debugging information (default: 0, supplying <category> is optional). If <category> is not supplied or if <category> = 1, -output all debugging information.<category> can be: addrman, -alert, bench, cmpctblock, coindb, db, http, libevent, lock, -mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc, -selectcoins, tor, zmq. +output all debugging information. <category> can be: net, tor, +mempool, http, bench, zmq, db, rpc, estimatefee, addrman, +selectcoins, reindex, cmpctblock, rand, prune, proxy, mempoolrej, +libevent, coindb, qt, leveldb. +.HP +\fB\-debugexclude=\fR<category> +.IP +Exclude debugging information for a category. Can be used in conjunction +with \fB\-debug\fR=\fI\,1\/\fR to output debug logs for all categories except one +or more specified categories. .HP \fB\-help\-debug\fR .IP @@ -392,11 +389,6 @@ Include IP addresses in debug output (default: 0) .IP Prepend debug output with timestamp (default: 1) .HP -\fB\-minrelaytxfee=\fR<amt> -.IP -Fees (in BTC/kB) smaller than this are considered zero fee for relaying, -mining and transaction creation (default: 0.00001) -.HP \fB\-maxtxfee=\fR<amt> .IP Maximum total fees (in BTC) to use in a single wallet transaction or raw @@ -436,21 +428,32 @@ Maximum size of data in data carrier transactions we relay and mine \fB\-mempoolreplacement\fR .IP Enable transaction replacement in the memory pool (default: 1) +.HP +\fB\-minrelaytxfee=\fR<amt> +.IP +Fees (in BTC/kB) smaller than this are considered zero fee for relaying, +mining and transaction creation (default: 0.00001) +.HP +\fB\-whitelistrelay\fR +.IP +Accept relayed transactions received from whitelisted peers even when +not relaying transactions (default: 1) +.HP +\fB\-whitelistforcerelay\fR +.IP +Force relay of transactions from whitelisted peers even if they violate +local relay policy (default: 1) .PP Block creation options: .HP \fB\-blockmaxweight=\fR<n> .IP -Set maximum BIP141 block weight (default: 3000000) +Set maximum BIP141 block weight (default: 3996000) .HP \fB\-blockmaxsize=\fR<n> .IP -Set maximum block size in bytes (default: 750000) -.HP -\fB\-blockprioritysize=\fR<n> -.IP -Set maximum size of high\-priority/low\-fee transactions in bytes -(default: 0) +Set maximum BIP141 block weight to this * 4. Deprecated, use +blockmaxweight .HP \fB\-blockmintxfee=\fR<amt> .IP @@ -467,11 +470,14 @@ Accept command line and JSON\-RPC commands .IP Accept public REST requests (default: 0) .HP -\fB\-rpcbind=\fR<addr> +\fB\-rpcbind=\fR<addr>[:port] .IP -Bind to given address to listen for JSON\-RPC connections. Use -[host]:port notation for IPv6. This option can be specified -multiple times (default: bind to all interfaces) +Bind to given address to listen for JSON\-RPC connections. This option is +ignored unless \fB\-rpcallowip\fR is also passed. Port is optional and +overrides \fB\-rpcport\fR. Use [host]:port notation for IPv6. This +option can be specified multiple times (default: 127.0.0.1 and +::1 i.e., localhost, or if \fB\-rpcallowip\fR has been specified, +0.0.0.0 and :: i.e., all addresses) .HP \fB\-rpccookiefile=\fR<loc> .IP @@ -506,6 +512,11 @@ single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times .HP +\fB\-rpcserialversion\fR +.IP +Sets the serialization of raw transaction or block hex returned in +non\-verbose mode, non\-segwit(0) or segwit(1) (default: 1) +.HP \fB\-rpcthreads=\fR<n> .IP Set the number of threads to service RPC calls (default: 4) diff --git a/doc/release-notes.md b/doc/release-notes.md index aa1d1bea14..4ecca7897c 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -8,7 +8,7 @@ Bitcoin Core version *version* is now available from: This is a new major version release, including new features, various bugfixes and performance improvements, as well as updated translations. -Please report bugs using the issue tracker at github: +Please report bugs using the issue tracker at GitHub: <https://github.com/bitcoin/bitcoin/issues> @@ -21,7 +21,7 @@ How to Upgrade If you are running an older version, shut it down. Wait until it has completely shut down (which might take a few minutes for older versions), then run the -installer (on Windows) or just copy over /Applications/Bitcoin-Qt (on Mac) +installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on Mac) or `bitcoind`/`bitcoin-qt` (on Linux). The first time you run version 0.15.0, your chainstate database will be converted to a @@ -56,6 +56,36 @@ frequently tested on them. Notable changes =============== +Miner block size limiting deprecated +------------------------------------ + +Though blockmaxweight has been preferred for limiting the size of blocks returned by +getblocktemplate since 0.13.0, blockmaxsize remained as an option for those who wished +to limit their block size directly. Using this option resulted in a few UI issues as +well as non-optimal fee selection and ever-so-slightly worse performance, and has thus +now been deprecated. Further, the blockmaxsize option is now used only to calculate an +implied blockmaxweight, instead of limiting block size directly. Any miners who wish +to limit their blocks by size, instead of by weight, will have to do so manually by +removing transactions from their block template directly. + +HD-wallets by default +--------------------- +Due to a backward-incompatible change in the wallet database, wallets created +with version 0.16.0 will be rejected by previous versions. Also, version 0.16.0 +will only create hierarchical deterministic (HD) wallets. + +Low-level RPC changes +---------------------- +- The "currentblocksize" value in getmininginfo has been removed. +- The deprecated RPC `getinfo` was removed. It is recommended that the more specific RPCs are used: + * `getblockchaininfo` + * `getnetworkinfo` + * `getwalletinfo` + * `getmininginfo` + +- `dumpwallet` no longer allows overwriting files. This is a security measure + as well as prevents dangerous user mistakes. + Credits ======= diff --git a/doc/release-notes/release-notes-0.15.0.1.md b/doc/release-notes/release-notes-0.15.0.1.md new file mode 100644 index 0000000000..53ab02cbb0 --- /dev/null +++ b/doc/release-notes/release-notes-0.15.0.1.md @@ -0,0 +1,87 @@ +Bitcoin Core version *0.15.0.1* is now available from: + + <https://bitcoin.org/bin/bitcoin-core-0.15.0.1/> + +and + + <https://bitcoincore.org/bin/bitcoin-core-0.15.0.1/> + +This is a minor bug fix for 0.15.0. + +Please report bugs using the issue tracker at GitHub: + + <https://github.com/bitcoin/bitcoin/issues> + +To receive security and update notifications, please subscribe to: + + <https://bitcoincore.org/en/list/announcements/join/> + +How to Upgrade +============== + +If you are running an older version, shut it down. Wait until it has completely +shut down (which might take a few minutes for older versions), then run the +installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on Mac) +or `bitcoind`/`bitcoin-qt` (on Linux). + +The first time you run version 0.15.0 or higher, your chainstate database will +be converted to a new format, which will take anywhere from a few minutes to +half an hour, depending on the speed of your machine. + +The file format of `fee_estimates.dat` changed in version 0.15.0. Hence, a +downgrade from version 0.15.0 or upgrade to version 0.15.0 will cause all fee +estimates to be discarded. + +Note that the block database format also changed in version 0.8.0 and there is no +automatic upgrade code from before version 0.8 to version 0.15.0. Upgrading +directly from 0.7.x and earlier without redownloading the blockchain is not supported. +However, as usual, old wallet versions are still supported. + +Downgrading warning +------------------- + +The chainstate database for this release is not compatible with previous +releases, so if you run 0.15 and then decide to switch back to any +older version, you will need to run the old release with the `-reindex-chainstate` +option to rebuild the chainstate data structures in the old format. + +If your node has pruning enabled, this will entail re-downloading and +processing the entire blockchain. + +Compatibility +============== + +Bitcoin Core is extensively tested on multiple operating systems using +the Linux kernel, macOS 10.8+, and Windows Vista and later. Windows XP is not supported. + +Bitcoin Core should also work on most other Unix-like systems but is not +frequently tested on them. + +Notable changes +=============== + +GUI startup crash issue +------------------------- + +After upgrade to 0.15.0, some clients would crash at startup because a custom +fee setting was configured that no longer exists in the GUI. This is a minimal +patch to avoid this issue from occuring. + +0.15.0.1 Change log +==================== + +- #11332 `46c8d23` Fix possible crash with invalid nCustomFeeRadio in QSettings (achow101, TheBlueMatt) + +Also the manpages were updated, as this was forgotten for 0.15.0. + +Credits +======= + +Thanks to everyone who directly contributed to this release: + +- Andrew Chow +- Matt Corallo +- Jonas Schnelli +- Wladimir J. van der Laan + +As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). diff --git a/doc/release-notes/release-notes-0.15.0.md b/doc/release-notes/release-notes-0.15.0.md new file mode 100644 index 0000000000..29816cacf7 --- /dev/null +++ b/doc/release-notes/release-notes-0.15.0.md @@ -0,0 +1,878 @@ +Bitcoin Core version *0.15.0* is now available from: + + <https://bitcoin.org/bin/bitcoin-core-0.15.0/> + +This is a new major version release, including new features, various bugfixes +and performance improvements, as well as updated translations. + +Please report bugs using the issue tracker at GitHub: + + <https://github.com/bitcoin/bitcoin/issues> + +To receive security and update notifications, please subscribe to: + + <https://bitcoincore.org/en/list/announcements/join/> + +How to Upgrade +============== + +If you are running an older version, shut it down. Wait until it has completely +shut down (which might take a few minutes for older versions), then run the +installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on Mac) +or `bitcoind`/`bitcoin-qt` (on Linux). + +The first time you run version 0.15.0, your chainstate database will be converted to a +new format, which will take anywhere from a few minutes to half an hour, +depending on the speed of your machine. + +The file format of `fee_estimates.dat` changed in version 0.15.0. Hence, a +downgrade from version 0.15.0 or upgrade to version 0.15.0 will cause all fee +estimates to be discarded. + +Note that the block database format also changed in version 0.8.0 and there is no +automatic upgrade code from before version 0.8 to version 0.15.0. Upgrading +directly from 0.7.x and earlier without redownloading the blockchain is not supported. +However, as usual, old wallet versions are still supported. + +Downgrading warning +------------------- + +The chainstate database for this release is not compatible with previous +releases, so if you run 0.15 and then decide to switch back to any +older version, you will need to run the old release with the `-reindex-chainstate` +option to rebuild the chainstate data structures in the old format. + +If your node has pruning enabled, this will entail re-downloading and +processing the entire blockchain. + +Compatibility +============== + +Bitcoin Core is extensively tested on multiple operating systems using +the Linux kernel, macOS 10.8+, and Windows Vista and later. Windows XP is not supported. + +Bitcoin Core should also work on most other Unix-like systems but is not +frequently tested on them. + +Notes for 0.15.0 +================ + +Current SegWit support +---------------------- + +Version 0.15.0 supports adding a segregated witness address via the `addwitnessaddress` RPC, but +please note that this is a testing/expert RPC, which does not guarantee recovery from backup. Only use +this RPC if you know what you are doing. More complete wallet support for segregated witness is coming +in a next version. + +Rescanning with encrypted wallets +--------------------------------- + +As in previous versions, when using an encrypted HD wallet, the keypool cannot be topped up without unlocking +the wallet. This means that currently, in order to recover from a backup of an encrypted HD wallet, the user +must unlock the wallet with a really long timeout and manually trigger a rescan, otherwise they risk missing +some keys when auto-topup cannot run. Unfortunately there is no `rescan` RPC in this version, that will be +included in a future version, so for now a rescan can be triggered using one of the `import*` commands, using +a dummy address generated by another (trusted) wallet. + +Notable changes +=============== + +Performance Improvements +------------------------ + +Version 0.15 contains a number of significant performance improvements, which make +Initial Block Download, startup, transaction and block validation much faster: + +- The chainstate database (which is used for tracking UTXOs) has been changed + from a per-transaction model to a per-output model (See [PR 10195](https://github.com/bitcoin/bitcoin/pull/10195)). Advantages of this model + are that it: + - avoids the CPU overhead of deserializing and serializing the unused outputs; + - has more predictable memory usage; + - uses simpler code; + - is adaptable to various future cache flushing strategies. + + As a result, validating the blockchain during Initial Block Download (IBD) and reindex + is ~30-40% faster, uses 10-20% less memory, and flushes to disk far less frequently. + The only downside is that the on-disk database is 15% larger. During the conversion from the previous format + a few extra gigabytes may be used. +- Earlier versions experienced a spike in memory usage while flushing UTXO updates to disk. + As a result, only half of the available memory was actually used as cache, and the other half was + reserved to accommodate flushing. This is no longer the case (See [PR 10148](https://github.com/bitcoin/bitcoin/pull/10148)), and the entirety of + the available cache (see `-dbcache`) is now actually used as cache. This reduces the flushing + frequency by a factor 2 or more. +- In previous versions, signature validation for transactions has been cached when the + transaction is accepted to the mempool. Version 0.15 extends this to cache the entire script + validity (See [PR 10192](https://github.com/bitcoin/bitcoin/pull/10192)). This means that if a transaction in a block has already been accepted to the + mempool, the scriptSig does not need to be re-evaluated. Empirical tests show that + this results in new block validation being 40-50% faster. +- LevelDB has been upgraded to version 1.20 (See [PR 10544](https://github.com/bitcoin/bitcoin/pull/10544)). This version contains hardware acceleration for CRC + on architectures supporting SSE 4.2. As a result, synchronization and block validation are now faster. +- SHA256 hashing has been optimized for architectures supporting SSE 4 (See [PR 10821](https://github.com/bitcoin/bitcoin/pull/10821)). SHA256 is around + 50% faster on supported hardware, which results in around 5% faster IBD and block + validation. In version 0.15, SHA256 hardware optimization is disabled in release builds by + default, but can be enabled by using `--enable-experimental-asm` when building. +- Refill of the keypool no longer flushes the wallet between each key which resulted in a ~20x speedup in creating a new wallet. Part of this speedup was used to increase the default keypool to 1000 keys to make recovery more robust. (See [PR 10831](https://github.com/bitcoin/bitcoin/pull/10831)). + +Fee Estimation Improvements +--------------------------- + +Fee estimation has been significantly improved in version 0.15, with more accurate fee estimates used by the wallet and a wider range of options for advanced users of the `estimatesmartfee` and `estimaterawfee` RPCs (See [PR 10199](https://github.com/bitcoin/bitcoin/pull/10199)). + +### Changes to internal logic and wallet behavior + +- Internally, estimates are now tracked on 3 different time horizons. This allows for longer targets and means estimates adjust more quickly to changes in conditions. +- Estimates can now be *conservative* or *economical*. *Conservative* estimates use longer time horizons to produce an estimate which is less susceptible to rapid changes in fee conditions. *Economical* estimates use shorter time horizons and will be more affected by short-term changes in fee conditions. Economical estimates may be considerably lower during periods of low transaction activity (for example over weekends), but may result in transactions being unconfirmed if prevailing fees increase rapidly. +- By default, the wallet will use conservative fee estimates to increase the reliability of transactions being confirmed within the desired target. For transactions that are marked as replaceable, the wallet will use an economical estimate by default, since the fee can be 'bumped' if the fee conditions change rapidly (See [PR 10589](https://github.com/bitcoin/bitcoin/pull/10589)). +- Estimates can now be made for confirmation targets up to 1008 blocks (one week). +- More data on historical fee rates is stored, leading to more precise fee estimates. +- Transactions which leave the mempool due to eviction or other non-confirmed reasons are now taken into account by the fee estimation logic, leading to more accurate fee estimates. +- The fee estimation logic will make sure enough data has been gathered to return a meaningful estimate. If there is insufficient data, a fallback default fee is used. + +### Changes to fee estimate RPCs + +- The `estimatefee` RPC is now deprecated in favor of using only `estimatesmartfee` (which is the implementation used by the GUI) +- The `estimatesmartfee` RPC interface has been changed (See [PR 10707](https://github.com/bitcoin/bitcoin/pull/10707)): + - The `nblocks` argument has been renamed to `conf_target` (to be consistent with other RPC methods). + - An `estimate_mode` argument has been added. This argument takes one of the following strings: `CONSERVATIVE`, `ECONOMICAL` or `UNSET` (which defaults to `CONSERVATIVE`). + - The RPC return object now contains an `errors` member, which returns errors encountered during processing. + - If Bitcoin Core has not been running for long enough and has not seen enough blocks or transactions to produce an accurate fee estimation, an error will be returned (previously a value of -1 was used to indicate an error, which could be confused for a feerate). +- A new `estimaterawfee` RPC is added to provide raw fee data. External clients can query and use this data in their own fee estimation logic. + +Multi-wallet support +-------------------- + +Bitcoin Core now supports loading multiple, separate wallets (See [PR 8694](https://github.com/bitcoin/bitcoin/pull/8694), [PR 10849](https://github.com/bitcoin/bitcoin/pull/10849)). The wallets are completely separated, with individual balances, keys and received transactions. + +Multi-wallet is enabled by using more than one `-wallet` argument when starting Bitcoin, either on the command line or in the Bitcoin config file. + +**In Bitcoin-Qt, only the first wallet will be displayed and accessible for creating and signing transactions.** GUI selectable multiple wallets will be supported in a future version. However, even in 0.15 other loaded wallets will remain synchronized to the node's current tip in the background. This can be useful if running a pruned node, since loading a wallet where the most recent sync is beyond the pruned height results in having to download and revalidate the whole blockchain. Continuing to synchronize all wallets in the background avoids this problem. + +Bitcoin Core 0.15.0 contains the following changes to the RPC interface and `bitcoin-cli` for multi-wallet: + +* When running Bitcoin Core with a single wallet, there are **no** changes to the RPC interface or `bitcoin-cli`. All RPC calls and `bitcoin-cli` commands continue to work as before. +* When running Bitcoin Core with multi-wallet, all *node-level* RPC methods continue to work as before. HTTP RPC requests should be send to the normal `<RPC IP address>:<RPC port>/` endpoint, and `bitcoin-cli` commands should be run as before. A *node-level* RPC method is any method which does not require access to the wallet. +* When running Bitcoin Core with multi-wallet, *wallet-level* RPC methods must specify the wallet for which they're intended in every request. HTTP RPC requests should be send to the `<RPC IP address>:<RPC port>/wallet/<wallet name>/` endpoint, for example `127.0.0.1:8332/wallet/wallet1.dat/`. `bitcoin-cli` commands should be run with a `-rpcwallet` option, for example `bitcoin-cli -rpcwallet=wallet1.dat getbalance`. +* A new *node-level* `listwallets` RPC method is added to display which wallets are currently loaded. The names returned by this method are the same as those used in the HTTP endpoint and for the `rpcwallet` argument. + +Note that while multi-wallet is now fully supported, the RPC multi-wallet interface should be considered unstable for version 0.15.0, and there may backwards-incompatible changes in future versions. + +Replace-by-fee control in the GUI +--------------------------------- + +Bitcoin Core has supported creating opt-in replace-by-fee (RBF) transactions +since version 0.12.0, and since version 0.14.0 has included a `bumpfee` RPC method to +replace unconfirmed opt-in RBF transactions with a new transaction that pays +a higher fee. + +In version 0.15, creating an opt-in RBF transaction and replacing the unconfirmed +transaction with a higher-fee transaction are both supported in the GUI (See [PR 9592](https://github.com/bitcoin/bitcoin/pull/9592)). + +Removal of Coin Age Priority +---------------------------- + +In previous versions of Bitcoin Core, a portion of each block could be reserved for transactions based on the age and value of UTXOs they spent. This concept (Coin Age Priority) is a policy choice by miners, and there are no consensus rules around the inclusion of Coin Age Priority transactions in blocks. In practice, only a few miners continue to use Coin Age Priority for transaction selection in blocks. Bitcoin Core 0.15 removes all remaining support for Coin Age Priority (See [PR 9602](https://github.com/bitcoin/bitcoin/pull/9602)). This has the following implications: + +- The concept of *free transactions* has been removed. High Coin Age Priority transactions would previously be allowed to be relayed even if they didn't attach a miner fee. This is no longer possible since there is no concept of Coin Age Priority. The `-limitfreerelay` and `-relaypriority` options which controlled relay of free transactions have therefore been removed. +- The `-sendfreetransactions` option has been removed, since almost all miners do not include transactions which do not attach a transaction fee. +- The `-blockprioritysize` option has been removed. +- The `estimatepriority` and `estimatesmartpriority` RPCs have been removed. +- The `getmempoolancestors`, `getmempooldescendants`, `getmempoolentry` and `getrawmempool` RPCs no longer return `startingpriority` and `currentpriority`. +- The `prioritisetransaction` RPC no longer takes a `priority_delta` argument, which is replaced by a `dummy` argument for backwards compatibility with clients using positional arguments. The RPC is still used to change the apparent fee-rate of the transaction by using the `fee_delta` argument. +- `-minrelaytxfee` can now be set to 0. If `minrelaytxfee` is set, then fees smaller than `minrelaytxfee` (per kB) are rejected from relaying, mining and transaction creation. This defaults to 1000 satoshi/kB. +- The `-printpriority` option has been updated to only output the fee rate and hash of transactions included in a block by the mining code. + +Mempool Persistence Across Restarts +----------------------------------- + +Version 0.14 introduced mempool persistence across restarts (the mempool is saved to a `mempool.dat` file in the data directory prior to shutdown and restores the mempool when the node is restarted). Version 0.15 allows this feature to be switched on or off using the `-persistmempool` command-line option (See [PR 9966](https://github.com/bitcoin/bitcoin/pull/9966)). By default, the option is set to true, and the mempool is saved on shutdown and reloaded on startup. If set to false, the `mempool.dat` file will not be loaded on startup or saved on shutdown. + +New RPC methods +--------------- + +Version 0.15 introduces several new RPC methods: + +- `abortrescan` stops current wallet rescan, e.g. when triggered by an `importprivkey` call (See [PR 10208](https://github.com/bitcoin/bitcoin/pull/10208)). +- `combinerawtransaction` accepts a JSON array of raw transactions and combines them into a single raw transaction (See [PR 10571](https://github.com/bitcoin/bitcoin/pull/10571)). +- `estimaterawfee` returns raw fee data so that customized logic can be implemented to analyze the data and calculate estimates. See [Fee Estimation Improvements](#fee-estimation-improvements) for full details on changes to the fee estimation logic and interface. +- `getchaintxstats` returns statistics about the total number and rate of transactions + in the chain (See [PR 9733](https://github.com/bitcoin/bitcoin/pull/9733)). +- `listwallets` lists wallets which are currently loaded. See the *Multi-wallet* section + of these release notes for full details (See [Multi-wallet support](#multi-wallet-support)). +- `uptime` returns the total runtime of the `bitcoind` server since its last start (See [PR 10400](https://github.com/bitcoin/bitcoin/pull/10400)). + +Low-level RPC changes +--------------------- + +- When using Bitcoin Core in multi-wallet mode, RPC requests for wallet methods must specify + the wallet that they're intended for. See [Multi-wallet support](#multi-wallet-support) for full details. + +- The new database model no longer stores information about transaction + versions of unspent outputs (See [Performance improvements](#performance-improvements)). This means that: + - The `gettxout` RPC no longer has a `version` field in the response. + - The `gettxoutsetinfo` RPC reports `hash_serialized_2` instead of `hash_serialized`, + which does not commit to the transaction versions of unspent outputs, but does + commit to the height and coinbase information. + - The `getutxos` REST path no longer reports the `txvers` field in JSON format, + and always reports 0 for transaction versions in the binary format + +- The `estimatefee` RPC is deprecated. Clients should switch to using the `estimatesmartfee` RPC, which returns better fee estimates. See [Fee Estimation Improvements](#fee-estimation-improvements) for full details on changes to the fee estimation logic and interface. + +- The `gettxoutsetinfo` response now contains `disk_size` and `bogosize` instead of + `bytes_serialized`. The first is a more accurate estimate of actual disk usage, but + is not deterministic. The second is unrelated to disk usage, but is a + database-independent metric of UTXO set size: it counts every UTXO entry as 50 + the + length of its scriptPubKey (See [PR 10426](https://github.com/bitcoin/bitcoin/pull/10426)). + +- `signrawtransaction` can no longer be used to combine multiple transactions into a single transaction. Instead, use the new `combinerawtransaction` RPC (See [PR 10571](https://github.com/bitcoin/bitcoin/pull/10571)). + +- `fundrawtransaction` no longer accepts a `reserveChangeKey` option. This option used to allow RPC users to fund a raw transaction using an key from the keypool for the change address without removing it from the available keys in the keypool. The key could then be re-used for a `getnewaddress` call, which could potentially result in confusing or dangerous behaviour (See [PR 10784](https://github.com/bitcoin/bitcoin/pull/10784)). + +- `estimatepriority` and `estimatesmartpriority` have been removed. See [Removal of Coin Age Priority](#removal-of-coin-age-priority). + +- The `listunspent` RPC now takes a `query_options` argument (see [PR 8952](https://github.com/bitcoin/bitcoin/pull/8952)), which is a JSON object + containing one or more of the following members: + - `minimumAmount` - a number specifying the minimum value of each UTXO + - `maximumAmount` - a number specifying the maximum value of each UTXO + - `maximumCount` - a number specifying the minimum number of UTXOs + - `minimumSumAmount` - a number specifying the minimum sum value of all UTXOs + +- The `getmempoolancestors`, `getmempooldescendants`, `getmempoolentry` and `getrawmempool` RPCs no longer return `startingpriority` and `currentpriority`. See [Removal of Coin Age Priority](#removal-of-coin-age-priority). + +- The `dumpwallet` RPC now returns the full absolute path to the dumped wallet. It + used to return no value, even if successful (See [PR 9740](https://github.com/bitcoin/bitcoin/pull/9740)). + +- In the `getpeerinfo` RPC, the return object for each peer now returns an `addrbind` member, which contains the ip address and port of the connection to the peer. This is in addition to the `addrlocal` member which contains the ip address and port of the local node as reported by the peer (See [PR 10478](https://github.com/bitcoin/bitcoin/pull/10478)). + +- The `disconnectnode` RPC can now disconnect a node specified by node ID (as well as by IP address/port). To disconnect a node based on node ID, call the RPC with the new `nodeid` argument (See [PR 10143](https://github.com/bitcoin/bitcoin/pull/10143)). + +- The second argument in `prioritisetransaction` has been renamed from `priority_delta` to `dummy` since Bitcoin Core no longer has a concept of coin age priority. The `dummy` argument has no functional effect, but is retained for positional argument compatibility. See [Removal of Coin Age Priority](#removal-of-coin-age-priority). + +- The `resendwallettransactions` RPC throws an error if the `-walletbroadcast` option is set to false (See [PR 10995](https://github.com/bitcoin/bitcoin/pull/10995)). + +- The second argument in the `submitblock` RPC argument has been renamed from `parameters` to `dummy`. This argument never had any effect, and the renaming is simply to communicate this fact to the user (See [PR 10191](https://github.com/bitcoin/bitcoin/pull/10191)) + (Clients should, however, use positional arguments for `submitblock` in order to be compatible with BIP 22.) + +- The `verbose` argument of `getblock` has been renamed to `verbosity` and now takes an integer from 0 to 2. Verbose level 0 is equivalent to `verbose=false`. Verbose level 1 is equivalent to `verbose=true`. Verbose level 2 will give the full transaction details of each transaction in the output as given by `getrawtransaction`. The old behavior of using the `verbose` named argument and a boolean value is still maintained for compatibility. + +- Error codes have been updated to be more accurate for the following error cases (See [PR 9853](https://github.com/bitcoin/bitcoin/pull/9853)): + - `getblock` now returns RPC_MISC_ERROR if the block can't be found on disk (for + example if the block has been pruned). Previously returned RPC_INTERNAL_ERROR. + - `pruneblockchain` now returns RPC_MISC_ERROR if the blocks cannot be pruned + because the node is not in pruned mode. Previously returned RPC_METHOD_NOT_FOUND. + - `pruneblockchain` now returns RPC_INVALID_PARAMETER if the blocks cannot be pruned + because the supplied timestamp is too late. Previously returned RPC_INTERNAL_ERROR. + - `pruneblockchain` now returns RPC_MISC_ERROR if the blocks cannot be pruned + because the blockchain is too short. Previously returned RPC_INTERNAL_ERROR. + - `setban` now returns RPC_CLIENT_INVALID_IP_OR_SUBNET if the supplied IP address + or subnet is invalid. Previously returned RPC_CLIENT_NODE_ALREADY_ADDED. + - `setban` now returns RPC_CLIENT_INVALID_IP_OR_SUBNET if the user tries to unban + a node that has not previously been banned. Previously returned RPC_MISC_ERROR. + - `removeprunedfunds` now returns RPC_WALLET_ERROR if `bitcoind` is unable to remove + the transaction. Previously returned RPC_INTERNAL_ERROR. + - `removeprunedfunds` now returns RPC_INVALID_PARAMETER if the transaction does not + exist in the wallet. Previously returned RPC_INTERNAL_ERROR. + - `fundrawtransaction` now returns RPC_INVALID_ADDRESS_OR_KEY if an invalid change + address is provided. Previously returned RPC_INVALID_PARAMETER. + - `fundrawtransaction` now returns RPC_WALLET_ERROR if `bitcoind` is unable to create + the transaction. The error message provides further details. Previously returned + RPC_INTERNAL_ERROR. + - `bumpfee` now returns RPC_INVALID_PARAMETER if the provided transaction has + descendants in the wallet. Previously returned RPC_MISC_ERROR. + - `bumpfee` now returns RPC_INVALID_PARAMETER if the provided transaction has + descendants in the mempool. Previously returned RPC_MISC_ERROR. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has + has been mined or conflicts with a mined transaction. Previously returned + RPC_INVALID_ADDRESS_OR_KEY. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction is not + BIP 125 replaceable. Previously returned RPC_INVALID_ADDRESS_OR_KEY. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has already + been bumped by a different transaction. Previously returned RPC_INVALID_REQUEST. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction contains + inputs which don't belong to this wallet. Previously returned RPC_INVALID_ADDRESS_OR_KEY. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has multiple change + outputs. Previously returned RPC_MISC_ERROR. + - `bumpfee` now returns RPC_WALLET_ERROR if the provided transaction has no change + output. Previously returned RPC_MISC_ERROR. + - `bumpfee` now returns RPC_WALLET_ERROR if the fee is too high. Previously returned + RPC_MISC_ERROR. + - `bumpfee` now returns RPC_WALLET_ERROR if the fee is too low. Previously returned + RPC_MISC_ERROR. + - `bumpfee` now returns RPC_WALLET_ERROR if the change output is too small to bump the + fee. Previously returned RPC_MISC_ERROR. + +0.15.0 Change log +================= + +### RPC and other APIs +- #9485 `61a640e` ZMQ example using python3 and asyncio (mcelrath) +- #9894 `0496e15` remove 'label' filter for rpc command help (instagibbs) +- #9853 `02bd6e9` Fix error codes from various RPCs (jnewbery) +- #9842 `598ef9c` Fix RPC failure testing (continuation of #9707) (jnewbery) +- #10038 `d34995a` Add mallocinfo mode to `getmemoryinfo` RPC (laanwj) +- #9500 `3568b30` [Qt][RPC] Autocomplete commands for 'help' command in debug console (achow101) +- #10056 `e6156a0` [zmq] Call va_end() on va_start()ed args (kallewoof) +- #10086 `7438cea` Trivial: move rpcserialversion into RPC option group (jlopp) +- #10150 `350b224` [rpc] Add logging rpc (jnewbery) +- #10208 `393160c` [wallet] Rescan abortability (kallewoof) +- #10143 `a987def` [net] Allow disconnectnode RPC to be called with node id (jnewbery) +- #10281 `0e8499c` doc: Add RPC interface guidelines (laanwj) +- #9733 `d4732f3` Add getchaintxstats RPC (sipa) +- #10310 `f4b15e2` [doc] Add hint about getmempoolentry to getrawmempool help (kallewoof) +- #8704 `96c850c` [RPC] Transaction details in getblock (achow101) +- #8952 `9390845` Add query options to listunspent RPC call (pedrobranco) +- #10413 `08ac35a` Fix docs (there's no rpc command setpaytxfee) (RHavar) +- #8384 `e317c0d` Add witness data output to TxInError messages (instagibbs) +- #9571 `4677151` RPC: getblockchaininfo returns BIP signaling statistics (pinheadmz) +- #10450 `ef2d062` Fix bumpfee rpc "errors" return value (ryanofsky) +- #10475 `39039b1` [RPC] getmempoolinfo mempoolminfee is a BTC/KB feerate (instagibbs) +- #10478 `296928e` rpc: Add listen address to incoming connections in `getpeerinfo` (laanwj) +- #10403 `08d0390` Fix importmulti failure to return rescan errors (ryanofsky) +- #9740 `9fec4da` Add friendly output to dumpwallet (aideca) +- #10426 `16f6c98` Replace bytes_serialized with bogosize (sipa) +- #10252 `980deaf` RPC/Mining: Restore API compatibility for prioritisetransaction (luke-jr) +- #9672 `46311e7` Opt-into-RBF for RPC & bitcoin-tx (luke-jr) +- #10481 `9c248e3` Decodehextx scripts sanity check (achow101) +- #10488 `fa1f106` Note that the prioritizetransaction dummy value is deprecated, and has no meaning (TheBlueMatt) +- #9738 `c94b89e` gettxoutproof() should return consistent result (jnewbery) +- #10191 `00350bd` [trivial] Rename unused RPC arguments 'dummy' (jnewbery) +- #10627 `b62b4c8` fixed listunspent rpc convert parameter (tnakagawa) +- #10412 `bef02fb` Improve wallet rescan API (ryanofsky) +- #10400 `1680ee0` [RPC] Add an uptime command that displays the amount of time (in seconds) bitcoind has been running (rvelhote) +- #10683 `d81bec7` rpc: Move the `generate` RPC call to rpcwallet (laanwj) +- #10710 `30bc0f6` REST/RPC example update (Mirobit) +- #10747 `9edda0c` [rpc] fix verbose argument for getblock in bitcoin-cli (jnewbery) +- #10589 `104f5f2` More economical fee estimates for RBF and RPC options to control (morcos) +- #10543 `b27b004` Change API to estimaterawfee (morcos) +- #10807 `afd2fca` getbalance example covers at least 6 confirms (instagibbs) +- #10707 `75b5643` Better API for estimatesmartfee RPC (morcos) +- #10784 `9e8d6a3` Do not allow users to get keys from keypool without reserving them (TheBlueMatt) +- #10857 `d445a2c` [RPC] Add a deprecation warning to getinfo's output (achow101) +- #10571 `adf170d` [RPC]Move transaction combining from signrawtransaction to new RPC (achow101) +- #10783 `041dad9` [RPC] Various rpc argument fixes (instagibbs) +- #9622 `6ef3c7e` [rpc] listsinceblock should include lost transactions when parameter is a reorg'd block (kallewoof) +- #10799 `8537187` Prevent user from specifying conflicting parameters to fundrawtx (TheBlueMatt) +- #10931 `0b11a07` Fix misleading "Method not found" multiwallet errors (ryanofsky) +- #10788 `f66c596` [RPC] Fix addwitnessaddress by replacing ismine with producesignature (achow101) +- #10999 `627c3c0` Fix amounts formatting in `decoderawtransaction` (laanwj) +- #11002 `4268426` [wallet] return correct error code from resendwallettransaction (jnewbery) +- #11029 `96a63a3` [RPC] trivial: gettxout no longer shows version of tx (FelixWeis) +- #11083 `6c2b008` Fix combinerawtransaction RPC help result section (jonasnick) +- #11027 `07164bb` [RPC] Only return hex field once in getrawtransaction (achow101) +- #10698 `5af6572` Be consistent in calling transactions "replaceable" for Opt-In RBF (TheBlueMatt) + +### Block and transaction handling +- #9801 `a8c5751` Removed redundant parameter from mempool.PrioritiseTransaction (gubatron) +- #9819 `1efc99c` Remove harmless read of unusued priority estimates (morcos) +- #9822 `b7547fa` Remove block file location upgrade code (benma) +- #9602 `30ff3a2` Remove coin age priority and free transactions - implementation (morcos) +- #9548 `47510ad` Remove min reasonable fee (morcos) +- #10249 `c73af54` Switch CCoinsMap from boost to std unordered_map (sipa) +- #9966 `2a183de` Control mempool persistence using a command line parameter (jnewbery) +- #10199 `318ea50` Better fee estimates (morcos) +- #10196 `bee3529` Bugfix: PrioritiseTransaction updates the mempool tx counter (sdaftuar) +- #10195 `1088b02` Switch chainstate db and cache to per-txout model (sipa) +- #10284 `c2ab38b` Always log debug information for fee calculation in CreateTransaction (morcos) +- #10503 `efbcf2b` Use REJECT_DUPLICATE for already known and conflicted txn (sipa) +- #10537 `b3eb0d6` Few Minor per-utxo assert-semantics re-adds and tweak (TheBlueMatt) +- #10626 `8c841a3` doc: Remove outdated minrelaytxfee comment (MarcoFalke) +- #10559 `234ffc6` Change semantics of HaveCoinInCache to match HaveCoin (morcos) +- #10581 `7878353` Simplify return values of GetCoin/HaveCoin(InCache) (sipa) +- #10684 `a381f6a` Remove no longer used mempool.exists(outpoint) (morcos) +- #10148 `d4e551a` Use non-atomic flushing with block replay (sipa) +- #10685 `30c2130` Clarify CCoinsViewMemPool documentation (TheBlueMatt) +- #10558 `90a002e` Address nits from per-utxo change (morcos) +- #10706 `6859ad2` Improve wallet fee logic and fix GUI bugs (morcos) +- #10526 `754aa02` Force on-the-fly compaction during pertxout upgrade (sipa) +- #10985 `d896d5c` Add undocumented -forcecompactdb to force LevelDB compactions (sipa) +- #10292 `e4bbd3d` Improved efficiency in COutPoint constructors (mm-s) +- #10290 `8d6d43e` Add -stopatheight for benchmarking (sipa) + +### P2P protocol and network code +- #9726 `7639d38` netbase: Do not print an error on connection timeouts through proxy (laanwj) +- #9805 `5b583ef` Add seed.btc.petertodd.org to mainnet DNS seeds (petertodd) +- #9861 `22f609f` Trivial: Debug log ambiguity fix for peer addrs (keystrike) +- #9774 `90cb2a2` Enable host lookups for -proxy and -onion parameters (jmcorgan) +- #9558 `7b585cf` Clarify assumptions made about when BlockCheck is called (TheBlueMatt) +- #10135 `e19586a` [p2p] Send the correct error code in reject messages (jnewbery) +- #9665 `eab00d9` Use cached [compact] blocks to respond to getdata messages (TheBlueMatt) +- #10215 `a077a90` Check interruptNet during dnsseed lookups (TheBlueMatt) +- #10234 `faf2dea` [net] listbanned RPC and QT should show correct banned subnets (jnewbery) +- #10134 `314ebdf` [qa] Fixes segwit block relay test after inv-direct-fetch was disabled (sdaftuar) +- #10351 `3f57c55` removed unused code in INV message (Greg-Griffith) +- #10061 `ae78609` [net] Added SetSocketNoDelay() utility function (tjps) +- #10408 `28c6e8d` Net: Improvements to Tor control port parser (str4d) +- #10460 `5c63d66` Broadcast address every day, not 9 hours (sipa) +- #10471 `400fdd0` Denote functions CNode::GetRecvVersion() and CNode::GetRefCount() as const (pavlosantoniou) +- #10345 `67700b3` [P2P] Timeout for headers sync (sdaftuar) +- #10564 `8d9f45e` Return early in IsBanned (gmaxwell) +- #10587 `de8db47` Net: Fix resource leak in ReadBinaryFile(...) (practicalswift) +- #9549 `b33ca14` [net] Avoid possibility of NULL pointer dereference in MarkBlockAsInFlight(...) (practicalswift) +- #10446 `2772dc9` net: avoid extra dns query per seed (theuni) +- #10824 `9dd6a2b` Avoid unnecessary work in SetNetworkActive (promag) +- #10948 `df3a6f4` p2p: Hardcoded seeds update pre-0.15 branch (laanwj) +- #10977 `02f4c4a` [net] Fix use of uninitialized value in getnetworkinfo(const JSONRPCRequest&) (practicalswift) +- #10982 `c8b62c7` Disconnect network service bits 6 and 8 until Aug 1, 2018 (TheBlueMatt) +- #11012 `0e5cff6` Make sure to clean up mapBlockSource if we've already seen the block (theuni) + +### Validation +- #9725 `67023e9` CValidationInterface Cleanups (TheBlueMatt) +- #10178 `2584925` Remove CValidationInterface::UpdatedTransaction (TheBlueMatt) +- #10201 `a6548a4` pass Consensus::Params& to functions in validation.cpp and make them static (mariodian) +- #10297 `431a548` Simplify DisconnectBlock arguments/return value (sipa) +- #10464 `f94b7d5` Introduce static DoWarning (simplify UpdateTip) (jtimon) +- #10569 `2e7d8f8` Fix stopatheight (achow101) +- #10192 `2935b46` Cache full script execution results in addition to signatures (TheBlueMatt) +- #10179 `21ed30a` Give CValidationInterface Support for calling notifications on the CScheduler Thread (TheBlueMatt) +- #10557 `66270a4` Make check to distinguish between orphan txs and old txs more efficient (morcos) +- #10775 `7c2400c` nCheckDepth chain height fix (romanornr) +- #10821 `16240f4` Add SSE4 optimized SHA256 (sipa) +- #10854 `04d395e` Avoid using sizes on non-fixed-width types to derive protocol constants (gmaxwell) +- #10945 `2a50b11` Update defaultAssumeValid according to release-process.md (gmaxwell) +- #10986 `2361208` Update chain transaction statistics (sipa) +- #11028 `6bdf4b3` Avoid masking of difficulty adjustment errors by checkpoints (sipa) +- #9533 `cb598cf` Allow non-power-of-2 signature cache sizes (sipa) +- #9208 `acd9957` Improve DisconnectTip performance (sdaftuar) +- #10618 `f90603a` Remove confusing MAX_BLOCK_BASE_SIZE (gmaxwell) +- #10758 `bd92424` Fix some chainstate-init-order bugs (TheBlueMatt) +- #10550 `b7296bc` Don't return stale data from CCoinsViewCache::Cursor() (ryanofsky) +- #10998 `2507fd5` Fix upgrade cancel warnings (TheBlueMatt) +- #9868 `cbdb473` Abstract out the command line options for block assembly (sipa) + +### Build system +- #9727 `5f0556d` Remove fallbacks for boost_filesystem < v3 (laanwj) +- #9788 `50a2265` gitian: bump descriptors for master (theuni) +- #9794 `7ca2f54` Minor update to qrencode package builder (mitchellcash) +- #9514 `2cc0df1` release: Windows signing script (theuni) +- #9921 `8b789d8` build: Probe MSG_DONTWAIT in the same way as MSG_NOSIGNAL (laanwj) +- #10011 `32d1b34` build: Fix typo s/HAVE_DONTWAIT/HAVE_MSG_DONTWAIT (laanwj) +- #9946 `90dd9e6` Fix build errors if spaces in path or parent directory (pinheadmz) +- #10136 `81da4c7` build: Disable Wshadow warning (laanwj) +- #10166 `64962ae` Ignore Doxyfile generated from Doxyfile.in template (paveljanik) +- #10239 `0416ea9` Make Boost use std::atomic internally (sipa) +- #10228 `27faa6c` build: regenerate bitcoin-config.h as necessary (theuni) +- #10273 `8979f45` [scripts] Minor improvements to `macdeployqtplus` script (chrisgavin) +- #10325 `a26280b` 0.15.0 Depends Updates (fanquake) +- #10328 `79aeff6` Update contrib/debian to latest Ubuntu PPA upload (TheBlueMatt) +- #7522 `d25449f` Bugfix: Only use git for build info if the repository is actually the right one (luke-jr) +- #10489 `e654d61` build: silence gcc7's implicit fallthrough warning (theuni) +- #10549 `ad1a13e` Avoid printing generic and duplicated "checking for QT" during ./configure (drizzt) +- #10628 `8465b68` [depends] expat 2.2.1 (fanquake) +- #10806 `db825d2` build: verify that the assembler can handle crc32 functions (theuni) +- #10766 `b4d03be` Building Environment: Set ARFLAGS to cr (ReneNyffenegger) +- #10803 `91edda8` Explicitly search for bdb5.3 (pstratem) +- #10855 `81560b0` random: only use getentropy on openbsd (theuni) +- #10508 `1caafa6` Run Qt wallet tests on travis (ryanofsky) +- #10851 `e222618` depends: fix fontconfig with newer glibc (theuni) +- #10971 `88b1e4b` build: fix missing sse42 in depends builds (theuni) +- #11097 `129b03f` gitian: quick hack to fix version string in releases (theuni) +- #10039 `919aaf6` Fix compile errors with Qt 5.3.2 and Boost 1.55.0 (ryanofsky) +- #10168 `7032021` Fix build warning from #error text (jnewbery) +- #10301 `318392c` Check if sys/random.h is required for getentropy (jameshilliard) + +### GUI +- #9724 `1a9fd5c` Qt/Intro: Add explanation of IBD process (luke-jr) +- #9834 `b00ba62` qt: clean up initialize/shutdown signals (benma) +- #9481 `ce01e62` [Qt] Show more significant warning if we fall back to the default fee (jonasschnelli) +- #9974 `b9f930b` Add basic Qt wallet test (ryanofsky) +- #9690 `a387d3a` Change 'Clear' button string to 'Reset' (da2x) +- #9592 `9c7b7cf` [Qt] Add checkbox in the GUI to opt-in to RBF when creating a transaction (ryanofsky) +- #10098 `2b477e6` Make qt wallet test compatible with qt4 (ryanofsky) +- #9890 `1fa4ae6` Add a button to open the config file in a text editor (ericshawlinux) +- #10156 `51833a1` Fix for issues with startup and multiple monitors on windows (AllanDoensen) +- #10177 `de01da7` Changed "Send" button default status from true to false (KibbledJiveElkZoo) +- #10221 `e96486c` Stop treating coinbase outputs differently in GUI: show them at 1conf (TheBlueMatt) +- #10231 `987a6c0` [Qt] Reduce a significant cs_main lock freeze (jonasschnelli) +- #10242 `f6f3b58` [qt] Don't call method on null WalletModel object (ryanofsky) +- #10093 `a3e756b` [Qt] Don't add arguments of sensitive command to console window (jonasschnelli) +- #10362 `95546c8` [GUI] Add OSX keystroke to RPCConsole info (spencerlievens) +- #9697 `962cd3f` [Qt] simple fee bumper with user verification (jonasschnelli) +- #10390 `e477516` [wallet] remove minimum total fee option (instagibbs) +- #10420 `4314544` Add Qt tests for wallet spends & bumpfee (ryanofsky) +- #10454 `c1c9a95` Fix broken q4 test build (ryanofsky) +- #10449 `64beb13` Overhaul Qt fee bumper (jonasschnelli) +- #10582 `7c72fb9` Pass in smart fee slider value to coin control dialog (morcos) +- #10673 `4c72cc3` [qt] Avoid potential null pointer dereference in TransactionView::exportClicked() (practicalswift) +- #10769 `8fdd23a` [Qt] replace fee slider with a Dropdown, extend conf. targets (jonasschnelli) +- #10870 `412b466` [Qt] Use wallet 0 in rpc console if running with multiple wallets (jonasschnelli) +- #10988 `a9dd111` qt: Increase BLOCK_CHAIN_SIZE constants (laanwj) +- #10644 `e292140` Slightly overhaul NSI pixmaps (jonasschnelli) +- #10660 `0c3542e` Allow to cancel the txdb upgrade via splashscreen keypress 'q' (jonasschnelli) + +### Wallet +- #9359 `f7ec7cf` Add test for CWalletTx::GetImmatureCredit() returning stale values (ryanofsky) +- #9576 `56ab672` [wallet] Remove redundant initialization (practicalswift) +- #9333 `fa625b0` Document CWalletTx::mapValue entries and remove erase of nonexistent "version" entry (ryanofsky) +- #9906 `72fb515` Disallow copy constructor CReserveKeys (instagibbs) +- #9369 `3178b2c` Factor out CWallet::nTimeSmart computation into a method (ryanofsky) +- #9830 `afcd7c0` Add safe flag to listunspent result (NicolasDorier) +- #9993 `c49355c` Initialize nRelockTime (pstratem) +- #9818 `3d857f3` Save watch only key timestamps when reimporting keys (ryanofsky) +- #9294 `f34cdcb` Use internal HD chain for change outputs (hd split) (jonasschnelli) +- #10164 `e183ea2` Wallet: reduce excess logic InMempool() (kewde) +- #10186 `c9ff4f8` Remove SYNC_TRANSACTION_NOT_IN_BLOCK magic number (jnewbery) +- #10226 `64c45aa` wallet: Use boost to more portably ensure -wallet specifies only a filename (luke-jr) +- #9827 `c91ca0a` Improve ScanForWalletTransactions return value (ryanofsky) +- #9951 `fa1ac28` Wallet database handling abstractions/simplifications (laanwj) +- #10265 `c29a0d4` [wallet] [moveonly] Check non-null pindex before potentially referencing (kallewoof) +- #10283 `a550f6e` Cleanup: reduce to one GetMinimumFee call signature (morcos) +- #10294 `e2b99b1` [Wallet] unset change position when there is no change (instagibbs) +- #10115 `d3dce0e` Avoid reading the old hd master key during wallet encryption (TheBlueMatt) +- #10341 `18c9deb` rpc/wallet: Workaround older UniValue which returns a std::string temporary for get_str (luke-jr) +- #10308 `94e5227` [wallet] Securely erase potentially sensitive keys/values (tjps) +- #10257 `ea1fd43` [test] Add test for getmemoryinfo (jimmysong) +- #10295 `ce8176d` [qt] Move some WalletModel functions into CWallet (ryanofsky) +- #10506 `7cc2c67` Fix bumpfee test after #10449 (ryanofsky) +- #10500 `098b01d` Avoid CWalletTx copies in GetAddressBalances and GetAddressGroupings (ryanofsky) +- #10455 `0747d33` Simplify feebumper minimum fee code slightly (ryanofsky) +- #10522 `2805d60` [wallet] Remove unused variables (practicalswift) +- #8694 `177433a` Basic multiwallet support (luke-jr) +- #10598 `7a74f88` Supress struct/class mismatch warnings introduced in #10284 (paveljanik) +- #9343 `209eef6` Don't create change at dust limit (morcos) +- #10744 `ed88e31` Use method name via __func__ macro (darksh1ne) +- #10712 `e8b9523` Add change output if necessary to reduce excess fee (morcos) +- #10816 `1c011ff` Properly forbid -salvagewallet and -zapwallettxes for multi wallet (morcos) +- #10235 `5cfdda2` Track keypool entries as internal vs external in memory (TheBlueMatt) +- #10330 `bf0a08b` [wallet] fix zapwallettxes interaction with persistent mempool (jnewbery) +- #10831 `0b01935` Batch flushing operations to the walletdb during top up and increase keypool size (gmaxwell) +- #10795 `7b6e8bc` No longer ever reuse keypool indexes (TheBlueMatt) +- #10849 `bde4f93` Multiwallet: simplest endpoint support (jonasschnelli) +- #10817 `9022aa3` Redefine Dust and add a discard_rate (morcos) +- #10883 `bf3b742` Rename -usewallet to -rpcwallet (morcos) +- #10604 `420238d` [wallet] [tests] Add listwallets RPC, include wallet name in `getwalletinfo` and add multiwallet test (jnewbery) +- #10885 `70888a3` Reject invalid wallets (promag) +- #10949 `af56397` Clarify help message for -discardfee (morcos) +- #10942 `2e857bb` Eliminate fee overpaying edge case when subtracting fee from recipients (morcos) +- #10995 `fa64636` Fix resendwallettransactions assert failure if -walletbroadcast=0 (TheBlueMatt) +- #11022 `653a46d` Basic keypool topup (jnewbery) +- #11081 `9fe1f6b` Add length check for CExtKey deserialization (jonasschnelli, guidovranken) +- #11044 `4ef8374` [wallet] Keypool topup cleanups (jnewbery) +- #11145 `e51bb71` Fix rounding bug in calculation of minimum change (morcos) +- #9605 `779f2f9` Use CScheduler for wallet flushing, remove ThreadFlushWalletDB (TheBlueMatt) +- #10108 `4e3efd4` ApproximateBestSubset should take inputs by reference, not value (RHavar) + +### Tests and QA +- #9744 `8efd1c8` Remove unused module from rpc-tests (34ro) +- #9657 `7ff4a53` Improve rpc-tests.py (jnewbery) +- #9766 `7146d96` Add --exclude option to rpc-tests.py (jnewbery) +- #9577 `d6064a8` Fix docstrings in qa tests (jnewbery) +- #9823 `a13a417` qa: Set correct path for binaries in rpc tests (MarcoFalke) +- #9847 `6206252` Extra test vector for BIP32 (sipa) +- #9350 `88c2ae3` [Trivial] Adding label for amount inside of tx_valid/tx_invalid.json (Christewart) +- #9888 `36afd4d` travis: Verify commits only for one target (MarcoFalke) +- #9904 `58861ad` test: Fail if InitBlockIndex fails (laanwj) +- #9828 `67c5cc1` Avoid -Wshadow warnings in wallet_tests (ryanofsky) +- #9832 `48c3429` [qa] assert_start_raises_init_error (NicolasDorier) +- #9739 `9d5fcbf` Fix BIP68 activation test (jnewbery) +- #9547 `d32581c` bench: Assert that division by zero is unreachable (practicalswift) +- #9843 `c78adbf` Fix segwit getblocktemplate test (jnewbery) +- #9929 `d5ce14e` tests: Delete unused function _rpchost_to_args (laanwj) +- #9555 `19be26a` [test] Avoid reading a potentially uninitialized variable in tx_invalid-test (transaction_tests.cpp) (practicalswift) +- #9945 `ac23a7c` Improve logging in bctest.py if there is a formatting mismatch (jnewbery) +- #9768 `8910b47` [qa] Add logging to test_framework.py (jnewbery) +- #9972 `21833f9` Fix extended rpc tests broken by #9768 (jnewbery) +- #9977 `857d1e1` QA: getblocktemplate_longpoll.py should always use >0 fee tx (sdaftuar) +- #9970 `3cc13ea` Improve readability of segwit.py, smartfees.py (sdaftuar) +- #9497 `2c781fb` CCheckQueue Unit Tests (JeremyRubin) +- #10024 `9225de2` [trivial] Use log.info() instead of print() in remaining functional test cases (jnewbery) +- #9956 `3192e52` Reorganise qa directory (jnewbery) +- #10017 `02d64bd` combine_logs.py - aggregates log files from multiple bitcoinds during functional tests (jnewbery) +- #10047 `dfef6b6` [tests] Remove unused variables and imports (practicalswift) +- #9701 `a230b05` Make bumpfee tests less fragile (ryanofsky) +- #10053 `ca20923` [test] Allow functional test cases to be skipped (jnewbery) +- #10052 `a0b1e57` [test] Run extended tests once daily in Travis (jnewbery) +- #10069 `1118493` [QA] Fix typo in fundrawtransaction test (NicolasDorier) +- #10083 `c044f03` [QA] Renaming rawTx into rawtx (NicolasDorier) +- #10073 `b1a4f27` Actually run assumevalid.py (jnewbery) +- #9780 `c412fd8` Suppress noisy output from qa tests in Travis (jnewbery) +- #10096 `79af9fb` Check that all test scripts in test/functional are being run (jnewbery) +- #10076 `5b029aa` [qa] combine_logs: Use ordered list for logfiles (MarcoFalke) +- #10107 `f2734c2` Remove unused variable. Remove accidental trailing semicolons in Python code (practicalswift) +- #10109 `8ac8041` Remove SingleNodeConnCB (jnewbery) +- #10114 `edc62c9` [tests] sync_with_ping should assert that ping hasn't timed out (jnewbery) +- #10128 `427d2fd` Speed Up CuckooCache tests (JeremyRubin) +- #10072 `12af74b` Remove sources of unreliablility in extended functional tests (jnewbery) +- #10077 `ebfd653` [qa] Add setnetworkactive smoke test (MarcoFalke) +- #10152 `080d7c7` [trivial] remove unused line in Travis config (jnewbery) +- #10159 `df1ca9e` [tests] color test results and sort alphabetically (jnewbery) +- #10124 `88799ea` [test] Suppress test logging spam (jnewbery) +- #10142 `ed09dd3` Run bitcoin_test-qt under minimal QPA platform (ryanofsky) +- #9949 `a27dbc5` [bench] Avoid function call arguments which are pointers to uninitialized values (practicalswift) +- #10187 `b44adf9` tests: Fix test_runner return value in case of skipped test (laanwj) +- #10197 `d86bb07` [tests] Functional test warnings (jnewbery) +- #10219 `9111df9` Tests: Order Python Tests Differently (jimmysong) +- #10229 `f3db4c6` Tests: Add test for getdifficulty (jimmysong) +- #10224 `2723bcd` [test] Add test for getaddednodeinfo (jimmysong) +- #10023 `c530c15` [tests] remove maxblocksinflight.py (functionality covered by other test) (jnewbery) +- #10097 `1b25b6d` Move zmq test skipping logic into individual test case (jnewbery) +- #10272 `54e2d87` [Tests] Prevent warning: variable 'x' is uninitialized (paveljanik) +- #10225 `e0a7e19` [test] Add aborttrescan tests (kallewoof) +- #10278 `8254a8a` [test] Add Unit Test for GetListenPort (jimmysong) +- #10280 `47535d7` [test] Unit test amount.h/amount.cpp (jimmysong) +- #10256 `80c3a73` [test] Add test for gettxout to wallet.py (jimmysong) +- #10264 `492d22f` [test] Add tests for getconnectioncount, getnettotals and ping (jimmysong) +- #10169 `8f3e384` [tests] Remove func test code duplication (jnewbery) +- #10198 `dc8fc0c` [tests] Remove is_network_split from functional test framework (jnewbery) +- #10255 `3c5e6c9` [test] Add test for listaddressgroupings (jimmysong) +- #10137 `75171f0` Remove unused import. Remove accidental trailing semicolons (practicalswift) +- #10307 `83073de` [tests] allow zmq test to be run in out-of-tree builds (jnewbery) +- #10344 `e927483` [tests] Fix abandonconflict.py intermittency (jnewbery) +- #10318 `170bc2c` [tests] fix wait_for_inv() (jnewbery) +- #10171 `fff72de` [tests] Add node methods to test framework (jnewbery) +- #10352 `23d78c4` test: Add elapsed time to RPC tracing (laanwj) +- #10342 `6a796b2` [tests] Improve mempool_persist test (jnewbery) +- #10287 `776ba23` [tests] Update Unit Test for addrman.h/addrman.cpp (jimmysong) +- #10365 `7ee5236` [tests] increase timeouts in sendheaders test (jnewbery) +- #10361 `f6241b3` qa: disablewallet: Check that wallet is really disabled (MarcoFalke) +- #10371 `4b766fc` [tests] Clean up addrman_tests.cpp (jimmysong) +- #10253 `87abe20` [test] Add test for getnetworkhashps (jimmysong) +- #10376 `8bd16ee` [tests] fix disconnect_ban intermittency (jnewbery) +- #10374 `5411997` qa: Warn when specified test is not found (MarcoFalke) +- #10405 `0542978` tests: Correct testcase in script_tests.json for large number OP_EQUAL (laanwj) +- #10429 `6b99daf` tests: fix spurious addrman test failure (theuni) +- #10433 `8e57256` [tests] improve tmpdir structure (jnewbery) +- #10415 `217b416` [tests] Speed up fuzzing by ~200x when using afl-fuzz (practicalswift) +- #10445 `b4b057a` Add test for empty chain and reorg consistency for gettxoutsetinfo (gmaxwell) +- #10423 `1aefc94` [tests] skipped tests should clean up after themselves (jnewbery) +- #10359 `329fc1d` [tests] functional tests should call BitcoinTestFramework start/stop node methods (jnewbery) +- #10514 `e103b3f` Bugfix: missing == 0 after randrange (sipa) +- #10515 `c871f32` [test] Add test for getchaintxstats (jimmysong) +- #10509 `bea5b00` Remove xvfb configuration from travis (ryanofsky) +- #10535 `30853e1` [qa] fundrawtx: Fix shutdown race (MarcoFalke) +- #9909 `300f8e7` tests: Add FindEarliestAtLeast test for edge cases (ryanofsky) +- #10331 `75e898c` Share config between util and functional tests (jnewbery) +- #10321 `e801084` Use FastRandomContext for all tests (sipa) +- #10524 `6c2d81f` [tests] Remove printf(...) (practicalswift) +- #10547 `71ab6e5` [tests] Use FastRandomContext instead of boost::random::{mt19937,uniform_int_distribution} (practicalswift) +- #10551 `6702617` [Tests] Wallet encryption functional tests (achow101) +- #10555 `643fa0b` [tests] various improvements to zmq_test.py (jnewbery) +- #10533 `d083bd9` [tests] Use cookie auth instead of rpcuser and rpcpassword (achow101) +- #10632 `c68a9a6` qa: Add stopatheight test (MarcoFalke) +- #10636 `4bc853b` [qa] util: Check return code after closing bitcoind proc (MarcoFalke) +- #10662 `e0a7801` Initialize randomness in benchmarks (achow101) +- #10612 `7c87a9c` The young person's guide to the test_framework (jnewbery) +- #10659 `acb1153` [qa] blockchain: Pass on closed connection during generate call (MarcoFalke) +- #10690 `416af3e` [qa] Bugfix: allow overriding extra_args in ComparisonTestFramework (sdaftuar) +- #10556 `65cc7aa` Move stop/start functions from utils.py into BitcoinTestFramework (jnewbery) +- #10704 `dd07f47` [tests] nits in dbcrash.py (jnewbery) +- #10743 `be82498` [test] don't run dbcrash.py on Travis (jnewbery) +- #10761 `d3b5870` [tests] fix replace_by_fee.py (jnewbery) +- #10759 `1d4805c` Fix multi_rpc test for hosts that dont default to utf8 (TheBlueMatt) +- #10190 `e4f226a` [tests] mining functional tests (including regression test for submitblock) (jnewbery) +- #10739 `1fc783f` test: Move variable `state` down where it is used (paveljanik) +- #9980 `fee0d80` Fix mem access violation merkleblock (Christewart) +- #10893 `0c173a1` [QA] Avoid running multiwallet.py twice (jonasschnelli) +- #10927 `9d5e8f9` test: Make sure wallet.backup is created in temp path (laanwj) +- #10899 `f29d5db` [test] Qt: Use _putenv_s instead of setenv on Windows builds (brianmcmichael) +- #10912 `5c8eb79` [tests] Fix incorrect memory_cleanse(…) call in crypto_tests.cpp (practicalswift) +- #11001 `fa8a063` [tests] Test disconnecting unsupported service bits logic (jnewbery) +- #10695 `929fd72` [qa] Rewrite BIP65/BIP66 functional tests (sdaftuar) +- #10963 `ecd2135` [bench] Restore format state of cout after printing with std::fixed/setprecision (practicalswift) +- #11025 `e5d26e4` qa: Fix inv race in example_test (MarcoFalke) +- #10765 `2c811e0` Tests: address placement should be deterministic by default (ReneNyffenegger) +- #11000 `ac016e1` test: Add resendwallettransactions functional tests (promag) +- #11032 `aeb3175` [qa] Fix block message processing error in sendheaders.py (sdaftuar) +- #10105 `0b9fb68` [tests] fixup - make all Travis test runs quiet, non just cron job runs (jnewbery) +- #10222 `6ce7337` [tests] test_runner - check unicode (jnewbery) +- #10327 `35da2ae` [tests] remove import-abort-rescan.py (jnewbery) +- #11023 `bf74d37` [tests] Add option to attach a python debugger if functional test fails (jnewbery) +- #10565 `8c2098a` [coverage] Remove subtrees and benchmarks from coverage report (achow101) + +### Miscellaneous +- #9871 `be8ba2c` Add a tree sha512 hash to merge commits (sipa) +- #9821 `d19d45a` util: Specific GetOSRandom for Linux/FreeBSD/OpenBSD (laanwj) +- #9903 `ba80a68` Docs: add details to -rpcclienttimeout doc (ian-kelling) +- #9910 `53c300f` Docs: correct and elaborate -rpcbind doc (ian-kelling) +- #9905 `01b7cda` [contrib] gh-merge: Move second sha512 check to the end (MarcoFalke) +- #9880 `4df8213` Verify Tree-SHA512s in merge commits, enforce sigs are not SHA1 (TheBlueMatt) +- #9932 `00c13ea` Fix verify-commits on travis and always check top commit's tree (TheBlueMatt) +- #9952 `6996e06` Add historical release notes for 0.14.0 (laanwj) +- #9940 `fa99663` Fix verify-commits on OSX, update for new bad Tree-SHA512, point travis to different keyservers (TheBlueMatt) +- #9963 `8040ae6` util: Properly handle errors during log message formatting (laanwj) +- #9984 `cce056d` devtools: Make github-merge compute SHA512 from git, instead of worktree (laanwj) +- #9995 `8bcf934` [doc] clarify blockchain size and pruning (askmike) +- #9734 `0c17afc` Add updating of chainTxData to release process (sipa) +- #10063 `530fcbd` add missing spaces so that markdown recognizes headline (flack) +- #10085 `db1ae54` Docs: remove 'noconnect' option (jlopp) +- #10090 `8e4f7e7` Update bitcoin.conf with example for pruning (coinables) +- #9424 `1a5aaab` Change LogAcceptCategory to use uint32_t rather than sets of strings (gmaxwell) +- #10036 `fbf36ca` Fix init README format to render correctly on github (jlopp) +- #10058 `a2cd0b0` No need to use OpenSSL malloc/free (tjps) +- #10123 `471ed00` Allow debug logs to be excluded from specified component (jnewbery) +- #10104 `fadf078` linearize script: Option to use RPC cookie (achow101) +- #10162 `a3a2160` [trivial] Log calls to getblocktemplate (jnewbery) +- #10155 `928695b` build: Deduplicate version numbers (laanwj) +- #10211 `a86255b` [doc] Contributor fixes & new "finding reviewers" section (kallewoof) +- #10250 `1428f30` Fix some empty vector references (sipa) +- #10270 `95f5e44` Remove Clang workaround for Boost 1.46 (fanquake) +- #10263 `cb007e4` Trivial: fix fee estimate write error log message (CryptAxe) +- #9670 `bd9ec0e` contrib: github-merge improvements (laanwj) +- #10260 `1d75597` [doc] Minor corrections to osx dependencies (fanquake) +- #10189 `750c5a5` devtools/net: add a verifier for scriptable changes. Use it to make CNode::id private (theuni) +- #10322 `bc64b5a` Use hardware timestamps in RNG seeding (sipa) +- #10381 `7f2b9e0` Shadowing warnings are not enabled by default, update doc accordingly (paveljanik) +- #10380 `b6ee855` [doc] Removing comments about dirty entries on txmempool (madeo) +- #10383 `d0c37ee` [logging] log system time and mock time (jnewbery) +- #10404 `b45a52a` doc: Add logging to FinalizeNode() (sdaftuar) +- #10388 `526e839` Output line to debug.log when IsInitialBlockDownload latches to false (morcos) +- #10372 `15254e9` Add perf counter data to GetStrongRandBytes state in scheduler (TheBlueMatt) +- #10461 `55b72f3` Update style guide (sipa) +- #10486 `10e8c0a` devtools: Retry after signing fails in github-merge (laanwj) +- #10447 `f259263` Make bitcoind invalid argument error message specific (laanwj) +- #10495 `6a38b79` contrib: Update location of seeds.txt (laanwj) +- #10469 `b6b150b` Fixing typo in rpcdump.cpp help message (keystrike) +- #10451 `27b9931` contrib/init/bitcoind.openrcconf: Don't disable wallet by default (luke-jr) +- #10323 `00d3692` Update to latest libsecp256k1 master (sipa) +- #10422 `cec9e1e` Fix timestamp in fee estimate debug message (morcos) +- #10566 `5d034ee` [docs] Use the "domain name setup" image (previously unused) in the gitian docs (practicalswift) +- #10534 `a514ac3` Clarify prevector::erase and avoid swap-to-clear (sipa) +- #10575 `22ec768` Header include guideline (sipa) +- #10480 `fbf5d3b` Improve commit-check-script.sh (sipa) +- #10502 `1ad3d4e` scripted-diff: Remove BOOST_FOREACH, Q_FOREACH and PAIRTYPE (jtimon) +- #10377 `b63be2c` Use rdrand as entropy source on supported platforms (sipa) +- #9895 `228c319` Turn TryCreateDirectory() into TryCreateDirectories() (benma) +- #10602 `d76e84a` Make clang-format use C++11 features (e.g. A<A<int>> instead of A<A<int> >) (practicalswift) +- #10623 `c38f540` doc: Add 0.14.2 release notes (MarcoFalke) +- #10276 `b750b33` contrib/verifybinaries: allow filtering by platform (knocte) +- #10248 `01c4b14` Rewrite addrdb with less duplication using CHashVerifier (sipa) +- #10577 `232508f` Add an explanation of quickly hashing onto a non-power of two range (gmaxwell) +- #10608 `eee398f` Add a comment explaining the use of MAX_BLOCK_BASE_SIZE (gmaxwell) +- #10728 `7397af9` fix typo in help text for removeprunedfunds (AkioNak) +- #10193 `6dbcc74` scripted-diff: Remove #include <boost/foreach.hpp> (jtimon) +- #10676 `379aed0` document script-based return fields for validateaddress (instagibbs) +- #10651 `cef4b5c` Verify binaries from bitcoincore.org and bitcoin.org (TheBlueMatt) +- #10786 `ca4c545` Add PR description to merge commit in github-merge.py (sipa) +- #10812 `c5904e8` [utils] Allow bitcoin-cli's -rpcconnect option to be used with square brackets (jnewbery) +- #10842 `3895e25` Fix incorrect Doxygen tag (@ince → @since). Doxygen parameter name matching (practicalswift) +- #10681 `df0793f` add gdb attach process to test README (instagibbs) +- #10789 `1124328` Punctuation/grammer fixes in rpcwallet.cpp (stevendlander) +- #10655 `78f307b` Properly document target_confirmations in listsinceblock (RHavar) +- #10917 `5c003cb` developer-notes: add reference to snake_case and PascalCase (benma) +- #11003 `4b5a7ce` Docs: Capitalize bullet points in CONTRIBUTING guide (eklitzke) +- #10968 `98aa3f6` Add instructions for parallel gitian builds (coblee) +- #11076 `1c4b9b3` 0.15 release-notes nits: fix redundancy, remove accidental parenthesis & fix range style (practicalswift) +- #11090 `8f0121c` Update contributor names in release-notes.md (Derek701) +- #11056 `cbdd338` disable jni in builds (instagibbs) +- #11080 `2b59cfb` doc: Update build-openbsd for 6.1 (laanwj) +- #11119 `0a6af47` [doc] build-windows: Mention that only trusty works (MarcoFalke) +- #11108 `e8ad101` Changing -txindex requires -reindex, not -reindex-chainstate (TheBlueMatt) +- #9792 `342b9bc` FastRandomContext improvements and switch to ChaCha20 (sipa) +- #9505 `67ed40e` Prevector Quick Destruct (JeremyRubin) +- #10820 `ef37f20` Use cpuid intrinsics instead of asm code (sipa) +- #9999 `a328904` [LevelDB] Plug leveldb logs to bitcoin logs (NicolasDorier) +- #9693 `c5e9e42` Prevent integer overflow in ReadVarInt (gmaxwell) +- #10129 `351d0ad` scheduler: fix sub-second precision with boost < 1.50 (theuni) +- #10153 `fade788` logging: Fix off-by-one for shrinkdebugfile default (MarcoFalke) +- #10305 `c45da32` Fix potential NPD introduced in b297426c (TheBlueMatt) +- #10338 `daf3e7d` Maintain state across GetStrongRandBytes calls (sipa) +- #10544 `a4fe077` Update to LevelDB 1.20 (sipa) +- #10614 `cafe24f` random: fix crash on some 64bit platforms (theuni) +- #10714 `2a09a38` Avoid printing incorrect block indexing time due to uninitialized variable (practicalswift) +- #10837 `8bc6d1f` Fix resource leak on error in GetDevURandom (corebob) +- #10832 `89bb036` init: Factor out AppInitLockDataDirectory and fix startup core dump issue (laanwj) +- #10914 `b995a37` Add missing lock in CScheduler::AreThreadsServicingQueue() (TheBlueMatt) +- #10958 `659c096` Update to latest Bitcoin patches for LevelDB (sipa) +- #10919 `c1c671f` Fix more init bugs (TheBlueMatt) + +Credits +======= + +Thanks to everyone who directly contributed to this release: + +- ロハン ダル +- Ahmad Kazi +- aideca +- Akio Nakamura +- Alex Morcos +- Allan Doensen +- Andres G. Aragoneses +- Andrew Chow +- Angel Leon +- Awemany +- Bob McElrath +- Brian McMichael +- BtcDrak +- Charlie Lee +- Chris Gavin +- Chris Stewart +- Cory Fields +- CryptAxe +- Dag Robole +- Daniel Aleksandersen +- Daniel Cousens +- darksh1ne +- Dimitris Tsapakidis +- Eric Shaw +- Evan Klitzke +- fanquake +- Felix Weis +- flack +- Guido Vranken +- Greg Griffith +- Gregory Maxwell +- Gregory Sanders +- Ian Kelling +- Jack Grigg +- James Evans +- James Hilliard +- Jameson Lopp +- Jeremy Rubin +- Jimmy Song +- João Barbosa +- Johnathan Corgan +- John Newbery +- Jonas Schnelli +- Jorge Timón +- Karl-Johan Alm +- kewde +- KibbledJiveElkZoo +- Kirit Thadaka +- kobake +- Kyle Honeycutt +- Lawrence Nahum +- Luke Dashjr +- Marco Falke +- Marcos Mayorga +- Marijn Stollenga +- Mario Dian +- Mark Friedenbach +- Marko Bencun +- Masahiko Hyuga +- Matt Corallo +- Matthew Zipkin +- Matthias Grundmann +- Michael Goldstein +- Michael Rotarius +- Mikerah +- Mike van Rossum +- Mitchell Cash +- Nicolas Dorier +- Patrick Strateman +- Pavel Janík +- Pavlos Antoniou +- Pavol Rusnak +- Pedro Branco +- Peter Todd +- Pieter Wuille +- practicalswift +- René Nyffenegger +- Ricardo Velhote +- romanornr +- Russell Yanofsky +- Rusty Russell +- Ryan Havar +- shaolinfry +- Shigeya Suzuki +- Simone Madeo +- Spencer Lievens +- Steven D. Lander +- Suhas Daftuar +- Takashi Mitsuta +- Thomas Snider +- Timothy Redaelli +- tintinweb +- tnaka +- Warren Togami +- Wladimir J. van der Laan + +As well as everyone that helped translating on [Transifex](https://www.transifex.com/projects/p/bitcoin/). diff --git a/share/certs/BitcoinFoundation_Apple_Cert.pem b/share/certs/BitcoinFoundation_Apple_Cert.pem deleted file mode 100644 index beb0d7073c..0000000000 --- a/share/certs/BitcoinFoundation_Apple_Cert.pem +++ /dev/null @@ -1,37 +0,0 @@ -Bag Attributes - friendlyName: Developer ID Application: BITCOIN FOUNDATION, INC., THE - localKeyID: 6B 9C 6C A8 A5 73 70 70 E2 57 A3 49 D8 62 FB 97 C7 A5 5D 5E -subject=/UID=PBV4GLS9J4/CN=Developer ID Application: BITCOIN FOUNDATION, INC., THE/OU=PBV4GLS9J4/O=BITCOIN FOUNDATION, INC., THE/C=US -issuer=/CN=Developer ID Certification Authority/OU=Apple Certification Authority/O=Apple Inc./C=US ------BEGIN CERTIFICATE----- -MIIFhzCCBG+gAwIBAgIIJ0r1rumyfZAwDQYJKoZIhvcNAQELBQAweTEtMCsGA1UE -AwwkRGV2ZWxvcGVyIElEIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSYwJAYDVQQL -DB1BcHBsZSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTETMBEGA1UECgwKQXBwbGUg -SW5jLjELMAkGA1UEBhMCVVMwHhcNMTMwMTEwMjIzOTAxWhcNMTgwMTExMjIzOTAx -WjCBqDEaMBgGCgmSJomT8ixkAQEMClBCVjRHTFM5SjQxQDA+BgNVBAMMN0RldmVs -b3BlciBJRCBBcHBsaWNhdGlvbjogQklUQ09JTiBGT1VOREFUSU9OLCBJTkMuLCBU -SEUxEzARBgNVBAsMClBCVjRHTFM5SjQxJjAkBgNVBAoMHUJJVENPSU4gRk9VTkRB -VElPTiwgSU5DLiwgVEhFMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBALTd5zURuZVoJviusr119aktXksenb9IN9vq6kBbq38vxEk7 -9wkKMES2XfBRh0HxcEizGzhMNy5OCXuTLMaNMihYdfwYSoBoR2foEU+6kjPUnyJ4 -dQBFLJZJr5/QeQmALmYHEgZ6lwXFD2lU8t92340zeJ4y5LZw5pcEHtH9IummYDut -OGCkCGXDcjL+5nHhNScJiXHhswM+62o6XXsQiP6EWbM1CsgrGTNLtaa0U/UvVDwE -79YKklSC5Bog2LD0jBcTuveI66mFzqu++L9X9u+ZArtebwCl7BPNQ+uboYy5uV2d -zf8lpNNZLfXCFjoLe9bLICKfZ7ub9V5aC8+GhckCAwEAAaOCAeEwggHdMD4GCCsG -AQUFBwEBBDIwMDAuBggrBgEFBQcwAYYiaHR0cDovL29jc3AuYXBwbGUuY29tL29j -c3AtZGV2aWQwMTAdBgNVHQ4EFgQUa5xsqKVzcHDiV6NJ2GL7l8elXV4wDAYDVR0T -AQH/BAIwADAfBgNVHSMEGDAWgBRXF+2iz9x8mKEQ4Py+hy0s8uMXVDCCAQ4GA1Ud -IASCAQUwggEBMIH+BgkqhkiG92NkBQEwgfAwKAYIKwYBBQUHAgEWHGh0dHA6Ly93 -d3cuYXBwbGUuY29tL2FwcGxlY2EwgcMGCCsGAQUFBwICMIG2DIGzUmVsaWFuY2Ug -b24gdGhpcyBjZXJ0aWZpY2F0ZSBieSBhbnkgcGFydHkgYXNzdW1lcyBhY2NlcHRh -bmNlIG9mIHRoZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNv -bmRpdGlvbnMgb2YgdXNlLCBjZXJ0aWZpY2F0ZSBwb2xpY3kgYW5kIGNlcnRpZmlj -YXRpb24gcHJhY3RpY2Ugc3RhdGVtZW50cy4wDgYDVR0PAQH/BAQDAgeAMBYGA1Ud -JQEB/wQMMAoGCCsGAQUFBwMDMBMGCiqGSIb3Y2QGAQ0BAf8EAgUAMA0GCSqGSIb3 -DQEBCwUAA4IBAQAfJ0BjID/1dS2aEeVyhAzPzCBjG8vm0gDf+/qfwRn3+yWeL9vS -nMdbilwM48IyQWTagjGGcojbsAd/vE4N7NhQyHInoCllNoeor1I5xx+blTaGRBK+ -dDhJbbdlGCjsLnH/BczGZi5fyEJds9lUIrp1hJidRcUKO76qb/9gc6qNZpl1vH5k -lDUuJYt7YhAs+L6rTXDyqcK9maeQr0gaOPsRRAQLLwiQCorPeMTUNsbVMdMwZYJs -R+PxiAnk+nyi7rfiFvPoASAYUuI6OzYL/Fa6QU4/gYyPgic944QYVkaQBnc0vEP1 -nXq6LGKwgVGcqJnkr/E2kui5gJoV5C3qll3e ------END CERTIFICATE----- diff --git a/share/certs/BitcoinFoundation_Comodo_Cert.pem b/share/certs/BitcoinFoundation_Comodo_Cert.pem deleted file mode 100644 index dc752d455c..0000000000 --- a/share/certs/BitcoinFoundation_Comodo_Cert.pem +++ /dev/null @@ -1,37 +0,0 @@ -Bag Attributes - friendlyName: The Bitcoin Foundation, Inc.'s COMODO CA Limited ID - localKeyID: 8C 94 64 E3 B5 B0 41 89 5B 89 B0 57 CC 74 B9 44 E5 B2 92 66 -subject=/C=US/postalCode=98104-1444/ST=WA/L=Seattle/street=Suite 300/street=71 Columbia St/O=The Bitcoin Foundation, Inc./CN=The Bitcoin Foundation, Inc. -issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO Code Signing CA 2 ------BEGIN CERTIFICATE----- -MIIFeDCCBGCgAwIBAgIRAJVYMd+waOER7lUqtiz3M2IwDQYJKoZIhvcNAQEFBQAw -ezELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G -A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxITAfBgNV -BAMTGENPTU9ETyBDb2RlIFNpZ25pbmcgQ0EgMjAeFw0xMzAxMTYwMDAwMDBaFw0x -NDAxMTYyMzU5NTlaMIG8MQswCQYDVQQGEwJVUzETMBEGA1UEEQwKOTgxMDQtMTQ0 -NDELMAkGA1UECAwCV0ExEDAOBgNVBAcMB1NlYXR0bGUxEjAQBgNVBAkMCVN1aXRl -IDMwMDEXMBUGA1UECQwONzEgQ29sdW1iaWEgU3QxJTAjBgNVBAoMHFRoZSBCaXRj -b2luIEZvdW5kYXRpb24sIEluYy4xJTAjBgNVBAMMHFRoZSBCaXRjb2luIEZvdW5k -YXRpb24sIEluYy4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChUwLD -u/hu5aFZ/n11B27awONaaDrmHm0pamiWHb01yL4JmTBtaLCrSftF8RhCscQ8jpI0 -UG1Cchmay0e3zH5o5XRs0H9C3x+SM5ozms0TWDmAYiB8aQEghsGovDk0D2nyTQeK -Q0xqyCh0m8ZPOnMnYrakHEmF6WvhLdJvI6Od4KIwbKxgN17cPFIfLVsZ7GrzmmbU -Gdi4wSQCHy5rxzvBxho8Qq/SfBl93uOMUrqOHjOUAPhNuTJG3t/MdhU8Zp24s29M -abHtYkT9W86hMjIiI8RTAR+WHKVglx9SB0cjDabXN8SZ3gME0+H++LyzlySHT8sI -ykepojZ7UBRgp9w3AgMBAAGjggGzMIIBrzAfBgNVHSMEGDAWgBQexbEsfYfaAmh8 -JbwMB4Q/ts/e8TAdBgNVHQ4EFgQUfPf+ZyDWl/4LH0Y5BuJTelkRd/EwDgYDVR0P -AQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwEQYJ -YIZIAYb4QgEBBAQDAgQQMEYGA1UdIAQ/MD0wOwYMKwYBBAGyMQECAQMCMCswKQYI -KwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5uZXQvQ1BTMEEGA1UdHwQ6 -MDgwNqA0oDKGMGh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET0NvZGVTaWdu -aW5nQ0EyLmNybDByBggrBgEFBQcBAQRmMGQwPAYIKwYBBQUHMAKGMGh0dHA6Ly9j -cnQuY29tb2RvY2EuY29tL0NPTU9ET0NvZGVTaWduaW5nQ0EyLmNydDAkBggrBgEF -BQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMCgGA1UdEQQhMB+BHWxpbmRz -YXlAYml0Y29pbmZvdW5kYXRpb24ub3JnMA0GCSqGSIb3DQEBBQUAA4IBAQAqibjo -D4HG5XSIIMCmYE5RgQBSEAJfI+EZERk1G9F83ZUWr0yNRZCw4O+RaM7xQhvJhEoD -G2kpk/q2bNOc71/VyZ6SrE1JRVUON41/Flhz4M6cP0BclTicXvh+efVwqZhIz+ws -UxF2hvC/1Xx6rqI7NYAlOYXk2MSUq3HREo+gWUPKM8em4MZZV/7XCH4QbsfxOl1J -xS6EOQmV8hfUN4KRXI5WfGUmedBxq7dM0RSJOSQl8fq2f+JjRLfjQwQucy7LDY+y -pRTsL2TdQV/DuDuI3s0NHRGznQNddoX5jqpXhSQFAAdgrhN1gGkWaaTPzr9IF2TG -qgr6PEp9tIYC+MbM ------END CERTIFICATE----- diff --git a/share/certs/PrivateKeyNotes.md b/share/certs/PrivateKeyNotes.md deleted file mode 100644 index 8d50144c21..0000000000 --- a/share/certs/PrivateKeyNotes.md +++ /dev/null @@ -1,46 +0,0 @@ -Code-signing private key notes -== - -The private keys for these certificates were generated on Gavin's main work machine, -following the certificate authority's recommendations for generating certificate -signing requests. - -For OSX, the private key was generated by Keychain.app on Gavin's main work machine. -The key and certificate is in a separate, passphrase-protected keychain file that is -unlocked to sign the Bitcoin-Qt.app bundle. - -For Windows, the private key was generated by Firefox running on Gavin's main work machine. -The key and certificate were exported into a separate, passphrase-protected PKCS#12 file, and -then deleted from Firefox's keystore. The exported file is used to sign the Windows setup.exe. - -Threat analysis --- - -Gavin is a single point of failure. He could be coerced to divulge the secret signing keys, -allowing somebody to distribute a Bitcoin-Qt.app or bitcoin-qt-setup.exe with a valid -signature but containing a malicious binary. - -Or the machine Gavin uses to sign the binaries could be compromised, either remotely or -by breaking in to his office, allowing the attacker to get the private key files and then -install a keylogger to get the passphrase that protects them. - -Threat Mitigation --- - -"Air gapping" the machine used to do the signing will not work, because the signing -process needs to access a timestamp server over the network. And it would not -prevent the "rubber hose cryptography" threat (coercing Gavin to sign a bad binary -or divulge the private keys). - -Windows binaries are reproducibly 'gitian-built', and the setup.exe file created -by the NSIS installer system is a 7zip archive, so you could check to make sure -that the bitcoin-qt.exe file inside the installer had not been tampered with. -However, an attacker could modify the installer's code, so when the setup.exe -was run it compromised users' systems. A volunteer to write an auditing tool -that checks the setup.exe for tampering, and checks the files in it against -the list of gitian signatures, is needed. - -The long-term solution is something like the 'gitian downloader' system, which -uses signatures from multiple developers to determine whether or not a binary -should be trusted. However, that just pushes the problem to "how will -non-technical users securely get the gitian downloader code to start?" diff --git a/share/qt/extract_strings_qt.py b/share/qt/extract_strings_qt.py index 5492fdb8c5..e3be18205d 100755 --- a/share/qt/extract_strings_qt.py +++ b/share/qt/extract_strings_qt.py @@ -58,7 +58,7 @@ XGETTEXT=os.getenv('XGETTEXT', 'xgettext') if not XGETTEXT: print('Cannot extract strings: xgettext utility is not installed or not configured.',file=sys.stderr) print('Please install package "gettext" and re-run \'./configure\'.',file=sys.stderr) - exit(1) + sys.exit(1) child = Popen([XGETTEXT,'--output=-','-n','--keyword=_'] + files, stdout=PIPE) (out, err) = child.communicate() diff --git a/src/Makefile.am b/src/Makefile.am index f7abab482e..90deff48b0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,7 +18,6 @@ else LIBUNIVALUE = $(UNIVALUE_LIBS) endif -BITCOIN_CONFIG_INCLUDES=-I$(builddir)/config BITCOIN_INCLUDES=-I$(builddir) -I$(builddir)/obj $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS) $(SSL_CFLAGS) BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include @@ -79,6 +78,7 @@ BITCOIN_CORE_H = \ addrdb.h \ addrman.h \ base58.h \ + bech32.h \ bloom.h \ blockencodings.h \ chain.h \ @@ -130,6 +130,7 @@ BITCOIN_CORE_H = \ rpc/client.h \ rpc/mining.h \ rpc/protocol.h \ + rpc/safemode.h \ rpc/server.h \ rpc/register.h \ scheduler.h \ @@ -162,6 +163,8 @@ BITCOIN_CORE_H = \ wallet/crypter.h \ wallet/db.h \ wallet/feebumper.h \ + wallet/fees.h \ + wallet/init.h \ wallet/rpcwallet.h \ wallet/wallet.h \ wallet/walletdb.h \ @@ -208,6 +211,7 @@ libbitcoin_server_a_SOURCES = \ rpc/misc.cpp \ rpc/net.cpp \ rpc/rawtransaction.cpp \ + rpc/safemode.cpp \ rpc/server.cpp \ script/sigcache.cpp \ script/ismine.cpp \ @@ -239,6 +243,8 @@ libbitcoin_wallet_a_SOURCES = \ wallet/crypter.cpp \ wallet/db.cpp \ wallet/feebumper.cpp \ + wallet/fees.cpp \ + wallet/init.cpp \ wallet/rpcdump.cpp \ wallet/rpcwallet.cpp \ wallet/wallet.cpp \ @@ -246,7 +252,7 @@ libbitcoin_wallet_a_SOURCES = \ $(BITCOIN_CORE_H) # crypto primitives library -crypto_libbitcoin_crypto_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_CONFIG_INCLUDES) +crypto_libbitcoin_crypto_a_CPPFLAGS = $(AM_CPPFLAGS) crypto_libbitcoin_crypto_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) crypto_libbitcoin_crypto_a_SOURCES = \ crypto/aes.cpp \ @@ -267,7 +273,7 @@ crypto_libbitcoin_crypto_a_SOURCES = \ crypto/sha512.cpp \ crypto/sha512.h -if EXPERIMENTAL_ASM +if USE_ASM crypto_libbitcoin_crypto_a_SOURCES += crypto/sha256_sse4.cpp endif @@ -311,6 +317,7 @@ libbitcoin_common_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_common_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_common_a_SOURCES = \ base58.cpp \ + bech32.cpp \ chainparams.cpp \ coins.cpp \ compressor.cpp \ @@ -469,8 +476,7 @@ CLEANFILES += univalue/*.gcda univalue/*.gcno CLEANFILES += wallet/*.gcda wallet/*.gcno CLEANFILES += wallet/test/*.gcda wallet/test/*.gcno CLEANFILES += zmq/*.gcda zmq/*.gcno - -DISTCLEANFILES = obj/build.h +CLEANFILES += obj/build.h EXTRA_DIST = $(CTAES_DIST) diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 2b1f70b25b..8e2e587d32 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -6,11 +6,12 @@ bin_PROGRAMS += bench/bench_bitcoin BENCH_SRCDIR = bench BENCH_BINARY = bench/bench_bitcoin$(EXEEXT) -RAW_TEST_FILES = \ +RAW_BENCH_FILES = \ bench/data/block413567.raw -GENERATED_TEST_FILES = $(RAW_TEST_FILES:.raw=.raw.h) +GENERATED_BENCH_FILES = $(RAW_BENCH_FILES:.raw=.raw.h) bench_bench_bitcoin_SOURCES = \ + $(RAW_BENCH_FILES) \ bench/bench_bitcoin.cpp \ bench/bench.cpp \ bench/bench.h \ @@ -28,7 +29,7 @@ bench_bench_bitcoin_SOURCES = \ bench/perf.h \ bench/prevector_destructor.cpp -nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_TEST_FILES) +nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES) bench_bench_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CLFAGS) $(EVENT_PTHREADS_CFLAGS) -I$(builddir)/bench/ bench_bench_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -56,7 +57,7 @@ endif bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_TEST_FILES) +CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES) CLEANFILES += $(CLEAN_BITCOIN_BENCH) diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include index 02f30bc952..ea2ed17472 100644 --- a/src/Makefile.qttest.include +++ b/src/Makefile.qttest.include @@ -25,12 +25,10 @@ TEST_QT_H = \ qt/test/wallettests.h TEST_BITCOIN_CPP = \ - test/test_bitcoin.cpp \ - test/testutil.cpp + test/test_bitcoin.cpp TEST_BITCOIN_H = \ - test/test_bitcoin.h \ - test/testutil.h + test/test_bitcoin.h qt_test_test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \ $(QT_INCLUDES) $(QT_TEST_INCLUDES) $(PROTOBUF_CFLAGS) diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 6415b3d2e3..d3e7b5da12 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -31,6 +31,7 @@ BITCOIN_TESTS =\ test/base32_tests.cpp \ test/base58_tests.cpp \ test/base64_tests.cpp \ + test/bech32_tests.cpp \ test/bip32_tests.cpp \ test/blockencodings_tests.cpp \ test/bloom_tests.cpp \ @@ -49,6 +50,7 @@ BITCOIN_TESTS =\ test/main_tests.cpp \ test/mempool_tests.cpp \ test/merkle_tests.cpp \ + test/merkleblock_tests.cpp \ test/miner_tests.cpp \ test/multisig_tests.cpp \ test/net_tests.cpp \ @@ -65,6 +67,7 @@ BITCOIN_TESTS =\ test/scheduler_tests.cpp \ test/script_P2SH_tests.cpp \ test/script_tests.cpp \ + test/script_standard_tests.cpp \ test/scriptnum_tests.cpp \ test/serialize_tests.cpp \ test/sighash_tests.cpp \ @@ -74,8 +77,6 @@ BITCOIN_TESTS =\ test/test_bitcoin.cpp \ test/test_bitcoin.h \ test/test_bitcoin_main.cpp \ - test/testutil.cpp \ - test/testutil.h \ test/timedata_tests.cpp \ test/torcontrol_tests.cpp \ test/transaction_tests.cpp \ @@ -148,7 +149,7 @@ bitcoin_test_clean : FORCE check-local: @echo "Running test/util/bitcoin-util-test.py..." - $(top_builddir)/test/util/bitcoin-util-test.py + $(PYTHON) $(top_builddir)/test/util/bitcoin-util-test.py $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check if EMBEDDED_UNIVALUE $(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C univalue check diff --git a/src/addrman.h b/src/addrman.h index 547088aedf..18f3062287 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -472,6 +472,8 @@ public: nTried = 0; nNew = 0; nLastGood = 1; //Initially at 1 so that "never" is strictly worse. + mapInfo.clear(); + mapAddr.clear(); } CAddrMan() diff --git a/src/base58.cpp b/src/base58.cpp index 3802f953f9..9d5a2f4964 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -4,17 +4,20 @@ #include "base58.h" +#include "bech32.h" #include "hash.h" +#include "script/script.h" #include "uint256.h" +#include "utilstrencodings.h" -#include <assert.h> -#include <stdint.h> -#include <string.h> -#include <vector> -#include <string> #include <boost/variant/apply_visitor.hpp> #include <boost/variant/static_visitor.hpp> +#include <algorithm> +#include <assert.h> +#include <string.h> + + /** All alphanumeric characters except for "0", "I", "O", and "l" */ static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; @@ -136,7 +139,7 @@ bool DecodeBase58Check(const char* psz, std::vector<unsigned char>& vchRet) } // re-calculate the checksum, ensure it matches the included 4-byte checksum uint256 hash = Hash(vchRet.begin(), vchRet.end() - 4); - if (memcmp(&hash, &vchRet.end()[-4], 4) != 0) { + if (memcmp(&hash, &vchRet[vchRet.size() - 4], 4) != 0) { vchRet.clear(); return false; } @@ -212,79 +215,113 @@ int CBase58Data::CompareTo(const CBase58Data& b58) const namespace { -class CBitcoinAddressVisitor : public boost::static_visitor<bool> +class DestinationEncoder : public boost::static_visitor<std::string> { private: - CBitcoinAddress* addr; + const CChainParams& m_params; public: - explicit CBitcoinAddressVisitor(CBitcoinAddress* addrIn) : addr(addrIn) {} - - bool operator()(const CKeyID& id) const { return addr->Set(id); } - bool operator()(const CScriptID& id) const { return addr->Set(id); } - bool operator()(const CNoDestination& no) const { return false; } -}; - -} // namespace - -bool CBitcoinAddress::Set(const CKeyID& id) -{ - SetData(Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS), &id, 20); - return true; -} + DestinationEncoder(const CChainParams& params) : m_params(params) {} -bool CBitcoinAddress::Set(const CScriptID& id) -{ - SetData(Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS), &id, 20); - return true; -} + std::string operator()(const CKeyID& id) const + { + std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } -bool CBitcoinAddress::Set(const CTxDestination& dest) -{ - return boost::apply_visitor(CBitcoinAddressVisitor(this), dest); -} + std::string operator()(const CScriptID& id) const + { + std::vector<unsigned char> data = m_params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); + data.insert(data.end(), id.begin(), id.end()); + return EncodeBase58Check(data); + } -bool CBitcoinAddress::IsValid() const -{ - return IsValid(Params()); -} + std::string operator()(const WitnessV0KeyHash& id) const + { + std::vector<unsigned char> data = {0}; + ConvertBits<8, 5, true>(data, id.begin(), id.end()); + return bech32::Encode(m_params.Bech32HRP(), data); + } -bool CBitcoinAddress::IsValid(const CChainParams& params) const -{ - bool fCorrectSize = vchData.size() == 20; - bool fKnownVersion = vchVersion == params.Base58Prefix(CChainParams::PUBKEY_ADDRESS) || - vchVersion == params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); - return fCorrectSize && fKnownVersion; -} + std::string operator()(const WitnessV0ScriptHash& id) const + { + std::vector<unsigned char> data = {0}; + ConvertBits<8, 5, true>(data, id.begin(), id.end()); + return bech32::Encode(m_params.Bech32HRP(), data); + } -CTxDestination CBitcoinAddress::Get() const -{ - if (!IsValid()) - return CNoDestination(); - uint160 id; - memcpy(&id, vchData.data(), 20); - if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) - return CKeyID(id); - else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) - return CScriptID(id); - else - return CNoDestination(); -} + std::string operator()(const WitnessUnknown& id) const + { + if (id.version < 1 || id.version > 16 || id.length < 2 || id.length > 40) { + return {}; + } + std::vector<unsigned char> data = {(unsigned char)id.version}; + ConvertBits<8, 5, true>(data, id.program, id.program + id.length); + return bech32::Encode(m_params.Bech32HRP(), data); + } -bool CBitcoinAddress::GetKeyID(CKeyID& keyID) const -{ - if (!IsValid() || vchVersion != Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) - return false; - uint160 id; - memcpy(&id, vchData.data(), 20); - keyID = CKeyID(id); - return true; -} + std::string operator()(const CNoDestination& no) const { return {}; } +}; -bool CBitcoinAddress::IsScript() const +CTxDestination DecodeDestination(const std::string& str, const CChainParams& params) { - return IsValid() && vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS); + std::vector<unsigned char> data; + uint160 hash; + if (DecodeBase58Check(str, data)) { + // base58-encoded Bitcoin addresses. + // Public-key-hash-addresses have version 0 (or 111 testnet). + // The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. + const std::vector<unsigned char>& pubkey_prefix = params.Base58Prefix(CChainParams::PUBKEY_ADDRESS); + if (data.size() == hash.size() + pubkey_prefix.size() && std::equal(pubkey_prefix.begin(), pubkey_prefix.end(), data.begin())) { + std::copy(data.begin() + pubkey_prefix.size(), data.end(), hash.begin()); + return CKeyID(hash); + } + // Script-hash-addresses have version 5 (or 196 testnet). + // The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. + const std::vector<unsigned char>& script_prefix = params.Base58Prefix(CChainParams::SCRIPT_ADDRESS); + if (data.size() == hash.size() + script_prefix.size() && std::equal(script_prefix.begin(), script_prefix.end(), data.begin())) { + std::copy(data.begin() + script_prefix.size(), data.end(), hash.begin()); + return CScriptID(hash); + } + } + data.clear(); + auto bech = bech32::Decode(str); + if (bech.second.size() > 0 && bech.first == params.Bech32HRP()) { + // Bech32 decoding + int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16) + // The rest of the symbols are converted witness program bytes. + if (ConvertBits<5, 8, false>(data, bech.second.begin() + 1, bech.second.end())) { + if (version == 0) { + { + WitnessV0KeyHash keyid; + if (data.size() == keyid.size()) { + std::copy(data.begin(), data.end(), keyid.begin()); + return keyid; + } + } + { + WitnessV0ScriptHash scriptid; + if (data.size() == scriptid.size()) { + std::copy(data.begin(), data.end(), scriptid.begin()); + return scriptid; + } + } + return CNoDestination(); + } + if (version > 16 || data.size() < 2 || data.size() > 40) { + return CNoDestination(); + } + WitnessUnknown unk; + unk.version = version; + std::copy(data.begin(), data.end(), unk.program); + unk.length = data.size(); + return unk; + } + } + return CNoDestination(); } +} // namespace void CBitcoinSecret::SetKey(const CKey& vchSecret) { @@ -318,3 +355,23 @@ bool CBitcoinSecret::SetString(const std::string& strSecret) { return SetString(strSecret.c_str()); } + +std::string EncodeDestination(const CTxDestination& dest) +{ + return boost::apply_visitor(DestinationEncoder(Params()), dest); +} + +CTxDestination DecodeDestination(const std::string& str) +{ + return DecodeDestination(str, Params()); +} + +bool IsValidDestinationString(const std::string& str, const CChainParams& params) +{ + return IsValidDestination(DecodeDestination(str, params)); +} + +bool IsValidDestinationString(const std::string& str) +{ + return IsValidDestinationString(str, Params()); +} diff --git a/src/base58.h b/src/base58.h index db12208f74..9dc4234248 100644 --- a/src/base58.h +++ b/src/base58.h @@ -17,7 +17,6 @@ #include "chainparams.h" #include "key.h" #include "pubkey.h" -#include "script/script.h" #include "script/standard.h" #include "support/allocators/zeroafterfree.h" @@ -95,30 +94,6 @@ public: bool operator> (const CBase58Data& b58) const { return CompareTo(b58) > 0; } }; -/** base58-encoded Bitcoin addresses. - * Public-key-hash-addresses have version 0 (or 111 testnet). - * The data vector contains RIPEMD160(SHA256(pubkey)), where pubkey is the serialized public key. - * Script-hash-addresses have version 5 (or 196 testnet). - * The data vector contains RIPEMD160(SHA256(cscript)), where cscript is the serialized redemption script. - */ -class CBitcoinAddress : public CBase58Data { -public: - bool Set(const CKeyID &id); - bool Set(const CScriptID &id); - bool Set(const CTxDestination &dest); - bool IsValid() const; - bool IsValid(const CChainParams ¶ms) const; - - CBitcoinAddress() {} - CBitcoinAddress(const CTxDestination &dest) { Set(dest); } - CBitcoinAddress(const std::string& strAddress) { SetString(strAddress); } - CBitcoinAddress(const char* pszAddress) { SetString(pszAddress); } - - CTxDestination Get() const; - bool GetKeyID(CKeyID &keyID) const; - bool IsScript() const; -}; - /** * A base58-encoded secret key */ @@ -167,4 +142,9 @@ public: typedef CBitcoinExtKeyBase<CExtKey, BIP32_EXTKEY_SIZE, CChainParams::EXT_SECRET_KEY> CBitcoinExtKey; typedef CBitcoinExtKeyBase<CExtPubKey, BIP32_EXTKEY_SIZE, CChainParams::EXT_PUBLIC_KEY> CBitcoinExtPubKey; +std::string EncodeDestination(const CTxDestination& dest); +CTxDestination DecodeDestination(const std::string& str); +bool IsValidDestinationString(const std::string& str); +bool IsValidDestinationString(const std::string& str, const CChainParams& params); + #endif // BITCOIN_BASE58_H diff --git a/src/bech32.cpp b/src/bech32.cpp new file mode 100644 index 0000000000..573eac58bb --- /dev/null +++ b/src/bech32.cpp @@ -0,0 +1,191 @@ +// Copyright (c) 2017 Pieter Wuille +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "bech32.h" + +namespace +{ + +typedef std::vector<uint8_t> data; + +/** The Bech32 character set for encoding. */ +const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + +/** The Bech32 character set for decoding. */ +const int8_t CHARSET_REV[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, + -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, + 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 +}; + +/** Concatenate two byte arrays. */ +data Cat(data x, const data& y) +{ + x.insert(x.end(), y.begin(), y.end()); + return x; +} + +/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to + * make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher + * bits correspond to earlier values. */ +uint32_t PolyMod(const data& v) +{ + // The input is interpreted as a list of coefficients of a polynomial over F = GF(32), with an + // implicit 1 in front. If the input is [v0,v1,v2,v3,v4], that polynomial is v(x) = + // 1*x^5 + v0*x^4 + v1*x^3 + v2*x^2 + v3*x + v4. The implicit 1 guarantees that + // [v0,v1,v2,...] has a distinct checksum from [0,v0,v1,v2,...]. + + // The output is a 30-bit integer whose 5-bit groups are the coefficients of the remainder of + // v(x) mod g(x), where g(x) is the Bech32 generator, + // x^6 + {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}. g(x) is chosen in such a way + // that the resulting code is a BCH code, guaranteeing detection of up to 3 errors within a + // window of 1023 characters. Among the various possible BCH codes, one was selected to in + // fact guarantee detection of up to 4 errors within a window of 89 characters. + + // Note that the coefficients are elements of GF(32), here represented as decimal numbers + // between {}. In this finite field, addition is just XOR of the corresponding numbers. For + // example, {27} + {13} = {27 ^ 13} = {22}. Multiplication is more complicated, and requires + // treating the bits of values themselves as coefficients of a polynomial over a smaller field, + // GF(2), and multiplying those polynomials mod a^5 + a^3 + 1. For example, {5} * {26} = + // (a^2 + 1) * (a^4 + a^3 + a) = (a^4 + a^3 + a) * a^2 + (a^4 + a^3 + a) = a^6 + a^5 + a^4 + a + // = a^3 + 1 (mod a^5 + a^3 + 1) = {9}. + + // During the course of the loop below, `c` contains the bitpacked coefficients of the + // polynomial constructed from just the values of v that were processed so far, mod g(x). In + // the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of + // v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value + // for `c`. + uint32_t c = 1; + for (auto v_i : v) { + // We want to update `c` to correspond to a polynomial with one extra term. If the initial + // value of `c` consists of the coefficients of c(x) = f(x) mod g(x), we modify it to + // correspond to c'(x) = (f(x) * x + v_i) mod g(x), where v_i is the next input to + // process. Simplifying: + // c'(x) = (f(x) * x + v_i) mod g(x) + // ((f(x) mod g(x)) * x + v_i) mod g(x) + // (c(x) * x + v_i) mod g(x) + // If c(x) = c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5, we want to compute + // c'(x) = (c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5) * x + v_i mod g(x) + // = c0*x^6 + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i mod g(x) + // = c0*(x^6 mod g(x)) + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i + // If we call (x^6 mod g(x)) = k(x), this can be written as + // c'(x) = (c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i) + c0*k(x) + + // First, determine the value of c0: + uint8_t c0 = c >> 25; + + // Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i: + c = ((c & 0x1ffffff) << 5) ^ v_i; + + // Finally, for each set bit n in c0, conditionally add {2^n}k(x): + if (c0 & 1) c ^= 0x3b6a57b2; // k(x) = {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18} + if (c0 & 2) c ^= 0x26508e6d; // {2}k(x) = {19}x^5 + {5}x^4 + x^3 + {3}x^2 + {19}x + {13} + if (c0 & 4) c ^= 0x1ea119fa; // {4}k(x) = {15}x^5 + {10}x^4 + {2}x^3 + {6}x^2 + {15}x + {26} + if (c0 & 8) c ^= 0x3d4233dd; // {8}k(x) = {30}x^5 + {20}x^4 + {4}x^3 + {12}x^2 + {30}x + {29} + if (c0 & 16) c ^= 0x2a1462b3; // {16}k(x) = {21}x^5 + x^4 + {8}x^3 + {24}x^2 + {21}x + {19} + } + return c; +} + +/** Convert to lower case. */ +inline unsigned char LowerCase(unsigned char c) +{ + return (c >= 'A' && c <= 'Z') ? (c - 'A') + 'a' : c; +} + +/** Expand a HRP for use in checksum computation. */ +data ExpandHRP(const std::string& hrp) +{ + data ret; + ret.reserve(hrp.size() + 90); + ret.resize(hrp.size() * 2 + 1); + for (size_t i = 0; i < hrp.size(); ++i) { + unsigned char c = hrp[i]; + ret[i] = c >> 5; + ret[i + hrp.size() + 1] = c & 0x1f; + } + ret[hrp.size()] = 0; + return ret; +} + +/** Verify a checksum. */ +bool VerifyChecksum(const std::string& hrp, const data& values) +{ + // PolyMod computes what value to xor into the final values to make the checksum 0. However, + // if we required that the checksum was 0, it would be the case that appending a 0 to a valid + // list of values would result in a new valid list. For that reason, Bech32 requires the + // resulting checksum to be 1 instead. + return PolyMod(Cat(ExpandHRP(hrp), values)) == 1; +} + +/** Create a checksum. */ +data CreateChecksum(const std::string& hrp, const data& values) +{ + data enc = Cat(ExpandHRP(hrp), values); + enc.resize(enc.size() + 6); // Append 6 zeroes + uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes. + data ret(6); + for (size_t i = 0; i < 6; ++i) { + // Convert the 5-bit groups in mod to checksum values. + ret[i] = (mod >> (5 * (5 - i))) & 31; + } + return ret; +} + +} // namespace + +namespace bech32 +{ + +/** Encode a Bech32 string. */ +std::string Encode(const std::string& hrp, const data& values) { + data checksum = CreateChecksum(hrp, values); + data combined = Cat(values, checksum); + std::string ret = hrp + '1'; + ret.reserve(ret.size() + combined.size()); + for (auto c : combined) { + ret += CHARSET[c]; + } + return ret; +} + +/** Decode a Bech32 string. */ +std::pair<std::string, data> Decode(const std::string& str) { + bool lower = false, upper = false; + for (size_t i = 0; i < str.size(); ++i) { + unsigned char c = str[i]; + if (c < 33 || c > 126) return {}; + if (c >= 'a' && c <= 'z') lower = true; + if (c >= 'A' && c <= 'Z') upper = true; + } + if (lower && upper) return {}; + size_t pos = str.rfind('1'); + if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) { + return {}; + } + data values(str.size() - 1 - pos); + for (size_t i = 0; i < str.size() - 1 - pos; ++i) { + unsigned char c = str[i + pos + 1]; + int8_t rev = (c < 33 || c > 126) ? -1 : CHARSET_REV[c]; + if (rev == -1) { + return {}; + } + values[i] = rev; + } + std::string hrp; + for (size_t i = 0; i < pos; ++i) { + hrp += LowerCase(str[i]); + } + if (!VerifyChecksum(hrp, values)) { + return {}; + } + return {hrp, data(values.begin(), values.end() - 6)}; +} + +} // namespace bech32 diff --git a/src/bech32.h b/src/bech32.h new file mode 100644 index 0000000000..7ef7b22213 --- /dev/null +++ b/src/bech32.h @@ -0,0 +1,25 @@ +// Copyright (c) 2017 Pieter Wuille +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +// Bech32 is a string encoding format used in newer address types. +// The output consists of a human-readable part (alphanumeric), a +// separator character (1), and a base32 data section, the last +// 6 characters of which are a checksum. +// +// For more information, see BIP 173. + +#include <stdint.h> +#include <string> +#include <vector> + +namespace bech32 +{ + +/** Encode a Bech32 string. Returns the empty string in case of failure. */ +std::string Encode(const std::string& hrp, const std::vector<uint8_t>& values); + +/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */ +std::pair<std::string, std::vector<uint8_t>> Decode(const std::string& str); + +} // namespace bech32 diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp index 849d924af2..7b307d6f42 100644 --- a/src/bench/bench.cpp +++ b/src/bench/bench.cpp @@ -55,13 +55,13 @@ bool benchmark::State::KeepRunning() else { now = gettimedouble(); double elapsed = now - lastTime; - double elapsedOne = elapsed * countMaskInv; + double elapsedOne = elapsed / (countMask + 1); if (elapsedOne < minTime) minTime = elapsedOne; if (elapsedOne > maxTime) maxTime = elapsedOne; // We only use relative values, so don't have to handle 64-bit wrap-around specially nowCycles = perf_cpucycles(); - uint64_t elapsedOneCycles = (nowCycles - lastCycles) * countMaskInv; + uint64_t elapsedOneCycles = (nowCycles - lastCycles) / (countMask + 1); if (elapsedOneCycles < minCycles) minCycles = elapsedOneCycles; if (elapsedOneCycles > maxCycles) maxCycles = elapsedOneCycles; @@ -69,7 +69,6 @@ bool benchmark::State::KeepRunning() // If the execution was much too fast (1/128th of maxElapsed), increase the count mask by 8x and restart timing. // The restart avoids including the overhead of this code in the measurement. countMask = ((countMask<<3)|7) & ((1LL<<60)-1); - countMaskInv = 1./(countMask+1); count = 0; minTime = std::numeric_limits<double>::max(); maxTime = std::numeric_limits<double>::min(); @@ -81,7 +80,6 @@ bool benchmark::State::KeepRunning() uint64_t newCountMask = ((countMask<<1)|1) & ((1LL<<60)-1); if ((count & newCountMask)==0) { countMask = newCountMask; - countMaskInv = 1./(countMask+1); } } } diff --git a/src/bench/bench.h b/src/bench/bench.h index 1f36f2a4bc..79109eaa56 100644 --- a/src/bench/bench.h +++ b/src/bench/bench.h @@ -41,7 +41,7 @@ namespace benchmark { std::string name; double maxElapsed; double beginTime; - double lastTime, minTime, maxTime, countMaskInv; + double lastTime, minTime, maxTime; uint64_t count; uint64_t countMask; uint64_t beginCycles; @@ -55,7 +55,6 @@ namespace benchmark { minCycles = std::numeric_limits<uint64_t>::max(); maxCycles = std::numeric_limits<uint64_t>::min(); countMask = 1; - countMaskInv = 1./(countMask + 1); } bool KeepRunning(); }; diff --git a/src/bench/crypto_hash.cpp b/src/bench/crypto_hash.cpp index 2914a36c7b..410a08e512 100644 --- a/src/bench/crypto_hash.cpp +++ b/src/bench/crypto_hash.cpp @@ -47,7 +47,7 @@ static void SHA256_32b(benchmark::State& state) std::vector<uint8_t> in(32,0); while (state.KeepRunning()) { for (int i = 0; i < 1000000; i++) { - CSHA256().Write(in.data(), in.size()).Finalize(&in[0]); + CSHA256().Write(in.data(), in.size()).Finalize(in.data()); } } } diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index fca6083ea8..e21a269221 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -37,6 +37,7 @@ std::string HelpMessageCli() strUsage += HelpMessageOpt("-?", _("This help message")); strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME)); strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory")); + strUsage += HelpMessageOpt("-getinfo", _("Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)")); AppendParamsHelpMessages(strUsage); strUsage += HelpMessageOpt("-named", strprintf(_("Pass named instead of positional arguments (default: %s)"), DEFAULT_NAMED)); strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT)); @@ -191,7 +192,96 @@ static void http_error_cb(enum evhttp_request_error err, void *ctx) } #endif -static UniValue CallRPC(const std::string& strMethod, const UniValue& params) +/** Class that handles the conversion from a command-line to a JSON-RPC request, + * as well as converting back to a JSON object that can be shown as result. + */ +class BaseRequestHandler +{ +public: + virtual UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) = 0; + virtual UniValue ProcessReply(const UniValue &batch_in) = 0; +}; + +/** Process getinfo requests */ +class GetinfoRequestHandler: public BaseRequestHandler +{ +public: + const int ID_NETWORKINFO = 0; + const int ID_BLOCKCHAININFO = 1; + const int ID_WALLETINFO = 2; + + /** Create a simulated `getinfo` request. */ + UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override + { + UniValue result(UniValue::VARR); + result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO)); + result.push_back(JSONRPCRequestObj("getblockchaininfo", NullUniValue, ID_BLOCKCHAININFO)); + result.push_back(JSONRPCRequestObj("getwalletinfo", NullUniValue, ID_WALLETINFO)); + return result; + } + + /** Collect values from the batch and form a simulated `getinfo` reply. */ + UniValue ProcessReply(const UniValue &batch_in) override + { + UniValue result(UniValue::VOBJ); + std::vector<UniValue> batch = JSONRPCProcessBatchReply(batch_in, 3); + // Errors in getnetworkinfo() and getblockchaininfo() are fatal, pass them on + // getwalletinfo() is allowed to fail in case there is no wallet. + if (!batch[ID_NETWORKINFO]["error"].isNull()) { + return batch[ID_NETWORKINFO]; + } + if (!batch[ID_BLOCKCHAININFO]["error"].isNull()) { + return batch[ID_BLOCKCHAININFO]; + } + result.pushKV("version", batch[ID_NETWORKINFO]["result"]["version"]); + result.pushKV("protocolversion", batch[ID_NETWORKINFO]["result"]["protocolversion"]); + if (!batch[ID_WALLETINFO].isNull()) { + result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]); + result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]); + } + result.pushKV("blocks", batch[ID_BLOCKCHAININFO]["result"]["blocks"]); + result.pushKV("timeoffset", batch[ID_NETWORKINFO]["result"]["timeoffset"]); + result.pushKV("connections", batch[ID_NETWORKINFO]["result"]["connections"]); + result.pushKV("proxy", batch[ID_NETWORKINFO]["result"]["networks"][0]["proxy"]); + result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]); + result.pushKV("testnet", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"].get_str() == "test")); + if (!batch[ID_WALLETINFO].isNull()) { + result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]); + result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]); + result.pushKV("keypoololdest", batch[ID_WALLETINFO]["result"]["keypoololdest"]); + result.pushKV("keypoolsize", batch[ID_WALLETINFO]["result"]["keypoolsize"]); + if (!batch[ID_WALLETINFO]["result"]["unlocked_until"].isNull()) { + result.pushKV("unlocked_until", batch[ID_WALLETINFO]["result"]["unlocked_until"]); + } + result.pushKV("paytxfee", batch[ID_WALLETINFO]["result"]["paytxfee"]); + } + result.pushKV("relayfee", batch[ID_NETWORKINFO]["result"]["relayfee"]); + result.pushKV("warnings", batch[ID_NETWORKINFO]["result"]["warnings"]); + return JSONRPCReplyObj(result, NullUniValue, 1); + } +}; + +/** Process default single requests */ +class DefaultRequestHandler: public BaseRequestHandler { +public: + UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override + { + UniValue params; + if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) { + params = RPCConvertNamedValues(method, args); + } else { + params = RPCConvertValues(method, args); + } + return JSONRPCRequestObj(method, params, 1); + } + + UniValue ProcessReply(const UniValue &reply) override + { + return reply.get_obj(); + } +}; + +static UniValue CallRPC(BaseRequestHandler *rh, const std::string& strMethod, const std::vector<std::string>& args) { std::string host; // In preference order, we choose the following for the port: @@ -238,7 +328,7 @@ static UniValue CallRPC(const std::string& strMethod, const UniValue& params) evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str()); // Attach request data - std::string strRequest = JSONRPCRequestObj(strMethod, params, 1).write() + "\n"; + std::string strRequest = rh->PrepareRequest(strMethod, args).write() + "\n"; struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get()); assert(output_buffer); evbuffer_add(output_buffer, strRequest.data(), strRequest.size()); @@ -277,7 +367,7 @@ static UniValue CallRPC(const std::string& strMethod, const UniValue& params) UniValue valReply(UniValue::VSTR); if (!valReply.read(response.body)) throw std::runtime_error("couldn't parse reply from server"); - const UniValue& reply = valReply.get_obj(); + const UniValue reply = rh->ProcessReply(valReply); if (reply.empty()) throw std::runtime_error("expected reply to have result, error and id properties"); @@ -296,34 +386,38 @@ int CommandLineRPC(int argc, char *argv[]) } std::string rpcPass; if (gArgs.GetBoolArg("-stdinrpcpass", false)) { - if(!std::getline(std::cin,rpcPass)) + if (!std::getline(std::cin, rpcPass)) { throw std::runtime_error("-stdinrpcpass specified but failed to read from standard input"); + } gArgs.ForceSetArg("-rpcpassword", rpcPass); } std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]); if (gArgs.GetBoolArg("-stdin", false)) { // Read one arg per line from stdin and append std::string line; - while (std::getline(std::cin,line)) + while (std::getline(std::cin, line)) { args.push_back(line); + } } - if (args.size() < 1) - throw std::runtime_error("too few parameters (need at least command)"); - std::string strMethod = args[0]; - args.erase(args.begin()); // Remove trailing method name from arguments vector - - UniValue params; - if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) { - params = RPCConvertNamedValues(strMethod, args); + std::unique_ptr<BaseRequestHandler> rh; + std::string method; + if (gArgs.GetBoolArg("-getinfo", false)) { + rh.reset(new GetinfoRequestHandler()); + method = ""; } else { - params = RPCConvertValues(strMethod, args); + rh.reset(new DefaultRequestHandler()); + if (args.size() < 1) { + throw std::runtime_error("too few parameters (need at least command)"); + } + method = args[0]; + args.erase(args.begin()); // Remove trailing method name from arguments vector } // Execute and handle connection failures with -rpcwait const bool fWait = gArgs.GetBoolArg("-rpcwait", false); do { try { - const UniValue reply = CallRPC(strMethod, params); + const UniValue reply = CallRPC(rh.get(), method, args); // Parse reply const UniValue& result = find_value(reply, "result"); diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index cff90d13af..e4f44435ba 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -271,11 +271,11 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strIn // extract and validate ADDRESS std::string strAddr = vStrInputParts[1]; - CBitcoinAddress addr(strAddr); - if (!addr.IsValid()) + CTxDestination destination = DecodeDestination(strAddr); + if (!IsValidDestination(destination)) { throw std::runtime_error("invalid TX output address"); - // build standard output script via GetScriptForDestination() - CScript scriptPubKey = GetScriptForDestination(addr.Get()); + } + CScript scriptPubKey = GetScriptForDestination(destination); // construct TxOut, append to transaction output list CTxOut txout(value, scriptPubKey); @@ -310,14 +310,15 @@ static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& str } if (bSegWit) { + if (!pubkey.IsCompressed()) { + throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs"); + } // Call GetScriptForWitness() to build a P2WSH scriptPubKey scriptPubKey = GetScriptForWitness(scriptPubKey); } if (bScriptHash) { - // Get the address for the redeem script, then call - // GetScriptForDestination() to construct a P2SH scriptPubKey. - CBitcoinAddress redeemScriptAddr(scriptPubKey); - scriptPubKey = GetScriptForDestination(redeemScriptAddr.Get()); + // Get the ID for the script, and then construct a P2SH destination for it. + scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } // construct TxOut, append to transaction output list @@ -377,14 +378,17 @@ static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& s CScript scriptPubKey = GetScriptForMultisig(required, pubkeys); if (bSegWit) { + for (CPubKey& pubkey : pubkeys) { + if (!pubkey.IsCompressed()) { + throw std::runtime_error("Uncompressed pubkeys are not useable for SegWit outputs"); + } + } // Call GetScriptForWitness() to build a P2WSH scriptPubKey scriptPubKey = GetScriptForWitness(scriptPubKey); } if (bScriptHash) { - // Get the address for the redeem script, then call - // GetScriptForDestination() to construct a P2SH scriptPubKey. - CBitcoinAddress addr(scriptPubKey); - scriptPubKey = GetScriptForDestination(addr.Get()); + // Get the ID for the script, and then construct a P2SH destination for it. + scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } // construct TxOut, append to transaction output list @@ -444,11 +448,10 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str } if (bSegWit) { - scriptPubKey = GetScriptForWitness(scriptPubKey); + scriptPubKey = GetScriptForWitness(scriptPubKey); } if (bScriptHash) { - CBitcoinAddress addr(scriptPubKey); - scriptPubKey = GetScriptForDestination(addr.Get()); + scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } // construct TxOut, append to transaction output list diff --git a/src/chain.cpp b/src/chain.cpp index 47acde882e..5e3dd9b31b 100644 --- a/src/chain.cpp +++ b/src/chain.cpp @@ -128,7 +128,7 @@ arith_uint256 GetBlockProof(const CBlockIndex& block) // We need to compute 2**256 / (bnTarget+1), but we can't represent 2**256 // as it's too large for an arith_uint256. However, as 2**256 is at least as large // as bnTarget+1, it is equal to ((2**256 - bnTarget - 1) / (bnTarget+1)) + 1, - // or ~bnTarget / (nTarget+1) + 1. + // or ~bnTarget / (bnTarget+1) + 1. return (~bnTarget / (bnTarget + 1)) + 1; } diff --git a/src/chain.h b/src/chain.h index ef7e6f9554..f1036e5d92 100644 --- a/src/chain.h +++ b/src/chain.h @@ -204,14 +204,14 @@ public: unsigned int nChainTx; //! Verification status of this block. See enum BlockStatus - unsigned int nStatus; + uint32_t nStatus; //! block header - int nVersion; + int32_t nVersion; uint256 hashMerkleRoot; - unsigned int nTime; - unsigned int nBits; - unsigned int nNonce; + uint32_t nTime; + uint32_t nBits; + uint32_t nNonce; //! (memory only) Sequential id assigned to distinguish order in which blocks are received. int32_t nSequenceId; diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 2021ec51db..85c9cd6934 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -137,6 +137,8 @@ public: base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x88, 0xB2, 0x1E}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x88, 0xAD, 0xE4}; + bech32_hrp = "bc"; + vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_main, pnSeed6_main + ARRAYLEN(pnSeed6_main)); fDefaultConsistencyChecks = false; @@ -236,6 +238,8 @@ public: base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; + bech32_hrp = "tb"; + vFixedSeeds = std::vector<SeedSpec6>(pnSeed6_test, pnSeed6_test + ARRAYLEN(pnSeed6_test)); fDefaultConsistencyChecks = false; @@ -330,6 +334,8 @@ public: base58Prefixes[SECRET_KEY] = std::vector<unsigned char>(1,239); base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; + + bech32_hrp = "bcrt"; } }; diff --git a/src/chainparams.h b/src/chainparams.h index f55ae4cf7f..3948c9163f 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -73,6 +73,7 @@ public: std::string NetworkIDString() const { return strNetworkID; } const std::vector<CDNSSeedData>& DNSSeeds() const { return vSeeds; } const std::vector<unsigned char>& Base58Prefix(Base58Type type) const { return base58Prefixes[type]; } + const std::string& Bech32HRP() const { return bech32_hrp; } const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; } const CCheckpointData& Checkpoints() const { return checkpointData; } const ChainTxData& TxData() const { return chainTxData; } @@ -86,6 +87,7 @@ protected: uint64_t nPruneAfterHeight; std::vector<CDNSSeedData> vSeeds; std::vector<unsigned char> base58Prefixes[MAX_BASE58_TYPES]; + std::string bech32_hrp; std::string strNetworkID; CBlock genesis; std::vector<SeedSpec6> vFixedSeeds; diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 224c9eb0ea..c966683b72 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -57,7 +57,7 @@ class CBaseRegTestParams : public CBaseChainParams public: CBaseRegTestParams() { - nRPCPort = 18332; + nRPCPort = 18443; strDataDir = "regtest"; } }; diff --git a/src/clientversion.cpp b/src/clientversion.cpp index d2344ded09..8a4b408831 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -10,7 +10,7 @@ /** * Name of client reported in the 'version' message. Report the same name - * for both bitcoind and bitcoin-core, to make it harder for attackers to + * for both bitcoind and bitcoin-qt, to make it harder for attackers to * target servers or GUI users specifically. */ const std::string CLIENT_NAME("Satoshi"); diff --git a/src/coins.h b/src/coins.h index efb5ce869c..181b2fd4b9 100644 --- a/src/coins.h +++ b/src/coins.h @@ -214,6 +214,11 @@ protected: public: CCoinsViewCache(CCoinsView *baseIn); + /** + * By deleting the copy constructor, we prevent accidentally using it when one intends to create a cache on top of a base cache. + */ + CCoinsViewCache(const CCoinsViewCache &) = delete; + // Standard CCoinsView methods bool GetCoin(const COutPoint &outpoint, Coin &coin) const override; bool HaveCoin(const COutPoint &outpoint) const override; @@ -290,11 +295,6 @@ public: private: CCoinsMap::iterator FetchCoin(const COutPoint &outpoint) const; - - /** - * By making the copy constructor private, we prevent accidentally using it when one intends to create a cache on top of a base cache. - */ - CCoinsViewCache(const CCoinsViewCache &); }; //! Utility function to add all of a transaction's outputs to a cache. diff --git a/src/compat/byteswap.h b/src/compat/byteswap.h index 3c5a5c0837..d93ff7413a 100644 --- a/src/compat/byteswap.h +++ b/src/compat/byteswap.h @@ -35,7 +35,7 @@ #if HAVE_DECL_BSWAP_16 == 0 inline uint16_t bswap_16(uint16_t x) { - return (x >> 8) | ((x & 0x00ff) << 8); + return (x >> 8) | (x << 8); } #endif // HAVE_DECL_BSWAP16 diff --git a/src/compat/endian.h b/src/compat/endian.h index 79d6b2fdbb..dbf178f53c 100644 --- a/src/compat/endian.h +++ b/src/compat/endian.h @@ -9,10 +9,10 @@ #include "config/bitcoin-config.h" #endif -#include <stdint.h> - #include "compat/byteswap.h" +#include <stdint.h> + #if defined(HAVE_ENDIAN_H) #include <endian.h> #elif defined(HAVE_SYS_ENDIAN_H) diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index 0a71915d1d..70aa9d7006 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -13,7 +13,7 @@ #include "chain.h" #include "coins.h" #include "utilmoneystr.h" - + bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime) { if (tx.nLockTime == 0) @@ -205,46 +205,46 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, bool fChe return true; } -bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight) +bool Consensus::CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee) { - // This doesn't trigger the DoS code on purpose; if it did, it would make it easier - // for an attacker to attempt to split the network. - if (!inputs.HaveInputs(tx)) - return state.Invalid(false, 0, "", "Inputs unavailable"); - - CAmount nValueIn = 0; - CAmount nFees = 0; - for (unsigned int i = 0; i < tx.vin.size(); i++) - { - const COutPoint &prevout = tx.vin[i].prevout; - const Coin& coin = inputs.AccessCoin(prevout); - assert(!coin.IsSpent()); - - // If prev is coinbase, check that it's matured - if (coin.IsCoinBase()) { - if (nSpendHeight - coin.nHeight < COINBASE_MATURITY) - return state.Invalid(false, - REJECT_INVALID, "bad-txns-premature-spend-of-coinbase", - strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight)); - } - - // Check for negative or overflow input values - nValueIn += coin.out.nValue; - if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange"); + // are the actual inputs available? + if (!inputs.HaveInputs(tx)) { + return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputs-missingorspent", false, + strprintf("%s: inputs missing/spent", __func__)); + } + + CAmount nValueIn = 0; + for (unsigned int i = 0; i < tx.vin.size(); ++i) { + const COutPoint &prevout = tx.vin[i].prevout; + const Coin& coin = inputs.AccessCoin(prevout); + assert(!coin.IsSpent()); + + // If prev is coinbase, check that it's matured + if (coin.IsCoinBase() && nSpendHeight - coin.nHeight < COINBASE_MATURITY) { + return state.Invalid(false, + REJECT_INVALID, "bad-txns-premature-spend-of-coinbase", + strprintf("tried to spend coinbase at depth %d", nSpendHeight - coin.nHeight)); + } + // Check for negative or overflow input values + nValueIn += coin.out.nValue; + if (!MoneyRange(coin.out.nValue) || !MoneyRange(nValueIn)) { + return state.DoS(100, false, REJECT_INVALID, "bad-txns-inputvalues-outofrange"); } + } + + const CAmount value_out = tx.GetValueOut(); + if (nValueIn < value_out) { + return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, + strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(value_out))); + } + + // Tally transaction fees + const CAmount txfee_aux = nValueIn - value_out; + if (!MoneyRange(txfee_aux)) { + return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange"); + } - if (nValueIn < tx.GetValueOut()) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-in-belowout", false, - strprintf("value in (%s) < value out (%s)", FormatMoney(nValueIn), FormatMoney(tx.GetValueOut()))); - - // Tally transaction fees - CAmount nTxFee = nValueIn - tx.GetValueOut(); - if (nTxFee < 0) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-negative"); - nFees += nTxFee; - if (!MoneyRange(nFees)) - return state.DoS(100, false, REJECT_INVALID, "bad-txns-fee-outofrange"); + txfee = txfee_aux; return true; } diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h index d46d3294ca..288892462d 100644 --- a/src/consensus/tx_verify.h +++ b/src/consensus/tx_verify.h @@ -5,6 +5,8 @@ #ifndef BITCOIN_CONSENSUS_TX_VERIFY_H #define BITCOIN_CONSENSUS_TX_VERIFY_H +#include "amount.h" + #include <stdint.h> #include <vector> @@ -22,9 +24,10 @@ namespace Consensus { /** * Check whether all inputs of this transaction are valid (no double spends and amounts) * This does not modify the UTXO set. This does not check scripts and sigs. + * @param[out] txfee Set to the transaction fee if successful. * Preconditions: tx.IsCoinBase() is false. */ -bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight); +bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee); } // namespace Consensus /** Auxiliary functions for transaction validation (ideally should not be exposed) */ diff --git a/src/consensus/validation.h b/src/consensus/validation.h index 5494ce40ea..b6740c9d9f 100644 --- a/src/consensus/validation.h +++ b/src/consensus/validation.h @@ -89,17 +89,16 @@ public: std::string GetDebugMessage() const { return strDebugMessage; } }; +// These implement the weight = (stripped_size * 4) + witness_size formula, +// using only serialization with and without witness data. As witness_size +// is equal to total_size - stripped_size, this formula is identical to: +// weight = (stripped_size * 3) + total_size. static inline int64_t GetTransactionWeight(const CTransaction& tx) { - return ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR -1) + ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + return ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); } - static inline int64_t GetBlockWeight(const CBlock& block) { - // This implements the weight = (stripped_size * 4) + witness_size formula, - // using only serialization with and without witness data. As witness_size - // is equal to total_size - stripped_size, this formula is identical to: - // weight = (stripped_size * 3) + total_size. return ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) * (WITNESS_SCALE_FACTOR - 1) + ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION); } diff --git a/src/core_write.cpp b/src/core_write.cpp index 1431fa0c9b..e16db13650 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -148,8 +148,9 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey, out.pushKV("type", GetTxnOutputType(type)); UniValue a(UniValue::VARR); - for (const CTxDestination& addr : addresses) - a.push_back(CBitcoinAddress(addr).ToString()); + for (const CTxDestination& addr : addresses) { + a.push_back(EncodeDestination(addr)); + } out.pushKV("addresses", a); } diff --git a/src/crypto/common.h b/src/crypto/common.h index bcca3d30ea..bd9bc9420b 100644 --- a/src/crypto/common.h +++ b/src/crypto/common.h @@ -6,7 +6,7 @@ #define BITCOIN_CRYPTO_COMMON_H #if defined(HAVE_CONFIG_H) -#include "bitcoin-config.h" +#include "config/bitcoin-config.h" #endif #include <stdint.h> diff --git a/src/crypto/hmac_sha256.h b/src/crypto/hmac_sha256.h index 1519c1457e..8c42fcfe14 100644 --- a/src/crypto/hmac_sha256.h +++ b/src/crypto/hmac_sha256.h @@ -10,7 +10,7 @@ #include <stdint.h> #include <stdlib.h> -/** A hasher class for HMAC-SHA-512. */ +/** A hasher class for HMAC-SHA-256. */ class CHMAC_SHA256 { private: diff --git a/src/crypto/sha256.cpp b/src/crypto/sha256.cpp index 15d6db90c2..29afe86ec7 100644 --- a/src/crypto/sha256.cpp +++ b/src/crypto/sha256.cpp @@ -10,7 +10,7 @@ #include <atomic> #if defined(__x86_64__) || defined(__amd64__) -#if defined(EXPERIMENTAL_ASM) +#if defined(USE_ASM) #include <cpuid.h> namespace sha256_sse4 { @@ -178,7 +178,7 @@ TransformType Transform = sha256::Transform; std::string SHA256AutoDetect() { -#if defined(EXPERIMENTAL_ASM) && (defined(__x86_64__) || defined(__amd64__)) +#if defined(USE_ASM) && (defined(__x86_64__) || defined(__amd64__)) uint32_t eax, ebx, ecx, edx; if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) && (ecx >> 19) & 1) { Transform = sha256_sse4::Transform; diff --git a/src/cuckoocache.h b/src/cuckoocache.h index 9246a3924e..947e1a7185 100644 --- a/src/cuckoocache.h +++ b/src/cuckoocache.h @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef _BITCOIN_CUCKOOCACHE_H_ -#define _BITCOIN_CUCKOOCACHE_H_ +#ifndef BITCOIN_CUCKOOCACHE_H +#define BITCOIN_CUCKOOCACHE_H #include <array> #include <algorithm> @@ -478,4 +478,4 @@ public: }; } // namespace CuckooCache -#endif +#endif // BITCOIN_CUCKOOCACHE_H diff --git a/src/dbwrapper.cpp b/src/dbwrapper.cpp index 72eeeef2c3..dfc90f3ab9 100644 --- a/src/dbwrapper.cpp +++ b/src/dbwrapper.cpp @@ -19,7 +19,7 @@ class CBitcoinLevelDBLogger : public leveldb::Logger { public: // This code is adapted from posix_logger.h, which is why it is using vsprintf. // Please do not do this in normal code - virtual void Logv(const char * format, va_list ap) override { + void Logv(const char * format, va_list ap) override { if (!LogAcceptCategory(BCLog::LEVELDB)) { return; } diff --git a/src/hash.h b/src/hash.h index ad59bb1817..474b13d65b 100644 --- a/src/hash.h +++ b/src/hash.h @@ -88,20 +88,6 @@ inline uint256 Hash(const T1 p1begin, const T1 p1end, return result; } -/** Compute the 256-bit hash of the concatenation of three objects. */ -template<typename T1, typename T2, typename T3> -inline uint256 Hash(const T1 p1begin, const T1 p1end, - const T2 p2begin, const T2 p2end, - const T3 p3begin, const T3 p3end) { - static const unsigned char pblank[1] = {}; - uint256 result; - CHash256().Write(p1begin == p1end ? pblank : (const unsigned char*)&p1begin[0], (p1end - p1begin) * sizeof(p1begin[0])) - .Write(p2begin == p2end ? pblank : (const unsigned char*)&p2begin[0], (p2end - p2begin) * sizeof(p2begin[0])) - .Write(p3begin == p3end ? pblank : (const unsigned char*)&p3begin[0], (p3end - p3begin) * sizeof(p3begin[0])) - .Finalize((unsigned char*)&result); - return result; -} - /** Compute the 160-bit hash an object. */ template<typename T1> inline uint160 Hash160(const T1 pbegin, const T1 pend) diff --git a/src/init.cpp b/src/init.cpp index e145c63ee5..539adc23d5 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -30,6 +30,7 @@ #include "policy/policy.h" #include "rpc/server.h" #include "rpc/register.h" +#include "rpc/safemode.h" #include "rpc/blockchain.h" #include "script/standard.h" #include "script/sigcache.h" @@ -43,7 +44,7 @@ #include "utilmoneystr.h" #include "validationinterface.h" #ifdef ENABLE_WALLET -#include "wallet/wallet.h" +#include "wallet/init.h" #endif #include "warnings.h" #include <stdint.h> @@ -69,7 +70,6 @@ bool fFeeEstimatesInitialized = false; static const bool DEFAULT_PROXYRANDOMIZE = true; static const bool DEFAULT_REST_ENABLE = false; -static const bool DEFAULT_DISABLE_SAFEMODE = true; static const bool DEFAULT_STOPAFTERBLOCKIMPORT = false; std::unique_ptr<CConnman> g_connman; @@ -102,12 +102,11 @@ static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat"; // created by AppInit() or the Qt main() function. // // A clean exit happens when StartShutdown() or the SIGTERM -// signal handler sets fRequestShutdown, which triggers -// the DetectShutdownThread(), which interrupts the main thread group. -// DetectShutdownThread() then exits, which causes AppInit() to -// continue (it .joins the shutdown thread). -// Shutdown() is then -// called to clean up database connections, and stop other +// signal handler sets fRequestShutdown, which makes main thread's +// WaitForShutdown() interrupts the thread group. +// And then, WaitForShutdown() makes all other on-going threads +// in the thread group join the main thread. +// Shutdown() is then called to clean up database connections, and stop other // threads that should only be stopped after the main network-processing // threads have exited. // @@ -188,17 +187,18 @@ void Shutdown() StopRPC(); StopHTTPServer(); #ifdef ENABLE_WALLET - for (CWalletRef pwallet : vpwallets) { - pwallet->Flush(false); - } + FlushWallets(); #endif MapPort(false); + + // Because these depend on each-other, we make sure that neither can be + // using the other before destroying them. UnregisterValidationInterface(peerLogic.get()); + if(g_connman) g_connman->Stop(); peerLogic.reset(); g_connman.reset(); StopTorControl(); - UnregisterNodeSignals(GetNodeSignals()); if (fDumpMempoolLater && gArgs.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { DumpMempool(); } @@ -245,9 +245,7 @@ void Shutdown() pblocktree = nullptr; } #ifdef ENABLE_WALLET - for (CWalletRef pwallet : vpwallets) { - pwallet->Flush(true); - } + StopWallets(); #endif #if ENABLE_ZMQ @@ -268,10 +266,7 @@ void Shutdown() UnregisterAllValidationInterfaces(); GetMainSignals().UnregisterBackgroundSignalScheduler(); #ifdef ENABLE_WALLET - for (CWalletRef pwallet : vpwallets) { - delete pwallet; - } - vpwallets.clear(); + CloseWallets(); #endif globalVerifyHandle.reset(); ECC_Stop(); @@ -317,15 +312,6 @@ void OnRPCStopped() LogPrint(BCLog::RPC, "RPC stopped.\n"); } -void OnRPCPreCommand(const CRPCCommand& cmd) -{ - // Observe safe mode - std::string strWarning = GetWarnings("rpc"); - if (strWarning != "" && !gArgs.GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE) && - !cmd.okSafeMode) - throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, std::string("Safe mode: ") + strWarning); -} - std::string HelpMessage(HelpMessageMode mode) { const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN); @@ -362,6 +348,9 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE)); strUsage += HelpMessageOpt("-mempoolexpiry=<n>", strprintf(_("Do not keep transactions in the mempool longer than <n> hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY)); + if (showDebug) { + strUsage += HelpMessageOpt("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex())); + } strUsage += HelpMessageOpt("-persistmempool", strprintf(_("Whether to save the mempool on shutdown and load on restart (default: %u)"), DEFAULT_PERSIST_MEMPOOL)); strUsage += HelpMessageOpt("-blockreconstructionextratxn=<n>", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN)); strUsage += HelpMessageOpt("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), @@ -420,7 +409,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-maxuploadtarget=<n>", strprintf(_("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)"), DEFAULT_MAX_UPLOAD_TARGET)); #ifdef ENABLE_WALLET - strUsage += CWallet::GetWalletHelpString(showDebug); + strUsage += GetWalletHelpString(showDebug); #endif #if ENABLE_ZMQ @@ -441,6 +430,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", defaultChainParams->DefaultConsistencyChecks())); strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED)); strUsage += HelpMessageOpt("-disablesafemode", strprintf("Disable safemode, override a real safe mode event (default: %u)", DEFAULT_DISABLE_SAFEMODE)); + strUsage += HelpMessageOpt("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used"); strUsage += HelpMessageOpt("-testsafemode", strprintf("Force safe mode (default: %u)", DEFAULT_TESTSAFEMODE)); strUsage += HelpMessageOpt("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages"); strUsage += HelpMessageOpt("-fuzzmessagestest=<n>", "Randomly fuzz 1 of every <n> network messages"); @@ -494,7 +484,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageGroup(_("Block creation options:")); strUsage += HelpMessageOpt("-blockmaxweight=<n>", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT)); - strUsage += HelpMessageOpt("-blockmaxsize=<n>", strprintf(_("Set maximum block size in bytes (default: %d)"), DEFAULT_BLOCK_MAX_SIZE)); + strUsage += HelpMessageOpt("-blockmaxsize=<n>", _("Set maximum BIP141 block weight to this * 4. Deprecated, use blockmaxweight")); strUsage += HelpMessageOpt("-blockmintxfee=<amt>", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE))); if (showDebug) strUsage += HelpMessageOpt("-blockversion=<n>", "Override block version to test forking scenarios"); @@ -547,9 +537,10 @@ static void BlockNotifyCallback(bool initialSync, const CBlockIndex *pBlockIndex return; std::string strCmd = gArgs.GetArg("-blocknotify", ""); - - boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex()); - boost::thread t(runCommand, strCmd); // thread runs free + if (!strCmd.empty()) { + boost::replace_all(strCmd, "%s", pBlockIndex->GetBlockHash().GetHex()); + boost::thread t(runCommand, strCmd); // thread runs free + } } static bool fHaveGenesis = false; @@ -720,7 +711,6 @@ bool AppInitServers(boost::thread_group& threadGroup) { RPCServer::OnStarted(&OnRPCStarted); RPCServer::OnStopped(&OnRPCStopped); - RPCServer::OnPreCommand(&OnRPCPreCommand); if (!InitHTTPServer()) return false; if (!StartRPC()) @@ -796,6 +786,15 @@ void InitParameterInteraction() if (gArgs.SoftSetBoolArg("-whitelistrelay", true)) LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__); } + + if (gArgs.IsArgSet("-blockmaxsize")) { + unsigned int max_size = gArgs.GetArg("-blockmaxsize", 0); + if (gArgs.SoftSetArg("blockmaxweight", strprintf("%d", max_size * WITNESS_SCALE_FACTOR))) { + LogPrintf("%s: parameter interaction: -blockmaxsize=%d -> setting -blockmaxweight=%d (-blockmaxsize is deprecated!)\n", __func__, max_size, max_size * WITNESS_SCALE_FACTOR); + } else { + LogPrintf("%s: Ignoring blockmaxsize setting which is overridden by blockmaxweight", __func__); + } + } } static std::string ResolveErrMsg(const char * const optname, const std::string& strBind) @@ -979,6 +978,20 @@ bool AppInitParameterInteraction() else LogPrintf("Validating signatures for all blocks.\n"); + if (gArgs.IsArgSet("-minimumchainwork")) { + const std::string minChainWorkStr = gArgs.GetArg("-minimumchainwork", ""); + if (!IsHexNumber(minChainWorkStr)) { + return InitError(strprintf("Invalid non-hex (%s) minimum chain work value specified", minChainWorkStr)); + } + nMinimumChainWork = UintToArith256(uint256S(minChainWorkStr)); + } else { + nMinimumChainWork = UintToArith256(chainparams.GetConsensus().nMinimumChainWork); + } + LogPrintf("Setting nMinimumChainWork=%s\n", nMinimumChainWork.GetHex()); + if (nMinimumChainWork < UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) { + LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainparams.GetConsensus().nMinimumChainWork.GetHex()); + } + // mempool limits int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; int64_t nMempoolSizeMin = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40; @@ -1023,7 +1036,7 @@ bool AppInitParameterInteraction() RegisterAllCoreRPCCommands(tableRPC); #ifdef ENABLE_WALLET - RegisterWalletRPCCommands(tableRPC); + RegisterWalletRPC(tableRPC); #endif nConnectTimeout = gArgs.GetArg("-timeout", DEFAULT_CONNECT_TIMEOUT); @@ -1035,7 +1048,7 @@ bool AppInitParameterInteraction() if (!ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n)) { return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", ""))); } - // High fee check is done afterward in CWallet::ParameterInteraction() + // High fee check is done afterward in WalletParameterInteraction() ::minRelayTxFee = CFeeRate(n); } else if (incrementalRelayFee > ::minRelayTxFee) { // Allow only setting incrementalRelayFee to control both @@ -1068,7 +1081,7 @@ bool AppInitParameterInteraction() nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp); #ifdef ENABLE_WALLET - if (!CWallet::ParameterInteraction()) + if (!WalletParameterInteraction()) return false; #endif @@ -1245,7 +1258,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 5: verify wallet database integrity #ifdef ENABLE_WALLET - if (!CWallet::Verify()) + if (!VerifyWallets()) return false; #endif // ********************************************************* Step 6: network initialization @@ -1260,7 +1273,6 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) peerLogic.reset(new PeerLogicValidation(&connman)); RegisterValidationInterface(peerLogic.get()); - RegisterNodeSignals(GetNodeSignals()); // sanitize comments per BIP-0014, format user agent and check total size std::vector<std::string> uacomments; @@ -1566,7 +1578,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 8: load wallet #ifdef ENABLE_WALLET - if (!CWallet::InitLoadWallet()) + if (!OpenWallets()) return false; #else LogPrintf("No wallet support compiled in!\n"); @@ -1631,9 +1643,16 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) // ********************************************************* Step 11: start node + int chain_active_height; + //// debug print - LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size()); - LogPrintf("nBestHeight = %d\n", chainActive.Height()); + { + LOCK(cs_main); + LogPrintf("mapBlockIndex.size() = %u\n", mapBlockIndex.size()); + chain_active_height = chainActive.Height(); + } + LogPrintf("nBestHeight = %d\n", chain_active_height); + if (gArgs.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) StartTorControl(threadGroup, scheduler); @@ -1649,10 +1668,12 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) connOptions.nMaxOutbound = std::min(MAX_OUTBOUND_CONNECTIONS, connOptions.nMaxConnections); connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS; connOptions.nMaxFeeler = 1; - connOptions.nBestHeight = chainActive.Height(); + connOptions.nBestHeight = chain_active_height; connOptions.uiInterface = &uiInterface; + connOptions.m_msgproc = peerLogic.get(); connOptions.nSendBufferMaxSize = 1000*gArgs.GetArg("-maxsendbuffer", DEFAULT_MAXSENDBUFFER); connOptions.nReceiveFloodSize = 1000*gArgs.GetArg("-maxreceivebuffer", DEFAULT_MAXRECEIVEBUFFER); + connOptions.m_added_nodes = gArgs.GetArgs("-addnode"); connOptions.nMaxOutboundTimeframe = nMaxOutboundTimeframe; connOptions.nMaxOutboundLimit = nMaxOutboundLimit; @@ -1683,10 +1704,16 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) connOptions.vWhitelistedRange.push_back(subnet); } - if (gArgs.IsArgSet("-seednode")) { - connOptions.vSeedNodes = gArgs.GetArgs("-seednode"); - } + connOptions.vSeedNodes = gArgs.GetArgs("-seednode"); + // Initiate outbound connections unless connect=0 + connOptions.m_use_addrman_outgoing = !gArgs.IsArgSet("-connect"); + if (!connOptions.m_use_addrman_outgoing) { + const auto connect = gArgs.GetArgs("-connect"); + if (connect.size() != 1 || connect[0] != "0") { + connOptions.m_specified_outgoing = connect; + } + } if (!connman.Start(scheduler, connOptions)) { return false; } @@ -1697,9 +1724,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler) uiInterface.InitMessage(_("Done loading")); #ifdef ENABLE_WALLET - for (CWalletRef pwallet : vpwallets) { - pwallet->postInitProcess(scheduler); - } + StartWallets(scheduler); #endif return !fRequestShutdown; @@ -56,11 +56,6 @@ public: keydata.resize(32); } - //! Destructor (again necessary because of memlocking). - ~CKey() - { - } - friend bool operator==(const CKey& a, const CKey& b) { return a.fCompressed == b.fCompressed && diff --git a/src/keystore.h b/src/keystore.h index 965ae0c79a..9b85ddb0ec 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -30,7 +30,7 @@ public: //! Check whether a key corresponding to a given address is present in the store. virtual bool HaveKey(const CKeyID &address) const =0; virtual bool GetKey(const CKeyID &address, CKey& keyOut) const =0; - virtual void GetKeys(std::set<CKeyID> &setAddress) const =0; + virtual std::set<CKeyID> GetKeys() const =0; virtual bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const =0; //! Support for BIP 0013 : see https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki @@ -71,18 +71,14 @@ public: } return result; } - void GetKeys(std::set<CKeyID> &setAddress) const override + std::set<CKeyID> GetKeys() const override { - setAddress.clear(); - { - LOCK(cs_KeyStore); - KeyMap::const_iterator mi = mapKeys.begin(); - while (mi != mapKeys.end()) - { - setAddress.insert((*mi).first); - mi++; - } + LOCK(cs_KeyStore); + std::set<CKeyID> set_address; + for (const auto& mi : mapKeys) { + set_address.insert(mi.first); } + return set_address; } bool GetKey(const CKeyID &address, CKey &keyOut) const override { @@ -97,14 +93,14 @@ public: } return false; } - virtual bool AddCScript(const CScript& redeemScript) override; - virtual bool HaveCScript(const CScriptID &hash) const override; - virtual bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const override; + bool AddCScript(const CScript& redeemScript) override; + bool HaveCScript(const CScriptID &hash) const override; + bool GetCScript(const CScriptID &hash, CScript& redeemScriptOut) const override; - virtual bool AddWatchOnly(const CScript &dest) override; - virtual bool RemoveWatchOnly(const CScript &dest) override; - virtual bool HaveWatchOnly(const CScript &dest) const override; - virtual bool HaveWatchOnly() const override; + bool AddWatchOnly(const CScript &dest) override; + bool RemoveWatchOnly(const CScript &dest) override; + bool HaveWatchOnly(const CScript &dest) const override; + bool HaveWatchOnly() const override; }; typedef std::vector<unsigned char, secure_allocator<unsigned char> > CKeyingMaterial; diff --git a/src/merkleblock.cpp b/src/merkleblock.cpp index f0abea0611..3f07b4dac4 100644 --- a/src/merkleblock.cpp +++ b/src/merkleblock.cpp @@ -9,33 +9,8 @@ #include "consensus/consensus.h" #include "utilstrencodings.h" -CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter& filter) -{ - header = block.GetBlockHeader(); - std::vector<bool> vMatch; - std::vector<uint256> vHashes; - - vMatch.reserve(block.vtx.size()); - vHashes.reserve(block.vtx.size()); - - for (unsigned int i = 0; i < block.vtx.size(); i++) - { - const uint256& hash = block.vtx[i]->GetHash(); - if (filter.IsRelevantAndUpdate(*block.vtx[i])) - { - vMatch.push_back(true); - vMatchedTxn.push_back(std::make_pair(i, hash)); - } - else - vMatch.push_back(false); - vHashes.push_back(hash); - } - - txn = CPartialMerkleTree(vHashes, vMatch); -} - -CMerkleBlock::CMerkleBlock(const CBlock& block, const std::set<uint256>& txids) +CMerkleBlock::CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set<uint256>* txids) { header = block.GetBlockHeader(); @@ -48,10 +23,14 @@ CMerkleBlock::CMerkleBlock(const CBlock& block, const std::set<uint256>& txids) for (unsigned int i = 0; i < block.vtx.size(); i++) { const uint256& hash = block.vtx[i]->GetHash(); - if (txids.count(hash)) + if (txids && txids->count(hash)) { vMatch.push_back(true); - else + } else if (filter && filter->IsRelevantAndUpdate(*block.vtx[i])) { + vMatch.push_back(true); + vMatchedTxn.emplace_back(i, hash); + } else { vMatch.push_back(false); + } vHashes.push_back(hash); } diff --git a/src/merkleblock.h b/src/merkleblock.h index 20f2b36886..6c05f2c1f8 100644 --- a/src/merkleblock.h +++ b/src/merkleblock.h @@ -131,8 +131,12 @@ public: CBlockHeader header; CPartialMerkleTree txn; -public: - /** Public only for unit testing and relay testing (not relayed) */ + /** + * Public only for unit testing and relay testing (not relayed). + * + * Used only when a bloom filter is specified to allow + * testing the transactions which matched the bloom filter. + */ std::vector<std::pair<unsigned int, uint256> > vMatchedTxn; /** @@ -140,10 +144,10 @@ public: * Note that this will call IsRelevantAndUpdate on the filter for each transaction, * thus the filter will likely be modified. */ - CMerkleBlock(const CBlock& block, CBloomFilter& filter); + CMerkleBlock(const CBlock& block, CBloomFilter& filter) : CMerkleBlock(block, &filter, nullptr) { } // Create from a CBlock, matching the txids in the set - CMerkleBlock(const CBlock& block, const std::set<uint256>& txids); + CMerkleBlock(const CBlock& block, const std::set<uint256>& txids) : CMerkleBlock(block, nullptr, &txids) { } CMerkleBlock() {} @@ -154,6 +158,10 @@ public: READWRITE(header); READWRITE(txn); } + +private: + // Combined constructor to consolidate code + CMerkleBlock(const CBlock& block, CBloomFilter* filter, const std::set<uint256>* txids); }; #endif // BITCOIN_MERKLEBLOCK_H diff --git a/src/miner.cpp b/src/miner.cpp index f1942ec570..a9989f4b17 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -43,7 +43,6 @@ // its ancestors. uint64_t nLastBlockTx = 0; -uint64_t nLastBlockSize = 0; uint64_t nLastBlockWeight = 0; int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev) @@ -64,7 +63,6 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam BlockAssembler::Options::Options() { blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE); nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT; - nBlockMaxSize = DEFAULT_BLOCK_MAX_SIZE; } BlockAssembler::BlockAssembler(const CChainParams& params, const Options& options) : chainparams(params) @@ -72,10 +70,6 @@ BlockAssembler::BlockAssembler(const CChainParams& params, const Options& option blockMinFeeRate = options.blockMinFeeRate; // Limit weight to between 4K and MAX_BLOCK_WEIGHT-4K for sanity: nBlockMaxWeight = std::max<size_t>(4000, std::min<size_t>(MAX_BLOCK_WEIGHT - 4000, options.nBlockMaxWeight)); - // Limit size to between 1K and MAX_BLOCK_SERIALIZED_SIZE-1K for sanity: - nBlockMaxSize = std::max<size_t>(1000, std::min<size_t>(MAX_BLOCK_SERIALIZED_SIZE - 1000, options.nBlockMaxSize)); - // Whether we need to account for byte usage (in addition to weight usage) - fNeedSizeAccounting = (nBlockMaxSize < MAX_BLOCK_SERIALIZED_SIZE - 1000); } static BlockAssembler::Options DefaultOptions(const CChainParams& params) @@ -85,20 +79,7 @@ static BlockAssembler::Options DefaultOptions(const CChainParams& params) // If only one is given, only restrict the specified resource. // If both are given, restrict both. BlockAssembler::Options options; - options.nBlockMaxWeight = DEFAULT_BLOCK_MAX_WEIGHT; - options.nBlockMaxSize = DEFAULT_BLOCK_MAX_SIZE; - bool fWeightSet = false; - if (gArgs.IsArgSet("-blockmaxweight")) { - options.nBlockMaxWeight = gArgs.GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT); - options.nBlockMaxSize = MAX_BLOCK_SERIALIZED_SIZE; - fWeightSet = true; - } - if (gArgs.IsArgSet("-blockmaxsize")) { - options.nBlockMaxSize = gArgs.GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE); - if (!fWeightSet) { - options.nBlockMaxWeight = options.nBlockMaxSize * WITNESS_SCALE_FACTOR; - } - } + options.nBlockMaxWeight = gArgs.GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT); if (gArgs.IsArgSet("-blockmintxfee")) { CAmount n = 0; ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n); @@ -116,7 +97,6 @@ void BlockAssembler::resetBlock() inBlock.clear(); // Reserve space for coinbase tx - nBlockSize = 1000; nBlockWeight = 4000; nBlockSigOpsCost = 400; fIncludeWitness = false; @@ -145,6 +125,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc LOCK2(cs_main, mempool.cs); CBlockIndex* pindexPrev = chainActive.Tip(); + assert(pindexPrev != nullptr); nHeight = pindexPrev->nHeight + 1; pblock->nVersion = ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); @@ -175,7 +156,6 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc int64_t nTime1 = GetTimeMicros(); nLastBlockTx = nBlockTx; - nLastBlockSize = nBlockSize; nLastBlockWeight = nBlockWeight; // Create coinbase transaction. @@ -190,8 +170,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc pblocktemplate->vchCoinbaseCommitment = GenerateCoinbaseCommitment(*pblock, pindexPrev, chainparams.GetConsensus()); pblocktemplate->vTxFees[0] = -nFees; - uint64_t nSerializeSize = GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION); - LogPrintf("CreateNewBlock(): total size: %u block weight: %u txs: %u fees: %ld sigops %d\n", nSerializeSize, GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost); + LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost); // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); @@ -238,22 +217,13 @@ bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOpsCost // - transaction finality (locktime) // - premature witness (in case segwit transactions are added to mempool before // segwit activation) -// - serialized size (in case -blockmaxsize is in use) bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& package) { - uint64_t nPotentialBlockSize = nBlockSize; // only used with fNeedSizeAccounting for (const CTxMemPool::txiter it : package) { if (!IsFinalTx(it->GetTx(), nHeight, nLockTimeCutoff)) return false; if (!fIncludeWitness && it->GetTx().HasWitness()) return false; - if (fNeedSizeAccounting) { - uint64_t nTxSize = ::GetSerializeSize(it->GetTx(), SER_NETWORK, PROTOCOL_VERSION); - if (nPotentialBlockSize + nTxSize >= nBlockMaxSize) { - return false; - } - nPotentialBlockSize += nTxSize; - } } return true; } @@ -263,9 +233,6 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) pblock->vtx.emplace_back(iter->GetSharedTx()); pblocktemplate->vTxFees.push_back(iter->GetFee()); pblocktemplate->vTxSigOpsCost.push_back(iter->GetSigOpCost()); - if (fNeedSizeAccounting) { - nBlockSize += ::GetSerializeSize(iter->GetTx(), SER_NETWORK, PROTOCOL_VERSION); - } nBlockWeight += iter->GetTxWeight(); ++nBlockTx; nBlockSigOpsCost += iter->GetSigOpCost(); diff --git a/src/miner.h b/src/miner.h index 6e5fe761db..db165e71c6 100644 --- a/src/miner.h +++ b/src/miner.h @@ -11,8 +11,8 @@ #include <stdint.h> #include <memory> -#include "boost/multi_index_container.hpp" -#include "boost/multi_index/ordered_index.hpp" +#include <boost/multi_index_container.hpp> +#include <boost/multi_index/ordered_index.hpp> class CBlockIndex; class CChainParams; @@ -139,13 +139,11 @@ private: // Configuration parameters for the block size bool fIncludeWitness; - unsigned int nBlockMaxWeight, nBlockMaxSize; - bool fNeedSizeAccounting; + unsigned int nBlockMaxWeight; CFeeRate blockMinFeeRate; // Information on the current status of the block uint64_t nBlockWeight; - uint64_t nBlockSize; uint64_t nBlockTx; uint64_t nBlockSigOpsCost; CAmount nFees; @@ -160,7 +158,6 @@ public: struct Options { Options(); size_t nBlockMaxWeight; - size_t nBlockMaxSize; CFeeRate blockMinFeeRate; }; diff --git a/src/net.cpp b/src/net.cpp index 1af317726a..ea3840a708 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -89,10 +89,6 @@ std::string strSubVersion; limitedmap<uint256, int64_t> mapAlreadyAskedFor(MAX_INV_SZ); -// Signals for message handling -static CNodeSignals g_signals; -CNodeSignals& GetNodeSignals() { return g_signals; } - void CConnman::AddOneShot(const std::string& strDest) { LOCK(cs_vOneShots); @@ -139,11 +135,10 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn const int64_t nOneWeek = 7*24*60*60; std::vector<CAddress> vSeedsOut; vSeedsOut.reserve(vSeedsIn.size()); - for (std::vector<SeedSpec6>::const_iterator i(vSeedsIn.begin()); i != vSeedsIn.end(); ++i) - { + for (const auto& seed_in : vSeedsIn) { struct in6_addr ip; - memcpy(&ip, i->addr, sizeof(ip)); - CAddress addr(CService(ip, i->port), NODE_NETWORK); + memcpy(&ip, seed_in.addr, sizeof(ip)); + CAddress addr(CService(ip, seed_in.port), NODE_NETWORK); addr.nTime = GetTime() - GetRand(nOneWeek) - nOneWeek; vSeedsOut.push_back(addr); } @@ -303,18 +298,22 @@ bool IsReachable(const CNetAddr& addr) CNode* CConnman::FindNode(const CNetAddr& ip) { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - if ((CNetAddr)pnode->addr == ip) - return (pnode); + for (CNode* pnode : vNodes) { + if ((CNetAddr)pnode->addr == ip) { + return pnode; + } + } return nullptr; } CNode* CConnman::FindNode(const CSubNet& subNet) { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - if (subNet.Match((CNetAddr)pnode->addr)) - return (pnode); + for (CNode* pnode : vNodes) { + if (subNet.Match((CNetAddr)pnode->addr)) { + return pnode; + } + } return nullptr; } @@ -323,7 +322,7 @@ CNode* CConnman::FindNode(const std::string& addrName) LOCK(cs_vNodes); for (CNode* pnode : vNodes) { if (pnode->GetAddrName() == addrName) { - return (pnode); + return pnode; } } return nullptr; @@ -332,9 +331,11 @@ CNode* CConnman::FindNode(const std::string& addrName) CNode* CConnman::FindNode(const CService& addr) { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - if ((CService)pnode->addr == addr) - return (pnode); + for (CNode* pnode : vNodes) { + if ((CService)pnode->addr == addr) { + return pnode; + } + } return nullptr; } @@ -384,19 +385,16 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo pszDest ? pszDest : addrConnect.ToString(), pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0); - // Connect - SOCKET hSocket; - bool proxyConnectionFailed = false; - if (pszDest ? ConnectSocketByName(addrConnect, hSocket, pszDest, Params().GetDefaultPort(), nConnectTimeout, &proxyConnectionFailed) : - ConnectSocket(addrConnect, hSocket, nConnectTimeout, &proxyConnectionFailed)) - { - if (!IsSelectableSocket(hSocket)) { - LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n"); - CloseSocket(hSocket); - return nullptr; - } - - if (pszDest && addrConnect.IsValid()) { + // Resolve + const int default_port = Params().GetDefaultPort(); + if (pszDest) { + std::vector<CService> resolved; + if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) { + addrConnect = CAddress(resolved[GetRand(resolved.size())], NODE_NONE); + if (!addrConnect.IsValid()) { + LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s", addrConnect.ToString(), pszDest); + return nullptr; + } // It is possible that we already have a connection to the IP/port pszDest resolved to. // In that case, drop the connection that was just created, and return the existing CNode instead. // Also store the name we used to connect in that CNode, so that future FindNode() calls to that @@ -406,13 +404,40 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo if (pnode) { pnode->MaybeSetAddrName(std::string(pszDest)); - CloseSocket(hSocket); LogPrintf("Failed to open new connection, already connected\n"); return nullptr; } } + } - addrman.Attempt(addrConnect, fCountFailure); + // Connect + bool connected = false; + SOCKET hSocket; + proxyType proxy; + if (addrConnect.IsValid()) { + bool proxyConnectionFailed = false; + + if (GetProxy(addrConnect.GetNetwork(), proxy)) + connected = ConnectThroughProxy(proxy, addrConnect.ToStringIP(), addrConnect.GetPort(), hSocket, nConnectTimeout, &proxyConnectionFailed); + else // no proxy needed (none set for target network) + connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout); + if (!proxyConnectionFailed) { + // If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to + // the proxy, mark this as an attempt. + addrman.Attempt(addrConnect, fCountFailure); + } + } else if (pszDest && GetNameProxy(proxy)) { + std::string host; + int port = default_port; + SplitHostPort(std::string(pszDest), port, host); + connected = ConnectThroughProxy(proxy, host, port, hSocket, nConnectTimeout, nullptr); + } + if (connected) { + if (!IsSelectableSocket(hSocket)) { + LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n"); + CloseSocket(hSocket); + return nullptr; + } // Add node NodeId id = GetNewNodeId(); @@ -423,10 +448,6 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo pnode->AddRef(); return pnode; - } else if (!proxyConnectionFailed) { - // If connecting to the node failed, and failure is not caused by a problem connecting to - // the proxy, mark this as an attempt. - addrman.Attempt(addrConnect, fCountFailure); } return nullptr; @@ -478,10 +499,9 @@ void CConnman::ClearBanned() bool CConnman::IsBanned(CNetAddr ip) { LOCK(cs_setBanned); - for (banmap_t::iterator it = setBanned.begin(); it != setBanned.end(); it++) - { - CSubNet subNet = (*it).first; - CBanEntry banEntry = (*it).second; + for (const auto& it : setBanned) { + CSubNet subNet = it.first; + CBanEntry banEntry = it.second; if (subNet.Match(ip) && GetTime() < banEntry.nBanUntil) { return true; @@ -956,7 +976,7 @@ bool CConnman::AttemptToEvictConnection() { LOCK(cs_vNodes); - for (CNode *node : vNodes) { + for (const CNode* node : vNodes) { if (node->fWhitelisted) continue; if (!node->fInbound) @@ -1034,9 +1054,9 @@ bool CConnman::AttemptToEvictConnection() // Disconnect from the network group with the most connections NodeId evicted = vEvictionCandidates.front().id; LOCK(cs_vNodes); - for(std::vector<CNode*>::const_iterator it(vNodes.begin()); it != vNodes.end(); ++it) { - if ((*it)->GetId() == evicted) { - (*it)->fDisconnect = true; + for (CNode* pnode : vNodes) { + if (pnode->GetId() == evicted) { + pnode->fDisconnect = true; return true; } } @@ -1060,9 +1080,9 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr); { LOCK(cs_vNodes); - for (CNode* pnode : vNodes) - if (pnode->fInbound) - nInbound++; + for (const CNode* pnode : vNodes) { + if (pnode->fInbound) nInbound++; + } } if (hSocket == INVALID_SOCKET) @@ -1114,7 +1134,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) { CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true); pnode->AddRef(); pnode->fWhitelisted = whitelisted; - GetNodeSignals().InitializeNode(pnode, *this); + m_msgproc->InitializeNode(pnode); LogPrint(BCLog::NET, "connection from %s accepted\n", addr.ToString()); @@ -1674,15 +1694,15 @@ void CConnman::ProcessOneShot() } } -void CConnman::ThreadOpenConnections() +void CConnman::ThreadOpenConnections(const std::vector<std::string> connect) { // Connect to specific addresses - if (gArgs.IsArgSet("-connect")) + if (!connect.empty()) { for (int64_t nLoop = 0;; nLoop++) { ProcessOneShot(); - for (const std::string& strAddr : gArgs.GetArgs("-connect")) + for (const std::string& strAddr : connect) { CAddress addr(CService(), NODE_NONE); OpenNetworkConnection(addr, false, nullptr, strAddr.c_str()); @@ -1854,8 +1874,7 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() { LOCK(cs_vAddedNodes); ret.reserve(vAddedNodes.size()); - for (const std::string& strAddNode : vAddedNodes) - lAddresses.push_back(strAddNode); + std::copy(vAddedNodes.cbegin(), vAddedNodes.cend(), std::back_inserter(lAddresses)); } @@ -1901,11 +1920,6 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() void CConnman::ThreadOpenAddedConnections() { - { - LOCK(cs_vAddedNodes); - vAddedNodes = gArgs.GetArgs("-addnode"); - } - while (true) { CSemaphoreGrant grant(*semAddnode); @@ -1918,11 +1932,9 @@ void CConnman::ThreadOpenAddedConnections() // the addednodeinfo state might change. break; } - // If strAddedNode is an IP/port, decode it immediately, so - // OpenNetworkConnection can detect existing connections to that IP/port. tried = true; - CService service(LookupNumeric(info.strAddedNode.c_str(), Params().GetDefaultPort())); - OpenNetworkConnection(CAddress(service, NODE_NONE), false, &grant, info.strAddedNode.c_str(), false, false, true); + CAddress addr(CService(), NODE_NONE); + OpenNetworkConnection(addr, false, &grant, info.strAddedNode.c_str(), false, false, true); if (!interruptNet.sleep_for(std::chrono::milliseconds(500))) return; } @@ -1966,7 +1978,7 @@ bool CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai if (fAddnode) pnode->fAddnode = true; - GetNodeSignals().InitializeNode(pnode, *this); + m_msgproc->InitializeNode(pnode); { LOCK(cs_vNodes); vNodes.push_back(pnode); @@ -1996,16 +2008,16 @@ void CConnman::ThreadMessageHandler() continue; // Receive messages - bool fMoreNodeWork = GetNodeSignals().ProcessMessages(pnode, *this, flagInterruptMsgProc); + bool fMoreNodeWork = m_msgproc->ProcessMessages(pnode, flagInterruptMsgProc); fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend); if (flagInterruptMsgProc) return; - // Send messages { LOCK(pnode->cs_sendProcessing); - GetNodeSignals().SendMessages(pnode, *this, flagInterruptMsgProc); + m_msgproc->SendMessages(pnode, flagInterruptMsgProc); } + if (flagInterruptMsgProc) return; } @@ -2324,6 +2336,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) // // Start threads // + assert(m_msgproc); InterruptSocks5(false); interruptNet.reset(); flagInterruptMsgProc = false; @@ -2344,9 +2357,16 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) // Initiate outbound connections from -addnode threadOpenAddedConnections = std::thread(&TraceThread<std::function<void()> >, "addcon", std::function<void()>(std::bind(&CConnman::ThreadOpenAddedConnections, this))); - // Initiate outbound connections unless connect=0 - if (!gArgs.IsArgSet("-connect") || gArgs.GetArgs("-connect").size() != 1 || gArgs.GetArgs("-connect")[0] != "0") - threadOpenConnections = std::thread(&TraceThread<std::function<void()> >, "opencon", std::function<void()>(std::bind(&CConnman::ThreadOpenConnections, this))); + if (connOptions.m_use_addrman_outgoing && !connOptions.m_specified_outgoing.empty()) { + if (clientInterface) { + clientInterface->ThreadSafeMessageBox( + _("Cannot provide specific connections and have addrman find outgoing connections at the same."), + "", CClientUIInterface::MSG_ERROR); + } + return false; + } + if (connOptions.m_use_addrman_outgoing || !connOptions.m_specified_outgoing.empty()) + threadOpenConnections = std::thread(&TraceThread<std::function<void()> >, "opencon", std::function<void()>(std::bind(&CConnman::ThreadOpenConnections, this, connOptions.m_specified_outgoing))); // Process messages threadMessageHandler = std::thread(&TraceThread<std::function<void()> >, "msghand", std::function<void()>(std::bind(&CConnman::ThreadMessageHandler, this))); @@ -2443,9 +2463,10 @@ void CConnman::DeleteNode(CNode* pnode) { assert(pnode); bool fUpdateConnectionTime = false; - GetNodeSignals().FinalizeNode(pnode->GetId(), fUpdateConnectionTime); - if(fUpdateConnectionTime) + m_msgproc->FinalizeNode(pnode->GetId(), fUpdateConnectionTime); + if(fUpdateConnectionTime) { addrman.Connected(pnode->addr); + } delete pnode; } @@ -2483,9 +2504,8 @@ std::vector<CAddress> CConnman::GetAddresses() bool CConnman::AddNode(const std::string& strNode) { LOCK(cs_vAddedNodes); - for(std::vector<std::string>::const_iterator it = vAddedNodes.begin(); it != vAddedNodes.end(); ++it) { - if (strNode == *it) - return false; + for (const std::string& it : vAddedNodes) { + if (strNode == it) return false; } vAddedNodes.push_back(strNode); @@ -2511,9 +2531,11 @@ size_t CConnman::GetNodeCount(NumConnections flags) return vNodes.size(); int nNum = 0; - for(std::vector<CNode*>::const_iterator it = vNodes.begin(); it != vNodes.end(); ++it) - if (flags & ((*it)->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT)) + for (const auto& pnode : vNodes) { + if (flags & (pnode->fInbound ? CONNECTIONS_IN : CONNECTIONS_OUT)) { nNum++; + } + } return nNum; } @@ -2523,8 +2545,7 @@ void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) vstats.clear(); LOCK(cs_vNodes); vstats.reserve(vNodes.size()); - for(std::vector<CNode*>::iterator it = vNodes.begin(); it != vNodes.end(); ++it) { - CNode* pnode = *it; + for (CNode* pnode : vNodes) { vstats.emplace_back(); pnode->copyStats(vstats.back()); } @@ -33,7 +33,6 @@ #include <arpa/inet.h> #endif -#include <boost/signals2/signal.hpp> class CScheduler; class CNode; @@ -116,7 +115,7 @@ struct CSerializedNetMsg std::string command; }; - +class NetEventsInterface; class CConnman { public: @@ -138,6 +137,7 @@ public: int nMaxFeeler = 0; int nBestHeight = 0; CClientUIInterface* uiInterface = nullptr; + NetEventsInterface* m_msgproc = nullptr; unsigned int nSendBufferMaxSize = 0; unsigned int nReceiveFloodSize = 0; uint64_t nMaxOutboundTimeframe = 0; @@ -145,6 +145,9 @@ public: std::vector<std::string> vSeedNodes; std::vector<CSubNet> vWhitelistedRange; std::vector<CService> vBinds, vWhiteBinds; + bool m_use_addrman_outgoing = true; + std::vector<std::string> m_specified_outgoing; + std::vector<std::string> m_added_nodes; }; void Init(const Options& connOptions) { @@ -156,11 +159,13 @@ public: nMaxFeeler = connOptions.nMaxFeeler; nBestHeight = connOptions.nBestHeight; clientInterface = connOptions.uiInterface; + m_msgproc = connOptions.m_msgproc; nSendBufferMaxSize = connOptions.nSendBufferMaxSize; nReceiveFloodSize = connOptions.nReceiveFloodSize; nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe; nMaxOutboundLimit = connOptions.nMaxOutboundLimit; vWhitelistedRange = connOptions.vWhitelistedRange; + vAddedNodes = connOptions.m_added_nodes; } CConnman(uint64_t seed0, uint64_t seed1); @@ -308,7 +313,7 @@ private: void ThreadOpenAddedConnections(); void AddOneShot(const std::string& strDest); void ProcessOneShot(); - void ThreadOpenConnections(); + void ThreadOpenConnections(std::vector<std::string> connect); void ThreadMessageHandler(); void AcceptConnection(const ListenSocket& hListenSocket); void ThreadSocketHandler(); @@ -396,6 +401,7 @@ private: int nMaxFeeler; std::atomic<int> nBestHeight; CClientUIInterface* clientInterface; + NetEventsInterface* m_msgproc; /** SipHasher seeds for deterministic randomness */ const uint64_t nSeed0, nSeed1; @@ -436,19 +442,18 @@ struct CombinerAll } }; -// Signals for message handling -struct CNodeSignals +/** + * Interface for message handling + */ +class NetEventsInterface { - boost::signals2::signal<bool (CNode*, CConnman&, std::atomic<bool>&), CombinerAll> ProcessMessages; - boost::signals2::signal<bool (CNode*, CConnman&, std::atomic<bool>&), CombinerAll> SendMessages; - boost::signals2::signal<void (CNode*, CConnman&)> InitializeNode; - boost::signals2::signal<void (NodeId, bool&)> FinalizeNode; +public: + virtual bool ProcessMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0; + virtual bool SendMessages(CNode* pnode, std::atomic<bool>& interrupt) = 0; + virtual void InitializeNode(CNode* pnode) = 0; + virtual void FinalizeNode(NodeId id, bool& update_connection_time) = 0; }; - -CNodeSignals& GetNodeSignals(); - - enum { LOCAL_NONE, // unknown @@ -699,13 +704,11 @@ public: CNode(NodeId id, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn, SOCKET hSocketIn, const CAddress &addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress &addrBindIn, const std::string &addrNameIn = "", bool fInboundIn = false); ~CNode(); + CNode(const CNode&) = delete; + CNode& operator=(const CNode&) = delete; private: - CNode(const CNode&); - void operator=(const CNode&); const NodeId id; - - const uint64_t nLocalHostNonce; // Services offered to this peer const ServiceFlags nLocalServices; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 596ae1139b..b5e37722df 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -61,6 +61,14 @@ static std::vector<std::pair<uint256, CTransactionRef>> vExtraTxnForCompact GUAR static const uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; // SHA256("main address relay")[0:8] +/// Age after which a stale block will no longer be served if requested as +/// protection against fingerprinting. Set to one month, denominated in seconds. +static const int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60; + +/// Age after which a block is considered historical for purposes of rate +/// limiting block relay. Set to one week, denominated in seconds. +static const int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60; + // Internal stuff namespace { /** Number of nodes with fSyncStarted. */ @@ -123,11 +131,6 @@ namespace { std::deque<std::pair<int64_t, MapRelay::iterator>> vRelayExpiration; } // namespace -////////////////////////////////////////////////////////////////////////////// -// -// Registration of network node signals. -// - namespace { struct CBlockReject { @@ -244,7 +247,7 @@ void UpdatePreferredDownload(CNode* node, CNodeState* state) nPreferredDownload += state->fPreferredDownload; } -void PushNodeVersion(CNode *pnode, CConnman& connman, int64_t nTime) +void PushNodeVersion(CNode *pnode, CConnman* connman, int64_t nTime) { ServiceFlags nLocalNodeServices = pnode->GetLocalServices(); uint64_t nonce = pnode->GetLocalNonce(); @@ -255,7 +258,7 @@ void PushNodeVersion(CNode *pnode, CConnman& connman, int64_t nTime) CAddress addrYou = (addr.IsRoutable() && !IsProxy(addr) ? addr : CAddress(CService(), addr.nServices)); CAddress addrMe = CAddress(CService(), nLocalNodeServices); - connman.PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe, + connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERSION, PROTOCOL_VERSION, (uint64_t)nLocalNodeServices, nTime, addrYou, addrMe, nonce, strSubVersion, nNodeStartingHeight, ::fRelayTxes)); if (fLogIPs) { @@ -265,49 +268,6 @@ void PushNodeVersion(CNode *pnode, CConnman& connman, int64_t nTime) } } -void InitializeNode(CNode *pnode, CConnman& connman) { - CAddress addr = pnode->addr; - std::string addrName = pnode->GetAddrName(); - NodeId nodeid = pnode->GetId(); - { - LOCK(cs_main); - mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName))); - } - if(!pnode->fInbound) - PushNodeVersion(pnode, connman, GetTime()); -} - -void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) { - fUpdateConnectionTime = false; - LOCK(cs_main); - CNodeState *state = State(nodeid); - - if (state->fSyncStarted) - nSyncStarted--; - - if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { - fUpdateConnectionTime = true; - } - - for (const QueuedBlock& entry : state->vBlocksInFlight) { - mapBlocksInFlight.erase(entry.hash); - } - EraseOrphansFor(nodeid); - nPreferredDownload -= state->fPreferredDownload; - nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0); - assert(nPeersWithValidatedDownloads >= 0); - - mapNodeState.erase(nodeid); - - if (mapNodeState.empty()) { - // Do a consistency check after the last peer is removed. - assert(mapBlocksInFlight.empty()); - assert(nPreferredDownload == 0); - assert(nPeersWithValidatedDownloads == 0); - } - LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid); -} - // Requires cs_main. // Returns a bool indicating whether we requested this block. // Also used if a block was /not/ received and timed out or started with another peer @@ -315,6 +275,7 @@ bool MarkBlockAsReceived(const uint256& hash) { std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash); if (itInFlight != mapBlocksInFlight.end()) { CNodeState *state = State(itInFlight->second.first); + assert(state != nullptr); state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders; if (state->nBlocksInFlightValidHeaders == 0 && itInFlight->second.second->fValidatedHeaders) { // Last validated block on the queue was received. @@ -402,7 +363,7 @@ void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) { } } -void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connman) { +void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman* connman) { AssertLockHeld(cs_main); CNodeState* nodestate = State(nodeid); if (!nodestate || !nodestate->fSupportsDesiredCmpctVersion) { @@ -417,20 +378,20 @@ void MaybeSetPeerAsAnnouncingHeaderAndIDs(NodeId nodeid, CConnman& connman) { return; } } - connman.ForNode(nodeid, [&connman](CNode* pfrom){ + connman->ForNode(nodeid, [connman](CNode* pfrom){ bool fAnnounceUsingCMPCTBLOCK = false; uint64_t nCMPCTBLOCKVersion = (pfrom->GetLocalServices() & NODE_WITNESS) ? 2 : 1; if (lNodesAnnouncingHeaderAndIDs.size() >= 3) { // As per BIP152, we only get 3 of our peers to announce // blocks using compact encodings. - connman.ForNode(lNodesAnnouncingHeaderAndIDs.front(), [&connman, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion](CNode* pnodeStop){ - connman.PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); + connman->ForNode(lNodesAnnouncingHeaderAndIDs.front(), [connman, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion](CNode* pnodeStop){ + connman->PushMessage(pnodeStop, CNetMsgMaker(pnodeStop->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); return true; }); lNodesAnnouncingHeaderAndIDs.pop_front(); } fAnnounceUsingCMPCTBLOCK = true; - connman.PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); + connman->PushMessage(pfrom, CNetMsgMaker(pfrom->GetSendVersion()).Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); lNodesAnnouncingHeaderAndIDs.push_back(pfrom->GetId()); return true; }); @@ -466,7 +427,7 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<con // Make sure pindexBestKnownBlock is up to date, we'll need it. ProcessBlockAvailability(nodeid); - if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < UintToArith256(consensusParams.nMinimumChainWork)) { + if (state->pindexBestKnownBlock == nullptr || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork || state->pindexBestKnownBlock->nChainWork < nMinimumChainWork) { // This peer has nothing interesting. return; } @@ -543,6 +504,50 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<con } // namespace +void PeerLogicValidation::InitializeNode(CNode *pnode) { + CAddress addr = pnode->addr; + std::string addrName = pnode->GetAddrName(); + NodeId nodeid = pnode->GetId(); + { + LOCK(cs_main); + mapNodeState.emplace_hint(mapNodeState.end(), std::piecewise_construct, std::forward_as_tuple(nodeid), std::forward_as_tuple(addr, std::move(addrName))); + } + if(!pnode->fInbound) + PushNodeVersion(pnode, connman, GetTime()); +} + +void PeerLogicValidation::FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) { + fUpdateConnectionTime = false; + LOCK(cs_main); + CNodeState *state = State(nodeid); + assert(state != nullptr); + + if (state->fSyncStarted) + nSyncStarted--; + + if (state->nMisbehavior == 0 && state->fCurrentlyConnected) { + fUpdateConnectionTime = true; + } + + for (const QueuedBlock& entry : state->vBlocksInFlight) { + mapBlocksInFlight.erase(entry.hash); + } + EraseOrphansFor(nodeid); + nPreferredDownload -= state->fPreferredDownload; + nPeersWithValidatedDownloads -= (state->nBlocksInFlightValidHeaders != 0); + assert(nPeersWithValidatedDownloads >= 0); + + mapNodeState.erase(nodeid); + + if (mapNodeState.empty()) { + // Do a consistency check after the last peer is removed. + assert(mapBlocksInFlight.empty()); + assert(nPreferredDownload == 0); + assert(nPeersWithValidatedDownloads == 0); + } + LogPrint(BCLog::NET, "Cleared nodestate for peer=%d\n", nodeid); +} + bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { LOCK(cs_main); CNodeState *state = State(nodeid); @@ -558,22 +563,6 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats) { return true; } -void RegisterNodeSignals(CNodeSignals& nodeSignals) -{ - nodeSignals.ProcessMessages.connect(&ProcessMessages); - nodeSignals.SendMessages.connect(&SendMessages); - nodeSignals.InitializeNode.connect(&InitializeNode); - nodeSignals.FinalizeNode.connect(&FinalizeNode); -} - -void UnregisterNodeSignals(CNodeSignals& nodeSignals) -{ - nodeSignals.ProcessMessages.disconnect(&ProcessMessages); - nodeSignals.SendMessages.disconnect(&SendMessages); - nodeSignals.InitializeNode.disconnect(&InitializeNode); - nodeSignals.FinalizeNode.disconnect(&FinalizeNode); -} - ////////////////////////////////////////////////////////////////////////////// // // mapOrphanTransactions @@ -725,6 +714,17 @@ void Misbehaving(NodeId pnode, int howmuch) // blockchain -> download logic notification // +// To prevent fingerprinting attacks, only send blocks/headers outside of the +// active chain if they are no more than a month older (both in time, and in +// best equivalent proof of work) than the best header chain we know about. +static bool StaleBlockRequestAllowed(const CBlockIndex* pindex, const Consensus::Params& consensusParams) +{ + AssertLockHeld(cs_main); + return (pindexBestHeader != nullptr) && + (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() < STALE_RELAY_AGE_LIMIT) && + (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, consensusParams) < STALE_RELAY_AGE_LIMIT); +} + PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn) : connman(connmanIn) { // Initialize global variables that cannot be constructed at startup. recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); @@ -865,7 +865,7 @@ void PeerLogicValidation::BlockChecked(const CBlock& block, const CValidationSta !IsInitialBlockDownload() && mapBlocksInFlight.count(hash) == mapBlocksInFlight.size()) { if (it != mapBlockSource.end()) { - MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, *connman); + MaybeSetPeerAsAnnouncingHeaderAndIDs(it->second.first, connman); } } if (it != mapBlockSource.end()) @@ -910,16 +910,16 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) return true; } -static void RelayTransaction(const CTransaction& tx, CConnman& connman) +static void RelayTransaction(const CTransaction& tx, CConnman* connman) { CInv inv(MSG_TX, tx.GetHash()); - connman.ForEachNode([&inv](CNode* pnode) + connman->ForEachNode([&inv](CNode* pnode) { pnode->PushInventory(inv); }); } -static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connman) +static void RelayAddress(const CAddress& addr, bool fReachable, CConnman* connman) { unsigned int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s) @@ -927,7 +927,7 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connma // Use deterministic randomness to send to the same nodes for 24 hours // at a time so the addrKnowns of the chosen nodes prevent repeats uint64_t hashAddr = addr.GetHash(); - const CSipHasher hasher = connman.GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60)); + const CSipHasher hasher = connman->GetDeterministicRandomizer(RANDOMIZER_ID_ADDRESS_RELAY).Write(hashAddr << 32).Write((GetTime() + hashAddr) / (24*60*60)); FastRandomContext insecure_rand; std::array<std::pair<uint64_t, CNode*>,2> best{{{0, nullptr}, {0, nullptr}}}; @@ -952,10 +952,10 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connma } }; - connman.ForEachNodeThen(std::move(sortfunc), std::move(pushfunc)); + connman->ForEachNodeThen(std::move(sortfunc), std::move(pushfunc)); } -void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman& connman, const std::atomic<bool>& interruptMsgProc) +void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman* connman, const std::atomic<bool>& interruptMsgProc) { std::deque<CInv>::iterator it = pfrom->vRecvGetData.begin(); std::vector<CInv> vNotFound; @@ -1002,13 +1002,8 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam if (chainActive.Contains(mi->second)) { send = true; } else { - static const int nOneMonth = 30 * 24 * 60 * 60; - // To prevent fingerprinting attacks, only send blocks outside of the active - // chain if they are valid, and no more than a month older (both in time, and in - // best equivalent proof of work) than the best header chain we know about. - send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && (pindexBestHeader != nullptr) && - (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() < nOneMonth) && - (GetBlockProofEquivalentTime(*pindexBestHeader, *mi->second, *pindexBestHeader, consensusParams) < nOneMonth); + send = mi->second->IsValid(BLOCK_VALID_SCRIPTS) && + StaleBlockRequestAllowed(mi->second, consensusParams); if (!send) { LogPrintf("%s: ignoring request from peer=%i for old block that isn't in the main chain\n", __func__, pfrom->GetId()); } @@ -1016,8 +1011,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } // disconnect node in case we have reached the outbound limit for serving historical blocks // never disconnect whitelisted nodes - static const int nOneWeek = 7 * 24 * 60 * 60; // assume > 1 week = historical - if (send && connman.OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > nOneWeek)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted) + if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - mi->second->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted) { LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId()); @@ -1040,9 +1034,9 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam pblock = pblockRead; } if (inv.type == MSG_BLOCK) - connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock)); + connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock)); else if (inv.type == MSG_WITNESS_BLOCK) - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock)); else if (inv.type == MSG_FILTERED_BLOCK) { bool sendMerkleBlock = false; @@ -1055,7 +1049,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam } } if (sendMerkleBlock) { - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock)); // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see // This avoids hurting performance by pointlessly requiring a round-trip // Note that there is currently no way for a node to request any single transactions we didn't send here - @@ -1064,7 +1058,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // however we MUST always provide at least what the remote peer needs typedef std::pair<unsigned int, uint256> PairType; for (PairType& pair : merkleBlock.vMatchedTxn) - connman.PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first])); + connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first])); } // else // no response @@ -1079,13 +1073,13 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; if (CanDirectFetch(consensusParams) && mi->second->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) { if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == mi->second->GetBlockHash()) { - connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block)); + connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block)); } else { CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness); - connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); + connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); } } else { - connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock)); + connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock)); } } @@ -1097,7 +1091,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // wait for other stuff first. std::vector<CInv> vInv; vInv.push_back(CInv(MSG_BLOCK, chainActive.Tip()->GetBlockHash())); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::INV, vInv)); pfrom->hashContinue.SetNull(); } } @@ -1109,14 +1103,14 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam auto mi = mapRelay.find(inv.hash); int nSendFlags = (inv.type == MSG_TX ? SERIALIZE_TRANSACTION_NO_WITNESS : 0); if (mi != mapRelay.end()) { - connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second)); + connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second)); push = true; } else if (pfrom->timeLastMempoolReq) { auto txinfo = mempool.info(inv.hash); // To protect privacy, do not answer getdata using the mempool when // that TX couldn't have been INVed in reply to a MEMPOOL request. if (txinfo.tx && txinfo.nTime <= pfrom->timeLastMempoolReq) { - connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx)); + connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx)); push = true; } } @@ -1143,7 +1137,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam // do that because they want to know about (and store and rebroadcast and // risk analyze) the dependencies of transactions relevant to them, without // having to download the entire memory pool. - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::NOTFOUND, vNotFound)); } } @@ -1155,7 +1149,7 @@ uint32_t GetFetchFlags(CNode* pfrom) { return nFetchFlags; } -inline void static SendBlockTransactions(const CBlock& block, const BlockTransactionsRequest& req, CNode* pfrom, CConnman& connman) { +inline void static SendBlockTransactions(const CBlock& block, const BlockTransactionsRequest& req, CNode* pfrom, CConnman* connman) { BlockTransactions resp(req); for (size_t i = 0; i < req.indexes.size(); i++) { if (req.indexes[i] >= block.vtx.size()) { @@ -1169,10 +1163,10 @@ inline void static SendBlockTransactions(const CBlock& block, const BlockTransac LOCK(cs_main); const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); int nSendFlags = State(pfrom->GetId())->fWantsCmpctWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; - connman.PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); + connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCKTXN, resp)); } -bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman& connman, const std::atomic<bool>& interruptMsgProc) +bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc) { LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId()); if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0) @@ -1225,7 +1219,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // Each connection can only send one version message if (pfrom->nVersion != 0) { - connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, std::string("Duplicate version message"))); + connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, std::string("Duplicate version message"))); LOCK(cs_main); Misbehaving(pfrom->GetId(), 1); return false; @@ -1249,12 +1243,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr nServices = ServiceFlags(nServiceInt); if (!pfrom->fInbound) { - connman.SetServices(pfrom->addr, nServices); + connman->SetServices(pfrom->addr, nServices); } if (pfrom->nServicesExpected & ~nServices) { LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, pfrom->nServicesExpected); - connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD, + connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD, strprintf("Expected to offer services %08x", pfrom->nServicesExpected))); pfrom->fDisconnect = true; return false; @@ -1275,7 +1269,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr { // disconnect from peers older than this proto version LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion); - connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, + connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION))); pfrom->fDisconnect = true; return false; @@ -1295,7 +1289,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (!vRecv.empty()) vRecv >> fRelay; // Disconnect if we connected to ourself - if (pfrom->fInbound && !connman.CheckIncomingNonce(nNonce)) + if (pfrom->fInbound && !connman->CheckIncomingNonce(nNonce)) { LogPrintf("connected to self at %s, disconnecting\n", pfrom->addr.ToString()); pfrom->fDisconnect = true; @@ -1311,7 +1305,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (pfrom->fInbound) PushNodeVersion(pfrom, connman, GetAdjustedTime()); - connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK)); + connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::VERACK)); pfrom->nServices = nServices; pfrom->SetAddrLocal(addrMe); @@ -1362,12 +1356,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } // Get recent addresses - if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman.GetAddressCount() < 1000) + if (pfrom->fOneShot || pfrom->nVersion >= CADDR_TIME_VERSION || connman->GetAddressCount() < 1000) { - connman.PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR)); + connman->PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make(NetMsgType::GETADDR)); pfrom->fGetAddr = true; } - connman.MarkAddressGood(pfrom->addr); + connman->MarkAddressGood(pfrom->addr); } std::string remoteAddr; @@ -1386,7 +1380,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // If the peer is old enough to have the old alert system, send it the final alert. if (pfrom->nVersion <= 70012) { CDataStream finalAlert(ParseHex("60010000000000000000000000ffffff7f00000000ffffff7ffeffff7f01ffffff7f00000000ffffff7f00ffffff7f002f555247454e543a20416c657274206b657920636f6d70726f6d697365642c2075706772616465207265717569726564004630440220653febd6410f470f6bae11cad19c48413becb1ac2c17f908fd0fd53bdc3abd5202206d0e9c96fe88d4a0f01ed9dedae2b6f9e00da94cad0fecaae66ecf689bf71b50"), SER_NETWORK, PROTOCOL_VERSION); - connman.PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert)); + connman->PushMessage(pfrom, CNetMsgMaker(nSendVersion).Make("alert", finalAlert)); } // Feeler connections exist only to verify if address is online. @@ -1424,7 +1418,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // We send this to non-NODE NETWORK peers as well, because even // non-NODE NETWORK peers can announce blocks (such as pruning // nodes) - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDHEADERS)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDHEADERS)); } if (pfrom->nVersion >= SHORT_IDS_BLOCKS_VERSION) { // Tell our peer we are willing to provide version 1 or 2 cmpctblocks @@ -1435,9 +1429,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr bool fAnnounceUsingCMPCTBLOCK = false; uint64_t nCMPCTBLOCKVersion = 2; if (pfrom->GetLocalServices() & NODE_WITNESS) - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); nCMPCTBLOCKVersion = 1; - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::SENDCMPCT, fAnnounceUsingCMPCTBLOCK, nCMPCTBLOCKVersion)); } pfrom->fSuccessfullyConnected = true; } @@ -1456,7 +1450,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr vRecv >> vAddr; // Don't want addr from older versions unless seeding - if (pfrom->nVersion < CADDR_TIME_VERSION && connman.GetAddressCount() > 1000) + if (pfrom->nVersion < CADDR_TIME_VERSION && connman->GetAddressCount() > 1000) return true; if (vAddr.size() > 1000) { @@ -1490,7 +1484,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (fReachable) vAddrOk.push_back(addr); } - connman.AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60); + connman->AddNewAddresses(vAddrOk, pfrom->addr, 2 * 60 * 60); if (vAddr.size() < 1000) pfrom->fGetAddr = false; if (pfrom->fOneShot) @@ -1568,7 +1562,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // fell back to inv we probably have a reorg which we should get the headers for first, // we now only provide a getheaders response here. When we receive the headers, we will // then ask for the blocks we need. - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), inv.hash)); LogPrint(BCLog::NET, "getheaders (%d) %s to peer=%d\n", pindexBestHeader->nHeight, inv.hash.ToString(), pfrom->GetId()); } } @@ -1742,6 +1736,12 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (mi == mapBlockIndex.end()) return true; pindex = (*mi).second; + + if (!chainActive.Contains(pindex) && + !StaleBlockRequestAllowed(pindex, chainparams.GetConsensus())) { + LogPrintf("%s: ignoring request from peer=%i for old block header that isn't in the main chain\n", __func__, pfrom->GetId()); + return true; + } } else { @@ -1774,7 +1774,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // will re-announce the new block via headers (or compact blocks again) // in the SendMessages logic. nodestate->pindexBestHeaderSent = pindex ? pindex : chainActive.Tip(); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); } @@ -1807,7 +1807,8 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr std::list<CTransactionRef> lRemovedTxn; - if (!AlreadyHave(inv) && AcceptToMemoryPool(mempool, state, ptx, true, &fMissingInputs, &lRemovedTxn)) { + if (!AlreadyHave(inv) && + AcceptToMemoryPool(mempool, state, ptx, &fMissingInputs, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) { mempool.check(pcoinsTip); RelayTransaction(tx, connman); for (unsigned int i = 0; i < tx.vout.size(); i++) { @@ -1845,7 +1846,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (setMisbehaving.count(fromPeer)) continue; - if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, true, &fMissingInputs2, &lRemovedTxn)) { + if (AcceptToMemoryPool(mempool, stateDummy, porphanTx, &fMissingInputs2, &lRemovedTxn, false /* bypass_limits */, 0 /* nAbsurdFee */)) { LogPrint(BCLog::MEMPOOL, " accepted orphan tx %s\n", orphanHash.ToString()); RelayTransaction(orphanTx, connman); for (unsigned int i = 0; i < orphanTx.vout.size(); i++) { @@ -1955,7 +1956,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr pfrom->GetId(), FormatStateMessage(state)); if (state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash)); if (nDoS > 0) { Misbehaving(pfrom->GetId(), nDoS); @@ -1975,7 +1976,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (mapBlockIndex.find(cmpctblock.header.hashPrevBlock) == mapBlockIndex.end()) { // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers if (!IsInitialBlockDownload()) - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); return true; } } @@ -2030,7 +2031,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // so we just grab the block via normal getdata std::vector<CInv> vInv(1); vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash()); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); } return true; } @@ -2074,7 +2075,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // Duplicate txindexes, the block is now in-flight, so just request it std::vector<CInv> vInv(1); vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash()); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); return true; } @@ -2091,7 +2092,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr fProcessBLOCKTXN = true; } else { req.blockhash = pindex->GetBlockHash(); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETBLOCKTXN, req)); } } else { // This block is either already in flight from a different @@ -2117,7 +2118,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // mempool will probably be useless - request the block normally std::vector<CInv> vInv(1); vInv[0] = CInv(MSG_BLOCK | GetFetchFlags(pfrom), cmpctblock.header.GetHash()); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vInv)); return true; } else { // If this was an announce-cmpctblock, we want the same treatment as a header message @@ -2191,7 +2192,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // Might have collided, fall back to getdata now :( std::vector<CInv> invs; invs.push_back(CInv(MSG_BLOCK | GetFetchFlags(pfrom), resp.blockhash)); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, invs)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, invs)); } else { // Block is either okay, or possibly we received // READ_STATUS_CHECKBLOCK_FAILED. @@ -2272,7 +2273,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // nUnconnectingHeaders gets reset back to 0. if (mapBlockIndex.find(headers[0].hashPrevBlock) == mapBlockIndex.end() && nCount < MAX_BLOCKS_TO_ANNOUNCE) { nodestate->nUnconnectingHeaders++; - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexBestHeader), uint256())); LogPrint(BCLog::NET, "received header %s: missing prev block %s, sending getheaders (%d) to end (peer=%d, nUnconnectingHeaders=%d)\n", headers[0].GetHash().ToString(), headers[0].hashPrevBlock.ToString(), @@ -2327,7 +2328,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // TODO: optimize: if pindexLast is an ancestor of chainActive.Tip or pindexBestHeader, continue // from there instead. LogPrint(BCLog::NET, "more getheaders (%d) to end to peer=%d (startheight:%d)\n", pindexLast->nHeight, pfrom->GetId(), pfrom->nStartingHeight); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256())); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexLast), uint256())); } bool fCanDirectFetch = CanDirectFetch(chainparams.GetConsensus()); @@ -2377,7 +2378,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // In any case, we want to download using a compact block, not a regular one vGetData[0] = CInv(MSG_CMPCT_BLOCK, vGetData[0].hash); } - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::GETDATA, vGetData)); } } } @@ -2438,7 +2439,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr pfrom->fSentAddr = true; pfrom->vAddrToSend.clear(); - std::vector<CAddress> vAddr = connman.GetAddresses(); + std::vector<CAddress> vAddr = connman->GetAddresses(); FastRandomContext insecure_rand; for (const CAddress &addr : vAddr) pfrom->PushAddress(addr, insecure_rand); @@ -2454,7 +2455,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr return true; } - if (connman.OutboundTargetReached(false) && !pfrom->fWhitelisted) + if (connman->OutboundTargetReached(false) && !pfrom->fWhitelisted) { LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId()); pfrom->fDisconnect = true; @@ -2483,7 +2484,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // it, if the remote node sends a ping once per second and this node takes 5 // seconds to respond to each, the 5th ping the remote sends would appear to // return very quickly. - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::PONG, nonce)); + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::PONG, nonce)); } } @@ -2629,13 +2630,13 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr return true; } -static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman& connman) +static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman) { AssertLockHeld(cs_main); CNodeState &state = *State(pnode->GetId()); for (const CBlockReject& reject : state.rejects) { - connman.PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, (std::string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock)); + connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, (std::string)NetMsgType::BLOCK, reject.chRejectCode, reject.strRejectReason, reject.hashBlock)); } state.rejects.clear(); @@ -2651,7 +2652,7 @@ static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman& connman) LogPrintf("Warning: not banning local peer %s!\n", pnode->addr.ToString()); else { - connman.Ban(pnode->addr, BanReasonNodeMisbehaving); + connman->Ban(pnode->addr, BanReasonNodeMisbehaving); } } return true; @@ -2659,7 +2660,7 @@ static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman& connman) return false; } -bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& interruptMsgProc) +bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& interruptMsgProc) { const CChainParams& chainparams = Params(); // @@ -2693,7 +2694,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& i // Just take one message msgs.splice(msgs.begin(), pfrom->vProcessMsg, pfrom->vProcessMsg.begin()); pfrom->nProcessQueueSize -= msgs.front().vRecv.size() + CMessageHeader::HEADER_SIZE; - pfrom->fPauseRecv = pfrom->nProcessQueueSize > connman.GetReceiveFloodSize(); + pfrom->fPauseRecv = pfrom->nProcessQueueSize > connman->GetReceiveFloodSize(); fMoreWork = !pfrom->vProcessMsg.empty(); } CNetMessage& msg(msgs.front()); @@ -2742,7 +2743,7 @@ bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& i } catch (const std::ios_base::failure& e) { - connman.PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message"))); + connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message"))); if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv @@ -2796,7 +2797,7 @@ public: } }; -bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interruptMsgProc) +bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptMsgProc) { const Consensus::Params& consensusParams = Params().GetConsensus(); { @@ -2828,11 +2829,11 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr pto->nPingUsecStart = GetTimeMicros(); if (pto->nVersion > BIP0031_VERSION) { pto->nPingNonceSent = nonce; - connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::PING, nonce)); } else { // Peer is too old to support ping command with nonce, pong will never arrive. pto->nPingNonceSent = 0; - connman.PushMessage(pto, msgMaker.Make(NetMsgType::PING)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::PING)); } } @@ -2867,14 +2868,14 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr // receiver rejects addr messages larger than 1000 if (vAddr.size() >= 1000) { - connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr)); vAddr.clear(); } } } pto->vAddrToSend.clear(); if (!vAddr.empty()) - connman.PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::ADDR, vAddr)); // we only send the big addr message once if (pto->vAddrToSend.capacity() > 40) pto->vAddrToSend.shrink_to_fit(); @@ -2901,7 +2902,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr if (pindexStart->pprev) pindexStart = pindexStart->pprev; LogPrint(BCLog::NET, "initial getheaders (%d) to peer=%d (startheight:%d)\n", pindexStart->nHeight, pto->GetId(), pto->nStartingHeight); - connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256())); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETHEADERS, chainActive.GetLocator(pindexStart), uint256())); } } @@ -2910,7 +2911,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr // transactions become unconfirmed and spams other nodes. if (!fReindex && !fImporting && !IsInitialBlockDownload()) { - GetMainSignals().Broadcast(nTimeBestReceived, &connman); + GetMainSignals().Broadcast(nTimeBestReceived, connman); } // @@ -2994,10 +2995,10 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr LOCK(cs_most_recent_block); if (most_recent_block_hash == pBestIndex->GetBlockHash()) { if (state.fWantsCmpctWitness || !fWitnessesPresentInMostRecentCompactBlock) - connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *most_recent_compact_block)); + connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *most_recent_compact_block)); else { CBlockHeaderAndShortTxIDs cmpctblock(*most_recent_block, state.fWantsCmpctWitness); - connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); + connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); } fGotBlockFromCache = true; } @@ -3007,7 +3008,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr bool ret = ReadBlockFromDisk(block, pBestIndex, consensusParams); assert(ret); CBlockHeaderAndShortTxIDs cmpctblock(block, state.fWantsCmpctWitness); - connman.PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); + connman->PushMessage(pto, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); } state.pindexBestHeaderSent = pBestIndex; } else if (state.fPreferHeaders) { @@ -3020,7 +3021,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr LogPrint(BCLog::NET, "%s: sending header %s to peer=%d\n", __func__, vHeaders.front().GetHash().ToString(), pto->GetId()); } - connman.PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::HEADERS, vHeaders)); state.pindexBestHeaderSent = pBestIndex; } else fRevertToInv = true; @@ -3066,7 +3067,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr for (const uint256& hash : pto->vInventoryBlockToSend) { vInv.push_back(CInv(MSG_BLOCK, hash)); if (vInv.size() == MAX_INV_SZ) { - connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); } } @@ -3112,7 +3113,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr pto->filterInventoryKnown.insert(hash); vInv.push_back(inv); if (vInv.size() == MAX_INV_SZ) { - connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); } } @@ -3178,7 +3179,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr } } if (vInv.size() == MAX_INV_SZ) { - connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); vInv.clear(); } pto->filterInventoryKnown.insert(hash); @@ -3186,7 +3187,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr } } if (!vInv.empty()) - connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv)); // Detect whether we're stalling nNow = GetTimeMicros(); @@ -3281,7 +3282,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr vGetData.push_back(inv); if (vGetData.size() >= 1000) { - connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); vGetData.clear(); } } else { @@ -3291,7 +3292,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr pto->mapAskFor.erase(pto->mapAskFor.begin()); } if (!vGetData.empty()) - connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData)); // // Message: feefilter @@ -3308,7 +3309,7 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr // We always have a fee filter of at least minRelayTxFee filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK()); if (filterToSend != pto->lastSentFeeFilter) { - connman.PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend)); + connman->PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend)); pto->lastSentFeeFilter = filterToSend; } pto->nextSendTimeFeeFilter = PoissonNextSend(timeNow, AVG_FEEFILTER_BROADCAST_INTERVAL); diff --git a/src/net_processing.h b/src/net_processing.h index f4a43980a5..79745cdd42 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -22,22 +22,31 @@ static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100; static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_BASE = 15 * 60 * 1000000; // 15 minutes static constexpr int64_t HEADERS_DOWNLOAD_TIMEOUT_PER_HEADER = 1000; // 1ms/header -/** Register with a network node to receive its signals */ -void RegisterNodeSignals(CNodeSignals& nodeSignals); -/** Unregister a network node */ -void UnregisterNodeSignals(CNodeSignals& nodeSignals); - -class PeerLogicValidation : public CValidationInterface { +class PeerLogicValidation : public CValidationInterface, public NetEventsInterface { private: CConnman* connman; public: - explicit PeerLogicValidation(CConnman* connmanIn); + explicit PeerLogicValidation(CConnman* connman); void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindexConnected, const std::vector<CTransactionRef>& vtxConflicted) override; void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) override; void BlockChecked(const CBlock& block, const CValidationState& state) override; void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& pblock) override; + + + void InitializeNode(CNode* pnode) override; + void FinalizeNode(NodeId nodeid, bool& fUpdateConnectionTime) override; + /** Process protocol messages received from a given node */ + bool ProcessMessages(CNode* pfrom, std::atomic<bool>& interrupt) override; + /** + * Send queued protocol messages to be sent to a give node. + * + * @param[in] pto The node which we are sending messages to. + * @param[in] interrupt Interrupt condition for processing threads + * @return True if there is more work to be done + */ + bool SendMessages(CNode* pto, std::atomic<bool>& interrupt) override; }; struct CNodeStateStats { @@ -52,16 +61,4 @@ bool GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats); /** Increase a node's misbehavior score. */ void Misbehaving(NodeId nodeid, int howmuch); -/** Process protocol messages received from a given node */ -bool ProcessMessages(CNode* pfrom, CConnman& connman, const std::atomic<bool>& interrupt); -/** - * Send queued protocol messages to be sent to a give node. - * - * @param[in] pto The node which we are sending messages to. - * @param[in] connman The connection manager for that node. - * @param[in] interrupt Interrupt condition for processing threads - * @return True if there is more work to be done - */ -bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interrupt); - #endif // BITCOIN_NET_PROCESSING_H diff --git a/src/netbase.cpp b/src/netbase.cpp index 05f9f6961c..5a560bc95a 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -184,6 +184,48 @@ struct timeval MillisToTimeval(int64_t nTimeout) return timeout; } +/** SOCKS version */ +enum SOCKSVersion: uint8_t { + SOCKS4 = 0x04, + SOCKS5 = 0x05 +}; + +/** Values defined for METHOD in RFC1928 */ +enum SOCKS5Method: uint8_t { + NOAUTH = 0x00, //! No authentication required + GSSAPI = 0x01, //! GSSAPI + USER_PASS = 0x02, //! Username/password + NO_ACCEPTABLE = 0xff, //! No acceptable methods +}; + +/** Values defined for CMD in RFC1928 */ +enum SOCKS5Command: uint8_t { + CONNECT = 0x01, + BIND = 0x02, + UDP_ASSOCIATE = 0x03 +}; + +/** Values defined for REP in RFC1928 */ +enum SOCKS5Reply: uint8_t { + SUCCEEDED = 0x00, //! Succeeded + GENFAILURE = 0x01, //! General failure + NOTALLOWED = 0x02, //! Connection not allowed by ruleset + NETUNREACHABLE = 0x03, //! Network unreachable + HOSTUNREACHABLE = 0x04, //! Network unreachable + CONNREFUSED = 0x05, //! Connection refused + TTLEXPIRED = 0x06, //! TTL expired + CMDUNSUPPORTED = 0x07, //! Command not supported + ATYPEUNSUPPORTED = 0x08, //! Address type not supported +}; + +/** Values defined for ATYPE in RFC1928 */ +enum SOCKS5Atyp: uint8_t { + IPV4 = 0x01, + DOMAINNAME = 0x03, + IPV6 = 0x04, +}; + +/** Status codes that can be returned by InterruptibleRecv */ enum class IntrRecvError { OK, Timeout, @@ -203,7 +245,7 @@ enum class IntrRecvError { * * @note This function requires that hSocket is in non-blocking mode. */ -static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, const SOCKET& hSocket) +static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const SOCKET& hSocket) { int64_t curTime = GetTimeMillis(); int64_t endTime = curTime + timeout; @@ -211,7 +253,7 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons // to break off in case of an interruption. const int64_t maxWait = 1000; while (len > 0 && curTime < endTime) { - ssize_t ret = recv(hSocket, data, len, 0); // Optimistically try the recv first + ssize_t ret = recv(hSocket, (char*)data, len, 0); // Optimistically try the recv first if (ret > 0) { len -= ret; data += ret; @@ -242,24 +284,35 @@ static IntrRecvError InterruptibleRecv(char* data, size_t len, int timeout, cons return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout; } +/** Credentials for proxy authentication */ struct ProxyCredentials { std::string username; std::string password; }; -std::string Socks5ErrorString(int err) +/** Convert SOCKS5 reply to a an error message */ +std::string Socks5ErrorString(uint8_t err) { switch(err) { - case 0x01: return "general failure"; - case 0x02: return "connection not allowed"; - case 0x03: return "network unreachable"; - case 0x04: return "host unreachable"; - case 0x05: return "connection refused"; - case 0x06: return "TTL expired"; - case 0x07: return "protocol error"; - case 0x08: return "address type not supported"; - default: return "unknown"; + case SOCKS5Reply::GENFAILURE: + return "general failure"; + case SOCKS5Reply::NOTALLOWED: + return "connection not allowed"; + case SOCKS5Reply::NETUNREACHABLE: + return "network unreachable"; + case SOCKS5Reply::HOSTUNREACHABLE: + return "host unreachable"; + case SOCKS5Reply::CONNREFUSED: + return "connection refused"; + case SOCKS5Reply::TTLEXPIRED: + return "TTL expired"; + case SOCKS5Reply::CMDUNSUPPORTED: + return "protocol error"; + case SOCKS5Reply::ATYPEUNSUPPORTED: + return "address type not supported"; + default: + return "unknown"; } } @@ -274,34 +327,34 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials } // Accepted authentication methods std::vector<uint8_t> vSocks5Init; - vSocks5Init.push_back(0x05); + vSocks5Init.push_back(SOCKSVersion::SOCKS5); if (auth) { - vSocks5Init.push_back(0x02); // # METHODS - vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED - vSocks5Init.push_back(0x02); // X'02' USERNAME/PASSWORD (RFC1929) + vSocks5Init.push_back(0x02); // Number of methods + vSocks5Init.push_back(SOCKS5Method::NOAUTH); + vSocks5Init.push_back(SOCKS5Method::USER_PASS); } else { - vSocks5Init.push_back(0x01); // # METHODS - vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED + vSocks5Init.push_back(0x01); // Number of methods + vSocks5Init.push_back(SOCKS5Method::NOAUTH); } ssize_t ret = send(hSocket, (const char*)vSocks5Init.data(), vSocks5Init.size(), MSG_NOSIGNAL); if (ret != (ssize_t)vSocks5Init.size()) { CloseSocket(hSocket); return error("Error sending to proxy"); } - char pchRet1[2]; + uint8_t pchRet1[2]; if ((recvr = InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) { CloseSocket(hSocket); LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port); return false; } - if (pchRet1[0] != 0x05) { + if (pchRet1[0] != SOCKSVersion::SOCKS5) { CloseSocket(hSocket); return error("Proxy failed to initialize"); } - if (pchRet1[1] == 0x02 && auth) { + if (pchRet1[1] == SOCKS5Method::USER_PASS && auth) { // Perform username/password authentication (as described in RFC1929) std::vector<uint8_t> vAuth; - vAuth.push_back(0x01); + vAuth.push_back(0x01); // Current (and only) version of user/pass subnegotiation if (auth->username.size() > 255 || auth->password.size() > 255) return error("Proxy username or password too long"); vAuth.push_back(auth->username.size()); @@ -314,7 +367,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials return error("Error sending authentication to proxy"); } LogPrint(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password); - char pchRetA[2]; + uint8_t pchRetA[2]; if ((recvr = InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) { CloseSocket(hSocket); return error("Error reading proxy authentication response"); @@ -323,17 +376,17 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials CloseSocket(hSocket); return error("Proxy authentication unsuccessful"); } - } else if (pchRet1[1] == 0x00) { + } else if (pchRet1[1] == SOCKS5Method::NOAUTH) { // Perform no authentication } else { CloseSocket(hSocket); return error("Proxy requested wrong authentication method %02x", pchRet1[1]); } std::vector<uint8_t> vSocks5; - vSocks5.push_back(0x05); // VER protocol version - vSocks5.push_back(0x01); // CMD CONNECT - vSocks5.push_back(0x00); // RSV Reserved - vSocks5.push_back(0x03); // ATYP DOMAINNAME + vSocks5.push_back(SOCKSVersion::SOCKS5); // VER protocol version + vSocks5.push_back(SOCKS5Command::CONNECT); // CMD CONNECT + vSocks5.push_back(0x00); // RSV Reserved must be 0 + vSocks5.push_back(SOCKS5Atyp::DOMAINNAME); // ATYP DOMAINNAME vSocks5.push_back(strDest.size()); // Length<=255 is checked at beginning of function vSocks5.insert(vSocks5.end(), strDest.begin(), strDest.end()); vSocks5.push_back((port >> 8) & 0xFF); @@ -343,7 +396,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials CloseSocket(hSocket); return error("Error sending to proxy"); } - char pchRet2[4]; + uint8_t pchRet2[4]; if ((recvr = InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) { CloseSocket(hSocket); if (recvr == IntrRecvError::Timeout) { @@ -355,26 +408,26 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials return error("Error while reading proxy response"); } } - if (pchRet2[0] != 0x05) { + if (pchRet2[0] != SOCKSVersion::SOCKS5) { CloseSocket(hSocket); return error("Proxy failed to accept request"); } - if (pchRet2[1] != 0x00) { + if (pchRet2[1] != SOCKS5Reply::SUCCEEDED) { // Failures to connect to a peer that are not proxy errors CloseSocket(hSocket); LogPrintf("Socks5() connect to %s:%d failed: %s\n", strDest, port, Socks5ErrorString(pchRet2[1])); return false; } - if (pchRet2[2] != 0x00) { + if (pchRet2[2] != 0x00) { // Reserved field must be 0 CloseSocket(hSocket); return error("Error: malformed proxy response"); } - char pchRet3[256]; + uint8_t pchRet3[256]; switch (pchRet2[3]) { - case 0x01: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break; - case 0x04: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break; - case 0x03: + case SOCKS5Atyp::IPV4: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break; + case SOCKS5Atyp::IPV6: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break; + case SOCKS5Atyp::DOMAINNAME: { recvr = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, hSocket); if (recvr != IntrRecvError::OK) { @@ -399,7 +452,7 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials return true; } -bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout) +bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout) { hSocketRet = INVALID_SOCKET; @@ -534,7 +587,7 @@ bool IsProxy(const CNetAddr &addr) { return false; } -static bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed) +bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed) { SOCKET hSocket = INVALID_SOCKET; // first connect to proxy server @@ -558,47 +611,6 @@ static bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDe hSocketRet = hSocket; return true; } - -bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed) -{ - proxyType proxy; - if (outProxyConnectionFailed) - *outProxyConnectionFailed = false; - - if (GetProxy(addrDest.GetNetwork(), proxy)) - return ConnectThroughProxy(proxy, addrDest.ToStringIP(), addrDest.GetPort(), hSocketRet, nTimeout, outProxyConnectionFailed); - else // no proxy needed (none set for target network) - return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout); -} - -bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed) -{ - std::string strDest; - int port = portDefault; - - if (outProxyConnectionFailed) - *outProxyConnectionFailed = false; - - SplitHostPort(std::string(pszDest), port, strDest); - - proxyType proxy; - GetNameProxy(proxy); - - std::vector<CService> addrResolved; - if (Lookup(strDest.c_str(), addrResolved, port, fNameLookup && !HaveNameProxy(), 256)) { - if (addrResolved.size() > 0) { - addr = addrResolved[GetRand(addrResolved.size())]; - return ConnectSocket(addr, hSocketRet, nTimeout); - } - } - - addr = CService(); - - if (!HaveNameProxy()) - return false; - return ConnectThroughProxy(proxy, strDest, port, hSocketRet, nTimeout, outProxyConnectionFailed); -} - bool LookupSubNet(const char* pszName, CSubNet& ret) { std::string strSubnet(pszName); diff --git a/src/netbase.h b/src/netbase.h index 6572f0a12e..e7d7bcb375 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -44,14 +44,15 @@ bool GetProxy(enum Network net, proxyType &proxyInfoOut); bool IsProxy(const CNetAddr &addr); bool SetNameProxy(const proxyType &addrProxy); bool HaveNameProxy(); +bool GetNameProxy(proxyType &nameProxyOut); bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup); bool LookupHost(const char *pszName, CNetAddr& addr, bool fAllowLookup); bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLookup); bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions); CService LookupNumeric(const char *pszName, int portDefault = 0); bool LookupSubNet(const char *pszName, CSubNet& subnet); -bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed = nullptr); -bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed = nullptr); +bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout); +bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed); /** Return readable error string for a network error code */ std::string NetworkErrorString(int err); /** Close socket and set hSocket to INVALID_SOCKET */ diff --git a/src/policy/feerate.h b/src/policy/feerate.h index 565da6c154..3449cdd699 100644 --- a/src/policy/feerate.h +++ b/src/policy/feerate.h @@ -20,13 +20,17 @@ class CFeeRate { private: CAmount nSatoshisPerK; // unit is satoshis-per-1,000-bytes + public: /** Fee rate of 0 satoshis per kB */ CFeeRate() : nSatoshisPerK(0) { } - explicit CFeeRate(const CAmount& _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { } + template<typename I> + CFeeRate(const I _nSatoshisPerK): nSatoshisPerK(_nSatoshisPerK) { + // We've previously had bugs creep in from silent double->int conversion... + static_assert(std::is_integral<I>::value, "CFeeRate should be used without floats"); + } /** Constructor for a fee rate in satoshis per kB. The size in bytes must not exceed (2^63 - 1)*/ CFeeRate(const CAmount& nFeePaid, size_t nBytes); - CFeeRate(const CFeeRate& other) { nSatoshisPerK = other.nSatoshisPerK; } /** * Return the fee in satoshis for the given size in bytes. */ diff --git a/src/policy/fees.cpp b/src/policy/fees.cpp index 5c92a0eb41..dcf49de5f1 100644 --- a/src/policy/fees.cpp +++ b/src/policy/fees.cpp @@ -715,7 +715,7 @@ CFeeRate CBlockPolicyEstimator::estimateRawFee(int confTarget, double successThr if (median < 0) return CFeeRate(0); - return CFeeRate(median); + return CFeeRate(llround(median)); } unsigned int CBlockPolicyEstimator::HighestTargetTracked(FeeEstimateHorizon horizon) const @@ -902,7 +902,7 @@ CFeeRate CBlockPolicyEstimator::estimateSmartFee(int confTarget, FeeCalculation if (median < 0) return CFeeRate(0); // error condition - return CFeeRate(median); + return CFeeRate(llround(median)); } @@ -1044,5 +1044,5 @@ CAmount FeeFilterRounder::round(CAmount currentMinFee) if ((it != feeset.begin() && insecure_rand.rand32() % 3 != 0) || it == feeset.end()) { it--; } - return *it; + return static_cast<CAmount>(*it); } diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 605e3e0696..b2fb284508 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -54,23 +54,6 @@ bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) return (txout.nValue < GetDustThreshold(txout, dustRelayFeeIn)); } - /** - * Check transaction inputs to mitigate two - * potential denial-of-service attacks: - * - * 1. scriptSigs with extra data stuffed into them, - * not consumed by scriptPubKey (or P2SH script) - * 2. P2SH scripts with a crazy number of expensive - * CHECKSIG/CHECKMULTISIG operations - * - * Why bother? To avoid denial-of-service attacks; an attacker - * can submit a standard HASH... OP_EQUAL transaction, - * which will get accepted into blocks. The redemption - * script can be anything; an attacker could use a very - * expensive-to-check-upon-redemption script like: - * DUP CHECKSIG DROP ... repeated 100 times... OP_1 - */ - bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool witnessEnabled) { std::vector<std::vector<unsigned char> > vSolutions; @@ -93,7 +76,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool w else if (!witnessEnabled && (whichType == TX_WITNESS_V0_KEYHASH || whichType == TX_WITNESS_V0_SCRIPTHASH)) return false; - return whichType != TX_NONSTANDARD; + return whichType != TX_NONSTANDARD && whichType != TX_WITNESS_UNKNOWN; } bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnessEnabled) @@ -160,6 +143,22 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes return true; } +/** + * Check transaction inputs to mitigate two + * potential denial-of-service attacks: + * + * 1. scriptSigs with extra data stuffed into them, + * not consumed by scriptPubKey (or P2SH script) + * 2. P2SH scripts with a crazy number of expensive + * CHECKSIG/CHECKMULTISIG operations + * + * Why bother? To avoid denial-of-service attacks; an attacker + * can submit a standard HASH... OP_EQUAL transaction, + * which will get accepted into blocks. The redemption + * script can be anything; an attacker could use a very + * expensive-to-check-upon-redemption script like: + * DUP CHECKSIG DROP ... repeated 100 times... OP_1 + */ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) { if (tx.IsCoinBase()) diff --git a/src/policy/policy.h b/src/policy/policy.h index c06820f84e..ef71dd73bc 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -16,10 +16,8 @@ class CCoinsViewCache; class CTxOut; -/** Default for -blockmaxsize, which controls the maximum size of block the mining code will create **/ -static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000; /** Default for -blockmaxweight, which controls the range of block weights the mining code will create **/ -static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = 3000000; +static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = MAX_BLOCK_WEIGHT - 4000; /** Default for -blockmintxfee, which sets the minimum feerate for a transaction in blocks created by mining code **/ static const unsigned int DEFAULT_BLOCK_MIN_TX_FEE = 1000; /** The maximum weight for transactions we're willing to relay/mine */ diff --git a/src/prevector.h b/src/prevector.h index f7bde8911a..eb29b3ae7e 100644 --- a/src/prevector.h +++ b/src/prevector.h @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#ifndef _BITCOIN_PREVECTOR_H_ -#define _BITCOIN_PREVECTOR_H_ +#ifndef BITCOIN_PREVECTOR_H +#define BITCOIN_PREVECTOR_H #include <assert.h> #include <stdlib.h> @@ -514,4 +514,4 @@ public: }; #pragma pack(pop) -#endif +#endif // BITCOIN_PREVECTOR_H diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 9b6a814e1f..e0a106adb9 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -55,7 +55,7 @@ std::string CTxOut::ToString() const } CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {} -CMutableTransaction::CMutableTransaction(const CTransaction& tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime) {} +CMutableTransaction::CMutableTransaction(const CTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime) {} uint256 CMutableTransaction::GetHash() const { @@ -76,9 +76,9 @@ uint256 CTransaction::GetWitnessHash() const } /* For backward compatibility, the hash is initialized to 0. TODO: remove the need for this default constructor entirely. */ -CTransaction::CTransaction() : nVersion(CTransaction::CURRENT_VERSION), vin(), vout(), nLockTime(0), hash() {} -CTransaction::CTransaction(const CMutableTransaction &tx) : nVersion(tx.nVersion), vin(tx.vin), vout(tx.vout), nLockTime(tx.nLockTime), hash(ComputeHash()) {} -CTransaction::CTransaction(CMutableTransaction &&tx) : nVersion(tx.nVersion), vin(std::move(tx.vin)), vout(std::move(tx.vout)), nLockTime(tx.nLockTime), hash(ComputeHash()) {} +CTransaction::CTransaction() : vin(), vout(), nVersion(CTransaction::CURRENT_VERSION), nLockTime(0), hash() {} +CTransaction::CTransaction(const CMutableTransaction &tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash(ComputeHash()) {} +CTransaction::CTransaction(CMutableTransaction &&tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash(ComputeHash()) {} CAmount CTransaction::GetValueOut() const { diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 041034bb8b..18d524e23d 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -278,9 +278,9 @@ public: // actually immutable; deserialization and assignment are implemented, // and bypass the constness. This is safe, as they update the entire // structure, including the hash. - const int32_t nVersion; const std::vector<CTxIn> vin; const std::vector<CTxOut> vout; + const int32_t nVersion; const uint32_t nLockTime; private: @@ -361,9 +361,9 @@ public: /** A mutable version of CTransaction. */ struct CMutableTransaction { - int32_t nVersion; std::vector<CTxIn> vin; std::vector<CTxOut> vout; + int32_t nVersion; uint32_t nLockTime; CMutableTransaction(); diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp index 2fa032abdc..0eb7ec4306 100644 --- a/src/qt/addresstablemodel.cpp +++ b/src/qt/addresstablemodel.cpp @@ -82,14 +82,14 @@ public: LOCK(wallet->cs_wallet); for (const std::pair<CTxDestination, CAddressBookData>& item : wallet->mapAddressBook) { - const CBitcoinAddress& address = item.first; - bool fMine = IsMine(*wallet, address.Get()); + const CTxDestination& address = item.first; + bool fMine = IsMine(*wallet, address); AddressTableEntry::Type addressType = translateTransactionType( QString::fromStdString(item.second.purpose), fMine); const std::string& strName = item.second.name; cachedAddressTable.append(AddressTableEntry(addressType, QString::fromStdString(strName), - QString::fromStdString(address.ToString()))); + QString::fromStdString(EncodeDestination(address)))); } } // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order @@ -246,7 +246,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, if(role == Qt::EditRole) { LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */ - CTxDestination curAddress = CBitcoinAddress(rec->address.toStdString()).Get(); + CTxDestination curAddress = DecodeDestination(rec->address.toStdString()); if(index.column() == Label) { // Do nothing, if old label == new label @@ -257,7 +257,7 @@ bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, } wallet->SetAddressBook(curAddress, value.toString().toStdString(), strPurpose); } else if(index.column() == Address) { - CTxDestination newAddress = CBitcoinAddress(value.toString().toStdString()).Get(); + CTxDestination newAddress = DecodeDestination(value.toString().toStdString()); // Refuse to set invalid address, set error status and return false if(boost::get<CNoDestination>(&newAddress)) { @@ -358,7 +358,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con // Check for duplicate addresses { LOCK(wallet->cs_wallet); - if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get())) + if(wallet->mapAddressBook.count(DecodeDestination(strAddress))) { editStatus = DUPLICATE_ADDRESS; return QString(); @@ -384,7 +384,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con return QString(); } } - strAddress = CBitcoinAddress(newKey.GetID()).ToString(); + strAddress = EncodeDestination(newKey.GetID()); } else { @@ -394,7 +394,7 @@ QString AddressTableModel::addRow(const QString &type, const QString &label, con // Add entry { LOCK(wallet->cs_wallet); - wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel, + wallet->SetAddressBook(DecodeDestination(strAddress), strLabel, (type == Send ? "send" : "receive")); } return QString::fromStdString(strAddress); @@ -412,7 +412,7 @@ bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent } { LOCK(wallet->cs_wallet); - wallet->DelAddressBook(CBitcoinAddress(rec->address.toStdString()).Get()); + wallet->DelAddressBook(DecodeDestination(rec->address.toStdString())); } return true; } @@ -423,8 +423,8 @@ QString AddressTableModel::labelForAddress(const QString &address) const { { LOCK(wallet->cs_wallet); - CBitcoinAddress address_parsed(address.toStdString()); - std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(address_parsed.Get()); + CTxDestination destination = DecodeDestination(address.toStdString()); + std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(destination); if (mi != wallet->mapAddressBook.end()) { return QString::fromStdString(mi->second.name); diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp index d712705c43..362a71f04d 100644 --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -67,7 +67,7 @@ QValidator::State BitcoinAddressEntryValidator::validate(QString &input, int &po if (((ch >= '0' && ch<='9') || (ch >= 'a' && ch<='z') || (ch >= 'A' && ch<='Z')) && - ch != 'l' && ch != 'I' && ch != '0' && ch != 'O') + ch != 'I' && ch != 'O') // Characters invalid in both Base58 and Bech32 { // Alphanumeric and not a 'forbidden' character } @@ -89,9 +89,9 @@ QValidator::State BitcoinAddressCheckValidator::validate(QString &input, int &po { Q_UNUSED(pos); // Validate the passed Bitcoin address - CBitcoinAddress addr(input.toStdString()); - if (addr.IsValid()) + if (IsValidDestinationString(input.toStdString())) { return QValidator::Acceptable; + } return QValidator::Invalid; } diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index e3970298e6..dc55141900 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -123,7 +123,11 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * spinnerFrame(0), platformStyle(_platformStyle) { - GUIUtil::restoreWindowGeometry("nWindow", QSize(850, 550), this); + QSettings settings; + if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) { + // Restore failed (perhaps missing setting), center the window + move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center()); + } QString windowTitle = tr(PACKAGE_NAME) + " - "; #ifdef ENABLE_WALLET @@ -261,7 +265,8 @@ BitcoinGUI::~BitcoinGUI() // Unsubscribe from notifications from core unsubscribeFromCoreSignals(); - GUIUtil::saveWindowGeometry("nWindow", this); + QSettings settings; + settings.setValue("MainWindowGeometry", saveGeometry()); if(trayIcon) // Hide tray icon, as deleting will let it linger until quit (on Ubuntu) trayIcon->hide(); #ifdef Q_OS_MAC @@ -454,6 +459,7 @@ void BitcoinGUI::createToolBars() if(walletFrame) { QToolBar *toolbar = addToolBar(tr("Tabs toolbar")); + toolbar->setContextMenuPolicy(Qt::PreventContextMenu); toolbar->setMovable(false); toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); toolbar->addAction(overviewAction); diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 0225dd3cd6..6952eb5064 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -18,6 +18,7 @@ #include "policy/fees.h" #include "policy/policy.h" #include "validation.h" // For mempool +#include "wallet/fees.h" #include "wallet/wallet.h" #include <QApplication> @@ -510,7 +511,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) nBytes -= 34; // Fee - nPayFee = CWallet::GetMinimumFee(nBytes, *coinControl, ::mempool, ::feeEstimator, nullptr /* FeeCalculation */); + nPayFee = GetMinimumFee(nBytes, *coinControl, ::mempool, ::feeEstimator, nullptr /* FeeCalculation */); if (nPayAmount > 0) { @@ -660,7 +661,7 @@ void CoinControlDialog::updateView() QString sAddress = ""; if(ExtractDestination(out.tx->tx->vout[out.i].scriptPubKey, outputAddress)) { - sAddress = QString::fromStdString(CBitcoinAddress(outputAddress).ToString()); + sAddress = QString::fromStdString(EncodeDestination(outputAddress)); // if listMode or change => show bitcoin address. In tree mode, address is not shown again for direct wallet address outputs if (!treeMode || (!(sAddress == sWalletAddress))) diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 14078b9ee8..e31bfee05e 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -199,10 +199,10 @@ <item> <widget class="QCheckBox" name="allowIncoming"> <property name="toolTip"> - <string>Accept connections from outside</string> + <string>Accept connections from outside.</string> </property> <property name="text"> - <string>Allow incoming connections</string> + <string>Allow incomin&g connections</string> </property> </widget> </item> @@ -399,7 +399,7 @@ <string>Connect to the Bitcoin network through a separate SOCKS5 proxy for Tor hidden services.</string> </property> <property name="text"> - <string>Use separate SOCKS5 proxy to reach peers via Tor hidden services:</string> + <string>Use separate SOCKS&5 proxy to reach peers via Tor hidden services:</string> </property> </widget> </item> @@ -507,10 +507,10 @@ <item> <widget class="QCheckBox" name="hideTrayIcon"> <property name="toolTip"> - <string>&Hide the icon from the system tray.</string> + <string>Hide the icon from the system tray.</string> </property> <property name="text"> - <string>Hide tray icon</string> + <string>&Hide tray icon</string> </property> </widget> </item> @@ -610,7 +610,7 @@ <string>Third party URLs (e.g. a block explorer) that appear in the transactions tab as context menu items. %s in the URL is replaced by transaction hash. Multiple URLs are separated by vertical bar |.</string> </property> <property name="text"> - <string>Third party transaction URLs</string> + <string>&Third party transaction URLs</string> </property> <property name="buddy"> <cstring>thirdPartyTxUrls</cstring> diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 1e2f2302b9..a0e48334c1 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -846,19 +846,13 @@ <item> <layout class="QHBoxLayout" name="horizontalLayoutFee13"> <item> - <widget class="QRadioButton" name="radioCustomPerKilobyte"> + <widget class="QLabel" name="labelCustomPerKilobyte"> <property name="toolTip"> <string>If the custom fee is set to 1000 satoshis and the transaction is only 250 bytes, then "per kilobyte" only pays 250 satoshis in fee, while "total at least" pays 1000 satoshis. For transactions bigger than a kilobyte both pay by kilobyte.</string> </property> <property name="text"> <string>per kilobyte</string> </property> - <property name="checked"> - <bool>true</bool> - </property> - <attribute name="buttonGroup"> - <string notr="true">groupCustomFee</string> - </attribute> </widget> </item> <item> @@ -1285,6 +1279,5 @@ <connections/> <buttongroups> <buttongroup name="groupFee"/> - <buttongroup name="groupCustomFee"/> </buttongroups> </ui> diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index f3c5daebec..d520d7d4be 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -112,8 +112,9 @@ static std::string DummyAddress(const CChainParams ¶ms) sourcedata.insert(sourcedata.end(), dummydata, dummydata + sizeof(dummydata)); for(int i=0; i<256; ++i) { // Try every trailing byte std::string s = EncodeBase58(sourcedata.data(), sourcedata.data() + sourcedata.size()); - if (!CBitcoinAddress(s).IsValid()) + if (!IsValidDestinationString(s)) { return s; + } sourcedata[sourcedata.size()-1] += 1; } return ""; @@ -248,7 +249,7 @@ QString formatBitcoinURI(const SendCoinsRecipient &info) bool isDust(const QString& address, const CAmount& amount) { - CTxDestination dest = CBitcoinAddress(address.toStdString()).Get(); + CTxDestination dest = DecodeDestination(address.toStdString()); CScript script = GetScriptForDestination(dest); CTxOut txOut(amount, script); return IsDust(txOut, ::dustRelayFee); @@ -743,9 +744,10 @@ bool SetStartOnSystemStartup(bool fAutoStart) else { char pszExePath[MAX_PATH+1]; - memset(pszExePath, 0, sizeof(pszExePath)); - if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1) + ssize_t r = readlink("/proc/self/exe", pszExePath, sizeof(pszExePath) - 1); + if (r == -1) return false; + pszExePath[r] = '\0'; fs::create_directories(GetAutostartDir()); @@ -780,47 +782,64 @@ bool SetStartOnSystemStartup(bool fAutoStart) LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl); LSSharedFileListItemRef findStartupItemInList(LSSharedFileListRef list, CFURLRef findUrl) { - // loop through the list of startup items and try to find the bitcoin app CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(list, nullptr); + if (listSnapshot == nullptr) { + return nullptr; + } + + // loop through the list of startup items and try to find the bitcoin app for(int i = 0; i < CFArrayGetCount(listSnapshot); i++) { LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(listSnapshot, i); UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes; CFURLRef currentItemURL = nullptr; #if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= 10100 - if(&LSSharedFileListItemCopyResolvedURL) - currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, nullptr); + if(&LSSharedFileListItemCopyResolvedURL) + currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, nullptr); #if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && MAC_OS_X_VERSION_MIN_REQUIRED < 10100 - else - LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, nullptr); + else + LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, nullptr); #endif #else - LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, nullptr); + LSSharedFileListItemResolve(item, resolutionFlags, ¤tItemURL, nullptr); #endif - if(currentItemURL && CFEqual(currentItemURL, findUrl)) { - // found - CFRelease(currentItemURL); - return item; - } if(currentItemURL) { + if (CFEqual(currentItemURL, findUrl)) { + // found + CFRelease(listSnapshot); + CFRelease(currentItemURL); + return item; + } CFRelease(currentItemURL); } } + + CFRelease(listSnapshot); return nullptr; } bool GetStartOnSystemStartup() { CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + if (bitcoinAppUrl == nullptr) { + return false; + } + LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr); LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl); + + CFRelease(bitcoinAppUrl); return !!foundItem; // return boolified object } bool SetStartOnSystemStartup(bool fAutoStart) { CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle()); + if (bitcoinAppUrl == nullptr) { + return false; + } + LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr); LSSharedFileListItemRef foundItem = findStartupItemInList(loginItems, bitcoinAppUrl); @@ -832,6 +851,8 @@ bool SetStartOnSystemStartup(bool fAutoStart) // remove item LSSharedFileListItemRemove(loginItems, foundItem); } + + CFRelease(bitcoinAppUrl); return true; } #pragma GCC diagnostic pop @@ -842,32 +863,6 @@ bool SetStartOnSystemStartup(bool fAutoStart) { return false; } #endif -void saveWindowGeometry(const QString& strSetting, QWidget *parent) -{ - QSettings settings; - settings.setValue(strSetting + "Pos", parent->pos()); - settings.setValue(strSetting + "Size", parent->size()); -} - -void restoreWindowGeometry(const QString& strSetting, const QSize& defaultSize, QWidget *parent) -{ - QSettings settings; - QPoint pos = settings.value(strSetting + "Pos").toPoint(); - QSize size = settings.value(strSetting + "Size", defaultSize).toSize(); - - parent->resize(size); - parent->move(pos); - - if ((!pos.x() && !pos.y()) || (QApplication::desktop()->screenNumber(parent) == -1)) - { - QRect screen = QApplication::desktop()->screenGeometry(); - QPoint defaultPos((screen.width() - defaultSize.width()) / 2, - (screen.height() - defaultSize.height()) / 2); - parent->resize(defaultSize); - parent->move(defaultPos); - } -} - void setClipboard(const QString& str) { QApplication::clipboard()->setText(str, QClipboard::Clipboard); diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index d6aa8c4ea6..d10818d0c8 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -179,11 +179,6 @@ namespace GUIUtil bool GetStartOnSystemStartup(); bool SetStartOnSystemStartup(bool fAutoStart); - /** Save window size and position */ - void saveWindowGeometry(const QString& strSetting, QWidget *parent); - /** Restore window size and position */ - void restoreWindowGeometry(const QString& strSetting, const QSize &defaultSizeIn, QWidget *parent); - /* Convert QString to OS specific boost path through UTF-8 */ fs::path qstringToBoostPath(const QString &path); diff --git a/src/qt/macnotificationhandler.h b/src/qt/macnotificationhandler.h index d4749b3d5f..3a005c3c46 100644 --- a/src/qt/macnotificationhandler.h +++ b/src/qt/macnotificationhandler.h @@ -7,20 +7,17 @@ #include <QObject> -/** Macintosh-specific notification handler (supports UserNotificationCenter and Growl). +/** Macintosh-specific notification handler (supports UserNotificationCenter). */ class MacNotificationHandler : public QObject { Q_OBJECT public: - /** shows a 10.8+ UserNotification in the UserNotificationCenter + /** shows a macOS 10.8+ UserNotification in the UserNotificationCenter */ void showNotification(const QString &title, const QString &text); - /** executes AppleScript */ - void sendAppleScript(const QString &script); - /** check if OS can handle UserNotifications */ bool hasUserNotificationCenterSupport(void); static MacNotificationHandler *instance(); diff --git a/src/qt/macnotificationhandler.mm b/src/qt/macnotificationhandler.mm index 4c96d08c8a..1b16c5f524 100644 --- a/src/qt/macnotificationhandler.mm +++ b/src/qt/macnotificationhandler.mm @@ -47,20 +47,6 @@ void MacNotificationHandler::showNotification(const QString &title, const QStrin } } -// sendAppleScript just take a QString and executes it as apple script -void MacNotificationHandler::sendAppleScript(const QString &script) -{ - QByteArray utf8 = script.toUtf8(); - char* cString = (char *)utf8.constData(); - NSString *scriptApple = [[NSString alloc] initWithUTF8String:cString]; - - NSAppleScript *as = [[NSAppleScript alloc] initWithSource:scriptApple]; - NSDictionary *err = nil; - [as executeAndReturnError:&err]; - [as release]; - [scriptApple release]; -} - bool MacNotificationHandler::hasUserNotificationCenterSupport(void) { Class possibleClass = NSClassFromString(@"NSUserNotificationCenter"); diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp index a83f285034..e32a0bdda8 100644 --- a/src/qt/modaloverlay.cpp +++ b/src/qt/modaloverlay.cpp @@ -82,36 +82,38 @@ void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVeri blockProcessTime.push_front(qMakePair(currentDate.toMSecsSinceEpoch(), nVerificationProgress)); // show progress speed if we have more then one sample - if (blockProcessTime.size() >= 2) - { - double progressStart = blockProcessTime[0].second; + if (blockProcessTime.size() >= 2) { double progressDelta = 0; double progressPerHour = 0; qint64 timeDelta = 0; qint64 remainingMSecs = 0; double remainingProgress = 1.0 - nVerificationProgress; - for (int i = 1; i < blockProcessTime.size(); i++) - { + for (int i = 1; i < blockProcessTime.size(); i++) { QPair<qint64, double> sample = blockProcessTime[i]; // take first sample after 500 seconds or last available one if (sample.first < (currentDate.toMSecsSinceEpoch() - 500 * 1000) || i == blockProcessTime.size() - 1) { - progressDelta = progressStart-sample.second; + progressDelta = blockProcessTime[0].second - sample.second; timeDelta = blockProcessTime[0].first - sample.first; - progressPerHour = progressDelta/(double)timeDelta*1000*3600; - remainingMSecs = remainingProgress / progressDelta * timeDelta; + progressPerHour = progressDelta / (double) timeDelta * 1000 * 3600; + remainingMSecs = (progressDelta > 0) ? remainingProgress / progressDelta * timeDelta : -1; break; } } // show progress increase per hour - ui->progressIncreasePerH->setText(QString::number(progressPerHour*100, 'f', 2)+"%"); + ui->progressIncreasePerH->setText(QString::number(progressPerHour * 100, 'f', 2)+"%"); // show expected remaining time - ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs/1000.0)); + if(remainingMSecs >= 0) { + ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs / 1000.0)); + } else { + ui->expectedTimeLeft->setText(QObject::tr("unknown")); + } static const int MAX_SAMPLES = 5000; - if (blockProcessTime.count() > MAX_SAMPLES) - blockProcessTime.remove(MAX_SAMPLES, blockProcessTime.count()-MAX_SAMPLES); + if (blockProcessTime.count() > MAX_SAMPLES) { + blockProcessTime.remove(MAX_SAMPLES, blockProcessTime.count() - MAX_SAMPLES); + } } // show the last block date diff --git a/src/qt/notificator.cpp b/src/qt/notificator.cpp index a7a7a4ce11..937928315b 100644 --- a/src/qt/notificator.cpp +++ b/src/qt/notificator.cpp @@ -60,22 +60,6 @@ Notificator::Notificator(const QString &_programName, QSystemTrayIcon *_trayIcon if( MacNotificationHandler::instance()->hasUserNotificationCenterSupport()) { mode = UserNotificationCenter; } - else { - // Check if Growl is installed (based on Qt's tray icon implementation) - CFURLRef cfurl; - OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl); - if (status != kLSApplicationNotFoundErr) { - CFBundleRef bundle = CFBundleCreate(0, cfurl); - if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"), kCFCompareCaseInsensitive | kCFCompareBackwards) == kCFCompareEqualTo) { - if (CFStringHasSuffix(CFURLGetString(cfurl), CFSTR("/Growl.app/"))) - mode = Growl13; - else - mode = Growl12; - } - CFRelease(cfurl); - CFRelease(bundle); - } - } #endif } @@ -241,52 +225,6 @@ void Notificator::notifySystray(Class cls, const QString &title, const QString & // Based on Qt's tray icon implementation #ifdef Q_OS_MAC -void Notificator::notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon) -{ - const QString script( - "tell application \"%5\"\n" - " set the allNotificationsList to {\"Notification\"}\n" // -- Make a list of all the notification types (all) - " set the enabledNotificationsList to {\"Notification\"}\n" // -- Make a list of the notifications (enabled) - " register as application \"%1\" all notifications allNotificationsList default notifications enabledNotificationsList\n" // -- Register our script with Growl - " notify with name \"Notification\" title \"%2\" description \"%3\" application name \"%1\"%4\n" // -- Send a Notification - "end tell" - ); - - QString notificationApp(QApplication::applicationName()); - if (notificationApp.isEmpty()) - notificationApp = "Application"; - - QPixmap notificationIconPixmap; - if (icon.isNull()) { // If no icon specified, set icon based on class - QStyle::StandardPixmap sicon = QStyle::SP_MessageBoxQuestion; - switch (cls) - { - case Information: sicon = QStyle::SP_MessageBoxInformation; break; - case Warning: sicon = QStyle::SP_MessageBoxWarning; break; - case Critical: sicon = QStyle::SP_MessageBoxCritical; break; - } - notificationIconPixmap = QApplication::style()->standardPixmap(sicon); - } - else { - QSize size = icon.actualSize(QSize(48, 48)); - notificationIconPixmap = icon.pixmap(size); - } - - QString notificationIcon; - QTemporaryFile notificationIconFile; - if (!notificationIconPixmap.isNull() && notificationIconFile.open()) { - QImageWriter writer(¬ificationIconFile, "PNG"); - if (writer.write(notificationIconPixmap.toImage())) - notificationIcon = QString(" image from location \"file://%1\"").arg(notificationIconFile.fileName()); - } - - QString quotedTitle(title), quotedText(text); - quotedTitle.replace("\\", "\\\\").replace("\"", "\\"); - quotedText.replace("\\", "\\\\").replace("\"", "\\"); - QString growlApp(this->mode == Notificator::Growl13 ? "Growl" : "GrowlHelperApp"); - MacNotificationHandler::instance()->sendAppleScript(script.arg(notificationApp, quotedTitle, quotedText, notificationIcon, growlApp)); -} - void Notificator::notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon) { // icon is not supported by the user notification center yet. OSX will use the app icon. MacNotificationHandler::instance()->showNotification(title, text); @@ -310,10 +248,6 @@ void Notificator::notify(Class cls, const QString &title, const QString &text, c case UserNotificationCenter: notifyMacUserNotificationCenter(cls, title, text, icon); break; - case Growl12: - case Growl13: - notifyGrowl(cls, title, text, icon); - break; #endif default: if(cls == Critical) diff --git a/src/qt/notificator.h b/src/qt/notificator.h index f92b791d4a..67f2b1df69 100644 --- a/src/qt/notificator.h +++ b/src/qt/notificator.h @@ -58,8 +58,6 @@ private: None, /**< Ignore informational notifications, and show a modal pop-up dialog for Critical notifications. */ Freedesktop, /**< Use DBus org.freedesktop.Notifications */ QSystemTray, /**< Use QSystemTray::showMessage */ - Growl12, /**< Use the Growl 1.2 notification system (Mac only) */ - Growl13, /**< Use the Growl 1.3 notification system (Mac only) */ UserNotificationCenter /**< Use the 10.8+ User Notification Center (Mac only) */ }; QString programName; @@ -72,7 +70,6 @@ private: #endif void notifySystray(Class cls, const QString &title, const QString &text, const QIcon &icon, int millisTimeout); #ifdef Q_OS_MAC - void notifyGrowl(Class cls, const QString &title, const QString &text, const QIcon &icon); void notifyMacUserNotificationCenter(Class cls, const QString &title, const QString &text, const QIcon &icon); #endif }; diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index b80b6541dd..53e2e5053c 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -17,10 +17,6 @@ #include "netbase.h" #include "txdb.h" // for -dbcache defaults -#ifdef ENABLE_WALLET -#include "wallet/wallet.h" // for CWallet::GetRequiredFee() -#endif - #include <QDataWidgetMapper> #include <QDir> #include <QIntValidator> @@ -80,6 +76,8 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : ui->bitcoinAtStartup->setToolTip(ui->bitcoinAtStartup->toolTip().arg(tr(PACKAGE_NAME))); ui->bitcoinAtStartup->setText(ui->bitcoinAtStartup->text().arg(tr(PACKAGE_NAME))); + ui->openBitcoinConfButton->setToolTip(ui->openBitcoinConfButton->toolTip().arg(tr(PACKAGE_NAME))); + ui->lang->setToolTip(ui->lang->toolTip().arg(tr(PACKAGE_NAME))); ui->lang->addItem(QString("(") + tr("default") + QString(")"), QVariant("")); for (const QString &langStr : translations.entryList()) diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index e9960a01b1..fb8c60d100 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -124,7 +124,7 @@ void OptionsModel::Init(bool resetSettings) if (!settings.contains("fUseProxy")) settings.setValue("fUseProxy", false); - if (!settings.contains("addrProxy")) + if (!settings.contains("addrProxy") || !settings.value("addrProxy").toString().contains(':')) settings.setValue("addrProxy", "127.0.0.1:9050"); // Only try to set -proxy, if user has enabled fUseProxy if (settings.value("fUseProxy").toBool() && !gArgs.SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString())) @@ -134,7 +134,7 @@ void OptionsModel::Init(bool resetSettings) if (!settings.contains("fUseSeparateProxyTor")) settings.setValue("fUseSeparateProxyTor", false); - if (!settings.contains("addrSeparateProxyTor")) + if (!settings.contains("addrSeparateProxyTor") || !settings.value("addrSeparateProxyTor").toString().contains(':')) settings.setValue("addrSeparateProxyTor", "127.0.0.1:9050"); // Only try to set -onion, if user has enabled fUseSeparateProxyTor if (settings.value("fUseSeparateProxyTor").toBool() && !gArgs.SoftSetArg("-onion", settings.value("addrSeparateProxyTor").toString().toStdString())) @@ -151,10 +151,32 @@ void OptionsModel::Init(bool resetSettings) language = settings.value("language").toString(); } +/** Helper function to copy contents from one QSettings to another. + * By using allKeys this also covers nested settings in a hierarchy. + */ +static void CopySettings(QSettings& dst, const QSettings& src) +{ + for (const QString& key : src.allKeys()) { + dst.setValue(key, src.value(key)); + } +} + +/** Back up a QSettings to an ini-formatted file. */ +static void BackupSettings(const fs::path& filename, const QSettings& src) +{ + qWarning() << "Backing up GUI settings to" << GUIUtil::boostPathToQString(filename); + QSettings dst(GUIUtil::boostPathToQString(filename), QSettings::IniFormat); + dst.clear(); + CopySettings(dst, src); +} + void OptionsModel::Reset() { QSettings settings; + // Backup old settings to chain-specific datadir for troubleshooting + BackupSettings(GetDataDir(true) / "guisettings.ini.bak", settings); + // Save the strDataDir setting QString dataDir = Intro::getDefaultDataDirectory(); dataDir = settings.value("strDataDir", dataDir).toString(); diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index d4137d280f..169684cf6d 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -218,17 +218,15 @@ void PaymentServer::ipcParseCommandLine(int argc, char* argv[]) SendCoinsRecipient r; if (GUIUtil::parseBitcoinURI(arg, &r) && !r.address.isEmpty()) { - CBitcoinAddress address(r.address.toStdString()); auto tempChainParams = CreateChainParams(CBaseChainParams::MAIN); - if (address.IsValid(*tempChainParams)) - { + if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) { SelectParams(CBaseChainParams::MAIN); - } - else { + } else { tempChainParams = CreateChainParams(CBaseChainParams::TESTNET); - if (address.IsValid(*tempChainParams)) + if (IsValidDestinationString(r.address.toStdString(), *tempChainParams)) { SelectParams(CBaseChainParams::TESTNET); + } } } } @@ -441,8 +439,7 @@ void PaymentServer::handleURIOrFile(const QString& s) SendCoinsRecipient recipient; if (GUIUtil::parseBitcoinURI(s, &recipient)) { - CBitcoinAddress address(recipient.address.toStdString()); - if (!address.IsValid()) { + if (!IsValidDestinationString(recipient.address.toStdString())) { Q_EMIT message(tr("URI handling"), tr("Invalid payment address %1").arg(recipient.address), CClientUIInterface::MSG_ERROR); } @@ -560,7 +557,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen CTxDestination dest; if (ExtractDestination(sendingTo.first, dest)) { // Append destination address - addresses.append(QString::fromStdString(CBitcoinAddress(dest).ToString())); + addresses.append(QString::fromStdString(EncodeDestination(dest))); } else if (!recipient.authenticatedMerchant.isEmpty()) { // Unauthenticated payment requests to custom bitcoin addresses are not supported diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 3590a98efa..d895fc1663 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -28,6 +28,7 @@ #include <wallet/wallet.h> #endif +#include <QDesktopWidget> #include <QKeyEvent> #include <QMenu> #include <QMessageBox> @@ -428,7 +429,11 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : consoleFontSize(0) { ui->setupUi(this); - GUIUtil::restoreWindowGeometry("nRPCConsoleWindow", this->size(), this); + QSettings settings; + if (!restoreGeometry(settings.value("RPCConsoleWindowGeometry").toByteArray())) { + // Restore failed (perhaps missing setting), center the window + move(QApplication::desktop()->availableGeometry().center() - frameGeometry().center()); + } ui->openDebugLogfileButton->setToolTip(ui->openDebugLogfileButton->toolTip().arg(tr(PACKAGE_NAME))); @@ -466,14 +471,14 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : ui->detailWidget->hide(); ui->peerHeading->setText(tr("Select a peer to view detailed information.")); - QSettings settings; consoleFontSize = settings.value(fontSizeSettingsKey, QFontInfo(QFont()).pointSize()).toInt(); clear(); } RPCConsole::~RPCConsole() { - GUIUtil::saveWindowGeometry("nRPCConsoleWindow", this); + QSettings settings; + settings.setValue("RPCConsoleWindowGeometry", saveGeometry()); RPCUnsetTimerInterface(rpcTimerInterface); delete rpcTimerInterface; delete ui; diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index a01886c3ea..6309070fef 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -22,7 +22,7 @@ #include "ui_interface.h" #include "txmempool.h" #include "policy/fees.h" -#include "wallet/wallet.h" +#include "wallet/fees.h" #include <QFontMetrics> #include <QMessageBox> @@ -114,10 +114,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p settings.setValue("nFeeRadio", 1); // custom if (!settings.contains("nFeeRadio")) settings.setValue("nFeeRadio", 0); // recommended - if (!settings.contains("nCustomFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility - settings.setValue("nCustomFeeRadio", 1); // total at least - if (!settings.contains("nCustomFeeRadio")) - settings.setValue("nCustomFeeRadio", 0); // per kilobyte if (!settings.contains("nSmartFeeSliderPosition")) settings.setValue("nSmartFeeSliderPosition", 0); if (!settings.contains("nTransactionFee")) @@ -127,8 +123,6 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p ui->groupFee->setId(ui->radioSmartFee, 0); ui->groupFee->setId(ui->radioCustomFee, 1); ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true); - ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0); - ui->groupCustomFee->button((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true); ui->customFee->setValue(settings.value("nTransactionFee").toLongLong()); ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool()); minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool()); @@ -178,14 +172,13 @@ void SendCoinsDialog::setModel(WalletModel *_model) connect(ui->confTargetSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls())); connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels())); - connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls())); connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels())); connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(updateSmartFeeLabel())); connect(ui->optInRBF, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels())); - ui->customFee->setSingleStep(CWallet::GetRequiredFee(1000)); + ui->customFee->setSingleStep(GetRequiredFee(1000)); updateFeeSectionControls(); updateMinFeeLabel(); updateSmartFeeLabel(); @@ -214,7 +207,6 @@ SendCoinsDialog::~SendCoinsDialog() QSettings settings; settings.setValue("fFeeSectionMinimized", fFeeMinimized); settings.setValue("nFeeRadio", ui->groupFee->checkedId()); - settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId()); settings.setValue("nConfTarget", getConfTargetForIndex(ui->confTargetSelector->currentIndex())); settings.setValue("nTransactionFee", (qint64)ui->customFee->value()); settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked()); @@ -609,8 +601,7 @@ void SendCoinsDialog::on_buttonMinimizeFee_clicked() void SendCoinsDialog::setMinimumFee() { - ui->radioCustomPerKilobyte->setChecked(true); - ui->customFee->setValue(CWallet::GetRequiredFee(1000)); + ui->customFee->setValue(GetRequiredFee(1000)); } void SendCoinsDialog::updateFeeSectionControls() @@ -622,7 +613,7 @@ void SendCoinsDialog::updateFeeSectionControls() ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked()); ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked()); ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked()); - ui->radioCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); + ui->labelCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked()); } @@ -634,8 +625,7 @@ void SendCoinsDialog::updateFeeMinimizedLabel() if (ui->radioSmartFee->isChecked()) ui->labelFeeMinimized->setText(ui->labelSmartFee->text()); else { - ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + - ((ui->radioCustomPerKilobyte->isChecked()) ? "/kB" : "")); + ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) + "/kB"); } } @@ -643,7 +633,7 @@ void SendCoinsDialog::updateMinFeeLabel() { if (model && model->getOptionsModel()) ui->checkBoxMinimumFee->setText(tr("Pay only the required fee of %1").arg( - BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), CWallet::GetRequiredFee(1000)) + "/kB") + BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), GetRequiredFee(1000)) + "/kB") ); } @@ -668,7 +658,7 @@ void SendCoinsDialog::updateSmartFeeLabel() updateCoinControlState(coin_control); coin_control.m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels FeeCalculation feeCalc; - CFeeRate feeRate = CFeeRate(CWallet::GetMinimumFee(1000, coin_control, ::mempool, ::feeEstimator, &feeCalc)); + CFeeRate feeRate = CFeeRate(GetMinimumFee(1000, coin_control, ::mempool, ::feeEstimator, &feeCalc)); ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB"); @@ -777,22 +767,19 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) CoinControlDialog::coinControl->destChange = CNoDestination(); ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); - CBitcoinAddress addr = CBitcoinAddress(text.toStdString()); + const CTxDestination dest = DecodeDestination(text.toStdString()); if (text.isEmpty()) // Nothing entered { ui->labelCoinControlChangeLabel->setText(""); } - else if (!addr.IsValid()) // Invalid address + else if (!IsValidDestination(dest)) // Invalid address { ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Bitcoin address")); } else // Valid address { - CKeyID keyid; - addr.GetKeyID(keyid); - if (!model->havePrivKey(keyid)) // Unknown change address - { + if (!model->IsSpendable(dest)) { ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address")); // confirmation dialog @@ -800,7 +787,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); if(btnRetVal == QMessageBox::Yes) - CoinControlDialog::coinControl->destChange = addr.Get(); + CoinControlDialog::coinControl->destChange = dest; else { ui->lineEditCoinControlChange->setText(""); @@ -819,7 +806,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) else ui->labelCoinControlChangeLabel->setText(tr("(no label)")); - CoinControlDialog::coinControl->destChange = addr.Get(); + CoinControlDialog::coinControl->destChange = dest; } } } diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp index 0950ed0234..cba9d4da38 100644 --- a/src/qt/signverifymessagedialog.cpp +++ b/src/qt/signverifymessagedialog.cpp @@ -117,16 +117,14 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() /* Clear old signature to ensure users don't get confused on error with an old signature displayed */ ui->signatureOut_SM->clear(); - CBitcoinAddress addr(ui->addressIn_SM->text().toStdString()); - if (!addr.IsValid()) - { + CTxDestination destination = DecodeDestination(ui->addressIn_SM->text().toStdString()); + if (!IsValidDestination(destination)) { ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); return; } - CKeyID keyID; - if (!addr.GetKeyID(keyID)) - { + const CKeyID* keyID = boost::get<CKeyID>(&destination); + if (!keyID) { ui->addressIn_SM->setValid(false); ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again.")); @@ -142,7 +140,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() } CKey key; - if (!model->getPrivKey(keyID, key)) + if (!model->getPrivKey(*keyID, key)) { ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_SM->setText(tr("Private key for the entered address is not available.")); @@ -164,7 +162,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked() ui->statusLabel_SM->setStyleSheet("QLabel { color: green; }"); ui->statusLabel_SM->setText(QString("<nobr>") + tr("Message signed.") + QString("</nobr>")); - ui->signatureOut_SM->setText(QString::fromStdString(EncodeBase64(&vchSig[0], vchSig.size()))); + ui->signatureOut_SM->setText(QString::fromStdString(EncodeBase64(vchSig.data(), vchSig.size()))); } void SignVerifyMessageDialog::on_copySignatureButton_SM_clicked() @@ -197,16 +195,13 @@ void SignVerifyMessageDialog::on_addressBookButton_VM_clicked() void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked() { - CBitcoinAddress addr(ui->addressIn_VM->text().toStdString()); - if (!addr.IsValid()) - { + CTxDestination destination = DecodeDestination(ui->addressIn_VM->text().toStdString()); + if (!IsValidDestination(destination)) { ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(tr("The entered address is invalid.") + QString(" ") + tr("Please check the address and try again.")); return; } - CKeyID keyID; - if (!addr.GetKeyID(keyID)) - { + if (!boost::get<CKeyID>(&destination)) { ui->addressIn_VM->setValid(false); ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(tr("The entered address does not refer to a key.") + QString(" ") + tr("Please check the address and try again.")); @@ -237,8 +232,7 @@ void SignVerifyMessageDialog::on_verifyMessageButton_VM_clicked() return; } - if (!(CBitcoinAddress(pubkey.GetID()) == addr)) - { + if (!(CTxDestination(pubkey.GetID()) == destination)) { ui->statusLabel_VM->setStyleSheet("QLabel { color: red; }"); ui->statusLabel_VM->setText(QString("<nobr>") + tr("Message verification failed.") + QString("</nobr>")); return; diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index 1b7cc69231..a1fbba963c 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -142,8 +142,8 @@ SplashScreen::~SplashScreen() bool SplashScreen::eventFilter(QObject * obj, QEvent * ev) { if (ev->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast<QKeyEvent *>(ev); - if(keyEvent->text()[0] == 'q' && breakAction != nullptr) { - breakAction(); + if(keyEvent->text()[0] == 'q') { + StartShutdown(); } } return QObject::eventFilter(obj, ev); @@ -170,27 +170,18 @@ static void InitMessage(SplashScreen *splash, const std::string &message) Q_ARG(QColor, QColor(55,55,55))); } -static void ShowProgress(SplashScreen *splash, const std::string &title, int nProgress) +static void ShowProgress(SplashScreen *splash, const std::string &title, int nProgress, bool resume_possible) { - InitMessage(splash, title + strprintf("%d", nProgress) + "%"); -} - -void SplashScreen::setBreakAction(const std::function<void(void)> &action) -{ - breakAction = action; -} - -static void SetProgressBreakAction(SplashScreen *splash, const std::function<void(void)> &action) -{ - QMetaObject::invokeMethod(splash, "setBreakAction", - Qt::QueuedConnection, - Q_ARG(std::function<void(void)>, action)); + InitMessage(splash, title + std::string("\n") + + (resume_possible ? _("(press q to shutdown and continue later)") + : _("press q to shutdown")) + + strprintf("\n%d", nProgress) + "%"); } #ifdef ENABLE_WALLET void SplashScreen::ConnectWallet(CWallet* wallet) { - wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); + wallet->ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2, false)); connectedWallets.push_back(wallet); } #endif @@ -199,8 +190,7 @@ void SplashScreen::subscribeToCoreSignals() { // Connect signals to client uiInterface.InitMessage.connect(boost::bind(InitMessage, this, _1)); - uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2)); - uiInterface.SetProgressBreakAction.connect(boost::bind(SetProgressBreakAction, this, _1)); + uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, _1, _2, _3)); #ifdef ENABLE_WALLET uiInterface.LoadWallet.connect(boost::bind(&SplashScreen::ConnectWallet, this, _1)); #endif @@ -210,10 +200,10 @@ void SplashScreen::unsubscribeFromCoreSignals() { // Disconnect signals from client uiInterface.InitMessage.disconnect(boost::bind(InitMessage, this, _1)); - uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); + uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2, _3)); #ifdef ENABLE_WALLET for (CWallet* const & pwallet : connectedWallets) { - pwallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2)); + pwallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, _1, _2, false)); } #endif } diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h index a88ebb98a8..c6cfd503f7 100644 --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -36,8 +36,6 @@ public Q_SLOTS: /** Show message and progress */ void showMessage(const QString &message, int alignment, const QColor &color); - /** Sets the break action */ - void setBreakAction(const std::function<void(void)> &action); protected: bool eventFilter(QObject * obj, QEvent * ev); @@ -55,8 +53,6 @@ private: int curAlignment; QList<CWallet*> connectedWallets; - - std::function<void(void)> breakAction; }; #endif // BITCOIN_QT_SPLASHSCREEN_H diff --git a/src/qt/test/paymentservertests.cpp b/src/qt/test/paymentservertests.cpp index c7830071ed..273bd10487 100644 --- a/src/qt/test/paymentservertests.cpp +++ b/src/qt/test/paymentservertests.cpp @@ -24,7 +24,7 @@ X509 *parse_b64der_cert(const char* cert_data) { std::vector<unsigned char> data = DecodeBase64(cert_data); assert(data.size() > 0); - const unsigned char* dptr = &data[0]; + const unsigned char* dptr = data.data(); X509 *cert = d2i_X509(nullptr, &dptr, data.size()); assert(cert); return cert; @@ -43,7 +43,7 @@ static SendCoinsRecipient handleRequest(PaymentServer* server, std::vector<unsig // Write data to a temp file: QTemporaryFile f; f.open(); - f.write((const char*)&data[0], data.size()); + f.write((const char*)data.data(), data.size()); f.close(); // Create a QObject, install event filter from PaymentServer @@ -139,7 +139,7 @@ void PaymentServerTests::paymentServerTests() // Contains a testnet paytoaddress, so payment request network doesn't match client network: data = DecodeBase64(paymentrequest1_cert2_BASE64); - byteArray = QByteArray((const char*)&data[0], data.size()); + byteArray = QByteArray((const char*)data.data(), data.size()); r.paymentRequest.parse(byteArray); // Ensure the request is initialized, because network "main" is default, even for // uninitialized payment requests and that will fail our test here. @@ -148,7 +148,7 @@ void PaymentServerTests::paymentServerTests() // Expired payment request (expires is set to 1 = 1970-01-01 00:00:01): data = DecodeBase64(paymentrequest2_cert2_BASE64); - byteArray = QByteArray((const char*)&data[0], data.size()); + byteArray = QByteArray((const char*)data.data(), data.size()); r.paymentRequest.parse(byteArray); // Ensure the request is initialized QVERIFY(r.paymentRequest.IsInitialized()); @@ -159,7 +159,7 @@ void PaymentServerTests::paymentServerTests() // 9223372036854775807 (uint64), 9223372036854775807 (int64_t) and -1 (int32_t) // -1 is 1969-12-31 23:59:59 (for a 32 bit time values) data = DecodeBase64(paymentrequest3_cert2_BASE64); - byteArray = QByteArray((const char*)&data[0], data.size()); + byteArray = QByteArray((const char*)data.data(), data.size()); r.paymentRequest.parse(byteArray); // Ensure the request is initialized QVERIFY(r.paymentRequest.IsInitialized()); @@ -170,7 +170,7 @@ void PaymentServerTests::paymentServerTests() // 9223372036854775808 (uint64), -9223372036854775808 (int64_t) and 0 (int32_t) // 0 is 1970-01-01 00:00:00 (for a 32 bit time values) data = DecodeBase64(paymentrequest4_cert2_BASE64); - byteArray = QByteArray((const char*)&data[0], data.size()); + byteArray = QByteArray((const char*)data.data(), data.size()); r.paymentRequest.parse(byteArray); // Ensure the request is initialized QVERIFY(r.paymentRequest.IsInitialized()); @@ -190,7 +190,7 @@ void PaymentServerTests::paymentServerTests() // Payment request with amount overflow (amount is set to 21000001 BTC): data = DecodeBase64(paymentrequest5_cert2_BASE64); - byteArray = QByteArray((const char*)&data[0], data.size()); + byteArray = QByteArray((const char*)data.data(), data.size()); r.paymentRequest.parse(byteArray); // Ensure the request is initialized QVERIFY(r.paymentRequest.IsInitialized()); diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp index cd9ab23457..70fdd4bf58 100644 --- a/src/qt/test/rpcnestedtests.cpp +++ b/src/qt/test/rpcnestedtests.cpp @@ -11,7 +11,6 @@ #include "rpc/register.h" #include "rpc/server.h" #include "rpcconsole.h" -#include "test/testutil.h" #include "test/test_bitcoin.h" #include "univalue.h" #include "util.h" @@ -29,7 +28,7 @@ static UniValue rpcNestedTest_rpc(const JSONRPCRequest& request) static const CRPCCommand vRPCCommands[] = { - { "test", "rpcNestedTest", &rpcNestedTest_rpc, true, {} }, + { "test", "rpcNestedTest", &rpcNestedTest_rpc, {} }, }; void RPCNestedTests::rpcNestedTests() @@ -37,11 +36,6 @@ void RPCNestedTests::rpcNestedTests() // do some test setup // could be moved to a more generic place when we add more tests on QT level tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]); - ClearDatadirCache(); - std::string path = QDir::tempPath().toStdString() + "/" + strprintf("test_bitcoin_qt_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000))); - QDir dir(QString::fromStdString(path)); - dir.mkpath("."); - gArgs.ForceSetArg("-datadir", path); //mempool.setSanityCheck(1.0); TestingSetup test; @@ -136,6 +130,4 @@ void RPCNestedTests::rpcNestedTests() QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,abc)"), std::runtime_error); //don't tollerate empty arguments when using , QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using , #endif - - fs::remove_all(fs::path(path)); } diff --git a/src/qt/test/test_main.cpp b/src/qt/test/test_main.cpp index 80a00a634a..4c04e67ccc 100644 --- a/src/qt/test/test_main.cpp +++ b/src/qt/test/test_main.cpp @@ -53,6 +53,10 @@ int main(int argc, char *argv[]) SetupNetworking(); SelectParams(CBaseChainParams::MAIN); noui_connect(); + ClearDatadirCache(); + fs::path pathTemp = fs::temp_directory_path() / strprintf("test_bitcoin-qt_%lu_%i", (unsigned long)GetTime(), (int)GetRand(100000)); + fs::create_directories(pathTemp); + gArgs.ForceSetArg("-datadir", pathTemp.string()); bool fInvalid = false; @@ -97,5 +101,7 @@ int main(int argc, char *argv[]) } #endif + fs::remove_all(pathTemp); + return fInvalid; } diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index ff1eb59f16..eeae58bd05 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -13,6 +13,10 @@ #include "test/test_bitcoin.h" #include "validation.h" #include "wallet/wallet.h" +#include "qt/overviewpage.h" +#include "qt/receivecoinsdialog.h" +#include "qt/recentrequeststablemodel.h" +#include "qt/receiverequestdialog.h" #include <QAbstractButton> #include <QAction> @@ -21,6 +25,9 @@ #include <QPushButton> #include <QTimer> #include <QVBoxLayout> +#include <QTextEdit> +#include <QListView> +#include <QDialogButtonBox> namespace { @@ -57,11 +64,11 @@ void ConfirmSend(QString* text = nullptr, bool cancel = false) } //! Send coins to address and return txid. -uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CBitcoinAddress& address, CAmount amount, bool rbf) +uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDestination& address, CAmount amount, bool rbf) { QVBoxLayout* entries = sendCoinsDialog.findChild<QVBoxLayout*>("entries"); SendCoinsEntry* entry = qobject_cast<SendCoinsEntry*>(entries->itemAt(0)->widget()); - entry->findChild<QValidatedLineEdit*>("payTo")->setText(QString::fromStdString(address.ToString())); + entry->findChild<QValidatedLineEdit*>("payTo")->setText(QString::fromStdString(EncodeDestination(address))); entry->findChild<BitcoinAmountField*>("payAmount")->setValue(amount); sendCoinsDialog.findChild<QFrame*>("frameFee") ->findChild<QFrame*>("frameFeeSelection") @@ -140,7 +147,7 @@ void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, st // src/qt/test/test_bitcoin-qt -platform xcb # Linux // src/qt/test/test_bitcoin-qt -platform windows # Windows // src/qt/test/test_bitcoin-qt -platform cocoa # macOS -void TestSendCoins() +void TestGUI() { // Set up wallet and chain with 105 blocks (5 mature blocks for spending). TestChain100Setup test; @@ -172,8 +179,8 @@ void TestSendCoins() // Send two transactions, and verify they are added to transaction list. TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel(); QCOMPARE(transactionTableModel->rowCount({}), 105); - uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 5 * COIN, false /* rbf */); - uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CBitcoinAddress(CKeyID()), 10 * COIN, true /* rbf */); + uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CKeyID(), 5 * COIN, false /* rbf */); + uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CKeyID(), 10 * COIN, true /* rbf */); QCOMPARE(transactionTableModel->rowCount({}), 107); QVERIFY(FindTx(*transactionTableModel, txid1).isValid()); QVERIFY(FindTx(*transactionTableModel, txid2).isValid()); @@ -184,6 +191,68 @@ void TestSendCoins() BumpFee(transactionView, txid2, false /* expect disabled */, {} /* expected error */, false /* cancel */); BumpFee(transactionView, txid2, true /* expect disabled */, "already bumped" /* expected error */, false /* cancel */); + // Check current balance on OverviewPage + OverviewPage overviewPage(platformStyle.get()); + overviewPage.setWalletModel(&walletModel); + QLabel* balanceLabel = overviewPage.findChild<QLabel*>("labelBalance"); + QString balanceText = balanceLabel->text(); + int unit = walletModel.getOptionsModel()->getDisplayUnit(); + CAmount balance = walletModel.getBalance(); + QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways); + QCOMPARE(balanceText, balanceComparison); + + // Check Request Payment button + ReceiveCoinsDialog receiveCoinsDialog(platformStyle.get()); + receiveCoinsDialog.setModel(&walletModel); + RecentRequestsTableModel* requestTableModel = walletModel.getRecentRequestsTableModel(); + + // Label input + QLineEdit* labelInput = receiveCoinsDialog.findChild<QLineEdit*>("reqLabel"); + labelInput->setText("TEST_LABEL_1"); + + // Amount input + BitcoinAmountField* amountInput = receiveCoinsDialog.findChild<BitcoinAmountField*>("reqAmount"); + amountInput->setValue(1); + + // Message input + QLineEdit* messageInput = receiveCoinsDialog.findChild<QLineEdit*>("reqMessage"); + messageInput->setText("TEST_MESSAGE_1"); + int initialRowCount = requestTableModel->rowCount({}); + QPushButton* requestPaymentButton = receiveCoinsDialog.findChild<QPushButton*>("receiveButton"); + requestPaymentButton->click(); + for (QWidget* widget : QApplication::topLevelWidgets()) { + if (widget->inherits("ReceiveRequestDialog")) { + ReceiveRequestDialog* receiveRequestDialog = qobject_cast<ReceiveRequestDialog*>(widget); + QTextEdit* rlist = receiveRequestDialog->QObject::findChild<QTextEdit*>("outUri"); + QString paymentText = rlist->toPlainText(); + QStringList paymentTextList = paymentText.split('\n'); + QCOMPARE(paymentTextList.at(0), QString("Payment information")); + QVERIFY(paymentTextList.at(1).indexOf(QString("URI: bitcoin:")) != -1); + QVERIFY(paymentTextList.at(2).indexOf(QString("Address:")) != -1); + QCOMPARE(paymentTextList.at(3), QString("Amount: 0.00000001 ") + QString::fromStdString(CURRENCY_UNIT)); + QCOMPARE(paymentTextList.at(4), QString("Label: TEST_LABEL_1")); + QCOMPARE(paymentTextList.at(5), QString("Message: TEST_MESSAGE_1")); + } + } + + // Clear button + QPushButton* clearButton = receiveCoinsDialog.findChild<QPushButton*>("clearButton"); + clearButton->click(); + QCOMPARE(labelInput->text(), QString("")); + QCOMPARE(amountInput->value(), CAmount(0)); + QCOMPARE(messageInput->text(), QString("")); + + // Check addition to history + int currentRowCount = requestTableModel->rowCount({}); + QCOMPARE(currentRowCount, initialRowCount+1); + + // Check Remove button + QTableView* table = receiveCoinsDialog.findChild<QTableView*>("recentRequestsView"); + table->selectRow(currentRowCount-1); + QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>("removeRequestButton"); + removeRequestButton->click(); + QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1); + bitdb.Flush(true); bitdb.Reset(); } @@ -192,5 +261,5 @@ void TestSendCoins() void WalletTests::walletTests() { - TestSendCoins(); + TestGUI(); } diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp index bcacc47ef3..74f5c774a0 100644 --- a/src/qt/transactiondesc.cpp +++ b/src/qt/transactiondesc.cpp @@ -91,9 +91,8 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco if (nNet > 0) { // Credit - if (CBitcoinAddress(rec->address).IsValid()) - { - CTxDestination address = CBitcoinAddress(rec->address).Get(); + CTxDestination address = DecodeDestination(rec->address); + if (IsValidDestination(address)) { if (wallet->mapAddressBook.count(address)) { strHTML += "<b>" + tr("From") + ":</b> " + tr("unknown") + "<br>"; @@ -118,7 +117,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco // Online transaction std::string strAddress = wtx.mapValue["to"]; strHTML += "<b>" + tr("To") + ":</b> "; - CTxDestination dest = CBitcoinAddress(strAddress).Get(); + CTxDestination dest = DecodeDestination(strAddress); if (wallet->mapAddressBook.count(dest) && !wallet->mapAddressBook[dest].name.empty()) strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[dest].name) + " "; strHTML += GUIUtil::HtmlEscape(strAddress) + "<br>"; @@ -189,7 +188,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco strHTML += "<b>" + tr("To") + ":</b> "; if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty()) strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; - strHTML += GUIUtil::HtmlEscape(CBitcoinAddress(address).ToString()); + strHTML += GUIUtil::HtmlEscape(EncodeDestination(address)); if(toSelf == ISMINE_SPENDABLE) strHTML += " (own address)"; else if(toSelf & ISMINE_WATCH_ONLY) @@ -304,7 +303,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, TransactionReco { if (wallet->mapAddressBook.count(address) && !wallet->mapAddressBook[address].name.empty()) strHTML += GUIUtil::HtmlEscape(wallet->mapAddressBook[address].name) + " "; - strHTML += QString::fromStdString(CBitcoinAddress(address).ToString()); + strHTML += QString::fromStdString(EncodeDestination(address)); } strHTML = strHTML + " " + tr("Amount") + "=" + BitcoinUnits::formatHtmlWithUnit(unit, vout.nValue); strHTML = strHTML + " IsMine=" + (wallet->IsMine(vout) & ISMINE_SPENDABLE ? tr("true") : tr("false")) + "</li>"; diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 36d98ce49d..d40ffd22cd 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -55,7 +55,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * { // Received by Bitcoin Address sub.type = TransactionRecord::RecvWithAddress; - sub.address = CBitcoinAddress(address).ToString(); + sub.address = EncodeDestination(address); } else { @@ -127,7 +127,7 @@ QList<TransactionRecord> TransactionRecord::decomposeTransaction(const CWallet * { // Sent to Bitcoin Address sub.type = TransactionRecord::SendToAddress; - sub.address = CBitcoinAddress(address).ToString(); + sub.address = EncodeDestination(address); } else { diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 53c38da9db..39dfdb587c 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -33,6 +33,7 @@ #include <QScrollBar> #include <QSignalMapper> #include <QTableView> +#include <QTimer> #include <QUrl> #include <QVBoxLayout> @@ -112,6 +113,17 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa amountWidget->setValidator(new QDoubleValidator(0, 1e20, 8, this)); hlayout->addWidget(amountWidget); + // Delay before filtering transactions in ms + static const int input_filter_delay = 200; + + QTimer* amount_typing_delay = new QTimer(this); + amount_typing_delay->setSingleShot(true); + amount_typing_delay->setInterval(input_filter_delay); + + QTimer* prefix_typing_delay = new QTimer(this); + prefix_typing_delay->setSingleShot(true); + prefix_typing_delay->setInterval(input_filter_delay); + QVBoxLayout *vlayout = new QVBoxLayout(this); vlayout->setContentsMargins(0,0,0,0); vlayout->setSpacing(0); @@ -173,8 +185,10 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa connect(dateWidget, SIGNAL(activated(int)), this, SLOT(chooseDate(int))); connect(typeWidget, SIGNAL(activated(int)), this, SLOT(chooseType(int))); connect(watchOnlyWidget, SIGNAL(activated(int)), this, SLOT(chooseWatchonly(int))); - connect(addressWidget, SIGNAL(textChanged(QString)), this, SLOT(changedPrefix(QString))); - connect(amountWidget, SIGNAL(textChanged(QString)), this, SLOT(changedAmount(QString))); + connect(amountWidget, SIGNAL(textChanged(QString)), amount_typing_delay, SLOT(start())); + connect(amount_typing_delay, SIGNAL(timeout()), this, SLOT(changedAmount())); + connect(addressWidget, SIGNAL(textChanged(QString)), prefix_typing_delay, SLOT(start())); + connect(prefix_typing_delay, SIGNAL(timeout()), this, SLOT(changedPrefix())); connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex))); connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); @@ -312,20 +326,19 @@ void TransactionView::chooseWatchonly(int idx) (TransactionFilterProxy::WatchOnlyFilter)watchOnlyWidget->itemData(idx).toInt()); } -void TransactionView::changedPrefix(const QString &prefix) +void TransactionView::changedPrefix() { if(!transactionProxyModel) return; - transactionProxyModel->setAddressPrefix(prefix); + transactionProxyModel->setAddressPrefix(addressWidget->text()); } -void TransactionView::changedAmount(const QString &amount) +void TransactionView::changedAmount() { if(!transactionProxyModel) return; CAmount amount_parsed = 0; - if(BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amount, &amount_parsed)) - { + if (BitcoinUnits::parse(model->getOptionsModel()->getDisplayUnit(), amountWidget->text(), &amount_parsed)) { transactionProxyModel->setMinAmount(amount_parsed); } else diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index 52e57cae4c..5b4cfd4a88 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -112,8 +112,8 @@ public Q_SLOTS: void chooseDate(int idx); void chooseType(int idx); void chooseWatchonly(int idx); - void changedPrefix(const QString &prefix); - void changedAmount(const QString &amount); + void changedAmount(); + void changedPrefix(); void exportClicked(); void focusTransaction(const QModelIndex&); diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index f3183320f0..714a594318 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -7,6 +7,7 @@ #include "bitcoingui.h" #include "walletview.h" +#include <cassert> #include <cstdio> #include <QHBoxLayout> @@ -69,6 +70,7 @@ bool WalletFrame::setCurrentWallet(const QString& name) WalletView *walletView = mapWalletViews.value(name); walletStack->setCurrentWidget(walletView); + assert(walletView); walletView->updateEncryptionStatus(); return true; } diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 445d00e9c8..53b1c2967c 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -188,8 +188,7 @@ void WalletModel::updateWatchOnlyFlag(bool fHaveWatchonly) bool WalletModel::validateAddress(const QString &address) { - CBitcoinAddress addressParsed(address.toStdString()); - return addressParsed.IsValid(); + return IsValidDestinationString(address.toStdString()); } WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, const CCoinControl& coinControl) @@ -247,7 +246,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact setAddress.insert(rcp.address); ++nAddresses; - CScript scriptPubKey = GetScriptForDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); + CScript scriptPubKey = GetScriptForDestination(DecodeDestination(rcp.address.toStdString())); CRecipient recipient = {scriptPubKey, rcp.amount, rcp.fSubtractFeeFromAmount}; vecSend.push_back(recipient); @@ -348,7 +347,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(WalletModelTransaction &tran if (!rcp.paymentRequest.IsInitialized()) { std::string strAddress = rcp.address.toStdString(); - CTxDestination dest = CBitcoinAddress(strAddress).Get(); + CTxDestination dest = DecodeDestination(strAddress); std::string strLabel = rcp.label.toStdString(); { LOCK(wallet->cs_wallet); @@ -464,7 +463,7 @@ static void NotifyAddressBookChanged(WalletModel *walletmodel, CWallet *wallet, const CTxDestination &address, const std::string &label, bool isMine, const std::string &purpose, ChangeType status) { - QString strAddress = QString::fromStdString(CBitcoinAddress(address).ToString()); + QString strAddress = QString::fromStdString(EncodeDestination(address)); QString strLabel = QString::fromStdString(label); QString strPurpose = QString::fromStdString(purpose); @@ -561,9 +560,9 @@ bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const return wallet->GetPubKey(address, vchPubKeyOut); } -bool WalletModel::havePrivKey(const CKeyID &address) const +bool WalletModel::IsSpendable(const CTxDestination& dest) const { - return wallet->HaveKey(address); + return IsMine(*wallet, dest) & ISMINE_SPENDABLE; } bool WalletModel::getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const @@ -596,7 +595,7 @@ bool WalletModel::isSpent(const COutPoint& outpoint) const void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins) const { for (auto& group : wallet->ListCoins()) { - auto& resultGroup = mapCoins[QString::fromStdString(CBitcoinAddress(group.first).ToString())]; + auto& resultGroup = mapCoins[QString::fromStdString(EncodeDestination(group.first))]; for (auto& coin : group.second) { resultGroup.emplace_back(std::move(coin)); } @@ -634,7 +633,7 @@ void WalletModel::loadReceiveRequests(std::vector<std::string>& vReceiveRequests bool WalletModel::saveReceiveRequest(const std::string &sAddress, const int64_t nId, const std::string &sRequest) { - CTxDestination dest = CBitcoinAddress(sAddress).Get(); + CTxDestination dest = DecodeDestination(sAddress); std::stringstream ss; ss << nId; diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 6be36a57e2..05733f8272 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -190,7 +190,7 @@ public: UnlockContext requestUnlock(); bool getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const; - bool havePrivKey(const CKeyID &address) const; + bool IsSpendable(const CTxDestination& dest) const; bool getPrivKey(const CKeyID &address, CKey& vchPrivKeyOut) const; void getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs); bool isSpent(const COutPoint& outpoint) const; diff --git a/src/rest.cpp b/src/rest.cpp index 154ee04eed..0b2c843d5f 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -190,9 +190,6 @@ static bool rest_headers(HTTPRequest* req, return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: .bin, .hex)"); } } - - // not reached - return true; // continue to process further HTTP reqs on this cxn } static bool rest_block(HTTPRequest* req, @@ -253,9 +250,6 @@ static bool rest_block(HTTPRequest* req, return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); } } - - // not reached - return true; // continue to process further HTTP reqs on this cxn } static bool rest_block_extended(HTTPRequest* req, const std::string& strURIPart) @@ -292,9 +286,6 @@ static bool rest_chaininfo(HTTPRequest* req, const std::string& strURIPart) return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)"); } } - - // not reached - return true; // continue to process further HTTP reqs on this cxn } static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart) @@ -317,9 +308,6 @@ static bool rest_mempool_info(HTTPRequest* req, const std::string& strURIPart) return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)"); } } - - // not reached - return true; // continue to process further HTTP reqs on this cxn } static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPart) @@ -342,9 +330,6 @@ static bool rest_mempool_contents(HTTPRequest* req, const std::string& strURIPar return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: json)"); } } - - // not reached - return true; // continue to process further HTTP reqs on this cxn } static bool rest_tx(HTTPRequest* req, const std::string& strURIPart) @@ -394,9 +379,6 @@ static bool rest_tx(HTTPRequest* req, const std::string& strURIPart) return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); } } - - // not reached - return true; // continue to process further HTTP reqs on this cxn } static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) @@ -581,9 +563,6 @@ static bool rest_getutxos(HTTPRequest* req, const std::string& strURIPart) return RESTERR(req, HTTP_NOT_FOUND, "output format not found (available: " + AvailableDataFormatsString() + ")"); } } - - // not reached - return true; // continue to process further HTTP reqs on this cxn } static const struct { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 34f553f3b0..68af376f35 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -24,6 +24,7 @@ #include "util.h" #include "utilstrencodings.h" #include "hash.h" +#include "warnings.h" #include <stdint.h> @@ -343,6 +344,7 @@ std::string EntryDescriptionString() " \"ancestorcount\" : n, (numeric) number of in-mempool ancestor transactions (including this one)\n" " \"ancestorsize\" : n, (numeric) virtual transaction size of in-mempool ancestors (including this one)\n" " \"ancestorfees\" : n, (numeric) modified fees (see above) of in-mempool ancestors (including this one)\n" + " \"wtxid\" : hash, (string) hash of serialized transaction, including witness data\n" " \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n" " \"transactionid\", (string) parent transaction id\n" " ... ]\n"; @@ -363,6 +365,7 @@ void entryToJSON(UniValue &info, const CTxMemPoolEntry &e) info.push_back(Pair("ancestorcount", e.GetCountWithAncestors())); info.push_back(Pair("ancestorsize", e.GetSizeWithAncestors())); info.push_back(Pair("ancestorfees", e.GetModFeesWithAncestors())); + info.push_back(Pair("wtxid", mempool.vTxHashes[e.vTxHashesIdx].first.ToString())); const CTransaction& tx = e.GetTx(); std::set<std::string> setDepends; for (const CTxIn& txin : tx.vin) @@ -812,6 +815,7 @@ static void ApplyStats(CCoinsStats &stats, CHashWriter& ss, const uint256& hash, static bool GetUTXOStats(CCoinsView *view, CCoinsStats &stats) { std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor()); + assert(pcursor); CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); stats.hashBlock = pcursor->GetBestBlock(); @@ -944,9 +948,10 @@ UniValue gettxout(const JSONRPCRequest& request) "gettxout \"txid\" n ( include_mempool )\n" "\nReturns details about an unspent transaction output.\n" "\nArguments:\n" - "1. \"txid\" (string, required) The transaction id\n" - "2. n (numeric, required) vout number\n" - "3. include_mempool (boolean, optional) Whether to include the mempool\n" + "1. \"txid\" (string, required) The transaction id\n" + "2. \"n\" (numeric, required) vout number\n" + "3. \"include_mempool\" (boolean, optional) Whether to include the mempool. Default: true." + " Note that an unspent output that is spent in the mempool won't appear.\n" "\nResult:\n" "{\n" " \"bestblock\" : \"hash\", (string) the block hash\n" @@ -1131,8 +1136,11 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) " \"mediantime\": xxxxxx, (numeric) median time for the current best block\n" " \"verificationprogress\": xxxx, (numeric) estimate of verification progress [0..1]\n" " \"chainwork\": \"xxxx\" (string) total amount of work in active chain, in hexadecimal\n" + " \"size_on_disk\": xxxxxx, (numeric) the estimated size of the block and undo files on disk\n" " \"pruned\": xx, (boolean) if the blocks are subject to pruning\n" - " \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored\n" + " \"pruneheight\": xxxxxx, (numeric) lowest-height complete block stored (only present if pruning is enabled)\n" + " \"automatic_pruning\": xx, (boolean) whether automatic pruning is enabled (only present if pruning is enabled)\n" + " \"prune_target_size\": xxxxxx, (numeric) the target size used by pruning (only present if automatic pruning is enabled)\n" " \"softforks\": [ (array) status of softforks in progress\n" " {\n" " \"id\": \"xxxx\", (string) name of softfork\n" @@ -1158,6 +1166,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) " }\n" " }\n" " }\n" + " \"warnings\" : \"...\", (string) any network and blockchain warnings.\n" "}\n" "\nExamples:\n" + HelpExampleCli("getblockchaininfo", "") @@ -1175,7 +1184,24 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) obj.push_back(Pair("mediantime", (int64_t)chainActive.Tip()->GetMedianTimePast())); obj.push_back(Pair("verificationprogress", GuessVerificationProgress(Params().TxData(), chainActive.Tip()))); obj.push_back(Pair("chainwork", chainActive.Tip()->nChainWork.GetHex())); + obj.push_back(Pair("size_on_disk", CalculateCurrentUsage())); obj.push_back(Pair("pruned", fPruneMode)); + if (fPruneMode) { + CBlockIndex* block = chainActive.Tip(); + assert(block); + while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) { + block = block->pprev; + } + + obj.push_back(Pair("pruneheight", block->nHeight)); + + // if 0, execution bypasses the whole if block. + bool automatic_pruning = (gArgs.GetArg("-prune", 0) != 1); + obj.push_back(Pair("automatic_pruning", automatic_pruning)); + if (automatic_pruning) { + obj.push_back(Pair("prune_target_size", nPruneTarget)); + } + } const Consensus::Params& consensusParams = Params().GetConsensus(); CBlockIndex* tip = chainActive.Tip(); @@ -1189,14 +1215,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) obj.push_back(Pair("softforks", softforks)); obj.push_back(Pair("bip9_softforks", bip9_softforks)); - if (fPruneMode) - { - CBlockIndex *block = chainActive.Tip(); - while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) - block = block->pprev; - - obj.push_back(Pair("pruneheight", block->nHeight)); - } + obj.push_back(Pair("warnings", GetWarnings("statusbar"))); return obj; } @@ -1343,7 +1362,7 @@ UniValue getmempoolinfo(const JSONRPCRequest& request) " \"bytes\": xxxxx, (numeric) Sum of all virtual transaction sizes as defined in BIP 141. Differs from actual serialized size because witness data is discounted\n" " \"usage\": xxxxx, (numeric) Total memory usage for the mempool\n" " \"maxmempool\": xxxxx, (numeric) Maximum memory usage for the mempool\n" - " \"mempoolminfee\": xxxxx (numeric) Minimum feerate (" + CURRENCY_UNIT + " per KB) for tx to be accepted\n" + " \"mempoolminfee\": xxxxx (numeric) Minimum fee rate in " + CURRENCY_UNIT + "/kB for tx to be accepted\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmempoolinfo", "") @@ -1477,9 +1496,12 @@ UniValue getchaintxstats(const JSONRPCRequest& request) "2. \"blockhash\" (string, optional) The hash of the block that ends the window.\n" "\nResult:\n" "{\n" - " \"time\": xxxxx, (numeric) The timestamp for the statistics in UNIX format.\n" - " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" - " \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window.\n" + " \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n" + " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" + " \"window_block_count\": xxxxx, (numeric) Size of the window in number of blocks.\n" + " \"window_tx_count\": xxxxx, (numeric) The number of transactions in the window. Only returned if \"window_block_count\" is > 0.\n" + " \"window_interval\": xxxxx, (numeric) The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0.\n" + " \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0.\n" "}\n" "\nExamples:\n" + HelpExampleCli("getchaintxstats", "") @@ -1489,10 +1511,6 @@ UniValue getchaintxstats(const JSONRPCRequest& request) const CBlockIndex* pindex; int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month - if (!request.params[0].isNull()) { - blockcount = request.params[0].get_int(); - } - bool havehash = !request.params[1].isNull(); uint256 hash; if (havehash) { @@ -1514,9 +1532,17 @@ UniValue getchaintxstats(const JSONRPCRequest& request) pindex = chainActive.Tip(); } } + + assert(pindex != nullptr); + + if (request.params[0].isNull()) { + blockcount = std::max(0, std::min(blockcount, pindex->nHeight - 1)); + } else { + blockcount = request.params[0].get_int(); - if (blockcount < 1 || blockcount >= pindex->nHeight) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 1 and the block's height"); + if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->nHeight)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 0 and the block's height - 1"); + } } const CBlockIndex* pindexPast = pindex->GetAncestor(pindex->nHeight - blockcount); @@ -1526,41 +1552,68 @@ UniValue getchaintxstats(const JSONRPCRequest& request) UniValue ret(UniValue::VOBJ); ret.push_back(Pair("time", (int64_t)pindex->nTime)); ret.push_back(Pair("txcount", (int64_t)pindex->nChainTx)); - ret.push_back(Pair("txrate", ((double)nTxDiff) / nTimeDiff)); + ret.push_back(Pair("window_block_count", blockcount)); + if (blockcount > 0) { + ret.push_back(Pair("window_tx_count", nTxDiff)); + ret.push_back(Pair("window_interval", nTimeDiff)); + if (nTimeDiff > 0) { + ret.push_back(Pair("txrate", ((double)nTxDiff) / nTimeDiff)); + } + } return ret; } +UniValue savemempool(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 0) { + throw std::runtime_error( + "savemempool\n" + "\nDumps the mempool to disk.\n" + "\nExamples:\n" + + HelpExampleCli("savemempool", "") + + HelpExampleRpc("savemempool", "") + ); + } + + if (!DumpMempool()) { + throw JSONRPCError(RPC_MISC_ERROR, "Unable to dump mempool to disk"); + } + + return NullUniValue; +} + static const CRPCCommand commands[] = -{ // category name actor (function) okSafe argNames - // --------------------- ------------------------ ----------------------- ------ ---------- - { "blockchain", "getblockchaininfo", &getblockchaininfo, true, {} }, - { "blockchain", "getchaintxstats", &getchaintxstats, true, {"nblocks", "blockhash"} }, - { "blockchain", "getbestblockhash", &getbestblockhash, true, {} }, - { "blockchain", "getblockcount", &getblockcount, true, {} }, - { "blockchain", "getblock", &getblock, true, {"blockhash","verbosity|verbose"} }, - { "blockchain", "getblockhash", &getblockhash, true, {"height"} }, - { "blockchain", "getblockheader", &getblockheader, true, {"blockhash","verbose"} }, - { "blockchain", "getchaintips", &getchaintips, true, {} }, - { "blockchain", "getdifficulty", &getdifficulty, true, {} }, - { "blockchain", "getmempoolancestors", &getmempoolancestors, true, {"txid","verbose"} }, - { "blockchain", "getmempooldescendants", &getmempooldescendants, true, {"txid","verbose"} }, - { "blockchain", "getmempoolentry", &getmempoolentry, true, {"txid"} }, - { "blockchain", "getmempoolinfo", &getmempoolinfo, true, {} }, - { "blockchain", "getrawmempool", &getrawmempool, true, {"verbose"} }, - { "blockchain", "gettxout", &gettxout, true, {"txid","n","include_mempool"} }, - { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true, {} }, - { "blockchain", "pruneblockchain", &pruneblockchain, true, {"height"} }, - { "blockchain", "verifychain", &verifychain, true, {"checklevel","nblocks"} }, - - { "blockchain", "preciousblock", &preciousblock, true, {"blockhash"} }, +{ // category name actor (function) argNames + // --------------------- ------------------------ ----------------------- ---------- + { "blockchain", "getblockchaininfo", &getblockchaininfo, {} }, + { "blockchain", "getchaintxstats", &getchaintxstats, {"nblocks", "blockhash"} }, + { "blockchain", "getbestblockhash", &getbestblockhash, {} }, + { "blockchain", "getblockcount", &getblockcount, {} }, + { "blockchain", "getblock", &getblock, {"blockhash","verbosity|verbose"} }, + { "blockchain", "getblockhash", &getblockhash, {"height"} }, + { "blockchain", "getblockheader", &getblockheader, {"blockhash","verbose"} }, + { "blockchain", "getchaintips", &getchaintips, {} }, + { "blockchain", "getdifficulty", &getdifficulty, {} }, + { "blockchain", "getmempoolancestors", &getmempoolancestors, {"txid","verbose"} }, + { "blockchain", "getmempooldescendants", &getmempooldescendants, {"txid","verbose"} }, + { "blockchain", "getmempoolentry", &getmempoolentry, {"txid"} }, + { "blockchain", "getmempoolinfo", &getmempoolinfo, {} }, + { "blockchain", "getrawmempool", &getrawmempool, {"verbose"} }, + { "blockchain", "gettxout", &gettxout, {"txid","n","include_mempool"} }, + { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, {} }, + { "blockchain", "pruneblockchain", &pruneblockchain, {"height"} }, + { "blockchain", "savemempool", &savemempool, {} }, + { "blockchain", "verifychain", &verifychain, {"checklevel","nblocks"} }, + + { "blockchain", "preciousblock", &preciousblock, {"blockhash"} }, /* Not shown in help */ - { "hidden", "invalidateblock", &invalidateblock, true, {"blockhash"} }, - { "hidden", "reconsiderblock", &reconsiderblock, true, {"blockhash"} }, - { "hidden", "waitfornewblock", &waitfornewblock, true, {"timeout"} }, - { "hidden", "waitforblock", &waitforblock, true, {"blockhash","timeout"} }, - { "hidden", "waitforblockheight", &waitforblockheight, true, {"height","timeout"} }, + { "hidden", "invalidateblock", &invalidateblock, {"blockhash"} }, + { "hidden", "reconsiderblock", &reconsiderblock, {"blockhash"} }, + { "hidden", "waitfornewblock", &waitfornewblock, {"timeout"} }, + { "hidden", "waitforblock", &waitforblock, {"blockhash","timeout"} }, + { "hidden", "waitforblockheight", &waitforblockheight, {"height","timeout"} }, }; void RegisterBlockchainRPCCommands(CRPCTable &t) diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 4179453782..f54f24e2a7 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -115,8 +115,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "keypoolrefill", 0, "newsize" }, { "getrawmempool", 0, "verbose" }, { "estimatefee", 0, "nblocks" }, - { "estimatesmartfee", 0, "nblocks" }, - { "estimaterawfee", 0, "nblocks" }, + { "estimatesmartfee", 0, "conf_target" }, + { "estimaterawfee", 0, "conf_target" }, { "estimaterawfee", 1, "threshold" }, { "prioritisetransaction", 1, "dummy" }, { "prioritisetransaction", 2, "fee_delta" }, @@ -129,6 +129,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "logging", 0, "include" }, { "logging", 1, "exclude" }, { "disconnectnode", 1, "nodeid" }, + { "addwitnessaddress", 1, "p2sh" }, // Echo with conversion (For testing only) { "echojson", 0, "arg0" }, { "echojson", 1, "arg1" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 2692e59155..f79439f038 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -176,12 +176,13 @@ UniValue generatetoaddress(const JSONRPCRequest& request) nMaxTries = request.params[2].get_int(); } - CBitcoinAddress address(request.params[1].get_str()); - if (!address.IsValid()) + CTxDestination destination = DecodeDestination(request.params[1].get_str()); + if (!IsValidDestination(destination)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Error: Invalid address"); + } std::shared_ptr<CReserveScript> coinbaseScript = std::make_shared<CReserveScript>(); - coinbaseScript->reserveScript = GetScriptForDestination(address.Get()); + coinbaseScript->reserveScript = GetScriptForDestination(destination); return generateBlocks(coinbaseScript, nGenerate, nMaxTries, false); } @@ -195,14 +196,14 @@ UniValue getmininginfo(const JSONRPCRequest& request) "\nResult:\n" "{\n" " \"blocks\": nnn, (numeric) The current block\n" - " \"currentblocksize\": nnn, (numeric) The last block size\n" " \"currentblockweight\": nnn, (numeric) The last block weight\n" " \"currentblocktx\": nnn, (numeric) The last block transaction\n" " \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n" - " \"errors\": \"...\" (string) Current errors\n" " \"networkhashps\": nnn, (numeric) The network hashes per second\n" " \"pooledtx\": n (numeric) The size of the mempool\n" " \"chain\": \"xxxx\", (string) current network name as defined in BIP70 (main, test, regtest)\n" + " \"warnings\": \"...\" (string) any network and blockchain warnings\n" + " \"errors\": \"...\" (string) DEPRECATED. Same as warnings. Only shown when bitcoind is started with -deprecatedrpc=getmininginfo\n" "}\n" "\nExamples:\n" + HelpExampleCli("getmininginfo", "") @@ -214,14 +215,17 @@ UniValue getmininginfo(const JSONRPCRequest& request) UniValue obj(UniValue::VOBJ); obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("currentblocksize", (uint64_t)nLastBlockSize)); obj.push_back(Pair("currentblockweight", (uint64_t)nLastBlockWeight)); obj.push_back(Pair("currentblocktx", (uint64_t)nLastBlockTx)); obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); obj.push_back(Pair("networkhashps", getnetworkhashps(request))); obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); obj.push_back(Pair("chain", Params().NetworkIDString())); + if (IsDeprecatedRPCEnabled("getmininginfo")) { + obj.push_back(Pair("errors", GetWarnings("statusbar"))); + } else { + obj.push_back(Pair("warnings", GetWarnings("statusbar"))); + } return obj; } @@ -336,7 +340,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request) " n (numeric) transactions before this one (by 1-based index in 'transactions' list) that must be present in the final block if this one is\n" " ,...\n" " ],\n" - " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in Satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" + " \"fee\": n, (numeric) difference in value between transaction inputs and outputs (in satoshis); for coinbase transactions, this is a negative Number of the total collected block fees (ie, not including the block subsidy); if key is not present, fee is unknown and clients MUST NOT assume there isn't one\n" " \"sigops\" : n, (numeric) total SigOps cost, as counted for purposes of block limits; if key is not present, sigop cost is unknown and clients MUST NOT assume it is zero\n" " \"weight\" : n, (numeric) total transaction weight, as counted for purposes of block limits\n" " \"required\" : true|false (boolean) if provided and true, this transaction must be in the final block\n" @@ -346,7 +350,7 @@ UniValue getblocktemplate(const JSONRPCRequest& request) " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n" " \"flags\" : \"xx\" (string) key name is to be ignored, and value included in scriptSig\n" " },\n" - " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in Satoshis)\n" + " \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)\n" " \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n" " \"target\" : \"xxxx\", (string) The hash target\n" " \"mintime\" : xxx, (numeric) The minimum timestamp appropriate for next block time in seconds since epoch (Jan 1 1970 GMT)\n" @@ -790,6 +794,12 @@ UniValue estimatefee(const JSONRPCRequest& request) + HelpExampleCli("estimatefee", "6") ); + if (!IsDeprecatedRPCEnabled("estimatefee")) { + throw JSONRPCError(RPC_METHOD_DEPRECATED, "estimatefee is deprecated and will be fully removed in v0.17. " + "To use estimatefee in v0.16, restart bitcoind with -deprecatedrpc=estimatefee.\n" + "Projects should transition to using estimatesmartfee before upgrading to v0.17"); + } + RPCTypeCheck(request.params, {UniValue::VNUM}); int nBlocks = request.params[0].get_int(); @@ -825,7 +835,7 @@ UniValue estimatesmartfee(const JSONRPCRequest& request) " \"CONSERVATIVE\"\n" "\nResult:\n" "{\n" - " \"feerate\" : x.x, (numeric, optional) estimate fee-per-kilobyte (in BTC)\n" + " \"feerate\" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + "/kB\n" " \"errors\": [ str... ] (json array of strings, optional) Errors encountered during processing\n" " \"blocks\" : n (numeric) block number where estimate was found\n" "}\n" @@ -884,7 +894,7 @@ UniValue estimaterawfee(const JSONRPCRequest& request) "\nResult:\n" "{\n" " \"short\" : { (json object, optional) estimate for short time horizon\n" - " \"feerate\" : x.x, (numeric, optional) estimate fee-per-kilobyte (in BTC)\n" + " \"feerate\" : x.x, (numeric, optional) estimate fee rate in " + CURRENCY_UNIT + "/kB\n" " \"decay\" : x.x, (numeric) exponential decay (per block) for historical moving average of confirmation data\n" " \"scale\" : x, (numeric) The resolution of confirmation targets at this time horizon\n" " \"pass\" : { (json object, optional) information about the lowest range of feerates to succeed in meeting the threshold\n" @@ -967,20 +977,21 @@ UniValue estimaterawfee(const JSONRPCRequest& request) } static const CRPCCommand commands[] = -{ // category name actor (function) okSafeMode +{ // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- - { "mining", "getnetworkhashps", &getnetworkhashps, true, {"nblocks","height"} }, - { "mining", "getmininginfo", &getmininginfo, true, {} }, - { "mining", "prioritisetransaction", &prioritisetransaction, true, {"txid","dummy","fee_delta"} }, - { "mining", "getblocktemplate", &getblocktemplate, true, {"template_request"} }, - { "mining", "submitblock", &submitblock, true, {"hexdata","dummy"} }, + { "mining", "getnetworkhashps", &getnetworkhashps, {"nblocks","height"} }, + { "mining", "getmininginfo", &getmininginfo, {} }, + { "mining", "prioritisetransaction", &prioritisetransaction, {"txid","dummy","fee_delta"} }, + { "mining", "getblocktemplate", &getblocktemplate, {"template_request"} }, + { "mining", "submitblock", &submitblock, {"hexdata","dummy"} }, + - { "generating", "generatetoaddress", &generatetoaddress, true, {"nblocks","address","maxtries"} }, + { "generating", "generatetoaddress", &generatetoaddress, {"nblocks","address","maxtries"} }, - { "util", "estimatefee", &estimatefee, true, {"nblocks"} }, - { "util", "estimatesmartfee", &estimatesmartfee, true, {"conf_target", "estimate_mode"} }, + { "util", "estimatefee", &estimatefee, {"nblocks"} }, + { "util", "estimatesmartfee", &estimatesmartfee, {"conf_target", "estimate_mode"} }, - { "hidden", "estimaterawfee", &estimaterawfee, true, {"conf_target", "threshold"} }, + { "hidden", "estimaterawfee", &estimaterawfee, {"conf_target", "threshold"} }, }; void RegisterMiningRPCCommands(CRPCTable &t) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index a6af24f7e1..521b49e2a7 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -7,6 +7,7 @@ #include "chain.h" #include "clientversion.h" #include "core_io.h" +#include "crypto/ripemd160.h" #include "init.h" #include "validation.h" #include "httpserver.h" @@ -31,94 +32,6 @@ #include <univalue.h> -/** - * @note Do not add or change anything in the information returned by this - * method. `getinfo` exists for backwards-compatibility only. It combines - * information from wildly different sources in the program, which is a mess, - * and is thus planned to be deprecated eventually. - * - * Based on the source of the information, new information should be added to: - * - `getblockchaininfo`, - * - `getnetworkinfo` or - * - `getwalletinfo` - * - * Or alternatively, create a specific query method for the information. - **/ -UniValue getinfo(const JSONRPCRequest& request) -{ - if (request.fHelp || request.params.size() != 0) - throw std::runtime_error( - "getinfo\n" - "\nDEPRECATED. Returns an object containing various state info.\n" - "\nResult:\n" - "{\n" - " \"deprecation-warning\": \"...\" (string) warning that the getinfo command is deprecated and will be removed in 0.16\n" - " \"version\": xxxxx, (numeric) the server version\n" - " \"protocolversion\": xxxxx, (numeric) the protocol version\n" - " \"walletversion\": xxxxx, (numeric) the wallet version\n" - " \"balance\": xxxxxxx, (numeric) the total bitcoin balance of the wallet\n" - " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" - " \"timeoffset\": xxxxx, (numeric) the time offset\n" - " \"connections\": xxxxx, (numeric) the number of connections\n" - " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n" - " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" - " \"testnet\": true|false, (boolean) if the server is using testnet or not\n" - " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since Unix epoch) of the oldest pre-generated key in the key pool\n" - " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" - " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" - " \"paytxfee\": x.xxxx, (numeric) the transaction fee set in " + CURRENCY_UNIT + "/kB\n" - " \"relayfee\": x.xxxx, (numeric) minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB\n" - " \"errors\": \"...\" (string) any error messages\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("getinfo", "") - + HelpExampleRpc("getinfo", "") - ); - -#ifdef ENABLE_WALLET - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); - - LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr); -#else - LOCK(cs_main); -#endif - - proxyType proxy; - GetProxy(NET_IPV4, proxy); - - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("deprecation-warning", "WARNING: getinfo is deprecated and will be fully removed in 0.16." - " Projects should transition to using getblockchaininfo, getnetworkinfo, and getwalletinfo before upgrading to 0.16")); - obj.push_back(Pair("version", CLIENT_VERSION)); - obj.push_back(Pair("protocolversion", PROTOCOL_VERSION)); -#ifdef ENABLE_WALLET - if (pwallet) { - obj.push_back(Pair("walletversion", pwallet->GetVersion())); - obj.push_back(Pair("balance", ValueFromAmount(pwallet->GetBalance()))); - } -#endif - obj.push_back(Pair("blocks", (int)chainActive.Height())); - obj.push_back(Pair("timeoffset", GetTimeOffset())); - if(g_connman) - obj.push_back(Pair("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL))); - obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : std::string()))); - obj.push_back(Pair("difficulty", (double)GetDifficulty())); - obj.push_back(Pair("testnet", Params().NetworkIDString() == CBaseChainParams::TESTNET)); -#ifdef ENABLE_WALLET - if (pwallet) { - obj.push_back(Pair("keypoololdest", pwallet->GetOldestKeyPoolTime())); - obj.push_back(Pair("keypoolsize", (int)pwallet->GetKeyPoolSize())); - } - if (pwallet && pwallet->IsCrypted()) { - obj.push_back(Pair("unlocked_until", pwallet->nRelockTime)); - } - obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); -#endif - obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); - obj.push_back(Pair("errors", GetWarnings("statusbar"))); - return obj; -} - #ifdef ENABLE_WALLET class DescribeAddressVisitor : public boost::static_visitor<UniValue> { @@ -133,6 +46,7 @@ public: UniValue obj(UniValue::VOBJ); CPubKey vchPubKey; obj.push_back(Pair("isscript", false)); + obj.push_back(Pair("iswitness", false)); if (pwallet && pwallet->GetPubKey(keyID, vchPubKey)) { obj.push_back(Pair("pubkey", HexStr(vchPubKey))); obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); @@ -144,6 +58,7 @@ public: UniValue obj(UniValue::VOBJ); CScript subscript; obj.push_back(Pair("isscript", true)); + obj.push_back(Pair("iswitness", false)); if (pwallet && pwallet->GetCScript(scriptID, subscript)) { std::vector<CTxDestination> addresses; txnouttype whichType; @@ -152,14 +67,56 @@ public: obj.push_back(Pair("script", GetTxnOutputType(whichType))); obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); UniValue a(UniValue::VARR); - for (const CTxDestination& addr : addresses) - a.push_back(CBitcoinAddress(addr).ToString()); + for (const CTxDestination& addr : addresses) { + a.push_back(EncodeDestination(addr)); + } obj.push_back(Pair("addresses", a)); if (whichType == TX_MULTISIG) obj.push_back(Pair("sigsrequired", nRequired)); } return obj; } + + UniValue operator()(const WitnessV0KeyHash& id) const + { + UniValue obj(UniValue::VOBJ); + CPubKey pubkey; + obj.push_back(Pair("isscript", false)); + obj.push_back(Pair("iswitness", true)); + obj.push_back(Pair("witness_version", 0)); + obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end()))); + if (pwallet && pwallet->GetPubKey(CKeyID(id), pubkey)) { + obj.push_back(Pair("pubkey", HexStr(pubkey))); + } + return obj; + } + + UniValue operator()(const WitnessV0ScriptHash& id) const + { + UniValue obj(UniValue::VOBJ); + CScript subscript; + obj.push_back(Pair("isscript", true)); + obj.push_back(Pair("iswitness", true)); + obj.push_back(Pair("witness_version", 0)); + obj.push_back(Pair("witness_program", HexStr(id.begin(), id.end()))); + CRIPEMD160 hasher; + uint160 hash; + hasher.Write(id.begin(), 32).Finalize(hash.begin()); + if (pwallet && pwallet->GetCScript(CScriptID(hash), subscript)) { + obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); + } + return obj; + } + + UniValue operator()(const WitnessUnknown& id) const + { + UniValue obj(UniValue::VOBJ); + CScript subscript; + obj.push_back(Pair("iswitness", true)); + obj.push_back(Pair("witness_version", (int)id.version)); + obj.push_back(Pair("witness_program", HexStr(id.program, id.program + id.length))); + return obj; + } }; #endif @@ -207,15 +164,14 @@ UniValue validateaddress(const JSONRPCRequest& request) LOCK(cs_main); #endif - CBitcoinAddress address(request.params[0].get_str()); - bool isValid = address.IsValid(); + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + bool isValid = IsValidDestination(dest); UniValue ret(UniValue::VOBJ); ret.push_back(Pair("isvalid", isValid)); if (isValid) { - CTxDestination dest = address.Get(); - std::string currentAddress = address.ToString(); + std::string currentAddress = EncodeDestination(dest); ret.push_back(Pair("address", currentAddress)); CScript scriptPubKey = GetScriptForDestination(dest); @@ -230,10 +186,10 @@ UniValue validateaddress(const JSONRPCRequest& request) if (pwallet && pwallet->mapAddressBook.count(dest)) { ret.push_back(Pair("account", pwallet->mapAddressBook[dest].name)); } - CKeyID keyID; if (pwallet) { const auto& meta = pwallet->mapKeyMetadata; - auto it = address.GetKeyID(keyID) ? meta.find(keyID) : meta.end(); + const CKeyID *keyID = boost::get<CKeyID>(&dest); + auto it = keyID ? meta.find(*keyID) : meta.end(); if (it == meta.end()) { it = meta.find(CScriptID(scriptPubKey)); } @@ -277,16 +233,15 @@ CScript _createmultisig_redeemScript(CWallet * const pwallet, const UniValue& pa const std::string& ks = keys[i].get_str(); #ifdef ENABLE_WALLET // Case 1: Bitcoin address and we have full public key: - CBitcoinAddress address(ks); - if (pwallet && address.IsValid()) { - CKeyID keyID; - if (!address.GetKeyID(keyID)) - throw std::runtime_error( - strprintf("%s does not refer to a key",ks)); + CTxDestination dest = DecodeDestination(ks); + if (pwallet && IsValidDestination(dest)) { + const CKeyID *keyID = boost::get<CKeyID>(&dest); + if (!keyID) { + throw std::runtime_error(strprintf("%s does not refer to a key", ks)); + } CPubKey vchPubKey; - if (!pwallet->GetPubKey(keyID, vchPubKey)) { - throw std::runtime_error( - strprintf("no full public key for address %s",ks)); + if (!pwallet->GetPubKey(*keyID, vchPubKey)) { + throw std::runtime_error(strprintf("no full public key for address %s", ks)); } if (!vchPubKey.IsFullyValid()) throw std::runtime_error(" Invalid public key: "+ks); @@ -357,10 +312,9 @@ UniValue createmultisig(const JSONRPCRequest& request) // Construct using pay-to-script-hash: CScript inner = _createmultisig_redeemScript(pwallet, request.params); CScriptID innerID(inner); - CBitcoinAddress address(innerID); UniValue result(UniValue::VOBJ); - result.push_back(Pair("address", address.ToString())); + result.push_back(Pair("address", EncodeDestination(innerID))); result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); return result; @@ -395,13 +349,15 @@ UniValue verifymessage(const JSONRPCRequest& request) std::string strSign = request.params[1].get_str(); std::string strMessage = request.params[2].get_str(); - CBitcoinAddress addr(strAddress); - if (!addr.IsValid()) + CTxDestination destination = DecodeDestination(strAddress); + if (!IsValidDestination(destination)) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + } - CKeyID keyID; - if (!addr.GetKeyID(keyID)) + const CKeyID *keyID = boost::get<CKeyID>(&destination); + if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + } bool fInvalid = false; std::vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid); @@ -417,7 +373,7 @@ UniValue verifymessage(const JSONRPCRequest& request) if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) return false; - return (pubkey.GetID() == keyID); + return (pubkey.GetID() == *keyID); } UniValue signmessagewithprivkey(const JSONRPCRequest& request) @@ -459,7 +415,7 @@ UniValue signmessagewithprivkey(const JSONRPCRequest& request) if (!key.SignCompact(ss.GetHash(), vchSig)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); - return EncodeBase64(&vchSig[0], vchSig.size()); + return EncodeBase64(vchSig.data(), vchSig.size()); } UniValue setmocktime(const JSONRPCRequest& request) @@ -649,20 +605,19 @@ UniValue echo(const JSONRPCRequest& request) } static const CRPCCommand commands[] = -{ // category name actor (function) okSafeMode +{ // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- - { "control", "getinfo", &getinfo, true, {} }, /* uses wallet if enabled */ - { "control", "getmemoryinfo", &getmemoryinfo, true, {"mode"} }, - { "util", "validateaddress", &validateaddress, true, {"address"} }, /* uses wallet if enabled */ - { "util", "createmultisig", &createmultisig, true, {"nrequired","keys"} }, - { "util", "verifymessage", &verifymessage, true, {"address","signature","message"} }, - { "util", "signmessagewithprivkey", &signmessagewithprivkey, true, {"privkey","message"} }, + { "control", "getmemoryinfo", &getmemoryinfo, {"mode"} }, + { "util", "validateaddress", &validateaddress, {"address"} }, /* uses wallet if enabled */ + { "util", "createmultisig", &createmultisig, {"nrequired","keys"} }, + { "util", "verifymessage", &verifymessage, {"address","signature","message"} }, + { "util", "signmessagewithprivkey", &signmessagewithprivkey, {"privkey","message"} }, /* Not shown in help */ - { "hidden", "setmocktime", &setmocktime, true, {"timestamp"}}, - { "hidden", "echo", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, - { "hidden", "echojson", &echo, true, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, - { "hidden", "logging", &logging, true, {"include", "exclude"}}, + { "hidden", "setmocktime", &setmocktime, {"timestamp"}}, + { "hidden", "echo", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, + { "hidden", "echojson", &echo, {"arg0","arg1","arg2","arg3","arg4","arg5","arg6","arg7","arg8","arg9"}}, + { "hidden", "logging", &logging, {"include", "exclude"}}, }; void RegisterMiscRPCCommands(CRPCTable &t) diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index f19b968244..a3d3df26a3 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -146,7 +146,7 @@ UniValue getpeerinfo(const JSONRPCRequest& request) obj.push_back(Pair("timeoffset", stats.nTimeOffset)); if (stats.dPingTime > 0.0) obj.push_back(Pair("pingtime", stats.dPingTime)); - if (stats.dMinPing < std::numeric_limits<int64_t>::max()/1e6) + if (stats.dMinPing < static_cast<double>(std::numeric_limits<int64_t>::max())/1e6) obj.push_back(Pair("minping", stats.dMinPing)); if (stats.dPingWait > 0.0) obj.push_back(Pair("pingwait", stats.dPingWait)); @@ -447,7 +447,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request) " }\n" " ,...\n" " ]\n" - " \"warnings\": \"...\" (string) any network warnings\n" + " \"warnings\": \"...\" (string) any network and blockchain warnings\n" "}\n" "\nExamples:\n" + HelpExampleCli("getnetworkinfo", "") @@ -623,20 +623,20 @@ UniValue setnetworkactive(const JSONRPCRequest& request) } static const CRPCCommand commands[] = -{ // category name actor (function) okSafeMode +{ // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- - { "network", "getconnectioncount", &getconnectioncount, true, {} }, - { "network", "ping", &ping, true, {} }, - { "network", "getpeerinfo", &getpeerinfo, true, {} }, - { "network", "addnode", &addnode, true, {"node","command"} }, - { "network", "disconnectnode", &disconnectnode, true, {"address", "nodeid"} }, - { "network", "getaddednodeinfo", &getaddednodeinfo, true, {"node"} }, - { "network", "getnettotals", &getnettotals, true, {} }, - { "network", "getnetworkinfo", &getnetworkinfo, true, {} }, - { "network", "setban", &setban, true, {"subnet", "command", "bantime", "absolute"} }, - { "network", "listbanned", &listbanned, true, {} }, - { "network", "clearbanned", &clearbanned, true, {} }, - { "network", "setnetworkactive", &setnetworkactive, true, {"state"} }, + { "network", "getconnectioncount", &getconnectioncount, {} }, + { "network", "ping", &ping, {} }, + { "network", "getpeerinfo", &getpeerinfo, {} }, + { "network", "addnode", &addnode, {"node","command"} }, + { "network", "disconnectnode", &disconnectnode, {"address", "nodeid"} }, + { "network", "getaddednodeinfo", &getaddednodeinfo, {"node"} }, + { "network", "getnettotals", &getnettotals, {} }, + { "network", "getnetworkinfo", &getnetworkinfo, {} }, + { "network", "setban", &setban, {"subnet", "command", "bantime", "absolute"} }, + { "network", "listbanned", &listbanned, {} }, + { "network", "clearbanned", &clearbanned, {} }, + { "network", "setnetworkactive", &setnetworkactive, {"state"} }, }; void RegisterNetRPCCommands(CRPCTable &t) diff --git a/src/rpc/protocol.cpp b/src/rpc/protocol.cpp index db0626b5e1..1f4ae75b18 100644 --- a/src/rpc/protocol.cpp +++ b/src/rpc/protocol.cpp @@ -19,7 +19,7 @@ * JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility, * but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were * unspecified (HTTP errors and contents of 'error'). - * + * * 1.0 spec: http://json-rpc.org/wiki/specification * 1.2 spec: http://jsonrpc.org/historical/json-rpc-over-http.html */ @@ -66,9 +66,14 @@ static const std::string COOKIEAUTH_USER = "__cookie__"; /** Default name for auth cookie file */ static const std::string COOKIEAUTH_FILE = ".cookie"; -fs::path GetAuthCookieFile() +/** Get name of RPC authentication cookie file */ +static fs::path GetAuthCookieFile(bool temp=false) { - fs::path path(gArgs.GetArg("-rpccookiefile", COOKIEAUTH_FILE)); + std::string arg = gArgs.GetArg("-rpccookiefile", COOKIEAUTH_FILE); + if (temp) { + arg += ".tmp"; + } + fs::path path(arg); if (!path.is_complete()) path = GetDataDir() / path; return path; } @@ -84,14 +89,20 @@ bool GenerateAuthCookie(std::string *cookie_out) * these are set to 077 in init.cpp unless overridden with -sysperms. */ std::ofstream file; - fs::path filepath = GetAuthCookieFile(); - file.open(filepath.string().c_str()); + fs::path filepath_tmp = GetAuthCookieFile(true); + file.open(filepath_tmp.string().c_str()); if (!file.is_open()) { - LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath.string()); + LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath_tmp.string()); return false; } file << cookie; file.close(); + + fs::path filepath = GetAuthCookieFile(false); + if (!RenameOver(filepath_tmp, filepath)) { + LogPrintf("Unable to rename cookie authentication file %s to %s\n", filepath_tmp.string(), filepath.string()); + return false; + } LogPrintf("Generated RPC authentication cookie %s\n", filepath.string()); if (cookie_out) @@ -124,3 +135,22 @@ void DeleteAuthCookie() } } +std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue &in, size_t num) +{ + if (!in.isArray()) { + throw std::runtime_error("Batch must be an array"); + } + std::vector<UniValue> batch(num); + for (size_t i=0; i<in.size(); ++i) { + const UniValue &rec = in[i]; + if (!rec.isObject()) { + throw std::runtime_error("Batch member must be object"); + } + size_t id = rec["id"].get_int(); + if (id >= num) { + throw std::runtime_error("Batch member id larger than size"); + } + batch[id] = rec; + } + return batch; +} diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h index 4bd4702d62..cb668f3db9 100644 --- a/src/rpc/protocol.h +++ b/src/rpc/protocol.h @@ -57,6 +57,7 @@ enum RPCErrorCode RPC_VERIFY_REJECTED = -26, //!< Transaction or block was rejected by network rules RPC_VERIFY_ALREADY_IN_CHAIN = -27, //!< Transaction already in chain RPC_IN_WARMUP = -28, //!< Client still warming up + RPC_METHOD_DEPRECATED = -32, //!< RPC method is deprecated //! Aliases for backward compatibility RPC_TRANSACTION_ERROR = RPC_VERIFY_ERROR, @@ -91,13 +92,13 @@ UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const Un std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id); UniValue JSONRPCError(int code, const std::string& message); -/** Get name of RPC authentication cookie file */ -fs::path GetAuthCookieFile(); /** Generate a new RPC authentication cookie and write it to disk */ bool GenerateAuthCookie(std::string *cookie_out); /** Read the RPC authentication cookie from disk */ bool GetAuthCookie(std::string *cookie_out); /** Delete RPC authentication cookie from disk */ void DeleteAuthCookie(); +/** Parse JSON-RPC batch reply into a vector */ +std::vector<UniValue> JSONRPCProcessBatchReply(const UniValue &in, size_t num); #endif // BITCOIN_RPCPROTOCOL_H diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 934576a391..d860dbc244 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -16,6 +16,7 @@ #include "policy/policy.h" #include "policy/rbf.h" #include "primitives/transaction.h" +#include "rpc/safemode.h" #include "rpc/server.h" #include "script/script.h" #include "script/script_error.h" @@ -383,7 +384,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request) rawTx.vin.push_back(in); } - std::set<CBitcoinAddress> setAddress; + std::set<CTxDestination> destinations; std::vector<std::string> addrList = sendTo.getKeys(); for (const std::string& name_ : addrList) { @@ -393,15 +394,16 @@ UniValue createrawtransaction(const JSONRPCRequest& request) CTxOut out(0, CScript() << OP_RETURN << data); rawTx.vout.push_back(out); } else { - CBitcoinAddress address(name_); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ")+name_); + CTxDestination destination = DecodeDestination(name_); + if (!IsValidDestination(destination)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_); + } - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ")+name_); - setAddress.insert(address); + if (!destinations.insert(destination).second) { + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); + } - CScript scriptPubKey = GetScriptForDestination(address.Get()); + CScript scriptPubKey = GetScriptForDestination(destination); CAmount nAmount = AmountFromValue(sendTo[name_]); CTxOut out(nAmount, scriptPubKey); @@ -528,7 +530,7 @@ UniValue decodescript(const JSONRPCRequest& request) if (type.isStr() && type.get_str() != "scripthash") { // P2SH cannot be wrapped in a P2SH. If this script is already a P2SH, // don't return the address for a P2SH of the P2SH. - r.push_back(Pair("p2sh", CBitcoinAddress(CScriptID(script)).ToString())); + r.push_back(Pair("p2sh", EncodeDestination(CScriptID(script)))); } return r; @@ -703,6 +705,7 @@ UniValue signrawtransaction(const JSONRPCRequest& request) + HelpExampleRpc("signrawtransaction", "\"myhex\"") ); + ObserveSafeMode(); #ifdef ENABLE_WALLET LOCK2(cs_main, pwallet ? &pwallet->cs_wallet : nullptr); #else @@ -870,7 +873,12 @@ UniValue signrawtransaction(const JSONRPCRequest& request) ScriptError serror = SCRIPT_ERR_OK; if (!VerifyScript(txin.scriptSig, prevPubKey, &txin.scriptWitness, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), &serror)) { - TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); + if (serror == SCRIPT_ERR_INVALID_STACK_OPERATION) { + // Unable to sign input and verification failed (possible attempt to partially sign). + TxInErrorToJSON(txin, vErrors, "Unable to sign input, invalid stack size (possibly missing key)"); + } else { + TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); + } } } bool fComplete = vErrors.empty(); @@ -908,6 +916,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) + HelpExampleRpc("sendrawtransaction", "\"signedhex\"") ); + ObserveSafeMode(); LOCK(cs_main); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL}); @@ -933,8 +942,8 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) // push to local node and sync with wallets CValidationState state; bool fMissingInputs; - bool fLimitFree = true; - if (!AcceptToMemoryPool(mempool, state, std::move(tx), fLimitFree, &fMissingInputs, nullptr, false, nMaxRawTxFee)) { + if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs, + nullptr /* plTxnReplaced */, false /* bypass_limits */, nMaxRawTxFee)) { if (state.IsInvalid()) { throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); } else { @@ -959,18 +968,18 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) } static const CRPCCommand commands[] = -{ // category name actor (function) okSafeMode +{ // category name actor (function) argNames // --------------------- ------------------------ ----------------------- ---------- - { "rawtransactions", "getrawtransaction", &getrawtransaction, true, {"txid","verbose"} }, - { "rawtransactions", "createrawtransaction", &createrawtransaction, true, {"inputs","outputs","locktime","replaceable"} }, - { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true, {"hexstring"} }, - { "rawtransactions", "decodescript", &decodescript, true, {"hexstring"} }, - { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, {"hexstring","allowhighfees"} }, - { "rawtransactions", "combinerawtransaction", &combinerawtransaction, true, {"txs"} }, - { "rawtransactions", "signrawtransaction", &signrawtransaction, false, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */ - - { "blockchain", "gettxoutproof", &gettxoutproof, true, {"txids", "blockhash"} }, - { "blockchain", "verifytxoutproof", &verifytxoutproof, true, {"proof"} }, + { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose"} }, + { "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} }, + { "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring"} }, + { "rawtransactions", "decodescript", &decodescript, {"hexstring"} }, + { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} }, + { "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} }, + { "rawtransactions", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} }, /* uses wallet if enabled */ + + { "blockchain", "gettxoutproof", &gettxoutproof, {"txids", "blockhash"} }, + { "blockchain", "verifytxoutproof", &verifytxoutproof, {"proof"} }, }; void RegisterRawTransactionRPCCommands(CRPCTable &t) diff --git a/src/rpc/safemode.cpp b/src/rpc/safemode.cpp new file mode 100644 index 0000000000..24770ad47f --- /dev/null +++ b/src/rpc/safemode.cpp @@ -0,0 +1,14 @@ +#include "safemode.h" + +#include "rpc/protocol.h" +#include "util.h" +#include "warnings.h" + +void ObserveSafeMode() +{ + std::string warning = GetWarnings("rpc"); + if (warning != "" && !gArgs.GetBoolArg("-disablesafemode", DEFAULT_DISABLE_SAFEMODE)) { + throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, std::string("Safe mode: ") + warning); + } +} + diff --git a/src/rpc/safemode.h b/src/rpc/safemode.h new file mode 100644 index 0000000000..8466d6b2f9 --- /dev/null +++ b/src/rpc/safemode.h @@ -0,0 +1,12 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_RPC_SAFEMODE_H +#define BITCOIN_RPC_SAFEMODE_H + +static const bool DEFAULT_DISABLE_SAFEMODE = true; + +void ObserveSafeMode(); + +#endif // BITCOIN_RPC_SAFEMODE_H diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 9ad8d228fa..a73b697e01 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -51,11 +51,6 @@ void RPCServer::OnStopped(std::function<void ()> slot) g_rpcSignals.Stopped.connect(slot); } -void RPCServer::OnPreCommand(std::function<void (const CRPCCommand&)> slot) -{ - g_rpcSignals.PreCommand.connect(boost::bind(slot, _1)); -} - void RPCTypeCheck(const UniValue& params, const std::list<UniValue::VType>& typesExpected, bool fAllowNull) @@ -267,12 +262,12 @@ UniValue uptime(const JSONRPCRequest& jsonRequest) * Call Table */ static const CRPCCommand vRPCCommands[] = -{ // category name actor (function) okSafe argNames - // --------------------- ------------------------ ----------------------- ------ ---------- +{ // category name actor (function) argNames + // --------------------- ------------------------ ----------------------- ---------- /* Overall control/query calls */ - { "control", "help", &help, true, {"command"} }, - { "control", "stop", &stop, true, {} }, - { "control", "uptime", &uptime, true, {} }, + { "control", "help", &help, {"command"} }, + { "control", "stop", &stop, {} }, + { "control", "uptime", &uptime, {} }, }; CRPCTable::CRPCTable() @@ -387,6 +382,13 @@ void JSONRPCRequest::parse(const UniValue& valRequest) throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object"); } +bool IsDeprecatedRPCEnabled(const std::string& method) +{ + const std::vector<std::string> enabled_methods = gArgs.GetArgs("-deprecatedrpc"); + + return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end(); +} + static UniValue JSONRPCExecOne(const UniValue& req) { UniValue rpc_result(UniValue::VOBJ); diff --git a/src/rpc/server.h b/src/rpc/server.h index 89b1d169d5..31d6304271 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -25,7 +25,6 @@ namespace RPCServer { void OnStarted(std::function<void ()> slot); void OnStopped(std::function<void ()> slot); - void OnPreCommand(std::function<void (const CRPCCommand&)> slot); } /** Wrapper for UniValue::VType, which includes typeAny: @@ -134,7 +133,6 @@ public: std::string category; std::string name; rpcfn_type actor; - bool okSafeMode; std::vector<std::string> argNames; }; @@ -173,6 +171,8 @@ public: bool appendCommand(const std::string& name, const CRPCCommand* pcmd); }; +bool IsDeprecatedRPCEnabled(const std::string& method); + extern CRPCTable tableRPC; /** diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index f9716dfc66..2f7b8e3a03 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -1168,29 +1168,36 @@ uint256 GetOutputsHash(const CTransaction& txTo) { PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) { - hashPrevouts = GetPrevoutHash(txTo); - hashSequence = GetSequenceHash(txTo); - hashOutputs = GetOutputsHash(txTo); + // Cache is calculated only for transactions with witness + if (txTo.HasWitness()) { + hashPrevouts = GetPrevoutHash(txTo); + hashSequence = GetSequenceHash(txTo); + hashOutputs = GetOutputsHash(txTo); + ready = true; + } } uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) { + assert(nIn < txTo.vin.size()); + if (sigversion == SIGVERSION_WITNESS_V0) { uint256 hashPrevouts; uint256 hashSequence; uint256 hashOutputs; + const bool cacheready = cache && cache->ready; if (!(nHashType & SIGHASH_ANYONECANPAY)) { - hashPrevouts = cache ? cache->hashPrevouts : GetPrevoutHash(txTo); + hashPrevouts = cacheready ? cache->hashPrevouts : GetPrevoutHash(txTo); } if (!(nHashType & SIGHASH_ANYONECANPAY) && (nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { - hashSequence = cache ? cache->hashSequence : GetSequenceHash(txTo); + hashSequence = cacheready ? cache->hashSequence : GetSequenceHash(txTo); } if ((nHashType & 0x1f) != SIGHASH_SINGLE && (nHashType & 0x1f) != SIGHASH_NONE) { - hashOutputs = cache ? cache->hashOutputs : GetOutputsHash(txTo); + hashOutputs = cacheready ? cache->hashOutputs : GetOutputsHash(txTo); } else if ((nHashType & 0x1f) == SIGHASH_SINGLE && nIn < txTo.vout.size()) { CHashWriter ss(SER_GETHASH, 0); ss << txTo.vout[nIn]; @@ -1221,10 +1228,6 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig } static const uint256 one(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); - if (nIn >= txTo.vin.size()) { - // nIn out of range - return one; - } // Check for invalid use of SIGHASH_SINGLE if ((nHashType & 0x1f) == SIGHASH_SINGLE) { @@ -1366,7 +1369,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, stack = std::vector<std::vector<unsigned char> >(witness.stack.begin(), witness.stack.end() - 1); uint256 hashScriptPubKey; CSHA256().Write(&scriptPubKey[0], scriptPubKey.size()).Finalize(hashScriptPubKey.begin()); - if (memcmp(hashScriptPubKey.begin(), &program[0], 32)) { + if (memcmp(hashScriptPubKey.begin(), program.data(), 32)) { return set_error(serror, SCRIPT_ERR_WITNESS_PROGRAM_MISMATCH); } } else if (program.size() == 20) { diff --git a/src/script/interpreter.h b/src/script/interpreter.h index f845e1943b..1cb9cc7899 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -113,6 +113,7 @@ bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned i struct PrecomputedTransactionData { uint256 hashPrevouts, hashSequence, hashOutputs; + bool ready = false; explicit PrecomputedTransactionData(const CTransaction& tx); }; diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index 0a39619734..6b68f0679e 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -46,6 +46,8 @@ isminetype IsMine(const CKeyStore &keystore, const CTxDestination& dest, bool& i isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& isInvalid, SigVersion sigversion) { + isInvalid = false; + std::vector<valtype> vSolutions; txnouttype whichType; if (!Solver(scriptPubKey, whichType, vSolutions)) { @@ -59,6 +61,7 @@ isminetype IsMine(const CKeyStore &keystore, const CScript& scriptPubKey, bool& { case TX_NONSTANDARD: case TX_NULL_DATA: + case TX_WITNESS_UNKNOWN: break; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); diff --git a/src/script/script.h b/src/script/script.h index 587f2d26eb..2a92060543 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -420,6 +420,7 @@ public: CScript& operator+=(const CScript& b) { + reserve(size() + b.size()); insert(end(), b.begin(), b.end()); return *this; } diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index c9d13c92a8..6c590f53e3 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -73,6 +73,8 @@ const char* ScriptErrorString(const ScriptError serror) return "Witness version reserved for soft-fork upgrades"; case SCRIPT_ERR_PUBKEYTYPE: return "Public key is neither compressed or uncompressed"; + case SCRIPT_ERR_CLEANSTACK: + return "Extra items left on stack after execution"; case SCRIPT_ERR_WITNESS_PROGRAM_WRONG_LENGTH: return "Witness program has incorrect length"; case SCRIPT_ERR_WITNESS_PROGRAM_WITNESS_EMPTY: diff --git a/src/script/sign.cpp b/src/script/sign.cpp index dc50467d3f..ac58b690a2 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -79,6 +79,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP { case TX_NONSTANDARD: case TX_NULL_DATA: + case TX_WITNESS_UNKNOWN: return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); @@ -309,6 +310,7 @@ static Stacks CombineSignatures(const CScript& scriptPubKey, const BaseSignature { case TX_NONSTANDARD: case TX_NULL_DATA: + case TX_WITNESS_UNKNOWN: // Don't know anything about this, assume bigger one is correct: if (sigs1.script.size() >= sigs2.script.size()) return sigs1; diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 2aed393921..f57f1f61b4 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -30,6 +30,7 @@ const char* GetTxnOutputType(txnouttype t) case TX_NULL_DATA: return "nulldata"; case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash"; case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash"; + case TX_WITNESS_UNKNOWN: return "witness_unknown"; } return nullptr; } @@ -75,6 +76,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v vSolutionsRet.push_back(witnessprogram); return true; } + if (witnessversion != 0) { + typeRet = TX_WITNESS_UNKNOWN; + vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion}); + vSolutionsRet.push_back(std::move(witnessprogram)); + return true; + } return false; } @@ -198,6 +205,23 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet) { addressRet = CScriptID(uint160(vSolutions[0])); return true; + } else if (whichType == TX_WITNESS_V0_KEYHASH) { + WitnessV0KeyHash hash; + std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin()); + addressRet = hash; + return true; + } else if (whichType == TX_WITNESS_V0_SCRIPTHASH) { + WitnessV0ScriptHash hash; + std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin()); + addressRet = hash; + return true; + } else if (whichType == TX_WITNESS_UNKNOWN) { + WitnessUnknown unk; + unk.version = vSolutions[0][0]; + std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program); + unk.length = vSolutions[1].size(); + addressRet = unk; + return true; } // Multisig txns have more than one address... return false; @@ -268,6 +292,27 @@ public: *script << OP_HASH160 << ToByteVector(scriptID) << OP_EQUAL; return true; } + + bool operator()(const WitnessV0KeyHash& id) const + { + script->clear(); + *script << OP_0 << ToByteVector(id); + return true; + } + + bool operator()(const WitnessV0ScriptHash& id) const + { + script->clear(); + *script << OP_0 << ToByteVector(id); + return true; + } + + bool operator()(const WitnessUnknown& id) const + { + script->clear(); + *script << CScript::EncodeOP_N(id.version) << std::vector<unsigned char>(id.program, id.program + id.length); + return true; + } }; } // namespace @@ -317,3 +362,7 @@ CScript GetScriptForWitness(const CScript& redeemscript) ret << OP_0 << ToByteVector(hash); return ret; } + +bool IsValidDestination(const CTxDestination& dest) { + return dest.which() != 0; +} diff --git a/src/script/standard.h b/src/script/standard.h index 7619192264..fa07ea88c1 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -64,6 +64,7 @@ enum txnouttype TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data TX_WITNESS_V0_SCRIPTHASH, TX_WITNESS_V0_KEYHASH, + TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above }; class CNoDestination { @@ -72,14 +73,45 @@ public: friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; } }; +struct WitnessV0ScriptHash : public uint256 {}; +struct WitnessV0KeyHash : public uint160 {}; + +//! CTxDestination subtype to encode any future Witness version +struct WitnessUnknown +{ + unsigned int version; + unsigned int length; + unsigned char program[40]; + + friend bool operator==(const WitnessUnknown& w1, const WitnessUnknown& w2) { + if (w1.version != w2.version) return false; + if (w1.length != w2.length) return false; + return std::equal(w1.program, w1.program + w1.length, w2.program); + } + + friend bool operator<(const WitnessUnknown& w1, const WitnessUnknown& w2) { + if (w1.version < w2.version) return true; + if (w1.version > w2.version) return false; + if (w1.length < w2.length) return true; + if (w1.length > w2.length) return false; + return std::lexicographical_compare(w1.program, w1.program + w1.length, w2.program, w2.program + w2.length); + } +}; + /** * A txout script template with a specific destination. It is either: * * CNoDestination: no destination set - * * CKeyID: TX_PUBKEYHASH destination - * * CScriptID: TX_SCRIPTHASH destination - * A CTxDestination is the internal data type encoded in a CBitcoinAddress + * * CKeyID: TX_PUBKEYHASH destination (P2PKH) + * * CScriptID: TX_SCRIPTHASH destination (P2SH) + * * WitnessV0ScriptHash: TX_WITNESS_V0_SCRIPTHASH destination (P2WSH) + * * WitnessV0KeyHash: TX_WITNESS_V0_KEYHASH destination (P2WPKH) + * * WitnessUnknown: TX_WITNESS_UNKNOWN destination (P2W???) + * A CTxDestination is the internal data type encoded in a bitcoin address */ -typedef boost::variant<CNoDestination, CKeyID, CScriptID> CTxDestination; +typedef boost::variant<CNoDestination, CKeyID, CScriptID, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination; + +/** Check whether a CTxDestination is a CNoDestination. */ +bool IsValidDestination(const CTxDestination& dest); /** Get the name of a txnouttype as a C string, or nullptr if unknown. */ const char* GetTxnOutputType(txnouttype t); @@ -101,7 +133,7 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v * Parse a standard scriptPubKey for the destination address. Assigns result to * the addressRet parameter and returns true if successful. For multisig * scripts, instead use ExtractDestinations. Currently only works for P2PK, - * P2PKH, and P2SH scripts. + * P2PKH, P2SH, P2WPKH, and P2WSH scripts. */ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet); diff --git a/src/secp256k1/contrib/lax_der_parsing.h b/src/secp256k1/contrib/lax_der_parsing.h index 6d27871a7c..7eaf63bf6a 100644 --- a/src/secp256k1/contrib/lax_der_parsing.h +++ b/src/secp256k1/contrib/lax_der_parsing.h @@ -48,14 +48,14 @@ * 8.3.1. */ -#ifndef _SECP256K1_CONTRIB_LAX_DER_PARSING_H_ -#define _SECP256K1_CONTRIB_LAX_DER_PARSING_H_ +#ifndef SECP256K1_CONTRIB_LAX_DER_PARSING_H +#define SECP256K1_CONTRIB_LAX_DER_PARSING_H #include <secp256k1.h> -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif /** Parse a signature in "lax DER" format * @@ -88,4 +88,4 @@ int ecdsa_signature_parse_der_lax( } #endif -#endif +#endif /* SECP256K1_CONTRIB_LAX_DER_PARSING_H */ diff --git a/src/secp256k1/contrib/lax_der_privatekey_parsing.h b/src/secp256k1/contrib/lax_der_privatekey_parsing.h index 2fd088f8ab..fece261fb9 100644 --- a/src/secp256k1/contrib/lax_der_privatekey_parsing.h +++ b/src/secp256k1/contrib/lax_der_privatekey_parsing.h @@ -25,14 +25,14 @@ * library are sufficient. */ -#ifndef _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_ -#define _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_ +#ifndef SECP256K1_CONTRIB_BER_PRIVATEKEY_H +#define SECP256K1_CONTRIB_BER_PRIVATEKEY_H #include <secp256k1.h> -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif /** Export a private key in DER format. * @@ -87,4 +87,4 @@ SECP256K1_WARN_UNUSED_RESULT int ec_privkey_import_der( } #endif -#endif +#endif /* SECP256K1_CONTRIB_BER_PRIVATEKEY_H */ diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h index fc4c5cefbb..3e9c098d19 100644 --- a/src/secp256k1/include/secp256k1.h +++ b/src/secp256k1/include/secp256k1.h @@ -1,9 +1,9 @@ -#ifndef _SECP256K1_ -# define _SECP256K1_ +#ifndef SECP256K1_H +#define SECP256K1_H -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif #include <stddef.h> @@ -61,7 +61,7 @@ typedef struct { * however guaranteed to be 64 bytes in size, and can be safely copied/moved. * If you need to convert to a format suitable for storage, transmission, or * comparison, use the secp256k1_ecdsa_signature_serialize_* and - * secp256k1_ecdsa_signature_serialize_* functions. + * secp256k1_ecdsa_signature_parse_* functions. */ typedef struct { unsigned char data[64]; @@ -159,6 +159,13 @@ typedef int (*secp256k1_nonce_function)( #define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION) #define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION) +/** Prefix byte used to tag various encoded curvepoints for specific purposes */ +#define SECP256K1_TAG_PUBKEY_EVEN 0x02 +#define SECP256K1_TAG_PUBKEY_ODD 0x03 +#define SECP256K1_TAG_PUBKEY_UNCOMPRESSED 0x04 +#define SECP256K1_TAG_PUBKEY_HYBRID_EVEN 0x06 +#define SECP256K1_TAG_PUBKEY_HYBRID_ODD 0x07 + /** Create a secp256k1 context object. * * Returns: a newly created context object. @@ -607,8 +614,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine( size_t n ) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); -# ifdef __cplusplus +#ifdef __cplusplus } -# endif - #endif + +#endif /* SECP256K1_H */ diff --git a/src/secp256k1/include/secp256k1_ecdh.h b/src/secp256k1/include/secp256k1_ecdh.h index 4b84d7a963..88492dc1a4 100644 --- a/src/secp256k1/include/secp256k1_ecdh.h +++ b/src/secp256k1/include/secp256k1_ecdh.h @@ -1,11 +1,11 @@ -#ifndef _SECP256K1_ECDH_ -# define _SECP256K1_ECDH_ +#ifndef SECP256K1_ECDH_H +#define SECP256K1_ECDH_H -# include "secp256k1.h" +#include "secp256k1.h" -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif /** Compute an EC Diffie-Hellman secret in constant time * Returns: 1: exponentiation was successful @@ -24,8 +24,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh( const unsigned char *privkey ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); -# ifdef __cplusplus +#ifdef __cplusplus } -# endif - #endif + +#endif /* SECP256K1_ECDH_H */ diff --git a/src/secp256k1/include/secp256k1_recovery.h b/src/secp256k1/include/secp256k1_recovery.h index 0553797253..cf6c5ed7f5 100644 --- a/src/secp256k1/include/secp256k1_recovery.h +++ b/src/secp256k1/include/secp256k1_recovery.h @@ -1,11 +1,11 @@ -#ifndef _SECP256K1_RECOVERY_ -# define _SECP256K1_RECOVERY_ +#ifndef SECP256K1_RECOVERY_H +#define SECP256K1_RECOVERY_H -# include "secp256k1.h" +#include "secp256k1.h" -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif /** Opaque data structured that holds a parsed ECDSA signature, * supporting pubkey recovery. @@ -103,8 +103,8 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover( const unsigned char *msg32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); -# ifdef __cplusplus +#ifdef __cplusplus } -# endif - #endif + +#endif /* SECP256K1_RECOVERY_H */ diff --git a/src/secp256k1/sage/group_prover.sage b/src/secp256k1/sage/group_prover.sage index 5198724bea..8521f07999 100644 --- a/src/secp256k1/sage/group_prover.sage +++ b/src/secp256k1/sage/group_prover.sage @@ -17,7 +17,7 @@ # - A constraint describing the requirements of the law, called "require" # * Implementations are transliterated into functions that operate as well on # algebraic input points, and are called once per combination of branches -# exectured. Each execution returns: +# executed. Each execution returns: # - A constraint describing the assumptions this implementation requires # (such as Z1=1), called "assumeFormula" # - A constraint describing the assumptions this specific branch requires, diff --git a/src/secp256k1/src/asm/field_10x26_arm.s b/src/secp256k1/src/asm/field_10x26_arm.s index bd2b629e1c..5a9cc3ffcf 100644 --- a/src/secp256k1/src/asm/field_10x26_arm.s +++ b/src/secp256k1/src/asm/field_10x26_arm.s @@ -23,7 +23,7 @@ Note: .eabi_attribute 10, 0 @ Tag_FP_arch = none .eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte .eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP - .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Agressive Speed + .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Aggressive Speed .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6 .text diff --git a/src/secp256k1/src/basic-config.h b/src/secp256k1/src/basic-config.h index c4c16eb7ca..fc588061ca 100644 --- a/src/secp256k1/src/basic-config.h +++ b/src/secp256k1/src/basic-config.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_BASIC_CONFIG_ -#define _SECP256K1_BASIC_CONFIG_ +#ifndef SECP256K1_BASIC_CONFIG_H +#define SECP256K1_BASIC_CONFIG_H #ifdef USE_BASIC_CONFIG @@ -28,5 +28,6 @@ #define USE_FIELD_10X26 1 #define USE_SCALAR_8X32 1 -#endif // USE_BASIC_CONFIG -#endif // _SECP256K1_BASIC_CONFIG_ +#endif /* USE_BASIC_CONFIG */ + +#endif /* SECP256K1_BASIC_CONFIG_H */ diff --git a/src/secp256k1/src/bench.h b/src/secp256k1/src/bench.h index d67f08a426..d5ebe01301 100644 --- a/src/secp256k1/src/bench.h +++ b/src/secp256k1/src/bench.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_BENCH_H_ -#define _SECP256K1_BENCH_H_ +#ifndef SECP256K1_BENCH_H +#define SECP256K1_BENCH_H #include <stdio.h> #include <math.h> @@ -63,4 +63,4 @@ void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), v printf("us\n"); } -#endif +#endif /* SECP256K1_BENCH_H */ diff --git a/src/secp256k1/src/ecdsa.h b/src/secp256k1/src/ecdsa.h index 54ae101b92..80590c7cc8 100644 --- a/src/secp256k1/src/ecdsa.h +++ b/src/secp256k1/src/ecdsa.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECDSA_ -#define _SECP256K1_ECDSA_ +#ifndef SECP256K1_ECDSA_H +#define SECP256K1_ECDSA_H #include <stddef.h> @@ -18,4 +18,4 @@ static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar* r, const secp256k1_scalar* s, const secp256k1_ge *pubkey, const secp256k1_scalar *message); static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid); -#endif +#endif /* SECP256K1_ECDSA_H */ diff --git a/src/secp256k1/src/ecdsa_impl.h b/src/secp256k1/src/ecdsa_impl.h index 453bb11880..c3400042d8 100644 --- a/src/secp256k1/src/ecdsa_impl.h +++ b/src/secp256k1/src/ecdsa_impl.h @@ -5,8 +5,8 @@ **********************************************************************/ -#ifndef _SECP256K1_ECDSA_IMPL_H_ -#define _SECP256K1_ECDSA_IMPL_H_ +#ifndef SECP256K1_ECDSA_IMPL_H +#define SECP256K1_ECDSA_IMPL_H #include "scalar.h" #include "field.h" @@ -81,8 +81,6 @@ static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned cha return -1; } while (lenleft > 0) { - if ((ret >> ((sizeof(size_t) - 1) * 8)) != 0) { - } ret = (ret << 8) | **sigp; if (ret + lenleft > (size_t)(sigend - *sigp)) { /* Result exceeds the length of the passed array. */ @@ -312,4 +310,4 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec return 1; } -#endif +#endif /* SECP256K1_ECDSA_IMPL_H */ diff --git a/src/secp256k1/src/eckey.h b/src/secp256k1/src/eckey.h index 42739a3bea..b621f1e6c3 100644 --- a/src/secp256k1/src/eckey.h +++ b/src/secp256k1/src/eckey.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECKEY_ -#define _SECP256K1_ECKEY_ +#ifndef SECP256K1_ECKEY_H +#define SECP256K1_ECKEY_H #include <stddef.h> @@ -22,4 +22,4 @@ static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak); static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak); -#endif +#endif /* SECP256K1_ECKEY_H */ diff --git a/src/secp256k1/src/eckey_impl.h b/src/secp256k1/src/eckey_impl.h index ce38071ac2..1ab9a68ec0 100644 --- a/src/secp256k1/src/eckey_impl.h +++ b/src/secp256k1/src/eckey_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECKEY_IMPL_H_ -#define _SECP256K1_ECKEY_IMPL_H_ +#ifndef SECP256K1_ECKEY_IMPL_H +#define SECP256K1_ECKEY_IMPL_H #include "eckey.h" @@ -15,16 +15,17 @@ #include "ecmult_gen.h" static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size) { - if (size == 33 && (pub[0] == 0x02 || pub[0] == 0x03)) { + if (size == 33 && (pub[0] == SECP256K1_TAG_PUBKEY_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_ODD)) { secp256k1_fe x; - return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == 0x03); + return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == SECP256K1_TAG_PUBKEY_ODD); } else if (size == 65 && (pub[0] == 0x04 || pub[0] == 0x06 || pub[0] == 0x07)) { secp256k1_fe x, y; if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) { return 0; } secp256k1_ge_set_xy(elem, &x, &y); - if ((pub[0] == 0x06 || pub[0] == 0x07) && secp256k1_fe_is_odd(&y) != (pub[0] == 0x07)) { + if ((pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_EVEN || pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD) && + secp256k1_fe_is_odd(&y) != (pub[0] == SECP256K1_TAG_PUBKEY_HYBRID_ODD)) { return 0; } return secp256k1_ge_is_valid_var(elem); @@ -42,10 +43,10 @@ static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *p secp256k1_fe_get_b32(&pub[1], &elem->x); if (compressed) { *size = 33; - pub[0] = 0x02 | (secp256k1_fe_is_odd(&elem->y) ? 0x01 : 0x00); + pub[0] = secp256k1_fe_is_odd(&elem->y) ? SECP256K1_TAG_PUBKEY_ODD : SECP256K1_TAG_PUBKEY_EVEN; } else { *size = 65; - pub[0] = 0x04; + pub[0] = SECP256K1_TAG_PUBKEY_UNCOMPRESSED; secp256k1_fe_get_b32(&pub[33], &elem->y); } return 1; @@ -96,4 +97,4 @@ static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, return 1; } -#endif +#endif /* SECP256K1_ECKEY_IMPL_H */ diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h index 20484134f5..6d44aba60b 100644 --- a/src/secp256k1/src/ecmult.h +++ b/src/secp256k1/src/ecmult.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_ -#define _SECP256K1_ECMULT_ +#ifndef SECP256K1_ECMULT_H +#define SECP256K1_ECMULT_H #include "num.h" #include "group.h" @@ -28,4 +28,4 @@ static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx /** Double multiply: R = na*A + ng*G */ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng); -#endif +#endif /* SECP256K1_ECMULT_H */ diff --git a/src/secp256k1/src/ecmult_const.h b/src/secp256k1/src/ecmult_const.h index 2b0097655c..72bf7d7582 100644 --- a/src/secp256k1/src/ecmult_const.h +++ b/src/secp256k1/src/ecmult_const.h @@ -4,12 +4,12 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_CONST_ -#define _SECP256K1_ECMULT_CONST_ +#ifndef SECP256K1_ECMULT_CONST_H +#define SECP256K1_ECMULT_CONST_H #include "scalar.h" #include "group.h" static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q); -#endif +#endif /* SECP256K1_ECMULT_CONST_H */ diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h index 0db314c48e..7d7a172b7b 100644 --- a/src/secp256k1/src/ecmult_const_impl.h +++ b/src/secp256k1/src/ecmult_const_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_CONST_IMPL_ -#define _SECP256K1_ECMULT_CONST_IMPL_ +#ifndef SECP256K1_ECMULT_CONST_IMPL_H +#define SECP256K1_ECMULT_CONST_IMPL_H #include "scalar.h" #include "group.h" @@ -42,11 +42,12 @@ } while(0) -/** Convert a number to WNAF notation. The number becomes represented by sum(2^{wi} * wnaf[i], i=0..return_val) - * with the following guarantees: +/** Convert a number to WNAF notation. + * The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val. + * It has the following guarantees: * - each wnaf[i] an odd integer between -(1 << w) and (1 << w) * - each wnaf[i] is nonzero - * - the number of words set is returned; this is always (WNAF_BITS + w - 1) / w + * - the number of words set is always WNAF_SIZE(w) + 1 * * Adapted from `The Width-w NAF Method Provides Small Memory and Fast Elliptic Scalar * Multiplications Secure against Side Channel Attacks`, Okeya and Tagaki. M. Joye (Ed.) @@ -236,4 +237,4 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons } } -#endif +#endif /* SECP256K1_ECMULT_CONST_IMPL_H */ diff --git a/src/secp256k1/src/ecmult_gen.h b/src/secp256k1/src/ecmult_gen.h index eb2cc9ead6..7564b7015f 100644 --- a/src/secp256k1/src/ecmult_gen.h +++ b/src/secp256k1/src/ecmult_gen.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_GEN_ -#define _SECP256K1_ECMULT_GEN_ +#ifndef SECP256K1_ECMULT_GEN_H +#define SECP256K1_ECMULT_GEN_H #include "scalar.h" #include "group.h" @@ -40,4 +40,4 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context* ctx, secp25 static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32); -#endif +#endif /* SECP256K1_ECMULT_GEN_H */ diff --git a/src/secp256k1/src/ecmult_gen_impl.h b/src/secp256k1/src/ecmult_gen_impl.h index 35f2546077..9615b932dd 100644 --- a/src/secp256k1/src/ecmult_gen_impl.h +++ b/src/secp256k1/src/ecmult_gen_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_GEN_IMPL_H_ -#define _SECP256K1_ECMULT_GEN_IMPL_H_ +#ifndef SECP256K1_ECMULT_GEN_IMPL_H +#define SECP256K1_ECMULT_GEN_IMPL_H #include "scalar.h" #include "group.h" @@ -207,4 +207,4 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const secp256k1_gej_clear(&gb); } -#endif +#endif /* SECP256K1_ECMULT_GEN_IMPL_H */ diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h index 4e40104ad4..93d3794cb4 100644 --- a/src/secp256k1/src/ecmult_impl.h +++ b/src/secp256k1/src/ecmult_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_ECMULT_IMPL_H_ -#define _SECP256K1_ECMULT_IMPL_H_ +#ifndef SECP256K1_ECMULT_IMPL_H +#define SECP256K1_ECMULT_IMPL_H #include <string.h> @@ -403,4 +403,4 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej } } -#endif +#endif /* SECP256K1_ECMULT_IMPL_H */ diff --git a/src/secp256k1/src/field.h b/src/secp256k1/src/field.h index bbb1ee866c..bb6692ad57 100644 --- a/src/secp256k1/src/field.h +++ b/src/secp256k1/src/field.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_ -#define _SECP256K1_FIELD_ +#ifndef SECP256K1_FIELD_H +#define SECP256K1_FIELD_H /** Field element module. * @@ -129,4 +129,4 @@ static void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_f /** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */ static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag); -#endif +#endif /* SECP256K1_FIELD_H */ diff --git a/src/secp256k1/src/field_10x26.h b/src/secp256k1/src/field_10x26.h index 61ee1e0965..727c5267fb 100644 --- a/src/secp256k1/src/field_10x26.h +++ b/src/secp256k1/src/field_10x26.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_REPR_ -#define _SECP256K1_FIELD_REPR_ +#ifndef SECP256K1_FIELD_REPR_H +#define SECP256K1_FIELD_REPR_H #include <stdint.h> @@ -44,4 +44,5 @@ typedef struct { #define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }} #define SECP256K1_FE_STORAGE_CONST_GET(d) d.n[7], d.n[6], d.n[5], d.n[4],d.n[3], d.n[2], d.n[1], d.n[0] -#endif + +#endif /* SECP256K1_FIELD_REPR_H */ diff --git a/src/secp256k1/src/field_10x26_impl.h b/src/secp256k1/src/field_10x26_impl.h index 234c13a644..94f8132fc8 100644 --- a/src/secp256k1/src/field_10x26_impl.h +++ b/src/secp256k1/src/field_10x26_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_REPR_IMPL_H_ -#define _SECP256K1_FIELD_REPR_IMPL_H_ +#ifndef SECP256K1_FIELD_REPR_IMPL_H +#define SECP256K1_FIELD_REPR_IMPL_H #include "util.h" #include "num.h" @@ -1158,4 +1158,4 @@ static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const se #endif } -#endif +#endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/secp256k1/src/field_5x52.h b/src/secp256k1/src/field_5x52.h index 8e69a560dc..bccd8feb4d 100644 --- a/src/secp256k1/src/field_5x52.h +++ b/src/secp256k1/src/field_5x52.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_REPR_ -#define _SECP256K1_FIELD_REPR_ +#ifndef SECP256K1_FIELD_REPR_H +#define SECP256K1_FIELD_REPR_H #include <stdint.h> @@ -44,4 +44,4 @@ typedef struct { (d6) | (((uint64_t)(d7)) << 32) \ }} -#endif +#endif /* SECP256K1_FIELD_REPR_H */ diff --git a/src/secp256k1/src/field_5x52_asm_impl.h b/src/secp256k1/src/field_5x52_asm_impl.h index 98cc004bf0..1fc3171f6b 100644 --- a/src/secp256k1/src/field_5x52_asm_impl.h +++ b/src/secp256k1/src/field_5x52_asm_impl.h @@ -11,8 +11,8 @@ * - December 2014, Pieter Wuille: converted from YASM to GCC inline assembly */ -#ifndef _SECP256K1_FIELD_INNER5X52_IMPL_H_ -#define _SECP256K1_FIELD_INNER5X52_IMPL_H_ +#ifndef SECP256K1_FIELD_INNER5X52_IMPL_H +#define SECP256K1_FIELD_INNER5X52_IMPL_H SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) { /** @@ -499,4 +499,4 @@ __asm__ __volatile__( ); } -#endif +#endif /* SECP256K1_FIELD_INNER5X52_IMPL_H */ diff --git a/src/secp256k1/src/field_5x52_impl.h b/src/secp256k1/src/field_5x52_impl.h index 8e8b286baf..957c61b014 100644 --- a/src/secp256k1/src/field_5x52_impl.h +++ b/src/secp256k1/src/field_5x52_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_REPR_IMPL_H_ -#define _SECP256K1_FIELD_REPR_IMPL_H_ +#ifndef SECP256K1_FIELD_REPR_IMPL_H +#define SECP256K1_FIELD_REPR_IMPL_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -493,4 +493,4 @@ static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const se #endif } -#endif +#endif /* SECP256K1_FIELD_REPR_IMPL_H */ diff --git a/src/secp256k1/src/field_5x52_int128_impl.h b/src/secp256k1/src/field_5x52_int128_impl.h index 0bf22bdd3e..95a0d1791c 100644 --- a/src/secp256k1/src/field_5x52_int128_impl.h +++ b/src/secp256k1/src/field_5x52_int128_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_INNER5X52_IMPL_H_ -#define _SECP256K1_FIELD_INNER5X52_IMPL_H_ +#ifndef SECP256K1_FIELD_INNER5X52_IMPL_H +#define SECP256K1_FIELD_INNER5X52_IMPL_H #include <stdint.h> @@ -274,4 +274,4 @@ SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t /* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ } -#endif +#endif /* SECP256K1_FIELD_INNER5X52_IMPL_H */ diff --git a/src/secp256k1/src/field_impl.h b/src/secp256k1/src/field_impl.h index 5127b279bc..20428648af 100644 --- a/src/secp256k1/src/field_impl.h +++ b/src/secp256k1/src/field_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_FIELD_IMPL_H_ -#define _SECP256K1_FIELD_IMPL_H_ +#ifndef SECP256K1_FIELD_IMPL_H +#define SECP256K1_FIELD_IMPL_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -312,4 +312,4 @@ static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) { #endif } -#endif +#endif /* SECP256K1_FIELD_IMPL_H */ diff --git a/src/secp256k1/src/group.h b/src/secp256k1/src/group.h index 4957b248fe..ea1302deb8 100644 --- a/src/secp256k1/src/group.h +++ b/src/secp256k1/src/group.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_GROUP_ -#define _SECP256K1_GROUP_ +#ifndef SECP256K1_GROUP_H +#define SECP256K1_GROUP_H #include "num.h" #include "field.h" @@ -141,4 +141,4 @@ static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_g /** Rescale a jacobian point by b which must be non-zero. Constant-time. */ static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b); -#endif +#endif /* SECP256K1_GROUP_H */ diff --git a/src/secp256k1/src/group_impl.h b/src/secp256k1/src/group_impl.h index 7d723532ff..b31b6c12ef 100644 --- a/src/secp256k1/src/group_impl.h +++ b/src/secp256k1/src/group_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_GROUP_IMPL_H_ -#define _SECP256K1_GROUP_IMPL_H_ +#ifndef SECP256K1_GROUP_IMPL_H +#define SECP256K1_GROUP_IMPL_H #include "num.h" #include "field.h" @@ -697,4 +697,4 @@ static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) { return secp256k1_fe_is_quad_var(&yz); } -#endif +#endif /* SECP256K1_GROUP_IMPL_H */ diff --git a/src/secp256k1/src/hash.h b/src/secp256k1/src/hash.h index fca98cab9f..e08d25d225 100644 --- a/src/secp256k1/src/hash.h +++ b/src/secp256k1/src/hash.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_HASH_ -#define _SECP256K1_HASH_ +#ifndef SECP256K1_HASH_H +#define SECP256K1_HASH_H #include <stdlib.h> #include <stdint.h> @@ -38,4 +38,4 @@ static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha2 static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256_t *rng, unsigned char *out, size_t outlen); static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng); -#endif +#endif /* SECP256K1_HASH_H */ diff --git a/src/secp256k1/src/hash_impl.h b/src/secp256k1/src/hash_impl.h index b47e65f830..4c9964ee06 100644 --- a/src/secp256k1/src/hash_impl.h +++ b/src/secp256k1/src/hash_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_HASH_IMPL_H_ -#define _SECP256K1_HASH_IMPL_H_ +#ifndef SECP256K1_HASH_IMPL_H +#define SECP256K1_HASH_IMPL_H #include "hash.h" @@ -278,4 +278,4 @@ static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 #undef Maj #undef Ch -#endif +#endif /* SECP256K1_HASH_IMPL_H */ diff --git a/src/secp256k1/src/modules/ecdh/main_impl.h b/src/secp256k1/src/modules/ecdh/main_impl.h index 9e30fb73dd..01ecba4d53 100644 --- a/src/secp256k1/src/modules/ecdh/main_impl.h +++ b/src/secp256k1/src/modules/ecdh/main_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_ECDH_MAIN_ -#define _SECP256K1_MODULE_ECDH_MAIN_ +#ifndef SECP256K1_MODULE_ECDH_MAIN_H +#define SECP256K1_MODULE_ECDH_MAIN_H #include "include/secp256k1_ecdh.h" #include "ecmult_const_impl.h" @@ -51,4 +51,4 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const se return ret; } -#endif +#endif /* SECP256K1_MODULE_ECDH_MAIN_H */ diff --git a/src/secp256k1/src/modules/ecdh/tests_impl.h b/src/secp256k1/src/modules/ecdh/tests_impl.h index 85a5d0a9a6..cec30b67c6 100644 --- a/src/secp256k1/src/modules/ecdh/tests_impl.h +++ b/src/secp256k1/src/modules/ecdh/tests_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_ECDH_TESTS_ -#define _SECP256K1_MODULE_ECDH_TESTS_ +#ifndef SECP256K1_MODULE_ECDH_TESTS_H +#define SECP256K1_MODULE_ECDH_TESTS_H void test_ecdh_api(void) { /* Setup context that just counts errors */ @@ -102,4 +102,4 @@ void run_ecdh_tests(void) { test_bad_scalar(); } -#endif +#endif /* SECP256K1_MODULE_ECDH_TESTS_H */ diff --git a/src/secp256k1/src/modules/recovery/main_impl.h b/src/secp256k1/src/modules/recovery/main_impl.h index c6fbe23981..2f6691c5a1 100755 --- a/src/secp256k1/src/modules/recovery/main_impl.h +++ b/src/secp256k1/src/modules/recovery/main_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_RECOVERY_MAIN_ -#define _SECP256K1_MODULE_RECOVERY_MAIN_ +#ifndef SECP256K1_MODULE_RECOVERY_MAIN_H +#define SECP256K1_MODULE_RECOVERY_MAIN_H #include "include/secp256k1_recovery.h" @@ -190,4 +190,4 @@ int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubk } } -#endif +#endif /* SECP256K1_MODULE_RECOVERY_MAIN_H */ diff --git a/src/secp256k1/src/modules/recovery/tests_impl.h b/src/secp256k1/src/modules/recovery/tests_impl.h index 765c7dd81e..5c9bbe8610 100644 --- a/src/secp256k1/src/modules/recovery/tests_impl.h +++ b/src/secp256k1/src/modules/recovery/tests_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_MODULE_RECOVERY_TESTS_ -#define _SECP256K1_MODULE_RECOVERY_TESTS_ +#ifndef SECP256K1_MODULE_RECOVERY_TESTS_H +#define SECP256K1_MODULE_RECOVERY_TESTS_H static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { (void) msg32; @@ -390,4 +390,4 @@ void run_recovery_tests(void) { test_ecdsa_recovery_edge_cases(); } -#endif +#endif /* SECP256K1_MODULE_RECOVERY_TESTS_H */ diff --git a/src/secp256k1/src/num.h b/src/secp256k1/src/num.h index 7bb9c5be8c..49f2dd791d 100644 --- a/src/secp256k1/src/num.h +++ b/src/secp256k1/src/num.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_NUM_ -#define _SECP256K1_NUM_ +#ifndef SECP256K1_NUM_H +#define SECP256K1_NUM_H #ifndef USE_NUM_NONE @@ -71,4 +71,4 @@ static void secp256k1_num_negate(secp256k1_num *r); #endif -#endif +#endif /* SECP256K1_NUM_H */ diff --git a/src/secp256k1/src/num_gmp.h b/src/secp256k1/src/num_gmp.h index 7dd813088a..3619844bd5 100644 --- a/src/secp256k1/src/num_gmp.h +++ b/src/secp256k1/src/num_gmp.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_NUM_REPR_ -#define _SECP256K1_NUM_REPR_ +#ifndef SECP256K1_NUM_REPR_H +#define SECP256K1_NUM_REPR_H #include <gmp.h> @@ -17,4 +17,4 @@ typedef struct { int limbs; } secp256k1_num; -#endif +#endif /* SECP256K1_NUM_REPR_H */ diff --git a/src/secp256k1/src/num_gmp_impl.h b/src/secp256k1/src/num_gmp_impl.h index 3a46495eea..0ae2a8ba0e 100644 --- a/src/secp256k1/src/num_gmp_impl.h +++ b/src/secp256k1/src/num_gmp_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_NUM_REPR_IMPL_H_ -#define _SECP256K1_NUM_REPR_IMPL_H_ +#ifndef SECP256K1_NUM_REPR_IMPL_H +#define SECP256K1_NUM_REPR_IMPL_H #include <string.h> #include <stdlib.h> @@ -285,4 +285,4 @@ static void secp256k1_num_negate(secp256k1_num *r) { r->neg ^= 1; } -#endif +#endif /* SECP256K1_NUM_REPR_IMPL_H */ diff --git a/src/secp256k1/src/num_impl.h b/src/secp256k1/src/num_impl.h index 0b0e3a072a..c45193b033 100644 --- a/src/secp256k1/src/num_impl.h +++ b/src/secp256k1/src/num_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_NUM_IMPL_H_ -#define _SECP256K1_NUM_IMPL_H_ +#ifndef SECP256K1_NUM_IMPL_H +#define SECP256K1_NUM_IMPL_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -21,4 +21,4 @@ #error "Please select num implementation" #endif -#endif +#endif /* SECP256K1_NUM_IMPL_H */ diff --git a/src/secp256k1/src/scalar.h b/src/secp256k1/src/scalar.h index 27e9d8375e..59304cb66e 100644 --- a/src/secp256k1/src/scalar.h +++ b/src/secp256k1/src/scalar.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_ -#define _SECP256K1_SCALAR_ +#ifndef SECP256K1_SCALAR_H +#define SECP256K1_SCALAR_H #include "num.h" @@ -103,4 +103,4 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar /** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */ static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift); -#endif +#endif /* SECP256K1_SCALAR_H */ diff --git a/src/secp256k1/src/scalar_4x64.h b/src/secp256k1/src/scalar_4x64.h index cff406038f..19c7495d1c 100644 --- a/src/secp256k1/src/scalar_4x64.h +++ b/src/secp256k1/src/scalar_4x64.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_ -#define _SECP256K1_SCALAR_REPR_ +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H #include <stdint.h> @@ -16,4 +16,4 @@ typedef struct { #define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{((uint64_t)(d1)) << 32 | (d0), ((uint64_t)(d3)) << 32 | (d2), ((uint64_t)(d5)) << 32 | (d4), ((uint64_t)(d7)) << 32 | (d6)}} -#endif +#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h index 56e7bd82af..db1ebf94be 100644 --- a/src/secp256k1/src/scalar_4x64_impl.h +++ b/src/secp256k1/src/scalar_4x64_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ -#define _SECP256K1_SCALAR_REPR_IMPL_H_ +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) @@ -946,4 +946,4 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1); } -#endif +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/secp256k1/src/scalar_8x32.h b/src/secp256k1/src/scalar_8x32.h index 1319664f65..2c9a348e24 100644 --- a/src/secp256k1/src/scalar_8x32.h +++ b/src/secp256k1/src/scalar_8x32.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_ -#define _SECP256K1_SCALAR_REPR_ +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H #include <stdint.h> @@ -16,4 +16,4 @@ typedef struct { #define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7)}} -#endif +#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/src/secp256k1/src/scalar_8x32_impl.h b/src/secp256k1/src/scalar_8x32_impl.h index aae4f35c08..4f9ed61fea 100644 --- a/src/secp256k1/src/scalar_8x32_impl.h +++ b/src/secp256k1/src/scalar_8x32_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ -#define _SECP256K1_SCALAR_REPR_IMPL_H_ +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H /* Limbs of the secp256k1 order. */ #define SECP256K1_N_0 ((uint32_t)0xD0364141UL) @@ -718,4 +718,4 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1); } -#endif +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h index 2690d86558..fa790570ff 100644 --- a/src/secp256k1/src/scalar_impl.h +++ b/src/secp256k1/src/scalar_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_IMPL_H_ -#define _SECP256K1_SCALAR_IMPL_H_ +#ifndef SECP256K1_SCALAR_IMPL_H +#define SECP256K1_SCALAR_IMPL_H #include "group.h" #include "scalar.h" @@ -330,4 +330,4 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar #endif #endif -#endif +#endif /* SECP256K1_SCALAR_IMPL_H */ diff --git a/src/secp256k1/src/scalar_low.h b/src/secp256k1/src/scalar_low.h index 5574c44c7a..5836febc5b 100644 --- a/src/secp256k1/src/scalar_low.h +++ b/src/secp256k1/src/scalar_low.h @@ -4,12 +4,12 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_ -#define _SECP256K1_SCALAR_REPR_ +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H #include <stdint.h> /** A scalar modulo the group order of the secp256k1 curve. */ typedef uint32_t secp256k1_scalar; -#endif +#endif /* SECP256K1_SCALAR_REPR_H */ diff --git a/src/secp256k1/src/scalar_low_impl.h b/src/secp256k1/src/scalar_low_impl.h index 4f94441f49..c80e70c5a2 100644 --- a/src/secp256k1/src/scalar_low_impl.h +++ b/src/secp256k1/src/scalar_low_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ -#define _SECP256K1_SCALAR_REPR_IMPL_H_ +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H #include "scalar.h" @@ -111,4 +111,4 @@ SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const return *a == *b; } -#endif +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ diff --git a/src/secp256k1/src/testrand.h b/src/secp256k1/src/testrand.h index f8efa93c7c..f1f9be077e 100644 --- a/src/secp256k1/src/testrand.h +++ b/src/secp256k1/src/testrand.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_TESTRAND_H_ -#define _SECP256K1_TESTRAND_H_ +#ifndef SECP256K1_TESTRAND_H +#define SECP256K1_TESTRAND_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -35,4 +35,4 @@ static void secp256k1_rand256_test(unsigned char *b32); /** Generate pseudorandom bytes with long sequences of zero and one bits. */ static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len); -#endif +#endif /* SECP256K1_TESTRAND_H */ diff --git a/src/secp256k1/src/testrand_impl.h b/src/secp256k1/src/testrand_impl.h index 15c7b9f12d..1255574209 100644 --- a/src/secp256k1/src/testrand_impl.h +++ b/src/secp256k1/src/testrand_impl.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_TESTRAND_IMPL_H_ -#define _SECP256K1_TESTRAND_IMPL_H_ +#ifndef SECP256K1_TESTRAND_IMPL_H +#define SECP256K1_TESTRAND_IMPL_H #include <stdint.h> #include <string.h> @@ -107,4 +107,4 @@ static void secp256k1_rand256_test(unsigned char *b32) { secp256k1_rand_bytes_test(b32, 32); } -#endif +#endif /* SECP256K1_TESTRAND_IMPL_H */ diff --git a/src/secp256k1/src/util.h b/src/secp256k1/src/util.h index 4092a86c91..b0441d8e30 100644 --- a/src/secp256k1/src/util.h +++ b/src/secp256k1/src/util.h @@ -4,8 +4,8 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#ifndef _SECP256K1_UTIL_H_ -#define _SECP256K1_UTIL_H_ +#ifndef SECP256K1_UTIL_H +#define SECP256K1_UTIL_H #if defined HAVE_CONFIG_H #include "libsecp256k1-config.h" @@ -110,4 +110,4 @@ static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_ SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t; #endif -#endif +#endif /* SECP256K1_UTIL_H */ diff --git a/src/streams.h b/src/streams.h index a3fc919714..9a3badea57 100644 --- a/src/streams.h +++ b/src/streams.h @@ -82,7 +82,7 @@ class CVectorWriter * @param[in] nVersionIn Serialization Version (including any flags) * @param[in] vchDataIn Referenced byte vector to overwrite/append * @param[in] nPosIn Starting position. Vector index where writes should start. The vector will initially - * grow as necessary to max(index, vec.size()). So to append, use vec.size(). + * grow as necessary to max(nPosIn, vec.size()). So to append, use vec.size(). */ CVectorWriter(int nTypeIn, int nVersionIn, std::vector<unsigned char>& vchDataIn, size_t nPosIn) : nType(nTypeIn), nVersion(nVersionIn), vchData(vchDataIn), nPos(nPosIn) { @@ -91,7 +91,7 @@ class CVectorWriter } /* * (other params same as above) - * @param[in] args A list of items to serialize starting at nPos. + * @param[in] args A list of items to serialize starting at nPosIn. */ template <typename... Args> CVectorWriter(int nTypeIn, int nVersionIn, std::vector<unsigned char>& vchDataIn, size_t nPosIn, Args&&... args) : CVectorWriter(nTypeIn, nVersionIn, vchDataIn, nPosIn) @@ -455,10 +455,6 @@ public: class CAutoFile { private: - // Disallow copies - CAutoFile(const CAutoFile&); - CAutoFile& operator=(const CAutoFile&); - const int nType; const int nVersion; @@ -475,6 +471,10 @@ public: fclose(); } + // Disallow copies + CAutoFile(const CAutoFile&) = delete; + CAutoFile& operator=(const CAutoFile&) = delete; + void fclose() { if (file) { @@ -564,10 +564,6 @@ public: class CBufferedFile { private: - // Disallow copies - CBufferedFile(const CBufferedFile&); - CBufferedFile& operator=(const CBufferedFile&); - const int nType; const int nVersion; @@ -609,6 +605,10 @@ public: fclose(); } + // Disallow copies + CBufferedFile(const CBufferedFile&) = delete; + CBufferedFile& operator=(const CBufferedFile&) = delete; + int GetVersion() const { return nVersion; } int GetType() const { return nType; } diff --git a/src/support/cleanse.cpp b/src/support/cleanse.cpp index a2141b2449..95899c9f02 100644 --- a/src/support/cleanse.cpp +++ b/src/support/cleanse.cpp @@ -5,9 +5,35 @@ #include "cleanse.h" -#include <openssl/crypto.h> +#include <cstring> +/* Compilers have a bad habit of removing "superfluous" memset calls that + * are trying to zero memory. For example, when memset()ing a buffer and + * then free()ing it, the compiler might decide that the memset is + * unobservable and thus can be removed. + * + * Previously we used OpenSSL which tried to stop this by a) implementing + * memset in assembly on x86 and b) putting the function in its own file + * for other platforms. + * + * This change removes those tricks in favour of using asm directives to + * scare the compiler away. As best as our compiler folks can tell, this is + * sufficient and will continue to be so. + * + * Adam Langley <agl@google.com> + * Commit: ad1907fe73334d6c696c8539646c21b11178f20f + * BoringSSL (LICENSE: ISC) + */ void memory_cleanse(void *ptr, size_t len) { - OPENSSL_cleanse(ptr, len); + std::memset(ptr, 0, len); + + /* As best as we can tell, this is sufficient to break any optimisations that + might try to eliminate "superfluous" memsets. If there's an easy way to + detect memset_s, it would be better to use that. */ +#if defined(_MSC_VER) + __asm; +#else + __asm__ __volatile__("" : : "r"(ptr) : "memory"); +#endif } diff --git a/src/support/lockedpool.h b/src/support/lockedpool.h index cecbdec1aa..834f0371e2 100644 --- a/src/support/lockedpool.h +++ b/src/support/lockedpool.h @@ -50,6 +50,9 @@ public: Arena(void *base, size_t size, size_t alignment); virtual ~Arena(); + Arena(const Arena& other) = delete; // non construction-copyable + Arena& operator=(const Arena&) = delete; // non copyable + /** Memory statistics. */ struct Stats { @@ -85,9 +88,6 @@ public: */ bool addressInArena(void *ptr) const { return ptr >= base && ptr < end; } private: - Arena(const Arena& other) = delete; // non construction-copyable - Arena& operator=(const Arena&) = delete; // non copyable - /** Map of chunk address to chunk information. This class makes use of the * sorted order to merge previous and next chunks during deallocation. */ @@ -153,6 +153,9 @@ public: explicit LockedPool(std::unique_ptr<LockedPageAllocator> allocator, LockingFailed_Callback lf_cb_in = nullptr); ~LockedPool(); + LockedPool(const LockedPool& other) = delete; // non construction-copyable + LockedPool& operator=(const LockedPool&) = delete; // non copyable + /** Allocate size bytes from this arena. * Returns pointer on success, or 0 if memory is full or * the application tried to allocate 0 bytes. @@ -168,9 +171,6 @@ public: /** Get pool usage statistics */ Stats stats() const; private: - LockedPool(const LockedPool& other) = delete; // non construction-copyable - LockedPool& operator=(const LockedPool&) = delete; // non copyable - std::unique_ptr<LockedPageAllocator> allocator; /** Create an arena from locked pages */ diff --git a/src/sync.cpp b/src/sync.cpp index 9c351ea487..87024ccdf2 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -98,7 +98,7 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, assert(false); } -static void push_lock(void* c, const CLockLocation& locklocation, bool fTry) +static void push_lock(void* c, const CLockLocation& locklocation) { if (lockstack.get() == nullptr) lockstack.reset(new LockStack); @@ -130,7 +130,7 @@ static void pop_lock() void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry) { - push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry), fTry); + push_lock(cs, CLockLocation(pszName, pszFile, nLine, fTry)); } void LeaveCritical() diff --git a/src/test/DoS_tests.cpp b/src/test/DoS_tests.cpp index ffbeeb7d91..b88ad5ed1b 100644 --- a/src/test/DoS_tests.cpp +++ b/src/test/DoS_tests.cpp @@ -50,26 +50,26 @@ BOOST_AUTO_TEST_CASE(DoS_banning) CAddress addr1(ip(0xa0b0c001), NODE_NONE); CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, CAddress(), "", true); dummyNode1.SetSendVersion(PROTOCOL_VERSION); - GetNodeSignals().InitializeNode(&dummyNode1, *connman); + peerLogic->InitializeNode(&dummyNode1); dummyNode1.nVersion = 1; dummyNode1.fSuccessfullyConnected = true; Misbehaving(dummyNode1.GetId(), 100); // Should get banned - SendMessages(&dummyNode1, *connman, interruptDummy); + peerLogic->SendMessages(&dummyNode1, interruptDummy); BOOST_CHECK(connman->IsBanned(addr1)); BOOST_CHECK(!connman->IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned CAddress addr2(ip(0xa0b0c002), NODE_NONE); CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, CAddress(), "", true); dummyNode2.SetSendVersion(PROTOCOL_VERSION); - GetNodeSignals().InitializeNode(&dummyNode2, *connman); + peerLogic->InitializeNode(&dummyNode2); dummyNode2.nVersion = 1; dummyNode2.fSuccessfullyConnected = true; Misbehaving(dummyNode2.GetId(), 50); - SendMessages(&dummyNode2, *connman, interruptDummy); + peerLogic->SendMessages(&dummyNode2, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr2)); // 2 not banned yet... BOOST_CHECK(connman->IsBanned(addr1)); // ... but 1 still should be Misbehaving(dummyNode2.GetId(), 50); - SendMessages(&dummyNode2, *connman, interruptDummy); + peerLogic->SendMessages(&dummyNode2, interruptDummy); BOOST_CHECK(connman->IsBanned(addr2)); } @@ -82,17 +82,17 @@ BOOST_AUTO_TEST_CASE(DoS_banscore) CAddress addr1(ip(0xa0b0c001), NODE_NONE); CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 3, 1, CAddress(), "", true); dummyNode1.SetSendVersion(PROTOCOL_VERSION); - GetNodeSignals().InitializeNode(&dummyNode1, *connman); + peerLogic->InitializeNode(&dummyNode1); dummyNode1.nVersion = 1; dummyNode1.fSuccessfullyConnected = true; Misbehaving(dummyNode1.GetId(), 100); - SendMessages(&dummyNode1, *connman, interruptDummy); + peerLogic->SendMessages(&dummyNode1, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr1)); Misbehaving(dummyNode1.GetId(), 10); - SendMessages(&dummyNode1, *connman, interruptDummy); + peerLogic->SendMessages(&dummyNode1, interruptDummy); BOOST_CHECK(!connman->IsBanned(addr1)); Misbehaving(dummyNode1.GetId(), 1); - SendMessages(&dummyNode1, *connman, interruptDummy); + peerLogic->SendMessages(&dummyNode1, interruptDummy); BOOST_CHECK(connman->IsBanned(addr1)); gArgs.ForceSetArg("-banscore", std::to_string(DEFAULT_BANSCORE_THRESHOLD)); } @@ -108,12 +108,12 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) CAddress addr(ip(0xa0b0c001), NODE_NONE); CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, 4, 4, CAddress(), "", true); dummyNode.SetSendVersion(PROTOCOL_VERSION); - GetNodeSignals().InitializeNode(&dummyNode, *connman); + peerLogic->InitializeNode(&dummyNode); dummyNode.nVersion = 1; dummyNode.fSuccessfullyConnected = true; Misbehaving(dummyNode.GetId(), 100); - SendMessages(&dummyNode, *connman, interruptDummy); + peerLogic->SendMessages(&dummyNode, interruptDummy); BOOST_CHECK(connman->IsBanned(addr)); SetMockTime(nStartTime+60*60); diff --git a/src/test/README.md b/src/test/README.md index eeb04c6ffa..dbaa9c27f3 100644 --- a/src/test/README.md +++ b/src/test/README.md @@ -5,7 +5,10 @@ and tests weren't explicitly disabled. After configuring, they can be run with `make check`. -To run the bitcoind tests manually, launch `src/test/test_bitcoin`. +To run the bitcoind tests manually, launch `src/test/test_bitcoin`. To recompile +after a test file was modified, run `make` and then run the test again. If you +modify a non-test file, use `make -C src/test` to recompile only what's needed +to run the bitcoind tests. To add more bitcoind tests, add `BOOST_AUTO_TEST_CASE` functions to the existing .cpp files in the `test/` directory or add new .cpp files that diff --git a/src/test/base58_tests.cpp b/src/test/base58_tests.cpp index ee633249e9..6bc6dd5187 100644 --- a/src/test/base58_tests.cpp +++ b/src/test/base58_tests.cpp @@ -10,14 +10,15 @@ #include "key.h" #include "script/script.h" +#include "test/test_bitcoin.h" #include "uint256.h" #include "util.h" #include "utilstrencodings.h" -#include "test/test_bitcoin.h" + +#include <univalue.h> #include <boost/test/unit_test.hpp> -#include <univalue.h> extern UniValue read_json(const std::string& jsondata); @@ -72,63 +73,18 @@ BOOST_AUTO_TEST_CASE(base58_DecodeBase58) BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); } -// Visitor to check address type -class TestAddrTypeVisitor : public boost::static_visitor<bool> -{ -private: - std::string exp_addrType; -public: - explicit TestAddrTypeVisitor(const std::string &_exp_addrType) : exp_addrType(_exp_addrType) { } - bool operator()(const CKeyID &id) const - { - return (exp_addrType == "pubkey"); - } - bool operator()(const CScriptID &id) const - { - return (exp_addrType == "script"); - } - bool operator()(const CNoDestination &no) const - { - return (exp_addrType == "none"); - } -}; - -// Visitor to check address payload -class TestPayloadVisitor : public boost::static_visitor<bool> -{ -private: - std::vector<unsigned char> exp_payload; -public: - explicit TestPayloadVisitor(std::vector<unsigned char> &_exp_payload) : exp_payload(_exp_payload) { } - bool operator()(const CKeyID &id) const - { - uint160 exp_key(exp_payload); - return exp_key == id; - } - bool operator()(const CScriptID &id) const - { - uint160 exp_key(exp_payload); - return exp_key == id; - } - bool operator()(const CNoDestination &no) const - { - return exp_payload.size() == 0; - } -}; - // Goal: check that parsed keys match test payload BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) { UniValue tests = read_json(std::string(json_tests::base58_keys_valid, json_tests::base58_keys_valid + sizeof(json_tests::base58_keys_valid))); CBitcoinSecret secret; - CBitcoinAddress addr; + CTxDestination destination; SelectParams(CBaseChainParams::MAIN); for (unsigned int idx = 0; idx < tests.size(); idx++) { UniValue test = tests[idx]; std::string strTest = test.write(); - if (test.size() < 3) // Allow for extra stuff (useful for comments) - { + if (test.size() < 3) { // Allow for extra stuff (useful for comments) BOOST_ERROR("Bad test: " << strTest); continue; } @@ -136,16 +92,11 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str()); const UniValue &metadata = test[2].get_obj(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); - bool isTestnet = find_value(metadata, "isTestnet").get_bool(); - if (isTestnet) - SelectParams(CBaseChainParams::TESTNET); - else - SelectParams(CBaseChainParams::MAIN); - if(isPrivkey) - { + SelectParams(find_value(metadata, "chain").get_str()); + bool try_case_flip = find_value(metadata, "tryCaseFlip").isNull() ? false : find_value(metadata, "tryCaseFlip").get_bool(); + if (isPrivkey) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); // Must be valid private key - // Note: CBitcoinSecret::SetString tests isValid, whereas CBitcoinAddress does not! BOOST_CHECK_MESSAGE(secret.SetString(exp_base58string), "!SetString:"+ strTest); BOOST_CHECK_MESSAGE(secret.IsValid(), "!IsValid:" + strTest); CKey privkey = secret.GetKey(); @@ -153,18 +104,29 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_parse) BOOST_CHECK_MESSAGE(privkey.size() == exp_payload.size() && std::equal(privkey.begin(), privkey.end(), exp_payload.begin()), "key mismatch:" + strTest); // Private key must be invalid public key - addr.SetString(exp_base58string); - BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid privkey as pubkey:" + strTest); - } - else - { - std::string exp_addrType = find_value(metadata, "addrType").get_str(); // "script" or "pubkey" + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid privkey as pubkey:" + strTest); + } else { // Must be valid public key - BOOST_CHECK_MESSAGE(addr.SetString(exp_base58string), "SetString:" + strTest); - BOOST_CHECK_MESSAGE(addr.IsValid(), "!IsValid:" + strTest); - BOOST_CHECK_MESSAGE(addr.IsScript() == (exp_addrType == "script"), "isScript mismatch" + strTest); - CTxDestination dest = addr.Get(); - BOOST_CHECK_MESSAGE(boost::apply_visitor(TestAddrTypeVisitor(exp_addrType), dest), "addrType mismatch" + strTest); + destination = DecodeDestination(exp_base58string); + CScript script = GetScriptForDestination(destination); + BOOST_CHECK_MESSAGE(IsValidDestination(destination), "!IsValid:" + strTest); + BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload)); + + // Try flipped case version + for (char& c : exp_base58string) { + if (c >= 'a' && c <= 'z') { + c = (c - 'a') + 'A'; + } else if (c >= 'A' && c <= 'Z') { + c = (c - 'A') + 'a'; + } + } + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(IsValidDestination(destination) == try_case_flip, "!IsValid case flipped:" + strTest); + if (IsValidDestination(destination)) { + script = GetScriptForDestination(destination); + BOOST_CHECK_EQUAL(HexStr(script), HexStr(exp_payload)); + } // Public key must be invalid private key secret.SetString(exp_base58string); @@ -190,13 +152,8 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) std::vector<unsigned char> exp_payload = ParseHex(test[1].get_str()); const UniValue &metadata = test[2].get_obj(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); - bool isTestnet = find_value(metadata, "isTestnet").get_bool(); - if (isTestnet) - SelectParams(CBaseChainParams::TESTNET); - else - SelectParams(CBaseChainParams::MAIN); - if(isPrivkey) - { + SelectParams(find_value(metadata, "chain").get_str()); + if (isPrivkey) { bool isCompressed = find_value(metadata, "isCompressed").get_bool(); CKey key; key.Set(exp_payload.begin(), exp_payload.end(), isCompressed); @@ -204,48 +161,26 @@ BOOST_AUTO_TEST_CASE(base58_keys_valid_gen) CBitcoinSecret secret; secret.SetKey(key); BOOST_CHECK_MESSAGE(secret.ToString() == exp_base58string, "result mismatch: " + strTest); - } - else - { - std::string exp_addrType = find_value(metadata, "addrType").get_str(); + } else { CTxDestination dest; - if(exp_addrType == "pubkey") - { - dest = CKeyID(uint160(exp_payload)); - } - else if(exp_addrType == "script") - { - dest = CScriptID(uint160(exp_payload)); - } - else if(exp_addrType == "none") - { - dest = CNoDestination(); - } - else - { - BOOST_ERROR("Bad addrtype: " << strTest); - continue; - } - CBitcoinAddress addrOut; - BOOST_CHECK_MESSAGE(addrOut.Set(dest), "encode dest: " + strTest); - BOOST_CHECK_MESSAGE(addrOut.ToString() == exp_base58string, "mismatch: " + strTest); + CScript exp_script(exp_payload.begin(), exp_payload.end()); + ExtractDestination(exp_script, dest); + std::string address = EncodeDestination(dest); + + BOOST_CHECK_EQUAL(address, exp_base58string); } } - // Visiting a CNoDestination must fail - CBitcoinAddress dummyAddr; - CTxDestination nodest = CNoDestination(); - BOOST_CHECK(!dummyAddr.Set(nodest)); - SelectParams(CBaseChainParams::MAIN); } + // Goal: check that base58 parsing code is robust against a variety of corrupted data BOOST_AUTO_TEST_CASE(base58_keys_invalid) { UniValue tests = read_json(std::string(json_tests::base58_keys_invalid, json_tests::base58_keys_invalid + sizeof(json_tests::base58_keys_invalid))); // Negative testcases CBitcoinSecret secret; - CBitcoinAddress addr; + CTxDestination destination; for (unsigned int idx = 0; idx < tests.size(); idx++) { UniValue test = tests[idx]; @@ -258,13 +193,15 @@ BOOST_AUTO_TEST_CASE(base58_keys_invalid) std::string exp_base58string = test[0].get_str(); // must be invalid as public and as private key - addr.SetString(exp_base58string); - BOOST_CHECK_MESSAGE(!addr.IsValid(), "IsValid pubkey:" + strTest); - secret.SetString(exp_base58string); - BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey:" + strTest); + for (auto chain : { CBaseChainParams::MAIN, CBaseChainParams::TESTNET, CBaseChainParams::REGTEST }) { + SelectParams(chain); + destination = DecodeDestination(exp_base58string); + BOOST_CHECK_MESSAGE(!IsValidDestination(destination), "IsValid pubkey in mainnet:" + strTest); + secret.SetString(exp_base58string); + BOOST_CHECK_MESSAGE(!secret.IsValid(), "IsValid privkey in mainnet:" + strTest); + } } } BOOST_AUTO_TEST_SUITE_END() - diff --git a/src/test/bech32_tests.cpp b/src/test/bech32_tests.cpp new file mode 100644 index 0000000000..ce4cddd64b --- /dev/null +++ b/src/test/bech32_tests.cpp @@ -0,0 +1,67 @@ +// Copyright (c) 2017 Pieter Wuille +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "bech32.h" +#include "test/test_bitcoin.h" + +#include <boost/test/unit_test.hpp> + +BOOST_FIXTURE_TEST_SUITE(bech32_tests, BasicTestingSetup) + +bool CaseInsensitiveEqual(const std::string &s1, const std::string &s2) +{ + if (s1.size() != s2.size()) return false; + for (size_t i = 0; i < s1.size(); ++i) { + char c1 = s1[i]; + if (c1 >= 'A' && c1 <= 'Z') c1 -= ('A' - 'a'); + char c2 = s2[i]; + if (c2 >= 'A' && c2 <= 'Z') c2 -= ('A' - 'a'); + if (c1 != c2) return false; + } + return true; +} + +BOOST_AUTO_TEST_CASE(bip173_testvectors_valid) +{ + static const std::string CASES[] = { + "A12UEL5L", + "a12uel5l", + "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", + "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", + "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", + "?1ezyfcl", + }; + for (const std::string& str : CASES) { + auto ret = bech32::Decode(str); + BOOST_CHECK(!ret.first.empty()); + std::string recode = bech32::Encode(ret.first, ret.second); + BOOST_CHECK(!recode.empty()); + BOOST_CHECK(CaseInsensitiveEqual(str, recode)); + } +} + +BOOST_AUTO_TEST_CASE(bip173_testvectors_invalid) +{ + static const std::string CASES[] = { + " 1nwldj5", + "\x7f""1axkwrx", + "\x80""1eym55h", + "an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx", + "pzry9x0s0muk", + "1pzry9x0s0muk", + "x1b4n0q5v", + "li1dgmt3", + "de1lg7wt\xff", + "A1G7SGD8", + "10a06t8", + "1qzzfhee", + }; + for (const std::string& str : CASES) { + auto ret = bech32::Decode(str); + BOOST_CHECK(ret.first.empty()); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp index c851ab2849..e123c26ad0 100644 --- a/src/test/bip32_tests.cpp +++ b/src/test/bip32_tests.cpp @@ -91,7 +91,7 @@ void RunTest(const TestVector &test) { std::vector<unsigned char> seed = ParseHex(test.strHexMaster); CExtKey key; CExtPubKey pubkey; - key.SetMaster(&seed[0], seed.size()); + key.SetMaster(seed.data(), seed.size()); pubkey = key.Neuter(); for (const TestDerivation &derive : test.vDerive) { unsigned char data[74]; diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 2085b5cb2b..eac2c102a6 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -8,6 +8,7 @@ #include "clientversion.h" #include "key.h" #include "merkleblock.h" +#include "primitives/block.h" #include "random.h" #include "serialize.h" #include "streams.h" @@ -154,8 +155,8 @@ BOOST_AUTO_TEST_CASE(bloom_match) COutPoint prevOutPoint(uint256S("0x90c122d70786e899529d71dbeba91ba216982fb6ba58f3bdaab65e73b7e9260b"), 0); { std::vector<unsigned char> data(32 + sizeof(unsigned int)); - memcpy(&data[0], prevOutPoint.hash.begin(), 32); - memcpy(&data[32], &prevOutPoint.n, sizeof(unsigned int)); + memcpy(data.data(), prevOutPoint.hash.begin(), 32); + memcpy(data.data()+32, &prevOutPoint.n, sizeof(unsigned int)); filter.insert(data); } BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(tx), "Simple Bloom filter didn't match manually serialized COutPoint"); @@ -179,20 +180,15 @@ BOOST_AUTO_TEST_CASE(bloom_match) BOOST_AUTO_TEST_CASE(merkle_block_1) { - // Random real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af) - // With 9 txes - CBlock block; - CDataStream stream(ParseHex("0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000"), SER_NETWORK, PROTOCOL_VERSION); - stream >> block; - + CBlock block = getBlock13b8a(); CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL); // Match the last transaction filter.insert(uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20")); CMerkleBlock merkleBlock(block, filter); - BOOST_CHECK(merkleBlock.header.GetHash() == block.GetHash()); + BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex()); - BOOST_CHECK(merkleBlock.vMatchedTxn.size() == 1); + BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 1); std::pair<unsigned int, uint256> pair = merkleBlock.vMatchedTxn[0]; BOOST_CHECK(merkleBlock.vMatchedTxn[0].second == uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20")); diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp index 391ad14ffa..c748b2448c 100644 --- a/src/test/crypto_tests.cpp +++ b/src/test/crypto_tests.cpp @@ -58,12 +58,12 @@ void TestRIPEMD160(const std::string &in, const std::string &hexout) { TestVecto void TestHMACSHA256(const std::string &hexkey, const std::string &hexin, const std::string &hexout) { std::vector<unsigned char> key = ParseHex(hexkey); - TestVector(CHMAC_SHA256(&key[0], key.size()), ParseHex(hexin), ParseHex(hexout)); + TestVector(CHMAC_SHA256(key.data(), key.size()), ParseHex(hexin), ParseHex(hexout)); } void TestHMACSHA512(const std::string &hexkey, const std::string &hexin, const std::string &hexout) { std::vector<unsigned char> key = ParseHex(hexkey); - TestVector(CHMAC_SHA512(&key[0], key.size()), ParseHex(hexin), ParseHex(hexout)); + TestVector(CHMAC_SHA512(key.data(), key.size()), ParseHex(hexin), ParseHex(hexout)); } void TestAES128(const std::string &hexkey, const std::string &hexin, const std::string &hexout) @@ -76,13 +76,13 @@ void TestAES128(const std::string &hexkey, const std::string &hexin, const std:: assert(key.size() == 16); assert(in.size() == 16); assert(correctout.size() == 16); - AES128Encrypt enc(&key[0]); + AES128Encrypt enc(key.data()); buf.resize(correctout.size()); buf2.resize(correctout.size()); - enc.Encrypt(&buf[0], &in[0]); + enc.Encrypt(buf.data(), in.data()); BOOST_CHECK_EQUAL(HexStr(buf), HexStr(correctout)); - AES128Decrypt dec(&key[0]); - dec.Decrypt(&buf2[0], &buf[0]); + AES128Decrypt dec(key.data()); + dec.Decrypt(buf2.data(), buf.data()); BOOST_CHECK_EQUAL(HexStr(buf2), HexStr(in)); } @@ -96,12 +96,12 @@ void TestAES256(const std::string &hexkey, const std::string &hexin, const std:: assert(key.size() == 32); assert(in.size() == 16); assert(correctout.size() == 16); - AES256Encrypt enc(&key[0]); + AES256Encrypt enc(key.data()); buf.resize(correctout.size()); - enc.Encrypt(&buf[0], &in[0]); + enc.Encrypt(buf.data(), in.data()); BOOST_CHECK(buf == correctout); - AES256Decrypt dec(&key[0]); - dec.Decrypt(&buf[0], &buf[0]); + AES256Decrypt dec(key.data()); + dec.Decrypt(buf.data(), buf.data()); BOOST_CHECK(buf == in); } @@ -114,16 +114,16 @@ void TestAES128CBC(const std::string &hexkey, const std::string &hexiv, bool pad std::vector<unsigned char> realout(in.size() + AES_BLOCKSIZE); // Encrypt the plaintext and verify that it equals the cipher - AES128CBCEncrypt enc(&key[0], &iv[0], pad); - int size = enc.Encrypt(&in[0], in.size(), &realout[0]); + AES128CBCEncrypt enc(key.data(), iv.data(), pad); + int size = enc.Encrypt(in.data(), in.size(), realout.data()); realout.resize(size); BOOST_CHECK(realout.size() == correctout.size()); BOOST_CHECK_MESSAGE(realout == correctout, HexStr(realout) + std::string(" != ") + hexout); // Decrypt the cipher and verify that it equals the plaintext std::vector<unsigned char> decrypted(correctout.size()); - AES128CBCDecrypt dec(&key[0], &iv[0], pad); - size = dec.Decrypt(&correctout[0], correctout.size(), &decrypted[0]); + AES128CBCDecrypt dec(key.data(), iv.data(), pad); + size = dec.Decrypt(correctout.data(), correctout.size(), decrypted.data()); decrypted.resize(size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(decrypted == in, HexStr(decrypted) + std::string(" != ") + hexin); @@ -133,12 +133,12 @@ void TestAES128CBC(const std::string &hexkey, const std::string &hexiv, bool pad { std::vector<unsigned char> sub(i, in.end()); std::vector<unsigned char> subout(sub.size() + AES_BLOCKSIZE); - int _size = enc.Encrypt(&sub[0], sub.size(), &subout[0]); + int _size = enc.Encrypt(sub.data(), sub.size(), subout.data()); if (_size != 0) { subout.resize(_size); std::vector<unsigned char> subdecrypted(subout.size()); - _size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]); + _size = dec.Decrypt(subout.data(), subout.size(), subdecrypted.data()); subdecrypted.resize(_size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub)); @@ -155,16 +155,16 @@ void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad std::vector<unsigned char> realout(in.size() + AES_BLOCKSIZE); // Encrypt the plaintext and verify that it equals the cipher - AES256CBCEncrypt enc(&key[0], &iv[0], pad); - int size = enc.Encrypt(&in[0], in.size(), &realout[0]); + AES256CBCEncrypt enc(key.data(), iv.data(), pad); + int size = enc.Encrypt(in.data(), in.size(), realout.data()); realout.resize(size); BOOST_CHECK(realout.size() == correctout.size()); BOOST_CHECK_MESSAGE(realout == correctout, HexStr(realout) + std::string(" != ") + hexout); // Decrypt the cipher and verify that it equals the plaintext std::vector<unsigned char> decrypted(correctout.size()); - AES256CBCDecrypt dec(&key[0], &iv[0], pad); - size = dec.Decrypt(&correctout[0], correctout.size(), &decrypted[0]); + AES256CBCDecrypt dec(key.data(), iv.data(), pad); + size = dec.Decrypt(correctout.data(), correctout.size(), decrypted.data()); decrypted.resize(size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(decrypted == in, HexStr(decrypted) + std::string(" != ") + hexin); @@ -174,12 +174,12 @@ void TestAES256CBC(const std::string &hexkey, const std::string &hexiv, bool pad { std::vector<unsigned char> sub(i, in.end()); std::vector<unsigned char> subout(sub.size() + AES_BLOCKSIZE); - int _size = enc.Encrypt(&sub[0], sub.size(), &subout[0]); + int _size = enc.Encrypt(sub.data(), sub.size(), subout.data()); if (_size != 0) { subout.resize(_size); std::vector<unsigned char> subdecrypted(subout.size()); - _size = dec.Decrypt(&subout[0], subout.size(), &subdecrypted[0]); + _size = dec.Decrypt(subout.data(), subout.size(), subdecrypted.data()); subdecrypted.resize(_size); BOOST_CHECK(decrypted.size() == in.size()); BOOST_CHECK_MESSAGE(subdecrypted == sub, HexStr(subdecrypted) + std::string(" != ") + HexStr(sub)); diff --git a/src/test/data/base58_keys_invalid.json b/src/test/data/base58_keys_invalid.json index a088620f1b..2056c7491c 100644 --- a/src/test/data/base58_keys_invalid.json +++ b/src/test/data/base58_keys_invalid.json @@ -148,5 +148,35 @@ ], [ "2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED" + ], + [ + "tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty" + ], + [ + "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5" + ], + [ + "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2" + ], + [ + "bc1rw5uspcuh" + ], + [ + "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90" + ], + [ + "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P" + ], + [ + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7" + ], + [ + "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du" + ], + [ + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv" + ], + [ + "bc1gmk9yu" ] ] diff --git a/src/test/data/base58_keys_valid.json b/src/test/data/base58_keys_valid.json index e1e252e22d..8418a6002d 100644 --- a/src/test/data/base58_keys_valid.json +++ b/src/test/data/base58_keys_valid.json @@ -1,452 +1,533 @@ [ [ - "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", - "65a16059864a2fdbc7c99a4723a8395bc6f188eb", + "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", + "76a91465a16059864a2fdbc7c99a4723a8395bc6f188eb88ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", - "74f209f6ea907e2ea48f74fae05782ae8a665257", + "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", + "a91474f209f6ea907e2ea48f74fae05782ae8a66525787", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", - "53c0307d6851aa0ce7825ba883c6bd9ad242b486", + "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", + "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", - "6349a418fc4578d10a372b54b45c280cc8c4382f", + "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", + "76a91453c0307d6851aa0ce7825ba883c6bd9ad242b48688ac", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "regtest" } - ], + ], [ - "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr", - "eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19", + "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", + "a9146349a418fc4578d10a372b54b45c280cc8c4382f87", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD", - "55c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c4", + "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr", + "eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", - "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", + "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD", + "55c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c4", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", - "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3", + "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", + "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ", - "6d23156cbbdcc82a5a47eee4c2c7c583c18b6bf4", + "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", + "36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "regtest" } - ], + ], [ - "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy", - "fcc5460dd6e2487c7d75b1963625da0e8f4c5975", + "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", + "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ", - "f1d470f9b02370fdec2e6b708b08ac431bf7a5f7", + "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", + "b9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f3", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "regtest" } - ], + ], [ - "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n", - "c579342c2c4c9220205e2cdc285617040c924a0a", + "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ", + "76a9146d23156cbbdcc82a5a47eee4c2c7c583c18b6bf488ac", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc", - "a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e", + "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy", + "a914fcc5460dd6e2487c7d75b1963625da0e8f4c597587", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi", - "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb4", + "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ", + "76a914f1d470f9b02370fdec2e6b708b08ac431bf7a5f788ac", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj", - "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203", + "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n", + "a914c579342c2c4c9220205e2cdc285617040c924a0a87", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN", - "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9", + "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc", + "a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv", - "7987ccaa53d02c8873487ef919677cd3db7a6912", + "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi", + "7d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb4", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks", - "63bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb", + "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj", + "d6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk", - "ef66444b5b17f14e8fae6e7e19b045a78c54fd79", + "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN", + "a81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d9", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o", - "c3e55fceceaa4391ed2a9677f4a4d34eacd021a0", + "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv", + "76a9147987ccaa53d02c8873487ef919677cd3db7a691288ac", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9", - "e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252", + "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks", + "a91463bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb87", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT", - "8248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c", + "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk", + "76a914ef66444b5b17f14e8fae6e7e19b045a78c54fd7988ac", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo", - "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52", + "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o", + "a914c3e55fceceaa4391ed2a9677f4a4d34eacd021a087", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7", - "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69", + "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9", + "e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu", - "adc1cc2081a27206fae25792f28bbc55b831549d", + "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT", + "8248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk", - "188f91a931947eddd7432d6e614387e32b244709", + "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo", + "44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H", - "1694f5bc1a7295b600f40018a618a6ea48eeb498", + "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7", + "d1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c69", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN", - "3b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f3", + "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu", + "76a914adc1cc2081a27206fae25792f28bbc55b831549d88ac", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR", - "091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0", + "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk", + "a914188f91a931947eddd7432d6e614387e32b24470987", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8", - "ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af", + "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H", + "76a9141694f5bc1a7295b600f40018a618a6ea48eeb49888ac", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", - "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", + "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN", + "a9143b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f387", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA", - "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef", + "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR", + "091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4", - "c4c1b72491ede1eedaca00618407ee0b772cad0d", + "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8", + "ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y", - "f6fe69bcb548a829cce4c57bf6fff8af3a5981f9", + "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", + "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6", - "261f83568a098a8638844bd7aeca039d5f2352c0", + "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", + "b4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "regtest" } - ], + ], [ - "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda", - "e930e1834a4d234702773951d627cce82fbb5d2e", + "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA", + "e7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg", - "d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0", + "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4", + "76a914c4c1b72491ede1eedaca00618407ee0b772cad0d88ac", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi", - "b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b3", + "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y", + "a914f6fe69bcb548a829cce4c57bf6fff8af3a5981f987", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys", - "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb", + "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6", + "76a914261f83568a098a8638844bd7aeca039d5f2352c088ac", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw", - "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de", + "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda", + "a914e930e1834a4d234702773951d627cce82fbb5d2e87", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r", - "5eadaf9bb7121f0f192561a5a62f5e5f54210292", + "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg", + "d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3", - "3f210e7277c899c3a155cc1c90f4106cbddeec6e", + "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi", + "b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b3", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "myoqcgYiehufrsnnkqdqbp69dddVDMopJu", - "c8a3c2a09a298592c3e180f02487cd91ba3400b5", + "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys", + "037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": true + "isCompressed": false, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C", - "99b31df7c9068d1481b596578ddbb4d3bd90baeb", + "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw", + "6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": true + "isCompressed": true, + "isPrivkey": true, + "chain": "test" } - ], + ], [ - "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4", - "c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae", + "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r", + "76a9145eadaf9bb7121f0f192561a5a62f5e5f5421029288ac", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2", - "07f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd", + "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3", + "a9143f210e7277c899c3a155cc1c90f4106cbddeec6e87", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": false + "isPrivkey": false, + "chain": "main" } - ], + ], [ - "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV", - "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801", + "myoqcgYiehufrsnnkqdqbp69dddVDMopJu", + "76a914c8a3c2a09a298592c3e180f02487cd91ba3400b588ac", { - "isCompressed": false, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h", - "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c", + "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C", + "a91499b31df7c9068d1481b596578ddbb4d3bd90baeb87", { - "isCompressed": true, - "isPrivkey": true, - "isTestnet": true + "isPrivkey": false, + "chain": "test" } - ], + ], [ - "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE", - "1ed467017f043e91ed4c44b4e8dd674db211c4e6", + "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4", + "c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae", { - "addrType": "pubkey", - "isPrivkey": false, - "isTestnet": false + "isCompressed": false, + "isPrivkey": true, + "chain": "main" } - ], + ], [ - "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G", - "5ece0cadddc415b1980f001785947120acdb36fc", + "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2", + "07f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd", { - "addrType": "script", - "isPrivkey": false, - "isTestnet": false + "isCompressed": true, + "isPrivkey": true, + "chain": "main" + } + ], + [ + "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV", + "ea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801", + { + "isCompressed": false, + "isPrivkey": true, + "chain": "test" + } + ], + [ + "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h", + "0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c", + { + "isCompressed": true, + "isPrivkey": true, + "chain": "test" + } + ], + [ + "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE", + "76a9141ed467017f043e91ed4c44b4e8dd674db211c4e688ac", + { + "isPrivkey": false, + "chain": "main" + } + ], + [ + "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G", + "a9145ece0cadddc415b1980f001785947120acdb36fc87", + { + "isPrivkey": false, + "chain": "main" + } + ], + [ + "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", + "0014751e76e8199196d454941c45d1b3a323f1433bd6", + { + "isPrivkey": false, + "chain": "main", + "tryCaseFlip": true + } + ], + [ + "bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kygt080", + "0014751e76e8199196d454941c45d1b3a323f1433bd6", + { + "isPrivkey": false, + "chain": "regtest", + "tryCaseFlip": true + } + ], + [ + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", + "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", + { + "isPrivkey": false, + "chain": "test", + "tryCaseFlip": true + } + ], + [ + "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx", + "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6", + { + "isPrivkey": false, + "chain": "main", + "tryCaseFlip": true + } + ], + [ + "bc1sw50qa3jx3s", + "6002751e", + { + "isPrivkey": false, + "chain": "main", + "tryCaseFlip": true + } + ], + [ + "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", + "5210751e76e8199196d454941c45d1b3a323", + { + "isPrivkey": false, + "chain": "main", + "tryCaseFlip": true + } + ], + [ + "tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", + "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", + { + "isPrivkey": false, + "chain": "test", + "tryCaseFlip": true + } + ], + [ + "bcrt1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvseswlauz7", + "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", + { + "isPrivkey": false, + "chain": "regtest", + "tryCaseFlip": true } ] ] diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp index 251d5a7142..efddafe17e 100644 --- a/src/test/dbwrapper_tests.cpp +++ b/src/test/dbwrapper_tests.cpp @@ -204,19 +204,31 @@ BOOST_AUTO_TEST_CASE(iterator_ordering) for (int x=0x00; x<256; ++x) { uint8_t key = x; uint32_t value = x*x; - BOOST_CHECK(dbw.Write(key, value)); + if (!(x & 1)) BOOST_CHECK(dbw.Write(key, value)); } + // Check that creating an iterator creates a snapshot std::unique_ptr<CDBIterator> it(const_cast<CDBWrapper&>(dbw).NewIterator()); + + for (int x=0x00; x<256; ++x) { + uint8_t key = x; + uint32_t value = x*x; + if (x & 1) BOOST_CHECK(dbw.Write(key, value)); + } + for (int seek_start : {0x00, 0x80}) { it->Seek((uint8_t)seek_start); - for (int x=seek_start; x<256; ++x) { + for (int x=seek_start; x<255; ++x) { uint8_t key; uint32_t value; BOOST_CHECK(it->Valid()); if (!it->Valid()) // Avoid spurious errors about invalid iterator's key and value in case of failure break; BOOST_CHECK(it->GetKey(key)); + if (x & 1) { + BOOST_CHECK_EQUAL(key, x + 1); + continue; + } BOOST_CHECK(it->GetValue(value)); BOOST_CHECK_EQUAL(key, x); BOOST_CHECK_EQUAL(value, x*x); diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index 18a7e59933..40f0ecd5f1 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -27,7 +27,7 @@ static void ResetArgs(const std::string& strArg) for (std::string& s : vecArg) vecChar.push_back(s.c_str()); - gArgs.ParseParameters(vecChar.size(), &vecChar[0]); + gArgs.ParseParameters(vecChar.size(), vecChar.data()); } BOOST_AUTO_TEST_CASE(boolarg) diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp index 559b3caf1c..91c0175412 100644 --- a/src/test/key_tests.cpp +++ b/src/test/key_tests.cpp @@ -16,17 +16,16 @@ #include <boost/test/unit_test.hpp> -static const std::string strSecret1 ("5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"); -static const std::string strSecret2 ("5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"); -static const std::string strSecret1C ("Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"); -static const std::string strSecret2C ("L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"); -static const CBitcoinAddress addr1 ("1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ"); -static const CBitcoinAddress addr2 ("1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ"); -static const CBitcoinAddress addr1C("1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs"); -static const CBitcoinAddress addr2C("1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs"); +static const std::string strSecret1 = "5HxWvvfubhXpYYpS3tJkw6fq9jE9j18THftkZjHHfmFiWtmAbrj"; +static const std::string strSecret2 = "5KC4ejrDjv152FGwP386VD1i2NYc5KkfSMyv1nGy1VGDxGHqVY3"; +static const std::string strSecret1C = "Kwr371tjA9u2rFSMZjTNun2PXXP3WPZu2afRHTcta6KxEUdm1vEw"; +static const std::string strSecret2C = "L3Hq7a8FEQwJkW1M2GNKDW28546Vp5miewcCzSqUD9kCAXrJdS3g"; +static const std::string addr1 = "1QFqqMUD55ZV3PJEJZtaKCsQmjLT6JkjvJ"; +static const std::string addr2 = "1F5y5E5FMc5YzdJtB9hLaUe43GDxEKXENJ"; +static const std::string addr1C = "1NoJrossxPBKfCHuJXT4HadJrXRE9Fxiqs"; +static const std::string addr2C = "1CRj2HyM1CXWzHAXLQtiGLyggNT9WQqsDs"; - -static const std::string strAddressBad("1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF"); +static const std::string strAddressBad = "1HV9Lc3sNHZxwj4Zk6fB38tEmBryq2cBiF"; BOOST_FIXTURE_TEST_SUITE(key_tests, BasicTestingSetup) @@ -74,10 +73,10 @@ BOOST_AUTO_TEST_CASE(key_test1) BOOST_CHECK(!key2C.VerifyPubKey(pubkey2)); BOOST_CHECK(key2C.VerifyPubKey(pubkey2C)); - BOOST_CHECK(addr1.Get() == CTxDestination(pubkey1.GetID())); - BOOST_CHECK(addr2.Get() == CTxDestination(pubkey2.GetID())); - BOOST_CHECK(addr1C.Get() == CTxDestination(pubkey1C.GetID())); - BOOST_CHECK(addr2C.Get() == CTxDestination(pubkey2C.GetID())); + BOOST_CHECK(DecodeDestination(addr1) == CTxDestination(pubkey1.GetID())); + BOOST_CHECK(DecodeDestination(addr2) == CTxDestination(pubkey2.GetID())); + BOOST_CHECK(DecodeDestination(addr1C) == CTxDestination(pubkey1C.GetID())); + BOOST_CHECK(DecodeDestination(addr2C) == CTxDestination(pubkey2C.GetID())); for (int n=0; n<16; n++) { diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index 51b28d09fa..116210a297 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -559,15 +559,15 @@ BOOST_AUTO_TEST_CASE(MempoolSizeLimitTest) // ... we should keep the same min fee until we get a block pool.removeForBlock(vtx, 1); SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE); - BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/2); + BOOST_CHECK_EQUAL(pool.GetMinFee(1).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/2.0)); // ... then feerate should drop 1/2 each halflife SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2); - BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/4); + BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 5 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/4.0)); // ... with a 1/2 halflife when mempool is < 1/2 its target size SetMockTime(42 + 2*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); - BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), (maxFeeRateRemoved.GetFeePerK() + 1000)/8); + BOOST_CHECK_EQUAL(pool.GetMinFee(pool.DynamicMemoryUsage() * 9 / 2).GetFeePerK(), llround((maxFeeRateRemoved.GetFeePerK() + 1000)/8.0)); // ... with a 1/4 halflife when mempool is < 1/4 its target size SetMockTime(42 + 7*CTxMemPool::ROLLING_FEE_HALFLIFE + CTxMemPool::ROLLING_FEE_HALFLIFE/2 + CTxMemPool::ROLLING_FEE_HALFLIFE/4); diff --git a/src/test/merkleblock_tests.cpp b/src/test/merkleblock_tests.cpp new file mode 100644 index 0000000000..3e66c6f2c6 --- /dev/null +++ b/src/test/merkleblock_tests.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2012-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "merkleblock.h" +#include "uint256.h" +#include "test/test_bitcoin.h" + +#include <boost/test/unit_test.hpp> + + +BOOST_FIXTURE_TEST_SUITE(merkleblock_tests, BasicTestingSetup) + +/** + * Create a CMerkleBlock using a list of txids which will be found in the + * given block. + */ +BOOST_AUTO_TEST_CASE(merkleblock_construct_from_txids_found) +{ + CBlock block = getBlock13b8a(); + + std::set<uint256> txids; + + // Last txn in block. + uint256 txhash1 = uint256S("0x74d681e0e03bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20"); + + // Second txn in block. + uint256 txhash2 = uint256S("0xf9fc751cb7dc372406a9f8d738d5e6f8f63bab71986a39cf36ee70ee17036d07"); + + txids.insert(txhash1); + txids.insert(txhash2); + + CMerkleBlock merkleBlock(block, txids); + + BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex()); + + // vMatchedTxn is only used when bloom filter is specified. + BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 0); + + std::vector<uint256> vMatched; + std::vector<unsigned int> vIndex; + + BOOST_CHECK_EQUAL(merkleBlock.txn.ExtractMatches(vMatched, vIndex).GetHex(), block.hashMerkleRoot.GetHex()); + BOOST_CHECK_EQUAL(vMatched.size(), 2); + + // Ordered by occurrence in depth-first tree traversal. + BOOST_CHECK_EQUAL(vMatched[0].ToString(), txhash2.ToString()); + BOOST_CHECK_EQUAL(vIndex[0], 1); + + BOOST_CHECK_EQUAL(vMatched[1].ToString(), txhash1.ToString()); + BOOST_CHECK_EQUAL(vIndex[1], 8); +} + + +/** + * Create a CMerkleBlock using a list of txids which will not be found in the + * given block. + */ +BOOST_AUTO_TEST_CASE(merkleblock_construct_from_txids_not_found) +{ + CBlock block = getBlock13b8a(); + + std::set<uint256> txids2; + txids2.insert(uint256S("0xc0ffee00003bafa802c8aa084379aa98d9fcd632ddc2ed9782b586ec87451f20")); + CMerkleBlock merkleBlock(block, txids2); + + BOOST_CHECK_EQUAL(merkleBlock.header.GetHash().GetHex(), block.GetHash().GetHex()); + BOOST_CHECK_EQUAL(merkleBlock.vMatchedTxn.size(), 0); + + std::vector<uint256> vMatched; + std::vector<unsigned int> vIndex; + + BOOST_CHECK_EQUAL(merkleBlock.txn.ExtractMatches(vMatched, vIndex).GetHex(), block.hashMerkleRoot.GetHex()); + BOOST_CHECK_EQUAL(vMatched.size(), 0); + BOOST_CHECK_EQUAL(vIndex.size(), 0); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 9fa9a8509c..41e0626eb9 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -32,7 +32,6 @@ static BlockAssembler AssemblerForTest(const CChainParams& params) { BlockAssembler::Options options; options.nBlockMaxWeight = MAX_BLOCK_WEIGHT; - options.nBlockMaxSize = MAX_BLOCK_SERIALIZED_SIZE; options.blockMinFeeRate = blockMinFeeRate; return BlockAssembler(params, options); } diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 5e89ef60d2..de7f3b48f5 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -16,8 +16,6 @@ #include <boost/test/unit_test.hpp> -typedef std::vector<unsigned char> valtype; - BOOST_FIXTURE_TEST_SUITE(multisig_tests, BasicTestingSetup) CScript @@ -173,95 +171,6 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard) BOOST_CHECK(!::IsStandard(malformed[i], whichType)); } -BOOST_AUTO_TEST_CASE(multisig_Solver1) -{ - // Tests Solver() that returns lists of keys that are - // required to satisfy a ScriptPubKey - // - // Also tests IsMine() and ExtractDestination() - // - // Note: ExtractDestination for the multisignature transactions - // always returns false for this release, even if you have - // one key that would satisfy an (a|b) or 2-of-3 keys needed - // to spend an escrow transaction. - // - CBasicKeyStore keystore, emptykeystore, partialkeystore; - CKey key[3]; - CTxDestination keyaddr[3]; - for (int i = 0; i < 3; i++) - { - key[i].MakeNewKey(true); - keystore.AddKey(key[i]); - keyaddr[i] = key[i].GetPubKey().GetID(); - } - partialkeystore.AddKey(key[0]); - - { - std::vector<valtype> solutions; - txnouttype whichType; - CScript s; - s << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK(solutions.size() == 1); - CTxDestination addr; - BOOST_CHECK(ExtractDestination(s, addr)); - BOOST_CHECK(addr == keyaddr[0]); - BOOST_CHECK(IsMine(keystore, s)); - BOOST_CHECK(!IsMine(emptykeystore, s)); - } - { - std::vector<valtype> solutions; - txnouttype whichType; - CScript s; - s << OP_DUP << OP_HASH160 << ToByteVector(key[0].GetPubKey().GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK(solutions.size() == 1); - CTxDestination addr; - BOOST_CHECK(ExtractDestination(s, addr)); - BOOST_CHECK(addr == keyaddr[0]); - BOOST_CHECK(IsMine(keystore, s)); - BOOST_CHECK(!IsMine(emptykeystore, s)); - } - { - std::vector<valtype> solutions; - txnouttype whichType; - CScript s; - s << OP_2 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(solutions.size(), 4U); - CTxDestination addr; - BOOST_CHECK(!ExtractDestination(s, addr)); - BOOST_CHECK(IsMine(keystore, s)); - BOOST_CHECK(!IsMine(emptykeystore, s)); - BOOST_CHECK(!IsMine(partialkeystore, s)); - } - { - std::vector<valtype> solutions; - txnouttype whichType; - CScript s; - s << OP_1 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK_EQUAL(solutions.size(), 4U); - std::vector<CTxDestination> addrs; - int nRequired; - BOOST_CHECK(ExtractDestinations(s, whichType, addrs, nRequired)); - BOOST_CHECK(addrs[0] == keyaddr[0]); - BOOST_CHECK(addrs[1] == keyaddr[1]); - BOOST_CHECK(nRequired == 1); - BOOST_CHECK(IsMine(keystore, s)); - BOOST_CHECK(!IsMine(emptykeystore, s)); - BOOST_CHECK(!IsMine(partialkeystore, s)); - } - { - std::vector<valtype> solutions; - txnouttype whichType; - CScript s; - s << OP_2 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << ToByteVector(key[2].GetPubKey()) << OP_3 << OP_CHECKMULTISIG; - BOOST_CHECK(Solver(s, whichType, solutions)); - BOOST_CHECK(solutions.size() == 5); - } -} - BOOST_AUTO_TEST_CASE(multisig_Sign) { // Test SignSignature() (and therefore the version of Solver() that signs transactions) diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index efd0f77d9f..58aa32c969 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -112,8 +112,7 @@ BOOST_AUTO_TEST_CASE(sign) { CScript sigSave = txTo[i].vin[0].scriptSig; txTo[i].vin[0].scriptSig = txTo[j].vin[0].scriptSig; - const CTxOut& output = txFrom.vout[txTo[i].vin[0].prevout.n]; - bool sigOK = CScriptCheck(output.scriptPubKey, output.nValue, txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)(); + bool sigOK = CScriptCheck(txFrom.vout[txTo[i].vin[0].prevout.n], txTo[i], 0, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, false, &txdata)(); if (i == j) BOOST_CHECK_MESSAGE(sigOK, strprintf("VerifySignature %d %d", i, j)); else diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp new file mode 100644 index 0000000000..bd2d9ed115 --- /dev/null +++ b/src/test/script_standard_tests.cpp @@ -0,0 +1,741 @@ +// Copyright (c) 2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "key.h" +#include "keystore.h" +#include "script/ismine.h" +#include "script/script.h" +#include "script/script_error.h" +#include "script/standard.h" +#include "test/test_bitcoin.h" + +#include <boost/test/unit_test.hpp> + + +BOOST_FIXTURE_TEST_SUITE(script_standard_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(script_standard_Solver_success) +{ + CKey keys[3]; + CPubKey pubkeys[3]; + for (int i = 0; i < 3; i++) { + keys[i].MakeNewKey(true); + pubkeys[i] = keys[i].GetPubKey(); + } + + CScript s; + txnouttype whichType; + std::vector<std::vector<unsigned char> > solutions; + + // TX_PUBKEY + s.clear(); + s << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_PUBKEY); + BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0])); + + // TX_PUBKEYHASH + s.clear(); + s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH); + BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID())); + + // TX_SCRIPTHASH + CScript redeemScript(s); // initialize with leftover P2PKH script + s.clear(); + s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH); + BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK(solutions[0] == ToByteVector(CScriptID(redeemScript))); + + // TX_MULTISIG + s.clear(); + s << OP_1 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); + BOOST_CHECK_EQUAL(solutions.size(), 4); + BOOST_CHECK(solutions[0] == std::vector<unsigned char>({1})); + BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0])); + BOOST_CHECK(solutions[2] == ToByteVector(pubkeys[1])); + BOOST_CHECK(solutions[3] == std::vector<unsigned char>({2})); + + s.clear(); + s << OP_2 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + ToByteVector(pubkeys[2]) << + OP_3 << OP_CHECKMULTISIG; + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); + BOOST_CHECK_EQUAL(solutions.size(), 5); + BOOST_CHECK(solutions[0] == std::vector<unsigned char>({2})); + BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0])); + BOOST_CHECK(solutions[2] == ToByteVector(pubkeys[1])); + BOOST_CHECK(solutions[3] == ToByteVector(pubkeys[2])); + BOOST_CHECK(solutions[4] == std::vector<unsigned char>({3})); + + // TX_NULL_DATA + s.clear(); + s << OP_RETURN << + std::vector<unsigned char>({0}) << + std::vector<unsigned char>({75}) << + std::vector<unsigned char>({255}); + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_NULL_DATA); + BOOST_CHECK_EQUAL(solutions.size(), 0); + + // TX_WITNESS_V0_KEYHASH + s.clear(); + s << OP_0 << ToByteVector(pubkeys[0].GetID()); + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_WITNESS_V0_KEYHASH); + BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID())); + + // TX_WITNESS_V0_SCRIPTHASH + uint256 scriptHash; + CSHA256().Write(&redeemScript[0], redeemScript.size()) + .Finalize(scriptHash.begin()); + + s.clear(); + s << OP_0 << ToByteVector(scriptHash); + BOOST_CHECK(Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_WITNESS_V0_SCRIPTHASH); + BOOST_CHECK_EQUAL(solutions.size(), 1); + BOOST_CHECK(solutions[0] == ToByteVector(scriptHash)); + + // TX_NONSTANDARD + s.clear(); + s << OP_9 << OP_ADD << OP_11 << OP_EQUAL; + BOOST_CHECK(!Solver(s, whichType, solutions)); + BOOST_CHECK_EQUAL(whichType, TX_NONSTANDARD); +} + +BOOST_AUTO_TEST_CASE(script_standard_Solver_failure) +{ + CKey key; + CPubKey pubkey; + key.MakeNewKey(true); + pubkey = key.GetPubKey(); + + CScript s; + txnouttype whichType; + std::vector<std::vector<unsigned char> > solutions; + + // TX_PUBKEY with incorrectly sized pubkey + s.clear(); + s << std::vector<unsigned char>(30, 0x01) << OP_CHECKSIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_PUBKEYHASH with incorrectly sized key hash + s.clear(); + s << OP_DUP << OP_HASH160 << ToByteVector(pubkey) << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_SCRIPTHASH with incorrectly sized script hash + s.clear(); + s << OP_HASH160 << std::vector<unsigned char>(21, 0x01) << OP_EQUAL; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_MULTISIG 0/2 + s.clear(); + s << OP_0 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_MULTISIG 2/1 + s.clear(); + s << OP_2 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_MULTISIG n = 2 with 1 pubkey + s.clear(); + s << OP_1 << ToByteVector(pubkey) << OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_MULTISIG n = 1 with 0 pubkeys + s.clear(); + s << OP_1 << OP_1 << OP_CHECKMULTISIG; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_NULL_DATA with other opcodes + s.clear(); + s << OP_RETURN << std::vector<unsigned char>({75}) << OP_ADD; + BOOST_CHECK(!Solver(s, whichType, solutions)); + + // TX_WITNESS with incorrect program size + s.clear(); + s << OP_0 << std::vector<unsigned char>(19, 0x01); + BOOST_CHECK(!Solver(s, whichType, solutions)); +} + +BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination) +{ + CKey key; + CPubKey pubkey; + key.MakeNewKey(true); + pubkey = key.GetPubKey(); + + CScript s; + CTxDestination address; + + // TX_PUBKEY + s.clear(); + s << ToByteVector(pubkey) << OP_CHECKSIG; + BOOST_CHECK(ExtractDestination(s, address)); + BOOST_CHECK(boost::get<CKeyID>(&address) && + *boost::get<CKeyID>(&address) == pubkey.GetID()); + + // TX_PUBKEYHASH + s.clear(); + s << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(ExtractDestination(s, address)); + BOOST_CHECK(boost::get<CKeyID>(&address) && + *boost::get<CKeyID>(&address) == pubkey.GetID()); + + // TX_SCRIPTHASH + CScript redeemScript(s); // initialize with leftover P2PKH script + s.clear(); + s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + BOOST_CHECK(ExtractDestination(s, address)); + BOOST_CHECK(boost::get<CScriptID>(&address) && + *boost::get<CScriptID>(&address) == CScriptID(redeemScript)); + + // TX_MULTISIG + s.clear(); + s << OP_1 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG; + BOOST_CHECK(!ExtractDestination(s, address)); + + // TX_NULL_DATA + s.clear(); + s << OP_RETURN << std::vector<unsigned char>({75}); + BOOST_CHECK(!ExtractDestination(s, address)); + + // TX_WITNESS_V0_KEYHASH + s.clear(); + s << OP_0 << ToByteVector(pubkey.GetID()); + BOOST_CHECK(ExtractDestination(s, address)); + WitnessV0KeyHash keyhash; + CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(keyhash.begin()); + BOOST_CHECK(boost::get<WitnessV0KeyHash>(&address) && *boost::get<WitnessV0KeyHash>(&address) == keyhash); + + // TX_WITNESS_V0_SCRIPTHASH + s.clear(); + WitnessV0ScriptHash scripthash; + CSHA256().Write(redeemScript.data(), redeemScript.size()).Finalize(scripthash.begin()); + s << OP_0 << ToByteVector(scripthash); + BOOST_CHECK(ExtractDestination(s, address)); + BOOST_CHECK(boost::get<WitnessV0ScriptHash>(&address) && *boost::get<WitnessV0ScriptHash>(&address) == scripthash); + + // TX_WITNESS with unknown version + s.clear(); + s << OP_1 << ToByteVector(pubkey); + BOOST_CHECK(ExtractDestination(s, address)); + WitnessUnknown unk; + unk.length = 33; + unk.version = 1; + std::copy(pubkey.begin(), pubkey.end(), unk.program); + BOOST_CHECK(boost::get<WitnessUnknown>(&address) && *boost::get<WitnessUnknown>(&address) == unk); +} + +BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations) +{ + CKey keys[3]; + CPubKey pubkeys[3]; + for (int i = 0; i < 3; i++) { + keys[i].MakeNewKey(true); + pubkeys[i] = keys[i].GetPubKey(); + } + + CScript s; + txnouttype whichType; + std::vector<CTxDestination> addresses; + int nRequired; + + // TX_PUBKEY + s.clear(); + s << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); + BOOST_CHECK_EQUAL(whichType, TX_PUBKEY); + BOOST_CHECK_EQUAL(addresses.size(), 1); + BOOST_CHECK_EQUAL(nRequired, 1); + BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) && + *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID()); + + // TX_PUBKEYHASH + s.clear(); + s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); + BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH); + BOOST_CHECK_EQUAL(addresses.size(), 1); + BOOST_CHECK_EQUAL(nRequired, 1); + BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) && + *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID()); + + // TX_SCRIPTHASH + CScript redeemScript(s); // initialize with leftover P2PKH script + s.clear(); + s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); + BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH); + BOOST_CHECK_EQUAL(addresses.size(), 1); + BOOST_CHECK_EQUAL(nRequired, 1); + BOOST_CHECK(boost::get<CScriptID>(&addresses[0]) && + *boost::get<CScriptID>(&addresses[0]) == CScriptID(redeemScript)); + + // TX_MULTISIG + s.clear(); + s << OP_2 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired)); + BOOST_CHECK_EQUAL(whichType, TX_MULTISIG); + BOOST_CHECK_EQUAL(addresses.size(), 2); + BOOST_CHECK_EQUAL(nRequired, 2); + BOOST_CHECK(boost::get<CKeyID>(&addresses[0]) && + *boost::get<CKeyID>(&addresses[0]) == pubkeys[0].GetID()); + BOOST_CHECK(boost::get<CKeyID>(&addresses[1]) && + *boost::get<CKeyID>(&addresses[1]) == pubkeys[1].GetID()); + + // TX_NULL_DATA + s.clear(); + s << OP_RETURN << std::vector<unsigned char>({75}); + BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired)); +} + +BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_) +{ + CKey keys[3]; + CPubKey pubkeys[3]; + for (int i = 0; i < 3; i++) { + keys[i].MakeNewKey(true); + pubkeys[i] = keys[i].GetPubKey(); + } + + CScript expected, result; + + // CKeyID + expected.clear(); + expected << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + result = GetScriptForDestination(pubkeys[0].GetID()); + BOOST_CHECK(result == expected); + + // CScriptID + CScript redeemScript(result); + expected.clear(); + expected << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + result = GetScriptForDestination(CScriptID(redeemScript)); + BOOST_CHECK(result == expected); + + // CNoDestination + expected.clear(); + result = GetScriptForDestination(CNoDestination()); + BOOST_CHECK(result == expected); + + // GetScriptForRawPubKey + expected.clear(); + expected << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + result = GetScriptForRawPubKey(pubkeys[0]); + BOOST_CHECK(result == expected); + + // GetScriptForMultisig + expected.clear(); + expected << OP_2 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + ToByteVector(pubkeys[2]) << + OP_3 << OP_CHECKMULTISIG; + result = GetScriptForMultisig(2, std::vector<CPubKey>(pubkeys, pubkeys + 3)); + BOOST_CHECK(result == expected); + + // GetScriptForWitness + CScript witnessScript; + + witnessScript << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + expected.clear(); + expected << OP_0 << ToByteVector(pubkeys[0].GetID()); + result = GetScriptForWitness(witnessScript); + BOOST_CHECK(result == expected); + + witnessScript.clear(); + witnessScript << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + result = GetScriptForWitness(witnessScript); + BOOST_CHECK(result == expected); + + witnessScript.clear(); + witnessScript << OP_1 << ToByteVector(pubkeys[0]) << OP_1 << OP_CHECKMULTISIG; + + uint256 scriptHash; + CSHA256().Write(&witnessScript[0], witnessScript.size()) + .Finalize(scriptHash.begin()); + + expected.clear(); + expected << OP_0 << ToByteVector(scriptHash); + result = GetScriptForWitness(witnessScript); + BOOST_CHECK(result == expected); +} + +BOOST_AUTO_TEST_CASE(script_standard_IsMine) +{ + CKey keys[2]; + CPubKey pubkeys[2]; + for (int i = 0; i < 2; i++) { + keys[i].MakeNewKey(true); + pubkeys[i] = keys[i].GetPubKey(); + } + + CKey uncompressedKey; + uncompressedKey.MakeNewKey(false); + CPubKey uncompressedPubkey = uncompressedKey.GetPubKey(); + + CScript scriptPubKey; + isminetype result; + bool isInvalid; + + // P2PK compressed + { + CBasicKeyStore keystore; + scriptPubKey.clear(); + scriptPubKey << ToByteVector(pubkeys[0]) << OP_CHECKSIG; + + // Keystore does not have key + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key + keystore.AddKey(keys[0]); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2PK uncompressed + { + CBasicKeyStore keystore; + scriptPubKey.clear(); + scriptPubKey << ToByteVector(uncompressedPubkey) << OP_CHECKSIG; + + // Keystore does not have key + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key + keystore.AddKey(uncompressedKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2PKH compressed + { + CBasicKeyStore keystore; + scriptPubKey.clear(); + scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + + // Keystore does not have key + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key + keystore.AddKey(keys[0]); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2PKH uncompressed + { + CBasicKeyStore keystore; + scriptPubKey.clear(); + scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(uncompressedPubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + + // Keystore does not have key + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key + keystore.AddKey(uncompressedKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2SH + { + CBasicKeyStore keystore; + + CScript redeemScript; + redeemScript << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; + + scriptPubKey.clear(); + scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + + // Keystore does not have redeemScript or key + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has redeemScript but no key + keystore.AddCScript(redeemScript); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has redeemScript and key + keystore.AddKey(keys[0]); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2WPKH compressed + { + CBasicKeyStore keystore; + keystore.AddKey(keys[0]); + + scriptPubKey.clear(); + scriptPubKey << OP_0 << ToByteVector(pubkeys[0].GetID()); + + // Keystore has key, but no P2SH redeemScript + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key and P2SH redeemScript + keystore.AddCScript(scriptPubKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2WPKH uncompressed + { + CBasicKeyStore keystore; + keystore.AddKey(uncompressedKey); + + scriptPubKey.clear(); + scriptPubKey << OP_0 << ToByteVector(uncompressedPubkey.GetID()); + + // Keystore has key, but no P2SH redeemScript + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has key and P2SH redeemScript + keystore.AddCScript(scriptPubKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(isInvalid); + } + + // scriptPubKey multisig + { + CBasicKeyStore keystore; + + scriptPubKey.clear(); + scriptPubKey << OP_2 << + ToByteVector(uncompressedPubkey) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + + // Keystore does not have any keys + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has 1/2 keys + keystore.AddKey(uncompressedKey); + + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has 2/2 keys + keystore.AddKey(keys[1]); + + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2SH multisig + { + CBasicKeyStore keystore; + keystore.AddKey(uncompressedKey); + keystore.AddKey(keys[1]); + + CScript redeemScript; + redeemScript << OP_2 << + ToByteVector(uncompressedPubkey) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + + scriptPubKey.clear(); + scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + + // Keystore has no redeemScript + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has redeemScript + keystore.AddCScript(redeemScript); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2WSH multisig with compressed keys + { + CBasicKeyStore keystore; + keystore.AddKey(keys[0]); + keystore.AddKey(keys[1]); + + CScript witnessScript; + witnessScript << OP_2 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + + uint256 scriptHash; + CSHA256().Write(&witnessScript[0], witnessScript.size()) + .Finalize(scriptHash.begin()); + + scriptPubKey.clear(); + scriptPubKey << OP_0 << ToByteVector(scriptHash); + + // Keystore has keys, but no witnessScript or P2SH redeemScript + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has keys and witnessScript, but no P2SH redeemScript + keystore.AddCScript(witnessScript); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has keys, witnessScript, P2SH redeemScript + keystore.AddCScript(scriptPubKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // P2WSH multisig with uncompressed key + { + CBasicKeyStore keystore; + keystore.AddKey(uncompressedKey); + keystore.AddKey(keys[1]); + + CScript witnessScript; + witnessScript << OP_2 << + ToByteVector(uncompressedPubkey) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + + uint256 scriptHash; + CSHA256().Write(&witnessScript[0], witnessScript.size()) + .Finalize(scriptHash.begin()); + + scriptPubKey.clear(); + scriptPubKey << OP_0 << ToByteVector(scriptHash); + + // Keystore has keys, but no witnessScript or P2SH redeemScript + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has keys and witnessScript, but no P2SH redeemScript + keystore.AddCScript(witnessScript); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has keys, witnessScript, P2SH redeemScript + keystore.AddCScript(scriptPubKey); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(isInvalid); + } + + // P2WSH multisig wrapped in P2SH + { + CBasicKeyStore keystore; + + CScript witnessScript; + witnessScript << OP_2 << + ToByteVector(pubkeys[0]) << + ToByteVector(pubkeys[1]) << + OP_2 << OP_CHECKMULTISIG; + + uint256 scriptHash; + CSHA256().Write(&witnessScript[0], witnessScript.size()) + .Finalize(scriptHash.begin()); + + CScript redeemScript; + redeemScript << OP_0 << ToByteVector(scriptHash); + + scriptPubKey.clear(); + scriptPubKey << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; + + // Keystore has no witnessScript, P2SH redeemScript, or keys + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has witnessScript and P2SH redeemScript, but no keys + keystore.AddCScript(redeemScript); + keystore.AddCScript(witnessScript); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + + // Keystore has keys, witnessScript, P2SH redeemScript + keystore.AddKey(keys[0]); + keystore.AddKey(keys[1]); + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_SPENDABLE); + BOOST_CHECK(!isInvalid); + } + + // OP_RETURN + { + CBasicKeyStore keystore; + keystore.AddKey(keys[0]); + + scriptPubKey.clear(); + scriptPubKey << OP_RETURN << ToByteVector(pubkeys[0]); + + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + } + + // Nonstandard + { + CBasicKeyStore keystore; + keystore.AddKey(keys[0]); + + scriptPubKey.clear(); + scriptPubKey << OP_9 << OP_ADD << OP_11 << OP_EQUAL; + + result = IsMine(keystore, scriptPubKey, isInvalid); + BOOST_CHECK_EQUAL(result, ISMINE_NO); + BOOST_CHECK(!isInvalid); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 17374edcc4..011a5db795 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -1451,4 +1451,21 @@ BOOST_AUTO_TEST_CASE(script_HasValidOps) BOOST_CHECK(!script.HasValidOps()); } +BOOST_AUTO_TEST_CASE(script_can_append_self) +{ + CScript s, d; + + s = ScriptFromHex("00"); + s += s; + d = ScriptFromHex("0000"); + BOOST_CHECK(s == d); + + // check doubling a script that's large enough to require reallocation + static const char hex[] = "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"; + s = CScript() << ParseHex(hex) << OP_CHECKSIG; + d = CScript() << ParseHex(hex) << OP_CHECKSIG << ParseHex(hex) << OP_CHECKSIG; + s += s; + BOOST_CHECK(s == d); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/scriptnum_tests.cpp b/src/test/scriptnum_tests.cpp index 1d5893bdc3..280eb59ce8 100644 --- a/src/test/scriptnum_tests.cpp +++ b/src/test/scriptnum_tests.cpp @@ -29,10 +29,7 @@ static void CheckCreateVch(const int64_t& num) CScriptNum scriptnum(num); BOOST_CHECK(verify(bignum, scriptnum)); - std::vector<unsigned char> vch = bignum.getvch(); - CScriptNum10 bignum2(bignum.getvch(), false); - vch = scriptnum.getvch(); CScriptNum scriptnum2(scriptnum.getvch(), false); BOOST_CHECK(verify(bignum2, scriptnum2)); @@ -90,11 +87,10 @@ static void CheckSubtract(const int64_t& num1, const int64_t& num2) const CScriptNum10 bignum2(num2); const CScriptNum scriptnum1(num1); const CScriptNum scriptnum2(num2); - bool invalid = false; // int64_t overflow is undefined. - invalid = ((num2 > 0 && num1 < std::numeric_limits<int64_t>::min() + num2) || - (num2 < 0 && num1 > std::numeric_limits<int64_t>::max() + num2)); + bool invalid = ((num2 > 0 && num1 < std::numeric_limits<int64_t>::min() + num2) || + (num2 < 0 && num1 > std::numeric_limits<int64_t>::max() + num2)); if (!invalid) { BOOST_CHECK(verify(bignum1 - bignum2, scriptnum1 - scriptnum2)); diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp index 1ca83a7cf8..ecbdf57788 100644 --- a/src/test/sighash_tests.cpp +++ b/src/test/sighash_tests.cpp @@ -124,11 +124,9 @@ BOOST_AUTO_TEST_CASE(sighash_test) #if defined(PRINT_SIGHASH_JSON) std::cout << "[\n"; std::cout << "\t[\"raw_transaction, script, input_index, hashType, signature_hash (result)\"],\n"; - #endif + int nRandomTests = 500; + #else int nRandomTests = 50000; - - #if defined(PRINT_SIGHASH_JSON) - nRandomTests = 500; #endif for (int i=0; i<nRandomTests; i++) { int nHashType = InsecureRand32(); diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp index e3654e67ad..164cbd873f 100644 --- a/src/test/skiplist_tests.cpp +++ b/src/test/skiplist_tests.cpp @@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(skiplist_test) BOOST_CHECK(vIndex[SKIPLIST_LENGTH - 1].GetAncestor(from) == &vIndex[from]); BOOST_CHECK(vIndex[from].GetAncestor(to) == &vIndex[to]); - BOOST_CHECK(vIndex[from].GetAncestor(0) == &vIndex[0]); + BOOST_CHECK(vIndex[from].GetAncestor(0) == vIndex.data()); } } @@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(getlocator_test) for (unsigned int i=0; i<vBlocksSide.size(); i++) { vHashSide[i] = ArithToUint256(i + 50000 + (arith_uint256(1) << 128)); // Add 1<<128 to the hashes, so GetLow64() still returns the height. vBlocksSide[i].nHeight = i + 50000; - vBlocksSide[i].pprev = i ? &vBlocksSide[i - 1] : &vBlocksMain[49999]; + vBlocksSide[i].pprev = i ? &vBlocksSide[i - 1] : (vBlocksMain.data()+49999); vBlocksSide[i].phashBlock = &vHashSide[i]; vBlocksSide[i].BuildSkip(); BOOST_CHECK_EQUAL((int)UintToArith256(vBlocksSide[i].GetBlockHash()).GetLow64(), vBlocksSide[i].nHeight); diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 94ec7c03f5..79bc48a118 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -18,12 +18,11 @@ #include "txdb.h" #include "txmempool.h" #include "ui_interface.h" +#include "streams.h" #include "rpc/server.h" #include "rpc/register.h" #include "script/sigcache.h" -#include "test/testutil.h" - #include <memory> uint256 insecure_rand_seed = GetRandHash(); @@ -50,7 +49,6 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName) BasicTestingSetup::~BasicTestingSetup() { ECC_Stop(); - g_connman.reset(); } TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(chainName) @@ -61,7 +59,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha RegisterAllCoreRPCCommands(tableRPC); ClearDatadirCache(); - pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(100000))); + pathTemp = fs::temp_directory_path() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(100000))); fs::create_directories(pathTemp); gArgs.ForceSetArg("-datadir", pathTemp.string()); @@ -88,16 +86,17 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha threadGroup.create_thread(&ThreadScriptCheck); g_connman = std::unique_ptr<CConnman>(new CConnman(0x1337, 0x1337)); // Deterministic randomness for tests. connman = g_connman.get(); - RegisterNodeSignals(GetNodeSignals()); + peerLogic.reset(new PeerLogicValidation(connman)); } TestingSetup::~TestingSetup() { - UnregisterNodeSignals(GetNodeSignals()); threadGroup.interrupt_all(); threadGroup.join_all(); GetMainSignals().FlushBackgroundCallbacks(); GetMainSignals().UnregisterBackgroundSignalScheduler(); + g_connman.reset(); + peerLogic.reset(); UnloadBlockIndex(); delete pcoinsTip; delete pcoinsdbview; @@ -160,3 +159,15 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransaction &txn) { return CTxMemPoolEntry(MakeTransactionRef(txn), nFee, nTime, nHeight, spendsCoinbase, sigOpCost, lp); } + +/** + * @returns a real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af) + * with 9 txs. + */ +CBlock getBlock13b8a() +{ + CBlock block; + CDataStream stream(ParseHex("0100000090f0a9f110702f808219ebea1173056042a714bad51b916cb6800000000000005275289558f51c9966699404ae2294730c3c9f9bda53523ce50e9b95e558da2fdb261b4d4c86041b1ab1bf930901000000010000000000000000000000000000000000000000000000000000000000000000ffffffff07044c86041b0146ffffffff0100f2052a01000000434104e18f7afbe4721580e81e8414fc8c24d7cfacf254bb5c7b949450c3e997c2dc1242487a8169507b631eb3771f2b425483fb13102c4eb5d858eef260fe70fbfae0ac00000000010000000196608ccbafa16abada902780da4dc35dafd7af05fa0da08cf833575f8cf9e836000000004a493046022100dab24889213caf43ae6adc41cf1c9396c08240c199f5225acf45416330fd7dbd022100fe37900e0644bf574493a07fc5edba06dbc07c311b947520c2d514bc5725dcb401ffffffff0100f2052a010000001976a914f15d1921f52e4007b146dfa60f369ed2fc393ce288ac000000000100000001fb766c1288458c2bafcfec81e48b24d98ec706de6b8af7c4e3c29419bfacb56d000000008c493046022100f268ba165ce0ad2e6d93f089cfcd3785de5c963bb5ea6b8c1b23f1ce3e517b9f022100da7c0f21adc6c401887f2bfd1922f11d76159cbc597fbd756a23dcbb00f4d7290141042b4e8625a96127826915a5b109852636ad0da753c9e1d5606a50480cd0c40f1f8b8d898235e571fe9357d9ec842bc4bba1827daaf4de06d71844d0057707966affffffff0280969800000000001976a9146963907531db72d0ed1a0cfb471ccb63923446f388ac80d6e34c000000001976a914f0688ba1c0d1ce182c7af6741e02658c7d4dfcd388ac000000000100000002c40297f730dd7b5a99567eb8d27b78758f607507c52292d02d4031895b52f2ff010000008b483045022100f7edfd4b0aac404e5bab4fd3889e0c6c41aa8d0e6fa122316f68eddd0a65013902205b09cc8b2d56e1cd1f7f2fafd60a129ed94504c4ac7bdc67b56fe67512658b3e014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffffca5065ff9617cbcba45eb23726df6498a9b9cafed4f54cbab9d227b0035ddefb000000008a473044022068010362a13c7f9919fa832b2dee4e788f61f6f5d344a7c2a0da6ae740605658022006d1af525b9a14a35c003b78b72bd59738cd676f845d1ff3fc25049e01003614014104732012cb962afa90d31b25d8fb0e32c94e513ab7a17805c14ca4c3423e18b4fb5d0e676841733cb83abaf975845c9f6f2a8097b7d04f4908b18368d6fc2d68ecffffffff01001ec4110200000043410469ab4181eceb28985b9b4e895c13fa5e68d85761b7eee311db5addef76fa8621865134a221bd01f28ec9999ee3e021e60766e9d1f3458c115fb28650605f11c9ac000000000100000001cdaf2f758e91c514655e2dc50633d1e4c84989f8aa90a0dbc883f0d23ed5c2fa010000008b48304502207ab51be6f12a1962ba0aaaf24a20e0b69b27a94fac5adf45aa7d2d18ffd9236102210086ae728b370e5329eead9accd880d0cb070aea0c96255fae6c4f1ddcce1fd56e014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff02404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac002d3101000000001976a9141befba0cdc1ad56529371864d9f6cb042faa06b588ac000000000100000001b4a47603e71b61bc3326efd90111bf02d2f549b067f4c4a8fa183b57a0f800cb010000008a4730440220177c37f9a505c3f1a1f0ce2da777c339bd8339ffa02c7cb41f0a5804f473c9230220585b25a2ee80eb59292e52b987dad92acb0c64eced92ed9ee105ad153cdb12d001410443bd44f683467e549dae7d20d1d79cbdb6df985c6e9c029c8d0c6cb46cc1a4d3cf7923c5021b27f7a0b562ada113bc85d5fda5a1b41e87fe6e8802817cf69996ffffffff0280651406000000001976a9145505614859643ab7b547cd7f1f5e7e2a12322d3788ac00aa0271000000001976a914ea4720a7a52fc166c55ff2298e07baf70ae67e1b88ac00000000010000000586c62cd602d219bb60edb14a3e204de0705176f9022fe49a538054fb14abb49e010000008c493046022100f2bc2aba2534becbdf062eb993853a42bbbc282083d0daf9b4b585bd401aa8c9022100b1d7fd7ee0b95600db8535bbf331b19eed8d961f7a8e54159c53675d5f69df8c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff03ad0e58ccdac3df9dc28a218bcf6f1997b0a93306faaa4b3a28ae83447b2179010000008b483045022100be12b2937179da88599e27bb31c3525097a07cdb52422d165b3ca2f2020ffcf702200971b51f853a53d644ebae9ec8f3512e442b1bcb6c315a5b491d119d10624c83014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff2acfcab629bbc8685792603762c921580030ba144af553d271716a95089e107b010000008b483045022100fa579a840ac258871365dd48cd7552f96c8eea69bd00d84f05b283a0dab311e102207e3c0ee9234814cfbb1b659b83671618f45abc1326b9edcc77d552a4f2a805c0014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffdcdc6023bbc9944a658ddc588e61eacb737ddf0a3cd24f113b5a8634c517fcd2000000008b4830450221008d6df731df5d32267954bd7d2dda2302b74c6c2a6aa5c0ca64ecbabc1af03c75022010e55c571d65da7701ae2da1956c442df81bbf076cdbac25133f99d98a9ed34c014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffffe15557cd5ce258f479dfd6dc6514edf6d7ed5b21fcfa4a038fd69f06b83ac76e010000008b483045022023b3e0ab071eb11de2eb1cc3a67261b866f86bf6867d4558165f7c8c8aca2d86022100dc6e1f53a91de3efe8f63512850811f26284b62f850c70ca73ed5de8771fb451014104462e76fd4067b3a0aa42070082dcb0bf2f388b6495cf33d789904f07d0f55c40fbd4b82963c69b3dc31895d0c772c812b1d5fbcade15312ef1c0e8ebbb12dcd4ffffffff01404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000010000000166d7577163c932b4f9690ca6a80b6e4eb001f0a2fa9023df5595602aae96ed8d000000008a4730440220262b42546302dfb654a229cefc86432b89628ff259dc87edd1154535b16a67e102207b4634c020a97c3e7bbd0d4d19da6aa2269ad9dded4026e896b213d73ca4b63f014104979b82d02226b3a4597523845754d44f13639e3bf2df5e82c6aab2bdc79687368b01b1ab8b19875ae3c90d661a3d0a33161dab29934edeb36aa01976be3baf8affffffff02404b4c00000000001976a9144854e695a02af0aeacb823ccbc272134561e0a1688ac40420f00000000001976a914abee93376d6b37b5c2940655a6fcaf1c8e74237988ac0000000001000000014e3f8ef2e91349a9059cb4f01e54ab2597c1387161d3da89919f7ea6acdbb371010000008c49304602210081f3183471a5ca22307c0800226f3ef9c353069e0773ac76bb580654d56aa523022100d4c56465bdc069060846f4fbf2f6b20520b2a80b08b168b31e66ddb9c694e240014104976c79848e18251612f8940875b2b08d06e6dc73b9840e8860c066b7e87432c477e9a59a453e71e6d76d5fe34058b800a098fc1740ce3012e8fc8a00c96af966ffffffff02c0e1e400000000001976a9144134e75a6fcb6042034aab5e18570cf1f844f54788ac404b4c00000000001976a9142b6ba7c9d796b75eef7942fc9288edd37c32f5c388ac00000000"), SER_NETWORK, PROTOCOL_VERSION); + stream >> block; + return block; +} diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index 2ddac2f076..2390aca342 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -49,12 +49,14 @@ struct BasicTestingSetup { * Included are data directory, coins database, script check threads setup. */ class CConnman; +class PeerLogicValidation; struct TestingSetup: public BasicTestingSetup { CCoinsViewDB *pcoinsdbview; fs::path pathTemp; boost::thread_group threadGroup; CConnman* connman; CScheduler scheduler; + std::unique_ptr<PeerLogicValidation> peerLogic; explicit TestingSetup(const std::string& chainName = CBaseChainParams::MAIN); ~TestingSetup(); @@ -97,7 +99,7 @@ struct TestMemPoolEntryHelper TestMemPoolEntryHelper() : nFee(0), nTime(0), nHeight(1), spendsCoinbase(false), sigOpCost(4) { } - + CTxMemPoolEntry FromTx(const CMutableTransaction &tx); CTxMemPoolEntry FromTx(const CTransaction &tx); @@ -108,4 +110,7 @@ struct TestMemPoolEntryHelper TestMemPoolEntryHelper &SpendsCoinbase(bool _flag) { spendsCoinbase = _flag; return *this; } TestMemPoolEntryHelper &SigOpsCost(unsigned int _sigopsCost) { sigOpCost = _sigopsCost; return *this; } }; + +CBlock getBlock13b8a(); + #endif diff --git a/src/test/test_bitcoin_fuzzy.cpp b/src/test/test_bitcoin_fuzzy.cpp index de14251601..581ad2ffa0 100644 --- a/src/test/test_bitcoin_fuzzy.cpp +++ b/src/test/test_bitcoin_fuzzy.cpp @@ -48,8 +48,8 @@ enum TEST_ID { TEST_ID_END }; -bool read_stdin(std::vector<char> &data) { - char buffer[1024]; +bool read_stdin(std::vector<uint8_t> &data) { + uint8_t buffer[1024]; ssize_t length=0; while((length = read(STDIN_FILENO, buffer, 1024)) > 0) { data.insert(data.end(), buffer, buffer+length); @@ -59,15 +59,11 @@ bool read_stdin(std::vector<char> &data) { return length==0; } -int do_fuzz() -{ - std::vector<char> buffer; - if (!read_stdin(buffer)) return 0; - +int test_one_input(std::vector<uint8_t> buffer) { if (buffer.size() < sizeof(uint32_t)) return 0; uint32_t test_id = 0xffffffff; - memcpy(&test_id, &buffer[0], sizeof(uint32_t)); + memcpy(&test_id, buffer.data(), sizeof(uint32_t)); buffer.erase(buffer.begin(), buffer.begin() + sizeof(uint32_t)); if (test_id >= TEST_ID_END) return 0; @@ -255,9 +251,32 @@ int do_fuzz() return 0; } +static std::unique_ptr<ECCVerifyHandle> globalVerifyHandle; +void initialize() { + globalVerifyHandle = std::unique_ptr<ECCVerifyHandle>(new ECCVerifyHandle()); +} + +// This function is used by libFuzzer +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + test_one_input(std::vector<uint8_t>(data, data + size)); + return 0; +} + +// This function is used by libFuzzer +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { + initialize(); + return 0; +} + +// Disabled under WIN32 due to clash with Cygwin's WinMain. +#ifndef WIN32 +// Declare main(...) "weak" to allow for libFuzzer linking. libFuzzer provides +// the main(...) function. +__attribute__((weak)) +#endif int main(int argc, char **argv) { - ECCVerifyHandle globalVerifyHandle; + initialize(); #ifdef __AFL_INIT // Enable AFL deferred forkserver mode. Requires compilation using // afl-clang-fast++. See fuzzing.md for details. @@ -267,11 +286,20 @@ int main(int argc, char **argv) #ifdef __AFL_LOOP // Enable AFL persistent mode. Requires compilation using afl-clang-fast++. // See fuzzing.md for details. + int ret = 0; while (__AFL_LOOP(1000)) { - do_fuzz(); + std::vector<uint8_t> buffer; + if (!read_stdin(buffer)) { + continue; + } + ret = test_one_input(buffer); } - return 0; + return ret; #else - return do_fuzz(); + std::vector<uint8_t> buffer; + if (!read_stdin(buffer)) { + return 0; + } + return test_one_input(buffer); #endif } diff --git a/src/test/testutil.cpp b/src/test/testutil.cpp deleted file mode 100644 index 591d0bf302..0000000000 --- a/src/test/testutil.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2009-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. - -#include "testutil.h" - -#ifdef WIN32 -#include <shlobj.h> -#endif - -#include "fs.h" - -fs::path GetTempPath() { - return fs::temp_directory_path(); -} diff --git a/src/test/testutil.h b/src/test/testutil.h deleted file mode 100644 index cbe784d640..0000000000 --- a/src/test/testutil.h +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2009-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. - -/** - * Utility functions shared by unit tests - */ -#ifndef BITCOIN_TEST_TESTUTIL_H -#define BITCOIN_TEST_TESTUTIL_H - -#include "fs.h" - -fs::path GetTempPath(); - -#endif // BITCOIN_TEST_TESTUTIL_H diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 6654634bf1..cb6ab7cdbe 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -480,8 +480,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction) { for(uint32_t i = 0; i < mtx.vin.size(); i++) { std::vector<CScriptCheck> vChecks; - const CTxOut& output = coins[tx.vin[i].prevout.n].out; - CScriptCheck check(output.scriptPubKey, output.nValue, tx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata); + CScriptCheck check(coins[tx.vin[i].prevout.n].out, tx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata); vChecks.push_back(CScriptCheck()); check.swap(vChecks.back()); control.Add(vChecks); diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index 2d25cb96c8..82ca93e7da 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -29,7 +29,8 @@ ToMemPool(CMutableTransaction& tx) LOCK(cs_main); CValidationState state; - return AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx), false, nullptr, nullptr, true, 0); + return AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx), nullptr /* pfMissingInputs */, + nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */); } BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 5679086969..6ec544290d 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -253,6 +253,31 @@ BOOST_AUTO_TEST_CASE(util_IsHex) BOOST_CHECK(!IsHex("0x0000")); } +BOOST_AUTO_TEST_CASE(util_IsHexNumber) +{ + BOOST_CHECK(IsHexNumber("0x0")); + BOOST_CHECK(IsHexNumber("0")); + BOOST_CHECK(IsHexNumber("0x10")); + BOOST_CHECK(IsHexNumber("10")); + BOOST_CHECK(IsHexNumber("0xff")); + BOOST_CHECK(IsHexNumber("ff")); + BOOST_CHECK(IsHexNumber("0xFfa")); + BOOST_CHECK(IsHexNumber("Ffa")); + BOOST_CHECK(IsHexNumber("0x00112233445566778899aabbccddeeffAABBCCDDEEFF")); + BOOST_CHECK(IsHexNumber("00112233445566778899aabbccddeeffAABBCCDDEEFF")); + + BOOST_CHECK(!IsHexNumber("")); // empty string not allowed + BOOST_CHECK(!IsHexNumber("0x")); // empty string after prefix not allowed + BOOST_CHECK(!IsHexNumber("0x0 ")); // no spaces at end, + BOOST_CHECK(!IsHexNumber(" 0x0")); // or beginning, + BOOST_CHECK(!IsHexNumber("0x 0")); // or middle, + BOOST_CHECK(!IsHexNumber(" ")); // etc. + BOOST_CHECK(!IsHexNumber("0x0ga")); // invalid character + BOOST_CHECK(!IsHexNumber("x0")); // broken prefix + BOOST_CHECK(!IsHexNumber("0x0x00")); // two prefixes not allowed + +} + BOOST_AUTO_TEST_CASE(util_seed_insecure_rand) { SeedInsecureRand(true); diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index f433aad889..882afb2e20 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -314,20 +314,20 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion) // Mine one period worth of blocks, and check that the bit will be on for the // next period. - lastBlock = secondChain.Mine(2016, nStartTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); + lastBlock = secondChain.Mine(2016, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0); // Mine another period worth of blocks, signaling the new bit. - lastBlock = secondChain.Mine(4032, nStartTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip(); + lastBlock = secondChain.Mine(4032, nTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip(); // After one period of setting the bit on each block, it should have locked in. // We keep setting the bit for one more period though, until activation. BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0); // Now check that we keep mining the block until the end of this period, and // then stop at the beginning of the next period. - lastBlock = secondChain.Mine(6047, nStartTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); + lastBlock = secondChain.Mine(6047, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0); - lastBlock = secondChain.Mine(6048, nStartTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); + lastBlock = secondChain.Mine(6048, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip(); BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0); // Finally, verify that after a soft fork has activated, CBV no longer uses diff --git a/src/txdb.cpp b/src/txdb.cpp index 797ae5713f..134bb8721b 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -371,9 +371,9 @@ bool CCoinsViewDB::Upgrade() { int64_t count = 0; LogPrintf("Upgrading utxo-set database...\n"); LogPrintf("[0%%]..."); + uiInterface.ShowProgress(_("Upgrading UTXO database"), 0, true); size_t batch_size = 1 << 24; CDBBatch batch(db); - uiInterface.SetProgressBreakAction(StartShutdown); int reportDone = 0; std::pair<unsigned char, uint256> key; std::pair<unsigned char, uint256> prev_key = {DB_COINS, uint256()}; @@ -386,7 +386,7 @@ bool CCoinsViewDB::Upgrade() { if (count++ % 256 == 0) { uint32_t high = 0x100 * *key.second.begin() + *(key.second.begin() + 1); int percentageDone = (int)(high * 100.0 / 65536.0 + 0.5); - uiInterface.ShowProgress(_("Upgrading UTXO database") + "\n"+ _("(press q to shutdown and continue later)") + "\n", percentageDone); + uiInterface.ShowProgress(_("Upgrading UTXO database"), percentageDone, true); if (reportDone < percentageDone/10) { // report max. every 10% step LogPrintf("[%d%%]...", percentageDone); @@ -420,7 +420,7 @@ bool CCoinsViewDB::Upgrade() { } db.WriteBatch(batch); db.CompactRange({DB_COINS, uint256()}, key); - uiInterface.SetProgressBreakAction(std::function<void(void)>()); + uiInterface.ShowProgress("", 100, false); LogPrintf("[%s].\n", ShutdownRequested() ? "CANCELLED" : "DONE"); return !ShutdownRequested(); } diff --git a/src/txdb.h b/src/txdb.h index d1cd5a4250..ec9f571b13 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -110,17 +110,17 @@ class CBlockTreeDB : public CDBWrapper { public: explicit CBlockTreeDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); -private: - CBlockTreeDB(const CBlockTreeDB&); - void operator=(const CBlockTreeDB&); -public: + + CBlockTreeDB(const CBlockTreeDB&) = delete; + CBlockTreeDB& operator=(const CBlockTreeDB&) = delete; + bool WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo); - bool ReadBlockFileInfo(int nFile, CBlockFileInfo &fileinfo); + bool ReadBlockFileInfo(int nFile, CBlockFileInfo &info); bool ReadLastBlockFile(int &nFile); - bool WriteReindexing(bool fReindex); - bool ReadReindexing(bool &fReindex); + bool WriteReindexing(bool fReindexing); + bool ReadReindexing(bool &fReindexing); bool ReadTxIndex(const uint256 &txid, CDiskTxPos &pos); - bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &list); + bool WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> > &vect); bool WriteFlag(const std::string &name, bool fValue); bool ReadFlag(const std::string &name, bool &fValue); bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 4a81055231..b0306811cb 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -39,11 +39,6 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFe nSigOpCostWithAncestors = sigOpCost; } -CTxMemPoolEntry::CTxMemPoolEntry(const CTxMemPoolEntry& other) -{ - *this = other; -} - void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta) { nModFeesWithDescendants += newFeeDelta - feeDelta; @@ -612,6 +607,15 @@ void CTxMemPool::clear() _clear(); } +static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& mempoolDuplicate, const int64_t spendheight) +{ + CValidationState state; + CAmount txfee = 0; + bool fCheckResult = tx.IsCoinBase() || Consensus::CheckTxInputs(tx, state, mempoolDuplicate, spendheight, txfee); + assert(fCheckResult); + UpdateCoins(tx, mempoolDuplicate, 1000000); +} + void CTxMemPool::check(const CCoinsViewCache *pcoins) const { if (nCheckFrequency == 0) @@ -626,7 +630,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const uint64_t innerUsage = 0; CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins)); - const int64_t nSpendHeight = GetSpendHeight(mempoolDuplicate); + const int64_t spendheight = GetSpendHeight(mempoolDuplicate); LOCK(cs); std::list<const CTxMemPoolEntry*> waitingOnDependants; @@ -705,11 +709,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const if (fDependsWait) waitingOnDependants.push_back(&(*it)); else { - CValidationState state; - bool fCheckResult = tx.IsCoinBase() || - Consensus::CheckTxInputs(tx, state, mempoolDuplicate, nSpendHeight); - assert(fCheckResult); - UpdateCoins(tx, mempoolDuplicate, 1000000); + CheckInputsAndUpdateCoins(tx, mempoolDuplicate, spendheight); } } unsigned int stepsSinceLastRemove = 0; @@ -722,10 +722,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const stepsSinceLastRemove++; assert(stepsSinceLastRemove < waitingOnDependants.size()); } else { - bool fCheckResult = entry->GetTx().IsCoinBase() || - Consensus::CheckTxInputs(entry->GetTx(), state, mempoolDuplicate, nSpendHeight); - assert(fCheckResult); - UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000); + CheckInputsAndUpdateCoins(entry->GetTx(), mempoolDuplicate, spendheight); stepsSinceLastRemove = 0; } } @@ -986,7 +983,7 @@ const CTxMemPool::setEntries & CTxMemPool::GetMemPoolChildren(txiter entry) cons CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const { LOCK(cs); if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0) - return CFeeRate(rollingMinimumFeeRate); + return CFeeRate(llround(rollingMinimumFeeRate)); int64_t time = GetTime(); if (time > lastRollingFeeUpdate + 10) { @@ -1004,7 +1001,7 @@ CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const { return CFeeRate(0); } } - return std::max(CFeeRate(rollingMinimumFeeRate), incrementalRelayFee); + return std::max(CFeeRate(llround(rollingMinimumFeeRate)), incrementalRelayFee); } void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) { diff --git a/src/txmempool.h b/src/txmempool.h index 5b0db5266e..929d223588 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -21,11 +21,10 @@ #include "sync.h" #include "random.h" -#include "boost/multi_index_container.hpp" -#include "boost/multi_index/ordered_index.hpp" -#include "boost/multi_index/hashed_index.hpp" +#include <boost/multi_index_container.hpp> +#include <boost/multi_index/hashed_index.hpp> +#include <boost/multi_index/ordered_index.hpp> #include <boost/multi_index/sequenced_index.hpp> - #include <boost/signals2/signal.hpp> class CBlockIndex; @@ -95,8 +94,6 @@ public: bool spendsCoinbase, int64_t nSigOpsCost, LockPoints lp); - CTxMemPoolEntry(const CTxMemPoolEntry& other); - const CTransaction& GetTx() const { return *this->tx; } CTransactionRef GetSharedTx() const { return this->tx; } const CAmount& GetFee() const { return nFee; } @@ -510,7 +507,7 @@ public: * check does nothing. */ void check(const CCoinsViewCache *pcoins) const; - void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = dFrequency * 4294967295.0; } + void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = static_cast<uint32_t>(dFrequency * 4294967295.0); } // addUnchecked must updated state for all ancestors of a given transaction, // to track size/count of descendant transactions. First version of diff --git a/src/ui_interface.h b/src/ui_interface.h index 762dd19b19..7f68c578ee 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -94,11 +94,11 @@ public: /** A wallet has been loaded. */ boost::signals2::signal<void (CWallet* wallet)> LoadWallet; - /** Show progress e.g. for verifychain */ - boost::signals2::signal<void (const std::string &title, int nProgress)> ShowProgress; - - /** Set progress break action (possible "cancel button" triggers that action) */ - boost::signals2::signal<void (std::function<void(void)> action)> SetProgressBreakAction; + /** + * Show progress e.g. for verifychain. + * resume_possible indicates shutting down now will result in the current progress action resuming upon restart. + */ + boost::signals2::signal<void (const std::string &title, int nProgress, bool resume_possible)> ShowProgress; /** New block has been accepted */ boost::signals2::signal<void (bool, const CBlockIndex *)> NotifyBlockTip; diff --git a/src/uint256.cpp b/src/uint256.cpp index c4c7b716fe..736a0d4fe2 100644 --- a/src/uint256.cpp +++ b/src/uint256.cpp @@ -14,7 +14,7 @@ template <unsigned int BITS> base_blob<BITS>::base_blob(const std::vector<unsigned char>& vch) { assert(vch.size() == sizeof(data)); - memcpy(data, &vch[0], sizeof(data)); + memcpy(data, vch.data(), sizeof(data)); } template <unsigned int BITS> diff --git a/src/uint256.h b/src/uint256.h index 3ed694d723..94a4f7fc30 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -111,7 +111,6 @@ public: class uint160 : public base_blob<160> { public: uint160() {} - explicit uint160(const base_blob<160>& b) : base_blob<160>(b) {} explicit uint160(const std::vector<unsigned char>& vch) : base_blob<160>(vch) {} }; @@ -123,7 +122,6 @@ public: class uint256 : public base_blob<256> { public: uint256() {} - explicit uint256(const base_blob<256>& b) : base_blob<256>(b) {} explicit uint256(const std::vector<unsigned char>& vch) : base_blob<256>(vch) {} /** A cheap hash function that just returns 64 bits from the result, it can be diff --git a/src/univalue/Makefile.am b/src/univalue/Makefile.am index 6c1ec81e63..e283fc890e 100644 --- a/src/univalue/Makefile.am +++ b/src/univalue/Makefile.am @@ -12,6 +12,7 @@ pkgconfig_DATA = pc/libunivalue.pc libunivalue_la_SOURCES = \ lib/univalue.cpp \ + lib/univalue_get.cpp \ lib/univalue_read.cpp \ lib/univalue_write.cpp @@ -20,7 +21,7 @@ libunivalue_la_LDFLAGS = \ -no-undefined libunivalue_la_CXXFLAGS = -I$(top_srcdir)/include -TESTS = test/unitester +TESTS = test/object test/unitester test/no_nul GENBIN = gen/gen$(BUILD_EXEEXT) GEN_SRCS = gen/gen.cpp @@ -33,7 +34,7 @@ gen: lib/univalue_escapes.h $(GENBIN) @echo Updating $< $(AM_V_at)$(GENBIN) > lib/univalue_escapes.h -noinst_PROGRAMS = $(TESTS) +noinst_PROGRAMS = $(TESTS) test/test_json TEST_DATA_DIR=test @@ -42,6 +43,21 @@ test_unitester_LDADD = libunivalue.la test_unitester_CXXFLAGS = -I$(top_srcdir)/include -DJSON_TEST_SRC=\"$(srcdir)/$(TEST_DATA_DIR)\" test_unitester_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) +test_test_json_SOURCES = test/test_json.cpp +test_test_json_LDADD = libunivalue.la +test_test_json_CXXFLAGS = -I$(top_srcdir)/include +test_test_json_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) + +test_no_nul_SOURCES = test/no_nul.cpp +test_no_nul_LDADD = libunivalue.la +test_no_nul_CXXFLAGS = -I$(top_srcdir)/include +test_no_nul_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) + +test_object_SOURCES = test/object.cpp +test_object_LDADD = libunivalue.la +test_object_CXXFLAGS = -I$(top_srcdir)/include +test_object_LDFLAGS = -static $(LIBTOOL_APP_LDFLAGS) + TEST_FILES = \ $(TEST_DATA_DIR)/fail10.json \ $(TEST_DATA_DIR)/fail11.json \ @@ -77,6 +93,8 @@ TEST_FILES = \ $(TEST_DATA_DIR)/fail39.json \ $(TEST_DATA_DIR)/fail40.json \ $(TEST_DATA_DIR)/fail41.json \ + $(TEST_DATA_DIR)/fail42.json \ + $(TEST_DATA_DIR)/fail44.json \ $(TEST_DATA_DIR)/fail3.json \ $(TEST_DATA_DIR)/fail4.json \ $(TEST_DATA_DIR)/fail5.json \ @@ -88,6 +106,11 @@ TEST_FILES = \ $(TEST_DATA_DIR)/pass2.json \ $(TEST_DATA_DIR)/pass3.json \ $(TEST_DATA_DIR)/round1.json \ - $(TEST_DATA_DIR)/round2.json + $(TEST_DATA_DIR)/round2.json \ + $(TEST_DATA_DIR)/round3.json \ + $(TEST_DATA_DIR)/round4.json \ + $(TEST_DATA_DIR)/round5.json \ + $(TEST_DATA_DIR)/round6.json \ + $(TEST_DATA_DIR)/round7.json EXTRA_DIST=$(TEST_FILES) $(GEN_SRCS) diff --git a/src/univalue/README b/src/univalue/README deleted file mode 100644 index 48167b083b..0000000000 --- a/src/univalue/README +++ /dev/null @@ -1,7 +0,0 @@ - - UniValue - -A universal value object, with JSON encoding (output) and decoding (input). - -Built as a single dynamic RAII C++ object class, and no templates. - diff --git a/src/univalue/README.md b/src/univalue/README.md new file mode 100644 index 0000000000..36aa786a4c --- /dev/null +++ b/src/univalue/README.md @@ -0,0 +1,32 @@ + +# UniValue + +## Summary + +A universal value class, with JSON encoding and decoding. + +UniValue is an abstract data type that may be a null, boolean, string, +number, array container, or a key/value dictionary container, nested to +an arbitrary depth. + +This class is aligned with the JSON standard, [RFC +7159](https://tools.ietf.org/html/rfc7159.html). + +## Installation + +This project is a standard GNU +[autotools](https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html) +project. Build and install instructions are available in the `INSTALL` +file provided with GNU autotools. + +``` +$ ./autogen.sh +$ ./configure +$ make +``` + +## Design + +UniValue provides a single dynamic RAII C++ object class, +and minimizes template use (contra json_spirit). + diff --git a/src/univalue/configure.ac b/src/univalue/configure.ac index 93d3ba945d..8298332ac1 100644 --- a/src/univalue/configure.ac +++ b/src/univalue/configure.ac @@ -1,7 +1,7 @@ m4_define([libunivalue_major_version], [1]) m4_define([libunivalue_minor_version], [1]) -m4_define([libunivalue_micro_version], [2]) -m4_define([libunivalue_interface_age], [2]) +m4_define([libunivalue_micro_version], [3]) +m4_define([libunivalue_interface_age], [3]) # If you need a modifier for the version number. # Normally empty, but can be used to make "fixup" releases. m4_define([libunivalue_extraversion], []) @@ -14,7 +14,7 @@ m4_define([libunivalue_age], [m4_eval(libunivalue_binary_age - libunivalue_inter m4_define([libunivalue_version], [libunivalue_major_version().libunivalue_minor_version().libunivalue_micro_version()libunivalue_extraversion()]) -AC_INIT([univalue], [1.0.2], +AC_INIT([univalue], [1.0.3], [http://github.com/jgarzik/univalue/]) dnl make the compilation flags quiet unless V=1 is used diff --git a/src/univalue/include/univalue.h b/src/univalue/include/univalue.h index e8ce283519..4fd2223b30 100644 --- a/src/univalue/include/univalue.h +++ b/src/univalue/include/univalue.h @@ -7,6 +7,7 @@ #define __UNIVALUE_H__ #include <stdint.h> +#include <string.h> #include <string> #include <vector> @@ -69,10 +70,11 @@ public: size_t size() const { return values.size(); } bool getBool() const { return isTrue(); } - bool checkObject(const std::map<std::string,UniValue::VType>& memberTypes); + void getObjMap(std::map<std::string,UniValue>& kv) const; + bool checkObject(const std::map<std::string,UniValue::VType>& memberTypes) const; const UniValue& operator[](const std::string& key) const; - const UniValue& operator[](unsigned int index) const; - bool exists(const std::string& key) const { return (findKey(key) >= 0); } + const UniValue& operator[](size_t index) const; + bool exists(const std::string& key) const { size_t i; return findKey(key, i); } bool isNull() const { return (typ == VNULL); } bool isTrue() const { return (typ == VBOOL) && (val == "1"); } @@ -92,8 +94,25 @@ public: std::string s(val_); return push_back(s); } + bool push_back(uint64_t val_) { + UniValue tmpVal(val_); + return push_back(tmpVal); + } + bool push_back(int64_t val_) { + UniValue tmpVal(val_); + return push_back(tmpVal); + } + bool push_back(int val_) { + UniValue tmpVal(val_); + return push_back(tmpVal); + } + bool push_back(double val_) { + UniValue tmpVal(val_); + return push_back(tmpVal); + } bool push_backV(const std::vector<UniValue>& vec); + void __pushKV(const std::string& key, const UniValue& val); bool pushKV(const std::string& key, const UniValue& val); bool pushKV(const std::string& key, const std::string& val_) { UniValue tmpVal(VSTR, val_); @@ -124,9 +143,10 @@ public: std::string write(unsigned int prettyIndent = 0, unsigned int indentLevel = 0) const; - bool read(const char *raw); + bool read(const char *raw, size_t len); + bool read(const char *raw) { return read(raw, strlen(raw)); } bool read(const std::string& rawStr) { - return read(rawStr.c_str()); + return read(rawStr.data(), rawStr.size()); } private: @@ -135,7 +155,7 @@ private: std::vector<std::string> keys; std::vector<UniValue> values; - int findKey(const std::string& key) const; + bool findKey(const std::string& key, size_t& retIdx) const; void writeArray(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const; void writeObject(unsigned int prettyIndent, unsigned int indentLevel, std::string& s) const; @@ -240,7 +260,7 @@ enum jtokentype { }; extern enum jtokentype getJsonToken(std::string& tokenVal, - unsigned int& consumed, const char *raw); + unsigned int& consumed, const char *raw, const char *end); extern const char *uvTypeName(UniValue::VType t); static inline bool jsonTokenIsValue(enum jtokentype jtt) diff --git a/src/univalue/lib/univalue.cpp b/src/univalue/lib/univalue.cpp index 5a2860c13f..d8ad7c4b90 100644 --- a/src/univalue/lib/univalue.cpp +++ b/src/univalue/lib/univalue.cpp @@ -4,75 +4,12 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <stdint.h> -#include <errno.h> #include <iomanip> -#include <limits> #include <sstream> -#include <stdexcept> #include <stdlib.h> -#include <string.h> #include "univalue.h" -namespace -{ -static bool ParsePrechecks(const std::string& str) -{ - if (str.empty()) // No empty string allowed - return false; - if (str.size() >= 1 && (json_isspace(str[0]) || json_isspace(str[str.size()-1]))) // No padding allowed - return false; - if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed - return false; - return true; -} - -bool ParseInt32(const std::string& str, int32_t *out) -{ - if (!ParsePrechecks(str)) - return false; - char *endp = NULL; - errno = 0; // strtol will not set errno if valid - long int n = strtol(str.c_str(), &endp, 10); - if(out) *out = (int32_t)n; - // Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow - // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit - // platforms the size of these types may be different. - return endp && *endp == 0 && !errno && - n >= std::numeric_limits<int32_t>::min() && - n <= std::numeric_limits<int32_t>::max(); -} - -bool ParseInt64(const std::string& str, int64_t *out) -{ - if (!ParsePrechecks(str)) - return false; - char *endp = NULL; - errno = 0; // strtoll will not set errno if valid - long long int n = strtoll(str.c_str(), &endp, 10); - if(out) *out = (int64_t)n; - // Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow - // we still have to check that the returned value is within the range of an *int64_t*. - return endp && *endp == 0 && !errno && - n >= std::numeric_limits<int64_t>::min() && - n <= std::numeric_limits<int64_t>::max(); -} - -bool ParseDouble(const std::string& str, double *out) -{ - if (!ParsePrechecks(str)) - return false; - if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed - return false; - std::istringstream text(str); - text.imbue(std::locale::classic()); - double result; - text >> result; - if(out) *out = result; - return text.eof() && !text.fail(); -} -} - using namespace std; const UniValue NullUniValue; @@ -104,7 +41,7 @@ static bool validNumStr(const string& s) { string tokenVal; unsigned int consumed; - enum jtokentype tt = getJsonToken(tokenVal, consumed, s.c_str()); + enum jtokentype tt = getJsonToken(tokenVal, consumed, s.data(), s.data() + s.size()); return (tt == JTOK_NUMBER); } @@ -189,13 +126,22 @@ bool UniValue::push_backV(const std::vector<UniValue>& vec) return true; } +void UniValue::__pushKV(const std::string& key, const UniValue& val_) +{ + keys.push_back(key); + values.push_back(val_); +} + bool UniValue::pushKV(const std::string& key, const UniValue& val_) { if (typ != VOBJ) return false; - keys.push_back(key); - values.push_back(val_); + size_t idx; + if (findKey(key, idx)) + values[idx] = val_; + else + __pushKV(key, val_); return true; } @@ -204,30 +150,43 @@ bool UniValue::pushKVs(const UniValue& obj) if (typ != VOBJ || obj.typ != VOBJ) return false; - for (unsigned int i = 0; i < obj.keys.size(); i++) { - keys.push_back(obj.keys[i]); - values.push_back(obj.values.at(i)); - } + for (size_t i = 0; i < obj.keys.size(); i++) + __pushKV(obj.keys[i], obj.values.at(i)); return true; } -int UniValue::findKey(const std::string& key) const +void UniValue::getObjMap(std::map<std::string,UniValue>& kv) const +{ + if (typ != VOBJ) + return; + + kv.clear(); + for (size_t i = 0; i < keys.size(); i++) + kv[keys[i]] = values[i]; +} + +bool UniValue::findKey(const std::string& key, size_t& retIdx) const { - for (unsigned int i = 0; i < keys.size(); i++) { - if (keys[i] == key) - return (int) i; + for (size_t i = 0; i < keys.size(); i++) { + if (keys[i] == key) { + retIdx = i; + return true; + } } - return -1; + return false; } -bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t) +bool UniValue::checkObject(const std::map<std::string,UniValue::VType>& t) const { + if (typ != VOBJ) + return false; + for (std::map<std::string,UniValue::VType>::const_iterator it = t.begin(); it != t.end(); ++it) { - int idx = findKey(it->first); - if (idx < 0) + size_t idx = 0; + if (!findKey(it->first, idx)) return false; if (values.at(idx).getType() != it->second) @@ -242,14 +201,14 @@ const UniValue& UniValue::operator[](const std::string& key) const if (typ != VOBJ) return NullUniValue; - int index = findKey(key); - if (index < 0) + size_t index = 0; + if (!findKey(key, index)) return NullUniValue; return values.at(index); } -const UniValue& UniValue::operator[](unsigned int index) const +const UniValue& UniValue::operator[](size_t index) const { if (typ != VOBJ && typ != VARR) return NullUniValue; @@ -283,75 +242,3 @@ const UniValue& find_value(const UniValue& obj, const std::string& name) return NullUniValue; } -const std::vector<std::string>& UniValue::getKeys() const -{ - if (typ != VOBJ) - throw std::runtime_error("JSON value is not an object as expected"); - return keys; -} - -const std::vector<UniValue>& UniValue::getValues() const -{ - if (typ != VOBJ && typ != VARR) - throw std::runtime_error("JSON value is not an object or array as expected"); - return values; -} - -bool UniValue::get_bool() const -{ - if (typ != VBOOL) - throw std::runtime_error("JSON value is not a boolean as expected"); - return getBool(); -} - -const std::string& UniValue::get_str() const -{ - if (typ != VSTR) - throw std::runtime_error("JSON value is not a string as expected"); - return getValStr(); -} - -int UniValue::get_int() const -{ - if (typ != VNUM) - throw std::runtime_error("JSON value is not an integer as expected"); - int32_t retval; - if (!ParseInt32(getValStr(), &retval)) - throw std::runtime_error("JSON integer out of range"); - return retval; -} - -int64_t UniValue::get_int64() const -{ - if (typ != VNUM) - throw std::runtime_error("JSON value is not an integer as expected"); - int64_t retval; - if (!ParseInt64(getValStr(), &retval)) - throw std::runtime_error("JSON integer out of range"); - return retval; -} - -double UniValue::get_real() const -{ - if (typ != VNUM) - throw std::runtime_error("JSON value is not a number as expected"); - double retval; - if (!ParseDouble(getValStr(), &retval)) - throw std::runtime_error("JSON double out of range"); - return retval; -} - -const UniValue& UniValue::get_obj() const -{ - if (typ != VOBJ) - throw std::runtime_error("JSON value is not an object as expected"); - return *this; -} - -const UniValue& UniValue::get_array() const -{ - if (typ != VARR) - throw std::runtime_error("JSON value is not an array as expected"); - return *this; -} - diff --git a/src/univalue/lib/univalue_get.cpp b/src/univalue/lib/univalue_get.cpp new file mode 100644 index 0000000000..eabcf2dad1 --- /dev/null +++ b/src/univalue/lib/univalue_get.cpp @@ -0,0 +1,147 @@ +// Copyright 2014 BitPay Inc. +// Copyright 2015 Bitcoin Core Developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <stdexcept> +#include <vector> +#include <limits> +#include <string> + +#include "univalue.h" + +namespace +{ +static bool ParsePrechecks(const std::string& str) +{ + if (str.empty()) // No empty string allowed + return false; + if (str.size() >= 1 && (json_isspace(str[0]) || json_isspace(str[str.size()-1]))) // No padding allowed + return false; + if (str.size() != strlen(str.c_str())) // No embedded NUL characters allowed + return false; + return true; +} + +bool ParseInt32(const std::string& str, int32_t *out) +{ + if (!ParsePrechecks(str)) + return false; + char *endp = NULL; + errno = 0; // strtol will not set errno if valid + long int n = strtol(str.c_str(), &endp, 10); + if(out) *out = (int32_t)n; + // Note that strtol returns a *long int*, so even if strtol doesn't report a over/underflow + // we still have to check that the returned value is within the range of an *int32_t*. On 64-bit + // platforms the size of these types may be different. + return endp && *endp == 0 && !errno && + n >= std::numeric_limits<int32_t>::min() && + n <= std::numeric_limits<int32_t>::max(); +} + +bool ParseInt64(const std::string& str, int64_t *out) +{ + if (!ParsePrechecks(str)) + return false; + char *endp = NULL; + errno = 0; // strtoll will not set errno if valid + long long int n = strtoll(str.c_str(), &endp, 10); + if(out) *out = (int64_t)n; + // Note that strtoll returns a *long long int*, so even if strtol doesn't report a over/underflow + // we still have to check that the returned value is within the range of an *int64_t*. + return endp && *endp == 0 && !errno && + n >= std::numeric_limits<int64_t>::min() && + n <= std::numeric_limits<int64_t>::max(); +} + +bool ParseDouble(const std::string& str, double *out) +{ + if (!ParsePrechecks(str)) + return false; + if (str.size() >= 2 && str[0] == '0' && str[1] == 'x') // No hexadecimal floats allowed + return false; + std::istringstream text(str); + text.imbue(std::locale::classic()); + double result; + text >> result; + if(out) *out = result; + return text.eof() && !text.fail(); +} +} + +const std::vector<std::string>& UniValue::getKeys() const +{ + if (typ != VOBJ) + throw std::runtime_error("JSON value is not an object as expected"); + return keys; +} + +const std::vector<UniValue>& UniValue::getValues() const +{ + if (typ != VOBJ && typ != VARR) + throw std::runtime_error("JSON value is not an object or array as expected"); + return values; +} + +bool UniValue::get_bool() const +{ + if (typ != VBOOL) + throw std::runtime_error("JSON value is not a boolean as expected"); + return getBool(); +} + +const std::string& UniValue::get_str() const +{ + if (typ != VSTR) + throw std::runtime_error("JSON value is not a string as expected"); + return getValStr(); +} + +int UniValue::get_int() const +{ + if (typ != VNUM) + throw std::runtime_error("JSON value is not an integer as expected"); + int32_t retval; + if (!ParseInt32(getValStr(), &retval)) + throw std::runtime_error("JSON integer out of range"); + return retval; +} + +int64_t UniValue::get_int64() const +{ + if (typ != VNUM) + throw std::runtime_error("JSON value is not an integer as expected"); + int64_t retval; + if (!ParseInt64(getValStr(), &retval)) + throw std::runtime_error("JSON integer out of range"); + return retval; +} + +double UniValue::get_real() const +{ + if (typ != VNUM) + throw std::runtime_error("JSON value is not a number as expected"); + double retval; + if (!ParseDouble(getValStr(), &retval)) + throw std::runtime_error("JSON double out of range"); + return retval; +} + +const UniValue& UniValue::get_obj() const +{ + if (typ != VOBJ) + throw std::runtime_error("JSON value is not an object as expected"); + return *this; +} + +const UniValue& UniValue::get_array() const +{ + if (typ != VARR) + throw std::runtime_error("JSON value is not an array as expected"); + return *this; +} + diff --git a/src/univalue/lib/univalue_read.cpp b/src/univalue/lib/univalue_read.cpp index 95bac6958d..ae75cb462a 100644 --- a/src/univalue/lib/univalue_read.cpp +++ b/src/univalue/lib/univalue_read.cpp @@ -43,21 +43,21 @@ static const char *hatoui(const char *first, const char *last, } enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, - const char *raw) + const char *raw, const char *end) { tokenVal.clear(); consumed = 0; const char *rawStart = raw; - while ((*raw) && (json_isspace(*raw))) // skip whitespace + while (raw < end && (json_isspace(*raw))) // skip whitespace raw++; - switch (*raw) { - - case 0: + if (raw >= end) return JTOK_NONE; + switch (*raw) { + case '{': raw++; consumed = (raw - rawStart); @@ -127,40 +127,40 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, numStr += *raw; // copy first char raw++; - if ((*first == '-') && (!json_isdigit(*raw))) + if ((*first == '-') && (raw < end) && (!json_isdigit(*raw))) return JTOK_ERR; - while ((*raw) && json_isdigit(*raw)) { // copy digits + while (raw < end && json_isdigit(*raw)) { // copy digits numStr += *raw; raw++; } // part 2: frac - if (*raw == '.') { + if (raw < end && *raw == '.') { numStr += *raw; // copy . raw++; - if (!json_isdigit(*raw)) + if (raw >= end || !json_isdigit(*raw)) return JTOK_ERR; - while ((*raw) && json_isdigit(*raw)) { // copy digits + while (raw < end && json_isdigit(*raw)) { // copy digits numStr += *raw; raw++; } } // part 3: exp - if (*raw == 'e' || *raw == 'E') { + if (raw < end && (*raw == 'e' || *raw == 'E')) { numStr += *raw; // copy E raw++; - if (*raw == '-' || *raw == '+') { // copy +/- + if (raw < end && (*raw == '-' || *raw == '+')) { // copy +/- numStr += *raw; raw++; } - if (!json_isdigit(*raw)) + if (raw >= end || !json_isdigit(*raw)) return JTOK_ERR; - while ((*raw) && json_isdigit(*raw)) { // copy digits + while (raw < end && json_isdigit(*raw)) { // copy digits numStr += *raw; raw++; } @@ -177,13 +177,16 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, string valStr; JSONUTF8StringFilter writer(valStr); - while (*raw) { - if ((unsigned char)*raw < 0x20) + while (true) { + if (raw >= end || (unsigned char)*raw < 0x20) return JTOK_ERR; else if (*raw == '\\') { raw++; // skip backslash + if (raw >= end) + return JTOK_ERR; + switch (*raw) { case '"': writer.push_back('\"'); break; case '\\': writer.push_back('\\'); break; @@ -196,7 +199,8 @@ enum jtokentype getJsonToken(string& tokenVal, unsigned int& consumed, case 'u': { unsigned int codepoint; - if (hatoui(raw + 1, raw + 1 + 4, codepoint) != + if (raw + 1 + 4 >= end || + hatoui(raw + 1, raw + 1 + 4, codepoint) != raw + 1 + 4) return JTOK_ERR; writer.push_back_u(codepoint); @@ -246,7 +250,7 @@ enum expect_bits { #define setExpect(bit) (expectMask |= EXP_##bit) #define clearExpect(bit) (expectMask &= ~EXP_##bit) -bool UniValue::read(const char *raw) +bool UniValue::read(const char *raw, size_t size) { clear(); @@ -257,10 +261,11 @@ bool UniValue::read(const char *raw) unsigned int consumed; enum jtokentype tok = JTOK_NONE; enum jtokentype last_tok = JTOK_NONE; + const char* end = raw + size; do { last_tok = tok; - tok = getJsonToken(tokenVal, consumed, raw); + tok = getJsonToken(tokenVal, consumed, raw, end); if (tok == JTOK_NONE || tok == JTOK_ERR) return false; raw += consumed; @@ -371,9 +376,6 @@ bool UniValue::read(const char *raw) case JTOK_KW_NULL: case JTOK_KW_TRUE: case JTOK_KW_FALSE: { - if (!stack.size()) - return false; - UniValue tmpVal; switch (tok) { case JTOK_KW_NULL: @@ -388,6 +390,11 @@ bool UniValue::read(const char *raw) default: /* impossible */ break; } + if (!stack.size()) { + *this = tmpVal; + break; + } + UniValue *top = stack.back(); top->values.push_back(tmpVal); @@ -396,10 +403,12 @@ bool UniValue::read(const char *raw) } case JTOK_NUMBER: { - if (!stack.size()) - return false; - UniValue tmpVal(VNUM, tokenVal); + if (!stack.size()) { + *this = tmpVal; + break; + } + UniValue *top = stack.back(); top->values.push_back(tmpVal); @@ -408,17 +417,18 @@ bool UniValue::read(const char *raw) } case JTOK_STRING: { - if (!stack.size()) - return false; - - UniValue *top = stack.back(); - if (expect(OBJ_NAME)) { + UniValue *top = stack.back(); top->keys.push_back(tokenVal); clearExpect(OBJ_NAME); setExpect(COLON); } else { UniValue tmpVal(VSTR, tokenVal); + if (!stack.size()) { + *this = tmpVal; + break; + } + UniValue *top = stack.back(); top->values.push_back(tmpVal); } @@ -432,7 +442,7 @@ bool UniValue::read(const char *raw) } while (!stack.empty ()); /* Check that nothing follows the initial construct (parsed above). */ - tok = getJsonToken(tokenVal, consumed, raw); + tok = getJsonToken(tokenVal, consumed, raw, end); if (tok != JTOK_NONE) return false; diff --git a/src/univalue/lib/univalue_utffilter.h b/src/univalue/lib/univalue_utffilter.h index 2fb6a492d1..20d4043009 100644 --- a/src/univalue/lib/univalue_utffilter.h +++ b/src/univalue/lib/univalue_utffilter.h @@ -46,19 +46,19 @@ public: } } // Write codepoint directly, possibly collating surrogate pairs - void push_back_u(unsigned int codepoint) + void push_back_u(unsigned int codepoint_) { if (state) // Only accept full codepoints in open state is_valid = false; - if (codepoint >= 0xD800 && codepoint < 0xDC00) { // First half of surrogate pair + if (codepoint_ >= 0xD800 && codepoint_ < 0xDC00) { // First half of surrogate pair if (surpair) // Two subsequent surrogate pair openers - fail is_valid = false; else - surpair = codepoint; - } else if (codepoint >= 0xDC00 && codepoint < 0xE000) { // Second half of surrogate pair + surpair = codepoint_; + } else if (codepoint_ >= 0xDC00 && codepoint_ < 0xE000) { // Second half of surrogate pair if (surpair) { // Open surrogate pair, expect second half // Compute code point from UTF-16 surrogate pair - append_codepoint(0x10000 | ((surpair - 0xD800)<<10) | (codepoint - 0xDC00)); + append_codepoint(0x10000 | ((surpair - 0xD800)<<10) | (codepoint_ - 0xDC00)); surpair = 0; } else // Second half doesn't follow a first half - fail is_valid = false; @@ -66,7 +66,7 @@ public: if (surpair) // First half of surrogate pair not followed by second - fail is_valid = false; else - append_codepoint(codepoint); + append_codepoint(codepoint_); } } // Check that we're in a state where the string can be ended @@ -96,22 +96,22 @@ private: // Two subsequent \u.... may have to be replaced with one actual codepoint. unsigned int surpair; // First half of open UTF-16 surrogate pair, or 0 - void append_codepoint(unsigned int codepoint) + void append_codepoint(unsigned int codepoint_) { - if (codepoint <= 0x7f) - str.push_back((char)codepoint); - else if (codepoint <= 0x7FF) { - str.push_back((char)(0xC0 | (codepoint >> 6))); - str.push_back((char)(0x80 | (codepoint & 0x3F))); - } else if (codepoint <= 0xFFFF) { - str.push_back((char)(0xE0 | (codepoint >> 12))); - str.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F))); - str.push_back((char)(0x80 | (codepoint & 0x3F))); - } else if (codepoint <= 0x1FFFFF) { - str.push_back((char)(0xF0 | (codepoint >> 18))); - str.push_back((char)(0x80 | ((codepoint >> 12) & 0x3F))); - str.push_back((char)(0x80 | ((codepoint >> 6) & 0x3F))); - str.push_back((char)(0x80 | (codepoint & 0x3F))); + if (codepoint_ <= 0x7f) + str.push_back((char)codepoint_); + else if (codepoint_ <= 0x7FF) { + str.push_back((char)(0xC0 | (codepoint_ >> 6))); + str.push_back((char)(0x80 | (codepoint_ & 0x3F))); + } else if (codepoint_ <= 0xFFFF) { + str.push_back((char)(0xE0 | (codepoint_ >> 12))); + str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F))); + str.push_back((char)(0x80 | (codepoint_ & 0x3F))); + } else if (codepoint_ <= 0x1FFFFF) { + str.push_back((char)(0xF0 | (codepoint_ >> 18))); + str.push_back((char)(0x80 | ((codepoint_ >> 12) & 0x3F))); + str.push_back((char)(0x80 | ((codepoint_ >> 6) & 0x3F))); + str.push_back((char)(0x80 | (codepoint_ & 0x3F))); } } }; diff --git a/src/univalue/lib/univalue_write.cpp b/src/univalue/lib/univalue_write.cpp index cfbdad3284..cf27835991 100644 --- a/src/univalue/lib/univalue_write.cpp +++ b/src/univalue/lib/univalue_write.cpp @@ -79,8 +79,6 @@ void UniValue::writeArray(unsigned int prettyIndent, unsigned int indentLevel, s s += values[i].write(prettyIndent, indentLevel + 1); if (i != (values.size() - 1)) { s += ","; - if (prettyIndent) - s += " "; } if (prettyIndent) s += "\n"; diff --git a/src/univalue/test/.gitignore b/src/univalue/test/.gitignore index 3d9347fe7e..7b27cf0da2 100644 --- a/src/univalue/test/.gitignore +++ b/src/univalue/test/.gitignore @@ -1,4 +1,8 @@ + +object unitester +test_json +no_nul *.trs *.log diff --git a/src/univalue/test/fail1.json b/src/univalue/test/fail1.json index 6216b865f1..8feb01a6d0 100644 --- a/src/univalue/test/fail1.json +++ b/src/univalue/test/fail1.json @@ -1 +1 @@ -"A JSON payload should be an object or array, not a string."
\ No newline at end of file +"This is a string that never ends, yes it goes on and on, my friends. diff --git a/src/univalue/test/fail42.json b/src/univalue/test/fail42.json Binary files differnew file mode 100644 index 0000000000..9c7565adbd --- /dev/null +++ b/src/univalue/test/fail42.json diff --git a/src/univalue/test/fail44.json b/src/univalue/test/fail44.json new file mode 100644 index 0000000000..80edceddf1 --- /dev/null +++ b/src/univalue/test/fail44.json @@ -0,0 +1 @@ +"This file ends without a newline or close-quote.
\ No newline at end of file diff --git a/src/univalue/test/no_nul.cpp b/src/univalue/test/no_nul.cpp new file mode 100644 index 0000000000..83d292200b --- /dev/null +++ b/src/univalue/test/no_nul.cpp @@ -0,0 +1,8 @@ +#include "univalue.h" + +int main (int argc, char *argv[]) +{ + char buf[] = "___[1,2,3]___"; + UniValue val; + return val.read(buf + 3, 7) ? 0 : 1; +} diff --git a/src/univalue/test/object.cpp b/src/univalue/test/object.cpp new file mode 100644 index 0000000000..02446292a1 --- /dev/null +++ b/src/univalue/test/object.cpp @@ -0,0 +1,395 @@ +// Copyright (c) 2014 BitPay Inc. +// 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. + +#include <stdint.h> +#include <vector> +#include <string> +#include <map> +#include <cassert> +#include <stdexcept> +#include <univalue.h> + +#define BOOST_FIXTURE_TEST_SUITE(a, b) +#define BOOST_AUTO_TEST_CASE(funcName) void funcName() +#define BOOST_AUTO_TEST_SUITE_END() +#define BOOST_CHECK(expr) assert(expr) +#define BOOST_CHECK_EQUAL(v1, v2) assert((v1) == (v2)) +#define BOOST_CHECK_THROW(stmt, excMatch) { \ + try { \ + (stmt); \ + } catch (excMatch & e) { \ + } catch (...) { \ + assert(0); \ + } \ + } +#define BOOST_CHECK_NO_THROW(stmt) { \ + try { \ + (stmt); \ + } catch (...) { \ + assert(0); \ + } \ + } + +BOOST_FIXTURE_TEST_SUITE(univalue_tests, BasicTestingSetup) + +BOOST_AUTO_TEST_CASE(univalue_constructor) +{ + UniValue v1; + BOOST_CHECK(v1.isNull()); + + UniValue v2(UniValue::VSTR); + BOOST_CHECK(v2.isStr()); + + UniValue v3(UniValue::VSTR, "foo"); + BOOST_CHECK(v3.isStr()); + BOOST_CHECK_EQUAL(v3.getValStr(), "foo"); + + UniValue numTest; + BOOST_CHECK(numTest.setNumStr("82")); + BOOST_CHECK(numTest.isNum()); + BOOST_CHECK_EQUAL(numTest.getValStr(), "82"); + + uint64_t vu64 = 82; + UniValue v4(vu64); + BOOST_CHECK(v4.isNum()); + BOOST_CHECK_EQUAL(v4.getValStr(), "82"); + + int64_t vi64 = -82; + UniValue v5(vi64); + BOOST_CHECK(v5.isNum()); + BOOST_CHECK_EQUAL(v5.getValStr(), "-82"); + + int vi = -688; + UniValue v6(vi); + BOOST_CHECK(v6.isNum()); + BOOST_CHECK_EQUAL(v6.getValStr(), "-688"); + + double vd = -7.21; + UniValue v7(vd); + BOOST_CHECK(v7.isNum()); + BOOST_CHECK_EQUAL(v7.getValStr(), "-7.21"); + + std::string vs("yawn"); + UniValue v8(vs); + BOOST_CHECK(v8.isStr()); + BOOST_CHECK_EQUAL(v8.getValStr(), "yawn"); + + const char *vcs = "zappa"; + UniValue v9(vcs); + BOOST_CHECK(v9.isStr()); + BOOST_CHECK_EQUAL(v9.getValStr(), "zappa"); +} + +BOOST_AUTO_TEST_CASE(univalue_typecheck) +{ + UniValue v1; + BOOST_CHECK(v1.setNumStr("1")); + BOOST_CHECK(v1.isNum()); + BOOST_CHECK_THROW(v1.get_bool(), std::runtime_error); + + UniValue v2; + BOOST_CHECK(v2.setBool(true)); + BOOST_CHECK_EQUAL(v2.get_bool(), true); + BOOST_CHECK_THROW(v2.get_int(), std::runtime_error); + + UniValue v3; + BOOST_CHECK(v3.setNumStr("32482348723847471234")); + BOOST_CHECK_THROW(v3.get_int64(), std::runtime_error); + BOOST_CHECK(v3.setNumStr("1000")); + BOOST_CHECK_EQUAL(v3.get_int64(), 1000); + + UniValue v4; + BOOST_CHECK(v4.setNumStr("2147483648")); + BOOST_CHECK_EQUAL(v4.get_int64(), 2147483648); + BOOST_CHECK_THROW(v4.get_int(), std::runtime_error); + BOOST_CHECK(v4.setNumStr("1000")); + BOOST_CHECK_EQUAL(v4.get_int(), 1000); + BOOST_CHECK_THROW(v4.get_str(), std::runtime_error); + BOOST_CHECK_EQUAL(v4.get_real(), 1000); + BOOST_CHECK_THROW(v4.get_array(), std::runtime_error); + BOOST_CHECK_THROW(v4.getKeys(), std::runtime_error); + BOOST_CHECK_THROW(v4.getValues(), std::runtime_error); + BOOST_CHECK_THROW(v4.get_obj(), std::runtime_error); + + UniValue v5; + BOOST_CHECK(v5.read("[true, 10]")); + BOOST_CHECK_NO_THROW(v5.get_array()); + std::vector<UniValue> vals = v5.getValues(); + BOOST_CHECK_THROW(vals[0].get_int(), std::runtime_error); + BOOST_CHECK_EQUAL(vals[0].get_bool(), true); + + BOOST_CHECK_EQUAL(vals[1].get_int(), 10); + BOOST_CHECK_THROW(vals[1].get_bool(), std::runtime_error); +} + +BOOST_AUTO_TEST_CASE(univalue_set) +{ + UniValue v(UniValue::VSTR, "foo"); + v.clear(); + BOOST_CHECK(v.isNull()); + BOOST_CHECK_EQUAL(v.getValStr(), ""); + + BOOST_CHECK(v.setObject()); + BOOST_CHECK(v.isObject()); + BOOST_CHECK_EQUAL(v.size(), 0); + BOOST_CHECK_EQUAL(v.getType(), UniValue::VOBJ); + BOOST_CHECK(v.empty()); + + BOOST_CHECK(v.setArray()); + BOOST_CHECK(v.isArray()); + BOOST_CHECK_EQUAL(v.size(), 0); + + BOOST_CHECK(v.setStr("zum")); + BOOST_CHECK(v.isStr()); + BOOST_CHECK_EQUAL(v.getValStr(), "zum"); + + BOOST_CHECK(v.setFloat(-1.01)); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "-1.01"); + + BOOST_CHECK(v.setInt((int)1023)); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "1023"); + + BOOST_CHECK(v.setInt((int64_t)-1023LL)); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "-1023"); + + BOOST_CHECK(v.setInt((uint64_t)1023ULL)); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "1023"); + + BOOST_CHECK(v.setNumStr("-688")); + BOOST_CHECK(v.isNum()); + BOOST_CHECK_EQUAL(v.getValStr(), "-688"); + + BOOST_CHECK(v.setBool(false)); + BOOST_CHECK_EQUAL(v.isBool(), true); + BOOST_CHECK_EQUAL(v.isTrue(), false); + BOOST_CHECK_EQUAL(v.isFalse(), true); + BOOST_CHECK_EQUAL(v.getBool(), false); + + BOOST_CHECK(v.setBool(true)); + BOOST_CHECK_EQUAL(v.isBool(), true); + BOOST_CHECK_EQUAL(v.isTrue(), true); + BOOST_CHECK_EQUAL(v.isFalse(), false); + BOOST_CHECK_EQUAL(v.getBool(), true); + + BOOST_CHECK(!v.setNumStr("zombocom")); + + BOOST_CHECK(v.setNull()); + BOOST_CHECK(v.isNull()); +} + +BOOST_AUTO_TEST_CASE(univalue_array) +{ + UniValue arr(UniValue::VARR); + + UniValue v((int64_t)1023LL); + BOOST_CHECK(arr.push_back(v)); + + std::string vStr("zippy"); + BOOST_CHECK(arr.push_back(vStr)); + + const char *s = "pippy"; + BOOST_CHECK(arr.push_back(s)); + + std::vector<UniValue> vec; + v.setStr("boing"); + vec.push_back(v); + + v.setStr("going"); + vec.push_back(v); + + BOOST_CHECK(arr.push_backV(vec)); + + BOOST_CHECK(arr.push_back((uint64_t) 400ULL)); + BOOST_CHECK(arr.push_back((int64_t) -400LL)); + BOOST_CHECK(arr.push_back((int) -401)); + BOOST_CHECK(arr.push_back(-40.1)); + + BOOST_CHECK_EQUAL(arr.empty(), false); + BOOST_CHECK_EQUAL(arr.size(), 9); + + BOOST_CHECK_EQUAL(arr[0].getValStr(), "1023"); + BOOST_CHECK_EQUAL(arr[1].getValStr(), "zippy"); + BOOST_CHECK_EQUAL(arr[2].getValStr(), "pippy"); + BOOST_CHECK_EQUAL(arr[3].getValStr(), "boing"); + BOOST_CHECK_EQUAL(arr[4].getValStr(), "going"); + BOOST_CHECK_EQUAL(arr[5].getValStr(), "400"); + BOOST_CHECK_EQUAL(arr[6].getValStr(), "-400"); + BOOST_CHECK_EQUAL(arr[7].getValStr(), "-401"); + BOOST_CHECK_EQUAL(arr[8].getValStr(), "-40.1"); + + BOOST_CHECK_EQUAL(arr[999].getValStr(), ""); + + arr.clear(); + BOOST_CHECK(arr.empty()); + BOOST_CHECK_EQUAL(arr.size(), 0); +} + +BOOST_AUTO_TEST_CASE(univalue_object) +{ + UniValue obj(UniValue::VOBJ); + std::string strKey, strVal; + UniValue v; + + strKey = "age"; + v.setInt(100); + BOOST_CHECK(obj.pushKV(strKey, v)); + + strKey = "first"; + strVal = "John"; + BOOST_CHECK(obj.pushKV(strKey, strVal)); + + strKey = "last"; + const char *cVal = "Smith"; + BOOST_CHECK(obj.pushKV(strKey, cVal)); + + strKey = "distance"; + BOOST_CHECK(obj.pushKV(strKey, (int64_t) 25)); + + strKey = "time"; + BOOST_CHECK(obj.pushKV(strKey, (uint64_t) 3600)); + + strKey = "calories"; + BOOST_CHECK(obj.pushKV(strKey, (int) 12)); + + strKey = "temperature"; + BOOST_CHECK(obj.pushKV(strKey, (double) 90.012)); + + UniValue obj2(UniValue::VOBJ); + BOOST_CHECK(obj2.pushKV("cat1", 9000)); + BOOST_CHECK(obj2.pushKV("cat2", 12345)); + + BOOST_CHECK(obj.pushKVs(obj2)); + + BOOST_CHECK_EQUAL(obj.empty(), false); + BOOST_CHECK_EQUAL(obj.size(), 9); + + BOOST_CHECK_EQUAL(obj["age"].getValStr(), "100"); + BOOST_CHECK_EQUAL(obj["first"].getValStr(), "John"); + BOOST_CHECK_EQUAL(obj["last"].getValStr(), "Smith"); + BOOST_CHECK_EQUAL(obj["distance"].getValStr(), "25"); + BOOST_CHECK_EQUAL(obj["time"].getValStr(), "3600"); + BOOST_CHECK_EQUAL(obj["calories"].getValStr(), "12"); + BOOST_CHECK_EQUAL(obj["temperature"].getValStr(), "90.012"); + BOOST_CHECK_EQUAL(obj["cat1"].getValStr(), "9000"); + BOOST_CHECK_EQUAL(obj["cat2"].getValStr(), "12345"); + + BOOST_CHECK_EQUAL(obj["nyuknyuknyuk"].getValStr(), ""); + + BOOST_CHECK(obj.exists("age")); + BOOST_CHECK(obj.exists("first")); + BOOST_CHECK(obj.exists("last")); + BOOST_CHECK(obj.exists("distance")); + BOOST_CHECK(obj.exists("time")); + BOOST_CHECK(obj.exists("calories")); + BOOST_CHECK(obj.exists("temperature")); + BOOST_CHECK(obj.exists("cat1")); + BOOST_CHECK(obj.exists("cat2")); + + BOOST_CHECK(!obj.exists("nyuknyuknyuk")); + + std::map<std::string, UniValue::VType> objTypes; + objTypes["age"] = UniValue::VNUM; + objTypes["first"] = UniValue::VSTR; + objTypes["last"] = UniValue::VSTR; + objTypes["distance"] = UniValue::VNUM; + objTypes["time"] = UniValue::VNUM; + objTypes["calories"] = UniValue::VNUM; + objTypes["temperature"] = UniValue::VNUM; + objTypes["cat1"] = UniValue::VNUM; + objTypes["cat2"] = UniValue::VNUM; + BOOST_CHECK(obj.checkObject(objTypes)); + + objTypes["cat2"] = UniValue::VSTR; + BOOST_CHECK(!obj.checkObject(objTypes)); + + obj.clear(); + BOOST_CHECK(obj.empty()); + BOOST_CHECK_EQUAL(obj.size(), 0); + BOOST_CHECK_EQUAL(obj.getType(), UniValue::VNULL); + + BOOST_CHECK_EQUAL(obj.setObject(), true); + UniValue uv; + uv.setInt(42); + obj.__pushKV("age", uv); + BOOST_CHECK_EQUAL(obj.size(), 1); + BOOST_CHECK_EQUAL(obj["age"].getValStr(), "42"); + + uv.setInt(43); + obj.pushKV("age", uv); + BOOST_CHECK_EQUAL(obj.size(), 1); + BOOST_CHECK_EQUAL(obj["age"].getValStr(), "43"); + + obj.pushKV("name", "foo bar"); + + std::map<std::string,UniValue> kv; + obj.getObjMap(kv); + BOOST_CHECK_EQUAL(kv["age"].getValStr(), "43"); + BOOST_CHECK_EQUAL(kv["name"].getValStr(), "foo bar"); + +} + +static const char *json1 = +"[1.10000000,{\"key1\":\"str\\u0000\",\"key2\":800,\"key3\":{\"name\":\"martian http://test.com\"}}]"; + +BOOST_AUTO_TEST_CASE(univalue_readwrite) +{ + UniValue v; + BOOST_CHECK(v.read(json1)); + + std::string strJson1(json1); + BOOST_CHECK(v.read(strJson1)); + + BOOST_CHECK(v.isArray()); + BOOST_CHECK_EQUAL(v.size(), 2); + + BOOST_CHECK_EQUAL(v[0].getValStr(), "1.10000000"); + + UniValue obj = v[1]; + BOOST_CHECK(obj.isObject()); + BOOST_CHECK_EQUAL(obj.size(), 3); + + BOOST_CHECK(obj["key1"].isStr()); + std::string correctValue("str"); + correctValue.push_back('\0'); + BOOST_CHECK_EQUAL(obj["key1"].getValStr(), correctValue); + BOOST_CHECK(obj["key2"].isNum()); + BOOST_CHECK_EQUAL(obj["key2"].getValStr(), "800"); + BOOST_CHECK(obj["key3"].isObject()); + + BOOST_CHECK_EQUAL(strJson1, v.write()); + + /* Check for (correctly reporting) a parsing error if the initial + JSON construct is followed by more stuff. Note that whitespace + is, of course, exempt. */ + + BOOST_CHECK(v.read(" {}\n ")); + BOOST_CHECK(v.isObject()); + BOOST_CHECK(v.read(" []\n ")); + BOOST_CHECK(v.isArray()); + + BOOST_CHECK(!v.read("@{}")); + BOOST_CHECK(!v.read("{} garbage")); + BOOST_CHECK(!v.read("[]{}")); + BOOST_CHECK(!v.read("{}[]")); + BOOST_CHECK(!v.read("{} 42")); +} + +BOOST_AUTO_TEST_SUITE_END() + +int main (int argc, char *argv[]) +{ + univalue_constructor(); + univalue_typecheck(); + univalue_set(); + univalue_array(); + univalue_object(); + univalue_readwrite(); + return 0; +} + diff --git a/src/univalue/test/round3.json b/src/univalue/test/round3.json new file mode 100644 index 0000000000..7182dc2f9b --- /dev/null +++ b/src/univalue/test/round3.json @@ -0,0 +1 @@ +"abcdefghijklmnopqrstuvwxyz" diff --git a/src/univalue/test/round4.json b/src/univalue/test/round4.json new file mode 100644 index 0000000000..7f8f011eb7 --- /dev/null +++ b/src/univalue/test/round4.json @@ -0,0 +1 @@ +7 diff --git a/src/univalue/test/round5.json b/src/univalue/test/round5.json new file mode 100644 index 0000000000..27ba77ddaf --- /dev/null +++ b/src/univalue/test/round5.json @@ -0,0 +1 @@ +true diff --git a/src/univalue/test/round6.json b/src/univalue/test/round6.json new file mode 100644 index 0000000000..c508d5366f --- /dev/null +++ b/src/univalue/test/round6.json @@ -0,0 +1 @@ +false diff --git a/src/univalue/test/round7.json b/src/univalue/test/round7.json new file mode 100644 index 0000000000..19765bd501 --- /dev/null +++ b/src/univalue/test/round7.json @@ -0,0 +1 @@ +null diff --git a/src/univalue/test/test_json.cpp b/src/univalue/test/test_json.cpp new file mode 100644 index 0000000000..2943bae2b1 --- /dev/null +++ b/src/univalue/test/test_json.cpp @@ -0,0 +1,24 @@ +// Test program that can be called by the JSON test suite at +// https://github.com/nst/JSONTestSuite. +// +// It reads JSON input from stdin and exits with code 0 if it can be parsed +// successfully. It also pretty prints the parsed JSON value to stdout. + +#include <iostream> +#include <string> +#include "univalue.h" + +using namespace std; + +int main (int argc, char *argv[]) +{ + UniValue val; + if (val.read(string(istreambuf_iterator<char>(cin), + istreambuf_iterator<char>()))) { + cout << val.write(1 /* prettyIndent */, 4 /* indentLevel */) << endl; + return 0; + } else { + cerr << "JSON Parse Error." << endl; + return 1; + } +} diff --git a/src/univalue/test/unitester.cpp b/src/univalue/test/unitester.cpp index 05f3842cd1..2c37794a4b 100644 --- a/src/univalue/test/unitester.cpp +++ b/src/univalue/test/unitester.cpp @@ -113,6 +113,8 @@ static const char *filenames[] = { "fail39.json", // invalid unicode: only second half of surrogate pair "fail40.json", // invalid unicode: broken UTF-8 "fail41.json", // invalid unicode: unfinished UTF-8 + "fail42.json", // valid json with garbage following a nul byte + "fail44.json", // unterminated string "fail3.json", "fail4.json", // extra comma "fail5.json", @@ -125,6 +127,11 @@ static const char *filenames[] = { "pass3.json", "round1.json", // round-trip test "round2.json", // unicode + "round3.json", // bare string + "round4.json", // bare number + "round5.json", // bare true + "round6.json", // bare false + "round7.json", // bare null }; // Test \u handling diff --git a/src/util.cpp b/src/util.cpp index 4659ff73c6..51ccc94787 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -810,6 +810,7 @@ fs::path GetSpecialFolderPath(int nFolder, bool fCreate) void runCommand(const std::string& strCommand) { + if (strCommand.empty()) return; int nErr = ::system(strCommand.c_str()); if (nErr) LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr); diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index fd233f6757..741680e93f 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -65,6 +65,19 @@ bool IsHex(const std::string& str) return (str.size() > 0) && (str.size()%2 == 0); } +bool IsHexNumber(const std::string& str) +{ + size_t starting_location = 0; + if (str.size() > 2 && *str.begin() == '0' && *(str.begin()+1) == 'x') { + starting_location = 2; + } + for (auto c : str.substr(starting_location)) { + if (HexDigit(c) < 0) return false; + } + // Return false for empty string or "0x". + return (str.size() > starting_location); +} + std::vector<unsigned char> ParseHex(const char* psz) { // convert hex dump to vector diff --git a/src/utilstrencodings.h b/src/utilstrencodings.h index 53da60e8f1..af33f0e5f8 100644 --- a/src/utilstrencodings.h +++ b/src/utilstrencodings.h @@ -38,7 +38,13 @@ std::string SanitizeString(const std::string& str, int rule = SAFE_CHARS_DEFAULT std::vector<unsigned char> ParseHex(const char* psz); std::vector<unsigned char> ParseHex(const std::string& str); signed char HexDigit(char c); +/* Returns true if each character in str is a hex character, and has an even + * number of hex digits.*/ bool IsHex(const std::string& str); +/** +* Return true if the string is a hex number, optionally prefixed with "0x" +*/ +bool IsHexNumber(const std::string& str); std::vector<unsigned char> DecodeBase64(const char* p, bool* pfInvalid = nullptr); std::string DecodeBase64(const std::string& str); std::string EncodeBase64(const unsigned char* pch, size_t len); @@ -143,4 +149,28 @@ bool TimingResistantEqual(const T& a, const T& b) */ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out); +/** Convert from one power-of-2 number base to another. */ +template<int frombits, int tobits, bool pad, typename O, typename I> +bool ConvertBits(O& out, I it, I end) { + size_t acc = 0; + size_t bits = 0; + constexpr size_t maxv = (1 << tobits) - 1; + constexpr size_t max_acc = (1 << (frombits + tobits - 1)) - 1; + while (it != end) { + acc = ((acc << frombits) | *it) & max_acc; + bits += frombits; + while (bits >= tobits) { + bits -= tobits; + out.push_back((acc >> bits) & maxv); + } + ++it; + } + if (pad) { + if (bits) out.push_back((acc << (tobits - bits)) & maxv); + } else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) { + return false; + } + return true; +} + #endif // BITCOIN_UTILSTRENCODINGS_H diff --git a/src/validation.cpp b/src/validation.cpp index d1a8b8460a..a073e3d1c0 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -53,6 +53,9 @@ # error "Bitcoin cannot be compiled without assertions." #endif +#define MICRO 0.000001 +#define MILLI 0.001 + /** * Global state */ @@ -66,7 +69,7 @@ CWaitableCriticalSection csBestBlock; CConditionVariable cvBlockChange; int nScriptCheckThreads = 0; std::atomic_bool fImporting(false); -bool fReindex = false; +std::atomic_bool fReindex(false); bool fTxIndex = false; bool fHavePruned = false; bool fPruneMode = false; @@ -80,6 +83,7 @@ int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE; bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT; uint256 hashAssumeValid; +arith_uint256 nMinimumChainWork; CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE); CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE; @@ -215,7 +219,7 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // IsFinalTx() with one more than chainActive.Height(). const int nBlockHeight = chainActive.Height() + 1; - // BIP113 will require that time-locked transactions have nLockTime set to + // BIP113 requires that time-locked transactions have nLockTime set to // less than the median time of the previous block they're contained in. // When the next block is created its previous block will be the current // chain tip, so we use that to calculate the median time passed to @@ -251,6 +255,8 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool AssertLockHeld(mempool.cs); CBlockIndex* tip = chainActive.Tip(); + assert(tip != nullptr); + CBlockIndex index; index.pprev = tip; // CheckSequenceLocks() uses chainActive.Height()+1 to evaluate @@ -378,7 +384,9 @@ void UpdateMempoolForReorg(DisconnectedBlockTransactions &disconnectpool, bool f while (it != disconnectpool.queuedTx.get<insertion_order>().rend()) { // ignore validation errors in resurrected transactions CValidationState stateDummy; - if (!fAddToMempool || (*it)->IsCoinBase() || !AcceptToMemoryPool(mempool, stateDummy, *it, false, nullptr, nullptr, true)) { + if (!fAddToMempool || (*it)->IsCoinBase() || + !AcceptToMemoryPool(mempool, stateDummy, *it, nullptr /* pfMissingInputs */, + nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */)) { // If the transaction doesn't make it in to the mempool, remove any // transactions that depend on it (which would now be orphans). mempool.removeRecursive(**it, MemPoolRemovalReason::REORG); @@ -437,9 +445,9 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt return CheckInputs(tx, state, view, true, flags, cacheSigStore, true, txdata); } -static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool fLimitFree, +static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced, - bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache) + bool bypass_limits, const CAmount& nAbsurdFee, std::vector<COutPoint>& coins_to_uncache) { const CTransaction& tx = *ptx; const uint256 hash = tx.GetHash(); @@ -526,7 +534,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool CCoinsView dummy; CCoinsViewCache view(&dummy); - CAmount nValueIn = 0; LockPoints lp; { LOCK(pool.cs); @@ -557,8 +564,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // Bring the best block into scope view.GetBestBlock(); - nValueIn = view.GetValueIn(tx); - // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool view.SetBackend(dummy); @@ -569,6 +574,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // CoinsViewCache instead of create its own if (!CheckSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final"); + + } // end LOCK(pool.cs) + + CAmount nFees = 0; + if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees)) { + return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state)); } // Check for non-standard pay-to-script-hash in inputs @@ -581,8 +592,6 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool int64_t nSigOpsCost = GetTransactionSigOpCost(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS); - CAmount nValueOut = tx.GetValueOut(); - CAmount nFees = nValueIn-nValueOut; // nModifiedFees includes any fee deltas from PrioritiseTransaction CAmount nModifiedFees = nFees; pool.ApplyDelta(hash, nModifiedFees); @@ -612,12 +621,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool strprintf("%d", nSigOpsCost)); CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); - if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { + if (!bypass_limits && mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee)); } // No transactions are allowed below minRelayTxFee except from disconnected blocks - if (fLimitFree && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) { + if (!bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met"); } @@ -849,17 +858,18 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool } pool.RemoveStaged(allConflicting, false, MemPoolRemovalReason::REPLACED); - // This transaction should only count for fee estimation if it isn't a - // BIP 125 replacement transaction (may not be widely supported), the - // node is not behind, and the transaction is not dependent on any other - // transactions in the mempool. - bool validForFeeEstimation = !fReplacementTransaction && IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); + // This transaction should only count for fee estimation if: + // - it isn't a BIP 125 replacement transaction (may not be widely supported) + // - it's not being readded during a reorg which bypasses typical mempool fee limits + // - the node is not behind + // - the transaction is not dependent on any other transactions in the mempool + bool validForFeeEstimation = !fReplacementTransaction && !bypass_limits && IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); // Store transaction in memory pool.addUnchecked(hash, entry, setAncestors, validForFeeEstimation); // trim mempool and check if tx was trimmed - if (!fOverrideMempoolLimit) { + if (!bypass_limits) { LimitMempoolSize(pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); if (!pool.exists(hash)) return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool full"); @@ -872,12 +882,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool } /** (try to) add transaction to memory pool with a specified acceptance time **/ -static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, +static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool* pfMissingInputs, int64_t nAcceptTime, std::list<CTransactionRef>* plTxnReplaced, - bool fOverrideMempoolLimit, const CAmount nAbsurdFee) + bool bypass_limits, const CAmount nAbsurdFee) { std::vector<COutPoint> coins_to_uncache; - bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, fLimitFree, pfMissingInputs, nAcceptTime, plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee, coins_to_uncache); + bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, plTxnReplaced, bypass_limits, nAbsurdFee, coins_to_uncache); if (!res) { for (const COutPoint& hashTx : coins_to_uncache) pcoinsTip->Uncache(hashTx); @@ -888,12 +898,12 @@ static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPo return res; } -bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced, - bool fOverrideMempoolLimit, const CAmount nAbsurdFee) + bool bypass_limits, const CAmount nAbsurdFee) { const CChainParams& chainparams = Params(); - return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, fLimitFree, pfMissingInputs, GetTime(), plTxnReplaced, fOverrideMempoolLimit, nAbsurdFee); + return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, pfMissingInputs, GetTime(), plTxnReplaced, bypass_limits, nAbsurdFee); } /** Return transaction in txOut, and if it was found inside a block, its hash is placed in hashBlock */ @@ -1032,8 +1042,6 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) bool IsInitialBlockDownload() { - const CChainParams& chainParams = Params(); - // Once this function has returned false, it must remain false. static std::atomic<bool> latchToFalse{false}; // Optimization: pre-test latch before taking the lock. @@ -1047,7 +1055,7 @@ bool IsInitialBlockDownload() return true; if (chainActive.Tip() == nullptr) return true; - if (chainActive.Tip()->nChainWork < UintToArith256(chainParams.GetConsensus().nMinimumChainWork)) + if (chainActive.Tip()->nChainWork < nMinimumChainWork) return true; if (chainActive.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge)) return true; @@ -1199,7 +1207,7 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; const CScriptWitness *witness = &ptxTo->vin[nIn].scriptWitness; - return VerifyScript(scriptSig, scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), &error); + return VerifyScript(scriptSig, m_tx_out.scriptPubKey, witness, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, cacheStore, *txdata), &error); } int GetSpendHeight(const CCoinsViewCache& inputs) @@ -1240,9 +1248,6 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi { if (!tx.IsCoinBase()) { - if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs))) - return false; - if (pvChecks) pvChecks->reserve(tx.vin.size()); @@ -1281,11 +1286,9 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // a sanity check that our caching is not introducing consensus // failures through additional data in, eg, the coins being // spent being checked as a part of CScriptCheck. - const CScript& scriptPubKey = coin.out.scriptPubKey; - const CAmount amount = coin.out.nValue; // Verify signature - CScriptCheck check(scriptPubKey, amount, tx, i, flags, cacheSigStore, &txdata); + CScriptCheck check(coin.out, tx, i, flags, cacheSigStore, &txdata); if (pvChecks) { pvChecks->push_back(CScriptCheck()); check.swap(pvChecks->back()); @@ -1297,7 +1300,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi // arguments; if so, don't trigger DoS protection to // avoid splitting the network between upgraded and // non-upgraded nodes. - CScriptCheck check2(scriptPubKey, amount, tx, i, + CScriptCheck check2(coin.out, tx, i, flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS, cacheSigStore, &txdata); if (check2()) return state.Invalid(false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); @@ -1623,6 +1626,7 @@ static int64_t nTimeConnect = 0; static int64_t nTimeIndex = 0; static int64_t nTimeCallbacks = 0; static int64_t nTimeTotal = 0; +static int64_t nBlocksTotal = 0; /** Apply the effects of this block (with given index) on the UTXO set represented by coins. * Validity checks that depend on the UTXO set are also done; ConnectBlock() @@ -1653,6 +1657,8 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd return true; } + nBlocksTotal++; + bool fScriptChecks = true; if (!hashAssumeValid.IsNull()) { // We've been configured with the hash of a block which has been externally verified to have a valid history. @@ -1664,7 +1670,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if (it != mapBlockIndex.end()) { if (it->second->GetAncestor(pindex->nHeight) == pindex && pindexBestHeader->GetAncestor(pindex->nHeight) == pindex && - pindexBestHeader->nChainWork >= UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) { + pindexBestHeader->nChainWork >= nMinimumChainWork) { // This block is a member of the assumed verified chain and an ancestor of the best header. // The equivalent time check discourages hash power from extorting the network via DOS attack // into accepting an invalid block through telling users they must manually set assumevalid. @@ -1680,7 +1686,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd } int64_t nTime1 = GetTimeMicros(); nTimeCheck += nTime1 - nTimeStart; - LogPrint(BCLog::BENCH, " - Sanity checks: %.2fms [%.2fs]\n", 0.001 * (nTime1 - nTimeStart), nTimeCheck * 0.000001); + LogPrint(BCLog::BENCH, " - Sanity checks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime1 - nTimeStart), nTimeCheck * MICRO, nTimeCheck * MILLI / nBlocksTotal); // Do not allow blocks that contain transactions which 'overwrite' older transactions, // unless those are already completely spent. @@ -1704,6 +1710,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd // before the first had been spent. Since those coinbases are sufficiently buried its no longer possible to create further // duplicate transactions descending from the known pairs either. // If we're on the known chain at height greater than where BIP34 activated, we can save the db accesses needed for the BIP30 check. + assert(pindex->pprev); CBlockIndex *pindexBIP34height = pindex->pprev->GetAncestor(chainparams.GetConsensus().BIP34Height); //Only continue to enforce if we're below BIP34 activation height or the block hash at that height doesn't correspond. fEnforceBIP30 = fEnforceBIP30 && (!pindexBIP34height || !(pindexBIP34height->GetBlockHash() == chainparams.GetConsensus().BIP34Hash)); @@ -1729,7 +1736,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd unsigned int flags = GetBlockScriptFlags(pindex, chainparams.GetConsensus()); int64_t nTime2 = GetTimeMicros(); nTimeForks += nTime2 - nTime1; - LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs]\n", 0.001 * (nTime2 - nTime1), nTimeForks * 0.000001); + LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime2 - nTime1), nTimeForks * MICRO, nTimeForks * MILLI / nBlocksTotal); CBlockUndo blockundo; @@ -1753,9 +1760,15 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if (!tx.IsCoinBase()) { - if (!view.HaveInputs(tx)) - return state.DoS(100, error("ConnectBlock(): inputs missing/spent"), - REJECT_INVALID, "bad-txns-inputs-missingorspent"); + CAmount txfee = 0; + if (!Consensus::CheckTxInputs(tx, state, view, pindex->nHeight, txfee)) { + return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state)); + } + nFees += txfee; + if (!MoneyRange(nFees)) { + return state.DoS(100, error("%s: accumulated fee in the block out of range.", __func__), + REJECT_INVALID, "bad-txns-accumulated-fee-outofrange"); + } // Check that transaction is BIP68 final // BIP68 lock checks (as opposed to nLockTime checks) must @@ -1783,8 +1796,6 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd txdata.emplace_back(tx); if (!tx.IsCoinBase()) { - nFees += view.GetValueIn(tx)-tx.GetValueOut(); - std::vector<CScriptCheck> vChecks; bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */ if (!CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txdata[i], nScriptCheckThreads ? &vChecks : nullptr)) @@ -1803,7 +1814,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } int64_t nTime3 = GetTimeMicros(); nTimeConnect += nTime3 - nTime2; - LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime3 - nTime2), 0.001 * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * 0.000001); + LogPrint(BCLog::BENCH, " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs (%.2fms/blk)]\n", (unsigned)block.vtx.size(), MILLI * (nTime3 - nTime2), MILLI * (nTime3 - nTime2) / block.vtx.size(), nInputs <= 1 ? 0 : MILLI * (nTime3 - nTime2) / (nInputs-1), nTimeConnect * MICRO, nTimeConnect * MILLI / nBlocksTotal); CAmount blockReward = nFees + GetBlockSubsidy(pindex->nHeight, chainparams.GetConsensus()); if (block.vtx[0]->GetValueOut() > blockReward) @@ -1815,7 +1826,7 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if (!control.Wait()) return state.DoS(100, error("%s: CheckQueue failed", __func__), REJECT_INVALID, "block-validation-failed"); int64_t nTime4 = GetTimeMicros(); nTimeVerify += nTime4 - nTime2; - LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs]\n", nInputs - 1, 0.001 * (nTime4 - nTime2), nInputs <= 1 ? 0 : 0.001 * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * 0.000001); + LogPrint(BCLog::BENCH, " - Verify %u txins: %.2fms (%.3fms/txin) [%.2fs (%.2fms/blk)]\n", nInputs - 1, MILLI * (nTime4 - nTime2), nInputs <= 1 ? 0 : MILLI * (nTime4 - nTime2) / (nInputs-1), nTimeVerify * MICRO, nTimeVerify * MILLI / nBlocksTotal); if (fJustCheck) return true; @@ -1843,14 +1854,15 @@ static bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockInd if (!pblocktree->WriteTxIndex(vPos)) return AbortNode(state, "Failed to write transaction index"); + assert(pindex->phashBlock); // add this block to the view's block chain view.SetBestBlock(pindex->GetBlockHash()); int64_t nTime5 = GetTimeMicros(); nTimeIndex += nTime5 - nTime4; - LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs]\n", 0.001 * (nTime5 - nTime4), nTimeIndex * 0.000001); + LogPrint(BCLog::BENCH, " - Index writing: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime5 - nTime4), nTimeIndex * MICRO, nTimeIndex * MILLI / nBlocksTotal); int64_t nTime6 = GetTimeMicros(); nTimeCallbacks += nTime6 - nTime5; - LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime6 - nTime5), nTimeCallbacks * 0.000001); + LogPrint(BCLog::BENCH, " - Callbacks: %.2fms [%.2fs (%.2fms/blk)]\n", MILLI * (nTime6 - nTime5), nTimeCallbacks * MICRO, nTimeCallbacks * MILLI / nBlocksTotal); return true; } @@ -2075,7 +2087,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara bool flushed = view.Flush(); assert(flushed); } - LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); + LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * MILLI); // Write the chain state to disk, if necessary. if (!FlushStateToDisk(chainparams, state, FLUSH_STATE_IF_NEEDED)) return false; @@ -2196,7 +2208,7 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, // Apply the block atomically to the chain state. int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1; int64_t nTime3; - LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * 0.001, nTimeReadFromDisk * 0.000001); + LogPrint(BCLog::BENCH, " - Load block from disk: %.2fms [%.2fs]\n", (nTime2 - nTime1) * MILLI, nTimeReadFromDisk * MICRO); { CCoinsViewCache view(pcoinsTip); bool rv = ConnectBlock(blockConnecting, state, pindexNew, view, chainparams); @@ -2207,17 +2219,17 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); } nTime3 = GetTimeMicros(); nTimeConnectTotal += nTime3 - nTime2; - LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs]\n", (nTime3 - nTime2) * 0.001, nTimeConnectTotal * 0.000001); + LogPrint(BCLog::BENCH, " - Connect total: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime3 - nTime2) * MILLI, nTimeConnectTotal * MICRO, nTimeConnectTotal * MILLI / nBlocksTotal); bool flushed = view.Flush(); assert(flushed); } int64_t nTime4 = GetTimeMicros(); nTimeFlush += nTime4 - nTime3; - LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs]\n", (nTime4 - nTime3) * 0.001, nTimeFlush * 0.000001); + LogPrint(BCLog::BENCH, " - Flush: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime4 - nTime3) * MILLI, nTimeFlush * MICRO, nTimeFlush * MILLI / nBlocksTotal); // Write the chain state to disk, if necessary. if (!FlushStateToDisk(chainparams, state, FLUSH_STATE_IF_NEEDED)) return false; int64_t nTime5 = GetTimeMicros(); nTimeChainState += nTime5 - nTime4; - LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs]\n", (nTime5 - nTime4) * 0.001, nTimeChainState * 0.000001); + LogPrint(BCLog::BENCH, " - Writing chainstate: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime5 - nTime4) * MILLI, nTimeChainState * MICRO, nTimeChainState * MILLI / nBlocksTotal); // Remove conflicting transactions from the mempool.; mempool.removeForBlock(blockConnecting.vtx, pindexNew->nHeight); disconnectpool.removeForBlock(blockConnecting.vtx); @@ -2225,8 +2237,8 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, UpdateTip(pindexNew, chainparams); int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; - LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs]\n", (nTime6 - nTime5) * 0.001, nTimePostConnect * 0.000001); - LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs]\n", (nTime6 - nTime1) * 0.001, nTimeTotal * 0.000001); + LogPrint(BCLog::BENCH, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, nTimePostConnect * MILLI / nBlocksTotal); + LogPrint(BCLog::BENCH, "- Connect block: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime1) * MILLI, nTimeTotal * MICRO, nTimeTotal * MILLI / nBlocksTotal); connectTrace.BlockConnected(pindexNew, std::move(pthisBlock)); return true; @@ -3182,7 +3194,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons CheckBlockIndex(chainparams.GetConsensus()); if (!ret) { GetMainSignals().BlockChecked(*pblock, state); - return error("%s: AcceptBlock FAILED", __func__); + return error("%s: AcceptBlock FAILED (%s)", __func__, state.GetDebugMessage()); } } @@ -3223,8 +3235,10 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, */ /* Calculate the amount of disk space the block & undo files currently use */ -static uint64_t CalculateCurrentUsage() +uint64_t CalculateCurrentUsage() { + LOCK(cs_LastBlockFile); + uint64_t retval = 0; for (const CBlockFileInfo &file : vinfoBlockFile) { retval += file.nSize + file.nUndoSize; @@ -3235,6 +3249,8 @@ static uint64_t CalculateCurrentUsage() /* Prune a block file (modify associated database entries)*/ void PruneOneBlockFile(const int fileNumber) { + LOCK(cs_LastBlockFile); + for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); ++it) { CBlockIndex* pindex = it->second; if (pindex->nFile == fileNumber) { @@ -3523,7 +3539,7 @@ bool static LoadBlockIndexDB(const CChainParams& chainparams) // Check whether we need to continue reindexing bool fReindexing = false; pblocktree->ReadReindexing(fReindexing); - fReindex |= fReindexing; + if(fReindexing) fReindex = true; // Check whether we have a transaction index pblocktree->ReadFlag("txindex", fTxIndex); @@ -3563,12 +3579,12 @@ bool LoadChainTip(const CChainParams& chainparams) CVerifyDB::CVerifyDB() { - uiInterface.ShowProgress(_("Verifying blocks..."), 0); + uiInterface.ShowProgress(_("Verifying blocks..."), 0, false); } CVerifyDB::~CVerifyDB() { - uiInterface.ShowProgress("", 100); + uiInterface.ShowProgress("", 100, false); } bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, int nCheckLevel, int nCheckDepth) @@ -3598,7 +3614,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, LogPrintf("[%d%%]...", percentageDone); reportDone = percentageDone/10; } - uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone); + uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone, false); if (pindex->nHeight < chainActive.Height()-nCheckDepth) break; if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) { @@ -3649,7 +3665,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, CBlockIndex *pindex = pindexState; while (pindex != chainActive.Tip()) { boost::this_thread::interruption_point(); - uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50)))); + uiInterface.ShowProgress(_("Verifying blocks..."), std::max(1, std::min(99, 100 - (int)(((double)(chainActive.Height() - pindex->nHeight)) / (double)nCheckDepth * 50))), false); pindex = chainActive.Next(pindex); CBlock block; if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) @@ -3696,7 +3712,7 @@ bool ReplayBlocks(const CChainParams& params, CCoinsView* view) if (hashHeads.empty()) return true; // We're already in a consistent state. if (hashHeads.size() != 2) return error("ReplayBlocks(): unknown inconsistent state"); - uiInterface.ShowProgress(_("Replaying blocks..."), 0); + uiInterface.ShowProgress(_("Replaying blocks..."), 0, false); LogPrintf("Replaying blocks\n"); const CBlockIndex* pindexOld = nullptr; // Old tip during the interrupted flush. @@ -3747,7 +3763,7 @@ bool ReplayBlocks(const CChainParams& params, CCoinsView* view) cache.SetBestBlock(pindexNew->GetBlockHash()); cache.Flush(); - uiInterface.ShowProgress("", 100); + uiInterface.ShowProgress("", 100, false); return true; } @@ -4237,6 +4253,8 @@ std::string CBlockFileInfo::ToString() const CBlockFileInfo* GetBlockFileInfo(size_t n) { + LOCK(cs_LastBlockFile); + return &vinfoBlockFile.at(n); } @@ -4299,7 +4317,8 @@ bool LoadMempool(void) CValidationState state; if (nTime + nExpiryTimeout > nNow) { LOCK(cs_main); - AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, true, nullptr, nTime, nullptr, false, 0); + AcceptToMemoryPoolWithTime(chainparams, mempool, state, tx, nullptr /* pfMissingInputs */, nTime, + nullptr /* plTxnReplaced */, false /* bypass_limits */, 0 /* nAbsurdFee */); if (state.IsValid()) { ++count; } else { @@ -4326,7 +4345,7 @@ bool LoadMempool(void) return true; } -void DumpMempool(void) +bool DumpMempool(void) { int64_t start = GetTimeMicros(); @@ -4346,7 +4365,7 @@ void DumpMempool(void) try { FILE* filestr = fsbridge::fopen(GetDataDir() / "mempool.dat.new", "wb"); if (!filestr) { - return; + return false; } CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); @@ -4367,10 +4386,12 @@ void DumpMempool(void) file.fclose(); RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat"); int64_t last = GetTimeMicros(); - LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*0.000001, (last-mid)*0.000001); + LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", (mid-start)*MICRO, (last-mid)*MICRO); } catch (const std::exception& e) { LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what()); + return false; } + return true; } //! Guess how far we are in the verification process at the given block index diff --git a/src/validation.h b/src/validation.h index d0f6cdc135..6bc52753c5 100644 --- a/src/validation.h +++ b/src/validation.h @@ -45,9 +45,9 @@ struct ChainTxData; struct PrecomputedTransactionData; struct LockPoints; -/** Default for DEFAULT_WHITELISTRELAY. */ +/** Default for -whitelistrelay. */ static const bool DEFAULT_WHITELISTRELAY = true; -/** Default for DEFAULT_WHITELISTFORCERELAY. */ +/** Default for -whitelistforcerelay. */ static const bool DEFAULT_WHITELISTFORCERELAY = true; /** Default for -minrelaytxfee, minimum relay fee for transactions */ static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 1000; @@ -94,8 +94,8 @@ static const int MAX_CMPCTBLOCK_DEPTH = 5; static const int MAX_BLOCKTXN_DEPTH = 10; /** Size of the "block download window": how far ahead of our current height do we fetch? * Larger windows tolerate larger download speed differences between peer, but increase the potential - * degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning - * harder). We'll probably want to make this a per-peer adaptive value at some point. */ + * degree of disordering of blocks on disk (which make reindexing and pruning harder). We'll probably + * want to make this a per-peer adaptive value at some point. */ static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024; /** Time to wait (in seconds) between writing blocks/block index to disk. */ static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60; @@ -161,13 +161,12 @@ extern CTxMemPool mempool; typedef std::unordered_map<uint256, CBlockIndex*, BlockHasher> BlockMap; extern BlockMap mapBlockIndex; extern uint64_t nLastBlockTx; -extern uint64_t nLastBlockSize; extern uint64_t nLastBlockWeight; extern const std::string strMessageMagic; extern CWaitableCriticalSection csBestBlock; extern CConditionVariable cvBlockChange; extern std::atomic_bool fImporting; -extern bool fReindex; +extern std::atomic_bool fReindex; extern int nScriptCheckThreads; extern bool fTxIndex; extern bool fIsBareMultisigStd; @@ -186,6 +185,9 @@ extern bool fEnableReplacement; /** Block hash whose ancestors we will assume to have valid scripts without checking them. */ extern uint256 hashAssumeValid; +/** Minimum work we will assume exists on some valid chain. */ +extern arith_uint256 nMinimumChainWork; + /** Best header we've seen so far (used for getheaders queries' starting points). */ extern CBlockIndex *pindexBestHeader; @@ -278,6 +280,9 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); /** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */ double GuessVerificationProgress(const ChainTxData& data, CBlockIndex* pindex); +/** Calculate the amount of disk space the block & undo files currently use */ +uint64_t CalculateCurrentUsage(); + /** * Mark one block file as pruned. */ @@ -299,9 +304,9 @@ void PruneBlockFilesManual(int nManualPruneHeight); /** (try to) add transaction to memory pool * plTxnReplaced will be appended to with all transactions replaced from mempool **/ -bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool fLimitFree, - bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced = nullptr, - bool fOverrideMempoolLimit=false, const CAmount nAbsurdFee=0); +bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, + bool* pfMissingInputs, std::list<CTransactionRef>* plTxnReplaced, + bool bypass_limits, const CAmount nAbsurdFee); /** Convert CValidationState to a human-readable message for logging */ std::string FormatStateMessage(const CValidationState &state); @@ -355,8 +360,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp = null class CScriptCheck { private: - CScript scriptPubKey; - CAmount amount; + CTxOut m_tx_out; const CTransaction *ptxTo; unsigned int nIn; unsigned int nFlags; @@ -365,17 +369,15 @@ private: PrecomputedTransactionData *txdata; public: - CScriptCheck(): amount(0), ptxTo(nullptr), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} - CScriptCheck(const CScript& scriptPubKeyIn, const CAmount amountIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) : - scriptPubKey(scriptPubKeyIn), amount(amountIn), - ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { } + CScriptCheck(): ptxTo(nullptr), nIn(0), nFlags(0), cacheStore(false), error(SCRIPT_ERR_UNKNOWN_ERROR) {} + CScriptCheck(const CTxOut& outIn, const CTransaction& txToIn, unsigned int nInIn, unsigned int nFlagsIn, bool cacheIn, PrecomputedTransactionData* txdataIn) : + m_tx_out(outIn), ptxTo(&txToIn), nIn(nInIn), nFlags(nFlagsIn), cacheStore(cacheIn), error(SCRIPT_ERR_UNKNOWN_ERROR), txdata(txdataIn) { } bool operator()(); void swap(CScriptCheck &check) { - scriptPubKey.swap(check.scriptPubKey); std::swap(ptxTo, check.ptxTo); - std::swap(amount, check.amount); + std::swap(m_tx_out, check.m_tx_out); std::swap(nIn, check.nIn); std::swap(nFlags, check.nFlags); std::swap(cacheStore, check.cacheStore); @@ -475,7 +477,7 @@ static const unsigned int REJECT_HIGHFEE = 0x100; CBlockFileInfo* GetBlockFileInfo(size_t n); /** Dump the mempool to disk. */ -void DumpMempool(); +bool DumpMempool(); /** Load the mempool from disk. */ bool LoadMempool(); diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index be5029dec3..281bc04b0a 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -4,7 +4,9 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "validationinterface.h" + #include "init.h" +#include "primitives/block.h" #include "scheduler.h" #include "sync.h" #include "util.h" diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index dcce88cedc..8db3bfd69c 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -27,8 +27,7 @@ int CCrypter::BytesToKeySHA512AES(const std::vector<unsigned char>& chSalt, cons CSHA512 di; di.Write((const unsigned char*)strKeyData.c_str(), strKeyData.size()); - if(chSalt.size()) - di.Write(&chSalt[0], chSalt.size()); + di.Write(chSalt.data(), chSalt.size()); di.Finalize(buf); for(int i = 0; i != count - 1; i++) @@ -82,7 +81,7 @@ bool CCrypter::Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned vchCiphertext.resize(vchPlaintext.size() + AES_BLOCKSIZE); AES256CBCEncrypt enc(vchKey.data(), vchIV.data(), true); - size_t nLen = enc.Encrypt(&vchPlaintext[0], vchPlaintext.size(), &vchCiphertext[0]); + size_t nLen = enc.Encrypt(&vchPlaintext[0], vchPlaintext.size(), vchCiphertext.data()); if(nLen < vchPlaintext.size()) return false; vchCiphertext.resize(nLen); @@ -101,7 +100,7 @@ bool CCrypter::Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingM vchPlaintext.resize(nLen); AES256CBCDecrypt dec(vchKey.data(), vchIV.data(), true); - nLen = dec.Decrypt(&vchCiphertext[0], vchCiphertext.size(), &vchPlaintext[0]); + nLen = dec.Decrypt(vchCiphertext.data(), vchCiphertext.size(), &vchPlaintext[0]); if(nLen == 0) return false; vchPlaintext.resize(nLen); @@ -113,7 +112,7 @@ static bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMateri { CCrypter cKeyCrypter; std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE); - memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE); + memcpy(chIV.data(), &nIV, WALLET_CRYPTO_IV_SIZE); if(!cKeyCrypter.SetKey(vMasterKey, chIV)) return false; return cKeyCrypter.Encrypt(*((const CKeyingMaterial*)&vchPlaintext), vchCiphertext); @@ -123,7 +122,7 @@ static bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<u { CCrypter cKeyCrypter; std::vector<unsigned char> chIV(WALLET_CRYPTO_IV_SIZE); - memcpy(&chIV[0], &nIV, WALLET_CRYPTO_IV_SIZE); + memcpy(chIV.data(), &nIV, WALLET_CRYPTO_IV_SIZE); if(!cKeyCrypter.SetKey(vMasterKey, chIV)) return false; return cKeyCrypter.Decrypt(vchCiphertext, *((CKeyingMaterial*)&vchPlaintext)); diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index f1e8a25650..1416ae7d02 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -9,6 +9,8 @@ #include "serialize.h" #include "support/allocators/secure.h" +#include <atomic> + const unsigned int WALLET_CRYPTO_KEY_SIZE = 32; const unsigned int WALLET_CRYPTO_SALT_SIZE = 8; const unsigned int WALLET_CRYPTO_IV_SIZE = 16; @@ -16,13 +18,13 @@ const unsigned int WALLET_CRYPTO_IV_SIZE = 16; /** * Private key encryption is done based on a CMasterKey, * which holds a salt and random encryption key. - * + * * CMasterKeys are encrypted using AES-256-CBC using a key * derived using derivation method nDerivationMethod * (0 == EVP_sha512()) and derivation iterations nDeriveIterations. * vchOtherDerivationParameters is provided for alternative algorithms * which may require more parameters (such as scrypt). - * + * * Wallet Private Keys are then encrypted using AES-256-CBC * with the double-sha256 of the public key as the IV, and the * master key's key as the encryption key (see keystore.[ch]). @@ -118,7 +120,7 @@ private: //! if fUseCrypto is true, mapKeys must be empty //! if fUseCrypto is false, vMasterKey must be empty - bool fUseCrypto; + std::atomic<bool> fUseCrypto; //! keeps track of whether Unlock has run a thorough check before bool fDecryptionThoroughlyChecked; @@ -162,28 +164,26 @@ public: { { LOCK(cs_KeyStore); - if (!IsCrypted()) + if (!IsCrypted()) { return CBasicKeyStore::HaveKey(address); + } return mapCryptedKeys.count(address) > 0; } return false; } bool GetKey(const CKeyID &address, CKey& keyOut) const override; bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override; - void GetKeys(std::set<CKeyID> &setAddress) const override + std::set<CKeyID> GetKeys() const override { - if (!IsCrypted()) - { - CBasicKeyStore::GetKeys(setAddress); - return; + LOCK(cs_KeyStore); + if (!IsCrypted()) { + return CBasicKeyStore::GetKeys(); } - setAddress.clear(); - CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); - while (mi != mapCryptedKeys.end()) - { - setAddress.insert((*mi).first); - mi++; + std::set<CKeyID> set_address; + for (const auto& mi : mapCryptedKeys) { + set_address.insert(mi.first); } + return set_address; } /** diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index d2fe4866fa..d66ba48421 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -558,7 +558,6 @@ bool CDB::Rewrite(CWalletDBWrapper& dbw, const char* pszSkip) } MilliSleep(100); } - return false; } @@ -684,7 +683,6 @@ bool CWalletDBWrapper::Backup(const std::string& strDest) } MilliSleep(100); } - return false; } void CWalletDBWrapper::Flush(bool shutdown) diff --git a/src/wallet/db.h b/src/wallet/db.h index 6f3cfe9557..14283ac8f8 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -156,6 +156,9 @@ public: explicit CDB(CWalletDBWrapper& dbw, const char* pszMode = "r+", bool fFlushOnCloseIn=true); ~CDB() { Close(); } + CDB(const CDB&) = delete; + CDB& operator=(const CDB&) = delete; + void Flush(); void Close(); static bool Recover(const std::string& filename, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename); @@ -168,10 +171,6 @@ public: /* verifies the database file */ static bool VerifyDatabaseFile(const std::string& walletFile, const fs::path& dataDir, std::string& warningStr, std::string& errorStr, CDBEnv::recoverFunc_type recoverFunc); -private: - CDB(const CDB&); - void operator=(const CDB&); - public: template <typename K, typename T> bool Read(const K& key, T& value) diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 4c0e3a5fe1..6abd060714 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -5,6 +5,7 @@ #include "consensus/validation.h" #include "wallet/coincontrol.h" #include "wallet/feebumper.h" +#include "wallet/fees.h" #include "wallet/wallet.h" #include "policy/fees.h" #include "policy/policy.h" @@ -156,7 +157,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin currentResult = BumpFeeResult::INVALID_PARAMETER; return; } - CAmount requiredFee = CWallet::GetRequiredFee(maxNewTxSize); + CAmount requiredFee = GetRequiredFee(maxNewTxSize); if (totalFee < requiredFee) { vErrors.push_back(strprintf("Insufficient totalFee (cannot be less than required fee %s)", FormatMoney(requiredFee))); @@ -166,7 +167,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, const CCoin nNewFee = totalFee; nNewFeeRate = CFeeRate(totalFee, maxNewTxSize); } else { - nNewFee = CWallet::GetMinimumFee(maxNewTxSize, coin_control, mempool, ::feeEstimator, nullptr /* FeeCalculation */); + nNewFee = GetMinimumFee(maxNewTxSize, coin_control, mempool, ::feeEstimator, nullptr /* FeeCalculation */); nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize); // New fee rate must be at least old rate + minimum incremental relay rate diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp new file mode 100644 index 0000000000..76eeeeda05 --- /dev/null +++ b/src/wallet/fees.cpp @@ -0,0 +1,89 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "wallet/fees.h" + +#include "policy/policy.h" +#include "txmempool.h" +#include "util.h" +#include "validation.h" +#include "wallet/coincontrol.h" +#include "wallet/wallet.h" + + +CAmount GetRequiredFee(unsigned int nTxBytes) +{ + return std::max(CWallet::minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); +} + + +CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc) +{ + /* User control of how to calculate fee uses the following parameter precedence: + 1. coin_control.m_feerate + 2. coin_control.m_confirm_target + 3. payTxFee (user-set global variable) + 4. nTxConfirmTarget (user-set global variable) + The first parameter that is set is used. + */ + CAmount fee_needed; + if (coin_control.m_feerate) { // 1. + fee_needed = coin_control.m_feerate->GetFee(nTxBytes); + if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; + // Allow to override automatic min/max check over coin control instance + if (coin_control.fOverrideFeeRate) return fee_needed; + } + else if (!coin_control.m_confirm_target && ::payTxFee != CFeeRate(0)) { // 3. TODO: remove magic value of 0 for global payTxFee + fee_needed = ::payTxFee.GetFee(nTxBytes); + if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; + } + else { // 2. or 4. + // We will use smart fee estimation + unsigned int target = coin_control.m_confirm_target ? *coin_control.m_confirm_target : ::nTxConfirmTarget; + // By default estimates are economical iff we are signaling opt-in-RBF + bool conservative_estimate = !coin_control.signalRbf; + // Allow to override the default fee estimate mode over the CoinControl instance + if (coin_control.m_fee_mode == FeeEstimateMode::CONSERVATIVE) conservative_estimate = true; + else if (coin_control.m_fee_mode == FeeEstimateMode::ECONOMICAL) conservative_estimate = false; + + fee_needed = estimator.estimateSmartFee(target, feeCalc, conservative_estimate).GetFee(nTxBytes); + if (fee_needed == 0) { + // if we don't have enough data for estimateSmartFee, then use fallbackFee + fee_needed = CWallet::fallbackFee.GetFee(nTxBytes); + if (feeCalc) feeCalc->reason = FeeReason::FALLBACK; + } + // Obey mempool min fee when using smart fee estimation + CAmount min_mempool_fee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nTxBytes); + if (fee_needed < min_mempool_fee) { + fee_needed = min_mempool_fee; + if (feeCalc) feeCalc->reason = FeeReason::MEMPOOL_MIN; + } + } + + // prevent user from paying a fee below minRelayTxFee or minTxFee + CAmount required_fee = GetRequiredFee(nTxBytes); + if (required_fee > fee_needed) { + fee_needed = required_fee; + if (feeCalc) feeCalc->reason = FeeReason::REQUIRED; + } + // But always obey the maximum + if (fee_needed > maxTxFee) { + fee_needed = maxTxFee; + if (feeCalc) feeCalc->reason = FeeReason::MAXTXFEE; + } + return fee_needed; +} + + +CFeeRate GetDiscardRate(const CBlockPolicyEstimator& estimator) +{ + unsigned int highest_target = estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); + CFeeRate discard_rate = estimator.estimateSmartFee(highest_target, nullptr /* FeeCalculation */, false /* conservative */); + // Don't let discard_rate be greater than longest possible fee estimate if we get a valid fee estimate + discard_rate = (discard_rate == CFeeRate(0)) ? CWallet::m_discard_rate : std::min(discard_rate, CWallet::m_discard_rate); + // Discard rate must be at least dustRelayFee + discard_rate = std::max(discard_rate, ::dustRelayFee); + return discard_rate; +} diff --git a/src/wallet/fees.h b/src/wallet/fees.h new file mode 100644 index 0000000000..7b8a7dc868 --- /dev/null +++ b/src/wallet/fees.h @@ -0,0 +1,34 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_WALLET_FEES_H +#define BITCOIN_WALLET_FEES_H + +#include "amount.h" + +class CBlockPolicyEstimator; +class CCoinControl; +class CFeeRate; +class CTxMemPool; +struct FeeCalculation; + +/** + * Return the minimum required fee taking into account the + * floating relay fee and user set minimum transaction fee + */ +CAmount GetRequiredFee(unsigned int nTxBytes); + +/** + * Estimate the minimum fee considering user set parameters + * and the required fee + */ +CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc); + +/** + * Return the maximum feerate for discarding change. + */ +CFeeRate GetDiscardRate(const CBlockPolicyEstimator& estimator); + +#endif // BITCOIN_WALLET_FEES_H diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp new file mode 100644 index 0000000000..c984df1df8 --- /dev/null +++ b/src/wallet/init.cpp @@ -0,0 +1,279 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "wallet/init.h" + +#include "net.h" +#include "util.h" +#include "utilmoneystr.h" +#include "validation.h" +#include "wallet/wallet.h" +#include "wallet/rpcwallet.h" + +std::string GetWalletHelpString(bool showDebug) +{ + std::string strUsage = HelpMessageGroup(_("Wallet options:")); + strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); + strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE)); + strUsage += HelpMessageOpt("-fallbackfee=<amt>", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"), + CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE))); + strUsage += HelpMessageOpt("-discardfee=<amt>", strprintf(_("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). " + "Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target"), + CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE))); + strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"), + CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); + strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), + CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); + strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup")); + strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup")); + strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); + strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); + strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (default: %u)"), DEFAULT_WALLET_RBF)); + strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); + strUsage += HelpMessageOpt("-wallet=<file>", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); + strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); + strUsage += HelpMessageOpt("-walletnotify=<cmd>", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); + strUsage += HelpMessageOpt("-zapwallettxes=<mode>", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + + " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)")); + + if (showDebug) + { + strUsage += HelpMessageGroup(_("Wallet debugging/testing options:")); + + strUsage += HelpMessageOpt("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE)); + strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET)); + strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB)); + strUsage += HelpMessageOpt("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS)); + } + + return strUsage; +} + +bool WalletParameterInteraction() +{ + gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT); + const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; + + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) + return true; + + if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) { + LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__); + } + + if (gArgs.GetBoolArg("-salvagewallet", false)) { + if (is_multiwallet) { + return InitError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet")); + } + // Rewrite just private keys: rescan to find transactions + if (gArgs.SoftSetBoolArg("-rescan", true)) { + LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); + } + } + + int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0); + // -zapwallettxes implies dropping the mempool on startup + if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-persistmempool", false)) { + LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0\n", __func__, zapwallettxes); + } + + // -zapwallettxes implies a rescan + if (zapwallettxes != 0) { + if (is_multiwallet) { + return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes")); + } + if (gArgs.SoftSetBoolArg("-rescan", true)) { + LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -rescan=1\n", __func__, zapwallettxes); + } + } + + if (is_multiwallet) { + if (gArgs.GetBoolArg("-upgradewallet", false)) { + return InitError(strprintf("%s is only allowed with a single wallet file", "-upgradewallet")); + } + } + + if (gArgs.GetBoolArg("-sysperms", false)) + return InitError("-sysperms is not allowed in combination with enabled wallet functionality"); + if (gArgs.GetArg("-prune", 0) && gArgs.GetBoolArg("-rescan", false)) + return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.")); + + if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-minrelaytxfee") + " " + + _("The wallet will avoid paying less than the minimum relay fee.")); + + if (gArgs.IsArgSet("-mintxfee")) + { + CAmount n = 0; + if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n) + return InitError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); + if (n > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-mintxfee") + " " + + _("This is the minimum transaction fee you pay on every transaction.")); + CWallet::minTxFee = CFeeRate(n); + } + if (gArgs.IsArgSet("-fallbackfee")) + { + CAmount nFeePerK = 0; + if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) + return InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), gArgs.GetArg("-fallbackfee", ""))); + if (nFeePerK > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-fallbackfee") + " " + + _("This is the transaction fee you may pay when fee estimates are not available.")); + CWallet::fallbackFee = CFeeRate(nFeePerK); + } + if (gArgs.IsArgSet("-discardfee")) + { + CAmount nFeePerK = 0; + if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK)) + return InitError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), gArgs.GetArg("-discardfee", ""))); + if (nFeePerK > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-discardfee") + " " + + _("This is the transaction fee you may discard if change is smaller than dust at this level")); + CWallet::m_discard_rate = CFeeRate(nFeePerK); + } + if (gArgs.IsArgSet("-paytxfee")) + { + CAmount nFeePerK = 0; + if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) + return InitError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); + if (nFeePerK > HIGH_TX_FEE_PER_KB) + InitWarning(AmountHighWarn("-paytxfee") + " " + + _("This is the transaction fee you will pay if you send a transaction.")); + + payTxFee = CFeeRate(nFeePerK, 1000); + if (payTxFee < ::minRelayTxFee) + { + return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"), + gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); + } + } + if (gArgs.IsArgSet("-maxtxfee")) + { + CAmount nMaxFee = 0; + if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) + return InitError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", ""))); + if (nMaxFee > HIGH_MAX_TX_FEE) + InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.")); + maxTxFee = nMaxFee; + if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) + { + return InitError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), + gArgs.GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString())); + } + } + nTxConfirmTarget = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); + bSpendZeroConfChange = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); + fWalletRbf = gArgs.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); + + return true; +} + +void RegisterWalletRPC(CRPCTable &t) +{ + if (gArgs.GetBoolArg("-disablewallet", false)) return; + + RegisterWalletRPCCommands(t); +} + +bool VerifyWallets() +{ + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) + return true; + + uiInterface.InitMessage(_("Verifying wallet(s)...")); + + // Keep track of each wallet absolute path to detect duplicates. + std::set<fs::path> wallet_paths; + + for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { + if (boost::filesystem::path(walletFile).filename() != walletFile) { + return InitError(strprintf(_("Error loading wallet %s. -wallet parameter must only specify a filename (not a path)."), walletFile)); + } + + if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { + return InitError(strprintf(_("Error loading wallet %s. Invalid characters in -wallet filename."), walletFile)); + } + + fs::path wallet_path = fs::absolute(walletFile, GetDataDir()); + + if (fs::exists(wallet_path) && (!fs::is_regular_file(wallet_path) || fs::is_symlink(wallet_path))) { + return InitError(strprintf(_("Error loading wallet %s. -wallet filename must be a regular file."), walletFile)); + } + + if (!wallet_paths.insert(wallet_path).second) { + return InitError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), walletFile)); + } + + std::string strError; + if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) { + return InitError(strError); + } + + if (gArgs.GetBoolArg("-salvagewallet", false)) { + // Recover readable keypairs: + CWallet dummyWallet; + std::string backup_filename; + if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter, backup_filename)) { + return false; + } + } + + std::string strWarning; + bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetDataDir().string(), strWarning, strError); + if (!strWarning.empty()) { + InitWarning(strWarning); + } + if (!dbV) { + InitError(strError); + return false; + } + } + + return true; +} + +bool OpenWallets() +{ + if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { + LogPrintf("Wallet disabled!\n"); + return true; + } + + for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { + CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile); + if (!pwallet) { + return false; + } + vpwallets.push_back(pwallet); + } + + return true; +} + +void StartWallets(CScheduler& scheduler) { + for (CWalletRef pwallet : vpwallets) { + pwallet->postInitProcess(scheduler); + } +} + +void FlushWallets() { + for (CWalletRef pwallet : vpwallets) { + pwallet->Flush(false); + } +} + +void StopWallets() { + for (CWalletRef pwallet : vpwallets) { + pwallet->Flush(true); + } +} + +void CloseWallets() { + for (CWalletRef pwallet : vpwallets) { + delete pwallet; + } + vpwallets.clear(); +} diff --git a/src/wallet/init.h b/src/wallet/init.h new file mode 100644 index 0000000000..0b3ee2dda2 --- /dev/null +++ b/src/wallet/init.h @@ -0,0 +1,43 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-2017 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_WALLET_INIT_H +#define BITCOIN_WALLET_INIT_H + +#include <string> + +class CRPCTable; +class CScheduler; + +//! Return the wallets help message. +std::string GetWalletHelpString(bool showDebug); + +//! Wallets parameter interaction +bool WalletParameterInteraction(); + +//! Register wallet RPCs. +void RegisterWalletRPC(CRPCTable &tableRPC); + +//! Responsible for reading and validating the -wallet arguments and verifying the wallet database. +// This function will perform salvage on the wallet if requested, as long as only one wallet is +// being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). +bool VerifyWallets(); + +//! Load wallet databases. +bool OpenWallets(); + +//! Complete startup of wallets. +void StartWallets(CScheduler& scheduler); + +//! Flush all wallets in preparation for shutdown. +void FlushWallets(); + +//! Stop all wallets. Wallets will be flushed first. +void StopWallets(); + +//! Close all wallets. +void CloseWallets(); + +#endif // BITCOIN_WALLET_INIT_H diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 67c6d9ec64..d6ea2a9db7 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -4,6 +4,7 @@ #include "base58.h" #include "chain.h" +#include "rpc/safemode.h" #include "rpc/server.h" #include "init.h" #include "validation.h" @@ -79,10 +80,10 @@ UniValue importprivkey(const JSONRPCRequest& request) if (request.fHelp || request.params.size() < 1 || request.params.size() > 3) throw std::runtime_error( - "importprivkey \"bitcoinprivkey\" ( \"label\" ) ( rescan )\n" + "importprivkey \"privkey\" ( \"label\" ) ( rescan )\n" "\nAdds a private key (as returned by dumpprivkey) to your wallet.\n" "\nArguments:\n" - "1. \"bitcoinprivkey\" (string, required) The private key (see dumpprivkey)\n" + "1. \"privkey\" (string, required) The private key (see dumpprivkey)\n" "2. \"label\" (string, optional, default=\"\") An optional label\n" "3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" "\nNote: This call can take minutes to complete if rescan is true.\n" @@ -174,12 +175,13 @@ UniValue abortrescan(const JSONRPCRequest& request) + HelpExampleRpc("abortrescan", "") ); + ObserveSafeMode(); if (!pwallet->IsScanning() || pwallet->IsAbortingRescan()) return false; pwallet->AbortRescan(); return true; } -void ImportAddress(CWallet*, const CBitcoinAddress& address, const std::string& strLabel); +void ImportAddress(CWallet*, const CTxDestination& dest, const std::string& strLabel); void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript) { if (!isRedeemScript && ::IsMine(*pwallet, script) == ISMINE_SPENDABLE) { @@ -196,7 +198,7 @@ void ImportScript(CWallet* const pwallet, const CScript& script, const std::stri if (!pwallet->HaveCScript(script) && !pwallet->AddCScript(script)) { throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); } - ImportAddress(pwallet, CBitcoinAddress(CScriptID(script)), strLabel); + ImportAddress(pwallet, CScriptID(script), strLabel); } else { CTxDestination destination; if (ExtractDestination(script, destination)) { @@ -205,13 +207,13 @@ void ImportScript(CWallet* const pwallet, const CScript& script, const std::stri } } -void ImportAddress(CWallet* const pwallet, const CBitcoinAddress& address, const std::string& strLabel) +void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, const std::string& strLabel) { - CScript script = GetScriptForDestination(address.Get()); + CScript script = GetScriptForDestination(dest); ImportScript(pwallet, script, strLabel, false); // add to address book or update label - if (address.IsValid()) - pwallet->SetAddressBook(address.Get(), strLabel, "receive"); + if (IsValidDestination(dest)) + pwallet->SetAddressBook(dest, strLabel, "receive"); } UniValue importaddress(const JSONRPCRequest& request) @@ -263,11 +265,12 @@ UniValue importaddress(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - CBitcoinAddress address(request.params[0].get_str()); - if (address.IsValid()) { - if (fP2SH) + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (IsValidDestination(dest)) { + if (fP2SH) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Cannot use the p2sh flag with an address - use a script instead"); - ImportAddress(pwallet, address, strLabel); + } + ImportAddress(pwallet, dest, strLabel); } else if (IsHex(request.params[0].get_str())) { std::vector<unsigned char> data(ParseHex(request.params[0].get_str())); ImportScript(pwallet, CScript(data.begin(), data.end()), strLabel, fP2SH); @@ -430,7 +433,7 @@ UniValue importpubkey(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - ImportAddress(pwallet, CBitcoinAddress(pubKey.GetID()), strLabel); + ImportAddress(pwallet, pubKey.GetID(), strLabel); ImportScript(pwallet, GetScriptForRawPubKey(pubKey), strLabel, false); if (fRescan) @@ -504,7 +507,7 @@ UniValue importwallet(const JSONRPCRequest& request) assert(key.VerifyPubKey(pubkey)); CKeyID keyid = pubkey.GetID(); if (pwallet->HaveKey(keyid)) { - LogPrintf("Skipping import of %s (key already present)\n", CBitcoinAddress(keyid).ToString()); + LogPrintf("Skipping import of %s (key already present)\n", EncodeDestination(keyid)); continue; } int64_t nTime = DecodeDumpTime(vstr[1]); @@ -522,7 +525,7 @@ UniValue importwallet(const JSONRPCRequest& request) fLabel = true; } } - LogPrintf("Importing %s...\n", CBitcoinAddress(keyid).ToString()); + LogPrintf("Importing %s...\n", EncodeDestination(keyid)); if (!pwallet->AddKeyPubKey(key, pubkey)) { fGood = false; continue; @@ -571,14 +574,16 @@ UniValue dumpprivkey(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); std::string strAddress = request.params[0].get_str(); - CBitcoinAddress address; - if (!address.SetString(strAddress)) + CTxDestination dest = DecodeDestination(strAddress); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); - CKeyID keyID; - if (!address.GetKeyID(keyID)) + } + const CKeyID *keyID = boost::get<CKeyID>(&dest); + if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); + } CKey vchSecret; - if (!pwallet->GetKey(keyID, vchSecret)) { + if (!pwallet->GetKey(*keyID, vchSecret)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); } return CBitcoinSecret(vchSecret).ToString(); @@ -595,7 +600,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) if (request.fHelp || request.params.size() != 1) throw std::runtime_error( "dumpwallet \"filename\"\n" - "\nDumps all wallet keys in a human-readable format.\n" + "\nDumps all wallet keys in a human-readable format to a server-side file. This does not allow overwriting existing files.\n" "\nArguments:\n" "1. \"filename\" (string, required) The filename with path (either absolute or relative to bitcoind)\n" "\nResult:\n" @@ -611,9 +616,19 @@ UniValue dumpwallet(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); - std::ofstream file; boost::filesystem::path filepath = request.params[0].get_str(); filepath = boost::filesystem::absolute(filepath); + + /* Prevent arbitrary files from being overwritten. There have been reports + * that users have overwritten wallet files this way: + * https://github.com/bitcoin/bitcoin/issues/9934 + * It may also avoid other security issues. + */ + if (boost::filesystem::exists(filepath)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, filepath.string() + " already exists. If you are sure this is what you want, move it out of the way first"); + } + + std::ofstream file; file.open(filepath.string().c_str()); if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); @@ -657,7 +672,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) { const CKeyID &keyid = it->second; std::string strTime = EncodeDumpTime(it->first); - std::string strAddr = CBitcoinAddress(keyid).ToString(); + std::string strAddr = EncodeDestination(keyid); CKey key; if (pwallet->GetKey(keyid, key)) { file << strprintf("%s %s ", CBitcoinSecret(key).ToString(), strTime); @@ -713,14 +728,14 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 // Parse the output. CScript script; - CBitcoinAddress address; + CTxDestination dest; if (!isScript) { - address = CBitcoinAddress(output); - if (!address.IsValid()) { + dest = DecodeDestination(output); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); } - script = GetScriptForDestination(address.Get()); + script = GetScriptForDestination(dest); } else { if (!IsHex(output)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scriptPubKey"); @@ -778,8 +793,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet"); } - CBitcoinAddress redeemAddress = CBitcoinAddress(CScriptID(redeemScript)); - CScript redeemDestination = GetScriptForDestination(redeemAddress.Get()); + CTxDestination redeem_dest = CScriptID(redeemScript); + CScript redeemDestination = GetScriptForDestination(redeem_dest); if (::IsMine(*pwallet, redeemDestination) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); @@ -792,8 +807,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 } // add to address book or update label - if (address.IsValid()) { - pwallet->SetAddressBook(address.Get(), label, "receive"); + if (IsValidDestination(dest)) { + pwallet->SetAddressBook(dest, label, "receive"); } // Import private keys. @@ -852,27 +867,25 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key"); } - CBitcoinAddress pubKeyAddress = CBitcoinAddress(pubKey.GetID()); + CTxDestination pubkey_dest = pubKey.GetID(); // Consistency check. - if (!isScript && !(pubKeyAddress.Get() == address.Get())) { + if (!isScript && !(pubkey_dest == dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } // Consistency check. if (isScript) { - CBitcoinAddress scriptAddress; CTxDestination destination; if (ExtractDestination(script, destination)) { - scriptAddress = CBitcoinAddress(destination); - if (!(scriptAddress.Get() == pubKeyAddress.Get())) { + if (!(destination == pubkey_dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } } } - CScript pubKeyScript = GetScriptForDestination(pubKeyAddress.Get()); + CScript pubKeyScript = GetScriptForDestination(pubkey_dest); if (::IsMine(*pwallet, pubKeyScript) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); @@ -885,8 +898,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 } // add to address book or update label - if (pubKeyAddress.IsValid()) { - pwallet->SetAddressBook(pubKeyAddress.Get(), label, "receive"); + if (IsValidDestination(pubkey_dest)) { + pwallet->SetAddressBook(pubkey_dest, label, "receive"); } // TODO Is this necessary? @@ -925,21 +938,19 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 CPubKey pubKey = key.GetPubKey(); assert(key.VerifyPubKey(pubKey)); - CBitcoinAddress pubKeyAddress = CBitcoinAddress(pubKey.GetID()); + CTxDestination pubkey_dest = pubKey.GetID(); // Consistency check. - if (!isScript && !(pubKeyAddress.Get() == address.Get())) { + if (!isScript && !(pubkey_dest == dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } // Consistency check. if (isScript) { - CBitcoinAddress scriptAddress; CTxDestination destination; if (ExtractDestination(script, destination)) { - scriptAddress = CBitcoinAddress(destination); - if (!(scriptAddress.Get() == pubKeyAddress.Get())) { + if (!(destination == pubkey_dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Consistency check failed"); } } @@ -978,8 +989,8 @@ UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int6 if (scriptPubKey.getType() == UniValue::VOBJ) { // add to address book or update label - if (address.IsValid()) { - pwallet->SetAddressBook(address.Get(), label, "receive"); + if (IsValidDestination(dest)) { + pwallet->SetAddressBook(dest, label, "receive"); } } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 513f7b32a5..5d98498a4b 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -8,7 +8,6 @@ #include "chain.h" #include "consensus/validation.h" #include "core_io.h" -#include "init.h" #include "httpserver.h" #include "validation.h" #include "net.h" @@ -17,6 +16,7 @@ #include "policy/policy.h" #include "policy/rbf.h" #include "rpc/mining.h" +#include "rpc/safemode.h" #include "rpc/server.h" #include "script/sign.h" #include "timedata.h" @@ -27,6 +27,8 @@ #include "wallet/wallet.h" #include "wallet/walletdb.h" +#include <init.h> // For StartShutdown + #include <stdint.h> #include <univalue.h> @@ -168,18 +170,18 @@ UniValue getnewaddress(const JSONRPCRequest& request) pwallet->SetAddressBook(keyID, strAccount, "receive"); - return CBitcoinAddress(keyID).ToString(); + return EncodeDestination(keyID); } -CBitcoinAddress GetAccountAddress(CWallet* const pwallet, std::string strAccount, bool bForceNew=false) +CTxDestination GetAccountAddress(CWallet* const pwallet, std::string strAccount, bool bForceNew=false) { CPubKey pubKey; if (!pwallet->GetAccountPubkey(pubKey, strAccount, bForceNew)) { throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); } - return CBitcoinAddress(pubKey.GetID()); + return pubKey.GetID(); } UniValue getaccountaddress(const JSONRPCRequest& request) @@ -211,7 +213,7 @@ UniValue getaccountaddress(const JSONRPCRequest& request) UniValue ret(UniValue::VSTR); - ret = GetAccountAddress(pwallet, strAccount).ToString(); + ret = EncodeDestination(GetAccountAddress(pwallet, strAccount)); return ret; } @@ -250,7 +252,7 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request) CKeyID keyID = vchPubKey.GetID(); - return CBitcoinAddress(keyID).ToString(); + return EncodeDestination(keyID); } @@ -275,24 +277,25 @@ UniValue setaccount(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - CBitcoinAddress address(request.params[0].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + } std::string strAccount; if (!request.params[1].isNull()) strAccount = AccountFromValue(request.params[1]); // Only add the account if the address is yours. - if (IsMine(*pwallet, address.Get())) { + if (IsMine(*pwallet, dest)) { // Detect when changing the account of an address that is the 'unused current key' of another account: - if (pwallet->mapAddressBook.count(address.Get())) { - std::string strOldAccount = pwallet->mapAddressBook[address.Get()].name; - if (address == GetAccountAddress(pwallet, strOldAccount)) { + if (pwallet->mapAddressBook.count(dest)) { + std::string strOldAccount = pwallet->mapAddressBook[dest].name; + if (dest == GetAccountAddress(pwallet, strOldAccount)) { GetAccountAddress(pwallet, strOldAccount, true); } } - pwallet->SetAddressBook(address.Get(), strAccount, "receive"); + pwallet->SetAddressBook(dest, strAccount, "receive"); } else throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address"); @@ -323,12 +326,13 @@ UniValue getaccount(const JSONRPCRequest& request) LOCK2(cs_main, pwallet->cs_wallet); - CBitcoinAddress address(request.params[0].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + } std::string strAccount; - std::map<CTxDestination, CAddressBookData>::iterator mi = pwallet->mapAddressBook.find(address.Get()); + std::map<CTxDestination, CAddressBookData>::iterator mi = pwallet->mapAddressBook.find(dest); if (mi != pwallet->mapAddressBook.end() && !(*mi).second.name.empty()) { strAccount = (*mi).second.name; } @@ -365,11 +369,12 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request) // Find all addresses that have the given account UniValue ret(UniValue::VARR); - for (const std::pair<CBitcoinAddress, CAddressBookData>& item : pwallet->mapAddressBook) { - const CBitcoinAddress& address = item.first; + for (const std::pair<CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) { + const CTxDestination& dest = item.first; const std::string& strName = item.second.name; - if (strName == strAccount) - ret.push_back(address.ToString()); + if (strName == strAccount) { + ret.push_back(EncodeDestination(dest)); + } } return ret; } @@ -449,11 +454,13 @@ UniValue sendtoaddress(const JSONRPCRequest& request) + HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); - CBitcoinAddress address(request.params[0].get_str()); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); + } // Amount CAmount nAmount = AmountFromValue(request.params[1]); @@ -490,7 +497,7 @@ UniValue sendtoaddress(const JSONRPCRequest& request) EnsureWalletIsUnlocked(pwallet); - SendMoney(pwallet, address.Get(), nAmount, fSubtractFeeFromAmount, wtx, coin_control); + SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, wtx, coin_control); return wtx.GetHash().GetHex(); } @@ -525,20 +532,21 @@ UniValue listaddressgroupings(const JSONRPCRequest& request) + HelpExampleRpc("listaddressgroupings", "") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); UniValue jsonGroupings(UniValue::VARR); std::map<CTxDestination, CAmount> balances = pwallet->GetAddressBalances(); - for (std::set<CTxDestination> grouping : pwallet->GetAddressGroupings()) { + for (const std::set<CTxDestination>& grouping : pwallet->GetAddressGroupings()) { UniValue jsonGrouping(UniValue::VARR); - for (CTxDestination address : grouping) + for (const CTxDestination& address : grouping) { UniValue addressInfo(UniValue::VARR); - addressInfo.push_back(CBitcoinAddress(address).ToString()); + addressInfo.push_back(EncodeDestination(address)); addressInfo.push_back(ValueFromAmount(balances[address])); { - if (pwallet->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwallet->mapAddressBook.end()) { - addressInfo.push_back(pwallet->mapAddressBook.find(CBitcoinAddress(address).Get())->second.name); + if (pwallet->mapAddressBook.find(address) != pwallet->mapAddressBook.end()) { + addressInfo.push_back(pwallet->mapAddressBook.find(address)->second.name); } } jsonGrouping.push_back(addressInfo); @@ -583,16 +591,18 @@ UniValue signmessage(const JSONRPCRequest& request) std::string strAddress = request.params[0].get_str(); std::string strMessage = request.params[1].get_str(); - CBitcoinAddress addr(strAddress); - if (!addr.IsValid()) + CTxDestination dest = DecodeDestination(strAddress); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); + } - CKeyID keyID; - if (!addr.GetKeyID(keyID)) + const CKeyID *keyID = boost::get<CKeyID>(&dest); + if (!keyID) { throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); + } CKey key; - if (!pwallet->GetKey(keyID, key)) { + if (!pwallet->GetKey(*keyID, key)) { throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); } @@ -604,7 +614,7 @@ UniValue signmessage(const JSONRPCRequest& request) if (!key.SignCompact(ss.GetHash(), vchSig)) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); - return EncodeBase64(&vchSig[0], vchSig.size()); + return EncodeBase64(vchSig.data(), vchSig.size()); } UniValue getreceivedbyaddress(const JSONRPCRequest& request) @@ -634,13 +644,15 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request) + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); // Bitcoin address - CBitcoinAddress address = CBitcoinAddress(request.params[0].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); - CScript scriptPubKey = GetScriptForDestination(address.Get()); + } + CScript scriptPubKey = GetScriptForDestination(dest); if (!IsMine(*pwallet, scriptPubKey)) { return ValueFromAmount(0); } @@ -694,6 +706,7 @@ UniValue getreceivedbyaccount(const JSONRPCRequest& request) + HelpExampleRpc("getreceivedbyaccount", "\"tabby\", 6") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); // Minimum confirmations @@ -766,6 +779,7 @@ UniValue getbalance(const JSONRPCRequest& request) + HelpExampleRpc("getbalance", "\"*\", 6") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); const UniValue& account_value = request.params[0]; @@ -810,6 +824,7 @@ UniValue getunconfirmedbalance(const JSONRPCRequest &request) "getunconfirmedbalance\n" "Returns the server's total unconfirmed balance\n"); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); return ValueFromAmount(pwallet->GetUnconfirmedBalance()); @@ -844,6 +859,7 @@ UniValue movecmd(const JSONRPCRequest& request) + HelpExampleRpc("move", "\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); std::string strFrom = AccountFromValue(request.params[0]); @@ -902,12 +918,14 @@ UniValue sendfrom(const JSONRPCRequest& request) + HelpExampleRpc("sendfrom", "\"tabby\", \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.01, 6, \"donation\", \"seans outpost\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); std::string strAccount = AccountFromValue(request.params[0]); - CBitcoinAddress address(request.params[1].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(request.params[1].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + } CAmount nAmount = AmountFromValue(request.params[2]); if (nAmount <= 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); @@ -930,7 +948,7 @@ UniValue sendfrom(const JSONRPCRequest& request) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); CCoinControl no_coin_control; // This is a deprecated API - SendMoney(pwallet, address.Get(), nAmount, false, wtx, no_coin_control); + SendMoney(pwallet, dest, nAmount, false, wtx, no_coin_control); return wtx.GetHash().GetHex(); } @@ -985,6 +1003,7 @@ UniValue sendmany(const JSONRPCRequest& request) + HelpExampleRpc("sendmany", "\"\", \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\", 6, \"testing\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); if (pwallet->GetBroadcastTransactions() && !g_connman) { @@ -1021,22 +1040,23 @@ UniValue sendmany(const JSONRPCRequest& request) } } - std::set<CBitcoinAddress> setAddress; + std::set<CTxDestination> destinations; std::vector<CRecipient> vecSend; CAmount totalAmount = 0; std::vector<std::string> keys = sendTo.getKeys(); - for (const std::string& name_ : keys) - { - CBitcoinAddress address(name_); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ")+name_); + for (const std::string& name_ : keys) { + CTxDestination dest = DecodeDestination(name_); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + name_); + } - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ")+name_); - setAddress.insert(address); + if (destinations.count(dest)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); + } + destinations.insert(dest); - CScript scriptPubKey = GetScriptForDestination(address.Get()); + CScript scriptPubKey = GetScriptForDestination(dest); CAmount nAmount = AmountFromValue(sendTo[name_]); if (nAmount <= 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); @@ -1127,18 +1147,17 @@ UniValue addmultisigaddress(const JSONRPCRequest& request) pwallet->AddCScript(inner); pwallet->SetAddressBook(innerID, strAccount, "send"); - return CBitcoinAddress(innerID).ToString(); + return EncodeDestination(innerID); } class Witnessifier : public boost::static_visitor<bool> { public: CWallet * const pwallet; - CScriptID result; - - explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet) {} + CTxDestination result; + bool already_witness; - bool operator()(const CNoDestination &dest) const { return false; } + explicit Witnessifier(CWallet *_pwallet) : pwallet(_pwallet), already_witness(false) {} bool operator()(const CKeyID &keyID) { if (pwallet) { @@ -1152,9 +1171,7 @@ public: !VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) { return false; } - pwallet->AddCScript(witscript); - result = CScriptID(witscript); - return true; + return ExtractDestination(witscript, result); } return false; } @@ -1165,7 +1182,8 @@ public: int witnessversion; std::vector<unsigned char> witprog; if (subscript.IsWitnessProgram(witnessversion, witprog)) { - result = scriptID; + ExtractDestination(subscript, result); + already_witness = true; return true; } CScript witscript = GetScriptForWitness(subscript); @@ -1177,12 +1195,27 @@ public: !VerifyScript(sigs.scriptSig, witscript, &sigs.scriptWitness, MANDATORY_SCRIPT_VERIFY_FLAGS | SCRIPT_VERIFY_WITNESS_PUBKEYTYPE, DummySignatureCreator(pwallet).Checker())) { return false; } - pwallet->AddCScript(witscript); - result = CScriptID(witscript); - return true; + return ExtractDestination(witscript, result); } return false; } + + bool operator()(const WitnessV0KeyHash& id) + { + already_witness = true; + result = id; + return true; + } + + bool operator()(const WitnessV0ScriptHash& id) + { + already_witness = true; + result = id; + return true; + } + + template<typename T> + bool operator()(const T& dest) { return false; } }; UniValue addwitnessaddress(const JSONRPCRequest& request) @@ -1192,17 +1225,18 @@ UniValue addwitnessaddress(const JSONRPCRequest& request) return NullUniValue; } - if (request.fHelp || request.params.size() < 1 || request.params.size() > 1) + if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) { - std::string msg = "addwitnessaddress \"address\"\n" + std::string msg = "addwitnessaddress \"address\" ( p2sh )\n" "\nAdd a witness address for a script (with pubkey or redeemscript known).\n" "It returns the witness script.\n" "\nArguments:\n" "1. \"address\" (string, required) An address known to the wallet\n" + "2. p2sh (bool, optional, default=true) Embed inside P2SH\n" "\nResult:\n" - "\"witnessaddress\", (string) The value of the new address (P2SH of witness script).\n" + "\"witnessaddress\", (string) The value of the new address (P2SH or BIP173).\n" "}\n" ; throw std::runtime_error(msg); @@ -1215,20 +1249,38 @@ UniValue addwitnessaddress(const JSONRPCRequest& request) } } - CBitcoinAddress address(request.params[0].get_str()); - if (!address.IsValid()) + CTxDestination dest = DecodeDestination(request.params[0].get_str()); + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); + } + + bool p2sh = true; + if (!request.params[1].isNull()) { + p2sh = request.params[1].get_bool(); + } Witnessifier w(pwallet); - CTxDestination dest = address.Get(); bool ret = boost::apply_visitor(w, dest); if (!ret) { throw JSONRPCError(RPC_WALLET_ERROR, "Public key or redeemscript not known to wallet, or the key is uncompressed"); } - pwallet->SetAddressBook(w.result, "", "receive"); + CScript witprogram = GetScriptForDestination(w.result); + + if (p2sh) { + w.result = CScriptID(witprogram); + } + + if (w.already_witness) { + if (!(dest == w.result)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Cannot convert between witness address types"); + } + } else { + pwallet->AddCScript(witprogram); + pwallet->SetAddressBook(w.result, "", "receive"); + } - return CBitcoinAddress(w.result).ToString(); + return EncodeDestination(w.result); } struct tallyitem @@ -1263,7 +1315,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA filter = filter | ISMINE_WATCH_ONLY; // Tally - std::map<CBitcoinAddress, tallyitem> mapTally; + std::map<CTxDestination, tallyitem> mapTally; for (const std::pair<uint256, CWalletTx>& pairWtx : pwallet->mapWallet) { const CWalletTx& wtx = pairWtx.second; @@ -1296,10 +1348,10 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA // Reply UniValue ret(UniValue::VARR); std::map<std::string, tallyitem> mapAccountTally; - for (const std::pair<CBitcoinAddress, CAddressBookData>& item : pwallet->mapAddressBook) { - const CBitcoinAddress& address = item.first; + for (const std::pair<CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) { + const CTxDestination& dest = item.first; const std::string& strAccount = item.second.name; - std::map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address); + std::map<CTxDestination, tallyitem>::iterator it = mapTally.find(dest); if (it == mapTally.end() && !fIncludeEmpty) continue; @@ -1325,7 +1377,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA UniValue obj(UniValue::VOBJ); if(fIsWatchonly) obj.push_back(Pair("involvesWatchonly", true)); - obj.push_back(Pair("address", address.ToString())); + obj.push_back(Pair("address", EncodeDestination(dest))); obj.push_back(Pair("account", strAccount)); obj.push_back(Pair("amount", ValueFromAmount(nAmount))); obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf))); @@ -1402,6 +1454,7 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request) + HelpExampleRpc("listreceivedbyaddress", "6, true, true") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); return ListReceived(pwallet, request.params, false); @@ -1441,6 +1494,7 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request) + HelpExampleRpc("listreceivedbyaccount", "6, true, true") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); return ListReceived(pwallet, request.params, true); @@ -1448,9 +1502,9 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request) static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) { - CBitcoinAddress addr; - if (addr.Set(dest)) - entry.push_back(Pair("address", addr.ToString())); + if (IsValidDestination(dest)) { + entry.push_back(Pair("address", EncodeDestination(dest))); + } } /** @@ -1628,6 +1682,7 @@ UniValue listtransactions(const JSONRPCRequest& request) + HelpExampleRpc("listtransactions", "\"*\", 20, 100") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); std::string strAccount = "*"; @@ -1721,6 +1776,7 @@ UniValue listaccounts(const JSONRPCRequest& request) + HelpExampleRpc("listaccounts", "6") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); int nMinDepth = 1; @@ -1829,6 +1885,7 @@ UniValue listsinceblock(const JSONRPCRequest& request) + HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); const CBlockIndex* pindex = nullptr; // Block index of the specified block or the common ancestor, if the block provided was in a deactivated chain. @@ -1960,6 +2017,7 @@ UniValue gettransaction(const JSONRPCRequest& request) + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); uint256 hash; @@ -2021,6 +2079,7 @@ UniValue abandontransaction(const JSONRPCRequest& request) + HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); uint256 hash; @@ -2453,6 +2512,7 @@ UniValue listlockunspent(const JSONRPCRequest& request) + HelpExampleRpc("listlockunspent", "") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); std::vector<COutPoint> vOutpts; @@ -2531,6 +2591,7 @@ UniValue getwalletinfo(const JSONRPCRequest& request) + HelpExampleRpc("getwalletinfo", "") ); + ObserveSafeMode(); LOCK2(cs_main, pwallet->cs_wallet); UniValue obj(UniValue::VOBJ); @@ -2683,6 +2744,8 @@ UniValue listunspent(const JSONRPCRequest& request) + HelpExampleRpc("listunspent", "6, 9999999, [] , true, { \"minimumAmount\": 0.005 } ") ); + ObserveSafeMode(); + int nMinDepth = 1; if (!request.params[0].isNull()) { RPCTypeCheckArgument(request.params[0], UniValue::VNUM); @@ -2695,18 +2758,19 @@ UniValue listunspent(const JSONRPCRequest& request) nMaxDepth = request.params[1].get_int(); } - std::set<CBitcoinAddress> setAddress; + std::set<CTxDestination> destinations; if (!request.params[2].isNull()) { RPCTypeCheckArgument(request.params[2], UniValue::VARR); UniValue inputs = request.params[2].get_array(); for (unsigned int idx = 0; idx < inputs.size(); idx++) { const UniValue& input = inputs[idx]; - CBitcoinAddress address(input.get_str()); - if (!address.IsValid()) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ")+input.get_str()); - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ")+input.get_str()); - setAddress.insert(address); + CTxDestination dest = DecodeDestination(input.get_str()); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + input.get_str()); + } + if (!destinations.insert(dest).second) { + throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + input.get_str()); + } } } @@ -2748,7 +2812,7 @@ UniValue listunspent(const JSONRPCRequest& request) const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey; bool fValidAddress = ExtractDestination(scriptPubKey, address); - if (setAddress.size() && (!fValidAddress || !setAddress.count(address))) + if (destinations.size() && (!fValidAddress || !destinations.count(address))) continue; UniValue entry(UniValue::VOBJ); @@ -2756,7 +2820,7 @@ UniValue listunspent(const JSONRPCRequest& request) entry.push_back(Pair("vout", out.i)); if (fValidAddress) { - entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); + entry.push_back(Pair("address", EncodeDestination(address))); if (pwallet->mapAddressBook.count(address)) { entry.push_back(Pair("account", pwallet->mapAddressBook[address].name)); @@ -2811,7 +2875,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) " \"changePosition\" (numeric, optional, default random) The index of the change output\n" " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n" " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n" - " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific feerate (" + CURRENCY_UNIT + " per KB)\n" + " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific fee rate in " + CURRENCY_UNIT + "/kB\n" " \"subtractFeeFromOutputs\" (array, optional) A json array of integers.\n" " The fee will be equally deducted from the amount of each specified output.\n" " The outputs are specified by their zero-based index, before any change output is added.\n" @@ -2844,6 +2908,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"") ); + ObserveSafeMode(); RPCTypeCheck(request.params, {UniValue::VSTR}); CCoinControl coinControl; @@ -2878,12 +2943,13 @@ UniValue fundrawtransaction(const JSONRPCRequest& request) true, true); if (options.exists("changeAddress")) { - CBitcoinAddress address(options["changeAddress"].get_str()); + CTxDestination dest = DecodeDestination(options["changeAddress"].get_str()); - if (!address.IsValid()) + if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "changeAddress must be a valid bitcoin address"); + } - coinControl.destChange = address.Get(); + coinControl.destChange = dest; } if (options.exists("changePosition")) @@ -3158,67 +3224,64 @@ extern UniValue removeprunedfunds(const JSONRPCRequest& request); extern UniValue importmulti(const JSONRPCRequest& request); static const CRPCCommand commands[] = -{ // category name actor (function) okSafeMode - // --------------------- ------------------------ ----------------------- ---------- - { "rawtransactions", "fundrawtransaction", &fundrawtransaction, false, {"hexstring","options"} }, - { "hidden", "resendwallettransactions", &resendwallettransactions, true, {} }, - { "wallet", "abandontransaction", &abandontransaction, false, {"txid"} }, - { "wallet", "abortrescan", &abortrescan, false, {} }, - { "wallet", "addmultisigaddress", &addmultisigaddress, true, {"nrequired","keys","account"} }, - { "wallet", "addwitnessaddress", &addwitnessaddress, true, {"address"} }, - { "wallet", "backupwallet", &backupwallet, true, {"destination"} }, - { "wallet", "bumpfee", &bumpfee, true, {"txid", "options"} }, - { "wallet", "dumpprivkey", &dumpprivkey, true, {"address"} }, - { "wallet", "dumpwallet", &dumpwallet, true, {"filename"} }, - { "wallet", "encryptwallet", &encryptwallet, true, {"passphrase"} }, - { "wallet", "getaccountaddress", &getaccountaddress, true, {"account"} }, - { "wallet", "getaccount", &getaccount, true, {"address"} }, - { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true, {"account"} }, - { "wallet", "getbalance", &getbalance, false, {"account","minconf","include_watchonly"} }, - { "wallet", "getnewaddress", &getnewaddress, true, {"account"} }, - { "wallet", "getrawchangeaddress", &getrawchangeaddress, true, {} }, - { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false, {"account","minconf"} }, - { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false, {"address","minconf"} }, - { "wallet", "gettransaction", &gettransaction, false, {"txid","include_watchonly"} }, - { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false, {} }, - { "wallet", "getwalletinfo", &getwalletinfo, false, {} }, - { "wallet", "importmulti", &importmulti, true, {"requests","options"} }, - { "wallet", "importprivkey", &importprivkey, true, {"privkey","label","rescan"} }, - { "wallet", "importwallet", &importwallet, true, {"filename"} }, - { "wallet", "importaddress", &importaddress, true, {"address","label","rescan","p2sh"} }, - { "wallet", "importprunedfunds", &importprunedfunds, true, {"rawtransaction","txoutproof"} }, - { "wallet", "importpubkey", &importpubkey, true, {"pubkey","label","rescan"} }, - { "wallet", "keypoolrefill", &keypoolrefill, true, {"newsize"} }, - { "wallet", "listaccounts", &listaccounts, false, {"minconf","include_watchonly"} }, - { "wallet", "listaddressgroupings", &listaddressgroupings, false, {} }, - { "wallet", "listlockunspent", &listlockunspent, false, {} }, - { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false, {"minconf","include_empty","include_watchonly"} }, - { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","include_empty","include_watchonly"} }, - { "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, - { "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} }, - { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, - { "wallet", "listwallets", &listwallets, true, {} }, - { "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} }, - { "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} }, - { "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, - { "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} }, - { "wallet", "sendtoaddress", &sendtoaddress, false, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} }, - { "wallet", "setaccount", &setaccount, true, {"address","account"} }, - { "wallet", "settxfee", &settxfee, true, {"amount"} }, - { "wallet", "signmessage", &signmessage, true, {"address","message"} }, - { "wallet", "walletlock", &walletlock, true, {} }, - { "wallet", "walletpassphrasechange", &walletpassphrasechange, true, {"oldpassphrase","newpassphrase"} }, - { "wallet", "walletpassphrase", &walletpassphrase, true, {"passphrase","timeout"} }, - { "wallet", "removeprunedfunds", &removeprunedfunds, true, {"txid"} }, - - { "generating", "generate", &generate, true, {"nblocks","maxtries"} }, +{ // category name actor (function) argNames + // --------------------- ------------------------ ----------------------- ---------- + { "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options"} }, + { "hidden", "resendwallettransactions", &resendwallettransactions, {} }, + { "wallet", "abandontransaction", &abandontransaction, {"txid"} }, + { "wallet", "abortrescan", &abortrescan, {} }, + { "wallet", "addmultisigaddress", &addmultisigaddress, {"nrequired","keys","account"} }, + { "wallet", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} }, + { "wallet", "backupwallet", &backupwallet, {"destination"} }, + { "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, + { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, + { "wallet", "dumpwallet", &dumpwallet, {"filename"} }, + { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} }, + { "wallet", "getaccountaddress", &getaccountaddress, {"account"} }, + { "wallet", "getaccount", &getaccount, {"address"} }, + { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, {"account"} }, + { "wallet", "getbalance", &getbalance, {"account","minconf","include_watchonly"} }, + { "wallet", "getnewaddress", &getnewaddress, {"account"} }, + { "wallet", "getrawchangeaddress", &getrawchangeaddress, {} }, + { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, {"account","minconf"} }, + { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, {"address","minconf"} }, + { "wallet", "gettransaction", &gettransaction, {"txid","include_watchonly"} }, + { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, {} }, + { "wallet", "getwalletinfo", &getwalletinfo, {} }, + { "wallet", "importmulti", &importmulti, {"requests","options"} }, + { "wallet", "importprivkey", &importprivkey, {"privkey","label","rescan"} }, + { "wallet", "importwallet", &importwallet, {"filename"} }, + { "wallet", "importaddress", &importaddress, {"address","label","rescan","p2sh"} }, + { "wallet", "importprunedfunds", &importprunedfunds, {"rawtransaction","txoutproof"} }, + { "wallet", "importpubkey", &importpubkey, {"pubkey","label","rescan"} }, + { "wallet", "keypoolrefill", &keypoolrefill, {"newsize"} }, + { "wallet", "listaccounts", &listaccounts, {"minconf","include_watchonly"} }, + { "wallet", "listaddressgroupings", &listaddressgroupings, {} }, + { "wallet", "listlockunspent", &listlockunspent, {} }, + { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, {"minconf","include_empty","include_watchonly"} }, + { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, {"minconf","include_empty","include_watchonly"} }, + { "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, + { "wallet", "listtransactions", &listtransactions, {"account","count","skip","include_watchonly"} }, + { "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, + { "wallet", "listwallets", &listwallets, {} }, + { "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} }, + { "wallet", "move", &movecmd, {"fromaccount","toaccount","amount","minconf","comment"} }, + { "wallet", "sendfrom", &sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, + { "wallet", "sendmany", &sendmany, {"fromaccount","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} }, + { "wallet", "sendtoaddress", &sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount","replaceable","conf_target","estimate_mode"} }, + { "wallet", "setaccount", &setaccount, {"address","account"} }, + { "wallet", "settxfee", &settxfee, {"amount"} }, + { "wallet", "signmessage", &signmessage, {"address","message"} }, + { "wallet", "walletlock", &walletlock, {} }, + { "wallet", "walletpassphrasechange", &walletpassphrasechange, {"oldpassphrase","newpassphrase"} }, + { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} }, + { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} }, + + { "generating", "generate", &generate, {"nblocks","maxtries"} }, }; void RegisterWalletRPCCommands(CRPCTable &t) { - if (gArgs.GetBoolArg("-disablewallet", false)) - return; - for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) t.appendCommand(commands[vcidx].name, &commands[vcidx]); } diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h index db0808b93b..14e51610d9 100644 --- a/src/wallet/rpcwallet.h +++ b/src/wallet/rpcwallet.h @@ -5,7 +5,10 @@ #ifndef BITCOIN_WALLET_RPCWALLET_H #define BITCOIN_WALLET_RPCWALLET_H +#include <string> + class CRPCTable; +class CWallet; class JSONRPCRequest; void RegisterWalletRPCCommands(CRPCTable &t); diff --git a/src/wallet/test/crypto_tests.cpp b/src/wallet/test/crypto_tests.cpp index cbd74b6f96..f4dabc50c0 100644 --- a/src/wallet/test/crypto_tests.cpp +++ b/src/wallet/test/crypto_tests.cpp @@ -80,7 +80,7 @@ BOOST_AUTO_TEST_CASE(passphrase) { std::string hash(GetRandHash().ToString()); std::vector<unsigned char> vchSalt(8); - GetRandBytes(&vchSalt[0], vchSalt.size()); + GetRandBytes(vchSalt.data(), vchSalt.size()); uint32_t rounds = InsecureRand32(); if (rounds > 30000) rounds = 30000; diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 4a2cc9a139..5ebacd57d3 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -364,6 +364,12 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset) empty_wallet(); } +static void AddKey(CWallet& wallet, const CKey& key) +{ + LOCK(wallet.cs_wallet); + wallet.AddKeyPubKey(key, key.GetPubKey()); +} + BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) { LOCK(cs_main); @@ -379,8 +385,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // and new block files. { CWallet wallet; - LOCK(wallet.cs_wallet); - wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); + AddKey(wallet, coinbaseKey); BOOST_CHECK_EQUAL(nullBlock, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 100 * COIN); } @@ -393,8 +398,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // file. { CWallet wallet; - LOCK(wallet.cs_wallet); - wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); + AddKey(wallet, coinbaseKey); BOOST_CHECK_EQUAL(oldTip, wallet.ScanForWalletTransactions(oldTip)); BOOST_CHECK_EQUAL(wallet.GetImmatureBalance(), 50 * COIN); } @@ -599,8 +603,7 @@ public: wallet.reset(new CWallet(std::unique_ptr<CWalletDBWrapper>(new CWalletDBWrapper(&bitdb, "wallet_test.dat")))); bool firstRun; wallet->LoadWallet(firstRun); - LOCK(wallet->cs_wallet); - wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); + AddKey(*wallet, coinbaseKey); wallet->ScanForWalletTransactions(chainActive.Genesis()); } @@ -635,7 +638,7 @@ public: BOOST_FIXTURE_TEST_CASE(ListCoins, ListCoinsTestingSetup) { std::string coinbaseAddress = coinbaseKey.GetPubKey().GetID().ToString(); - LOCK(wallet->cs_wallet); + LOCK2(cs_main, wallet->cs_wallet); // Confirm ListCoins initially returns 1 coin grouped under coinbaseKey // address. diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 6d94154da8..50c1375f44 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -30,6 +30,7 @@ #include "util.h" #include "ui_interface.h" #include "utilmoneystr.h" +#include "wallet/fees.h" #include <assert.h> @@ -110,7 +111,26 @@ public: Process(script); } - void operator()(const CNoDestination &none) {} + void operator()(const WitnessV0ScriptHash& scriptID) + { + CScriptID id; + CRIPEMD160().Write(scriptID.begin(), 32).Finalize(id.begin()); + CScript script; + if (keystore.GetCScript(id, script)) { + Process(script); + } + } + + void operator()(const WitnessV0KeyHash& keyid) + { + CKeyID id(keyid); + if (keystore.HaveKey(id)) { + vKeys.push_back(id); + } + } + + template<typename X> + void operator()(const X &none) {} }; const CWalletTx* CWallet::GetWalletTx(const uint256& hash) const @@ -306,7 +326,7 @@ bool CWallet::LoadCScript(const CScript& redeemScript) * these. Do not add them to the wallet and warn. */ if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) { - std::string strAddr = CBitcoinAddress(CScriptID(redeemScript)).ToString(); + std::string strAddr = EncodeDestination(CScriptID(redeemScript)); LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n", __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr); return true; @@ -389,11 +409,11 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, { int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); - pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); + pMasterKey.second.nDeriveIterations = static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime)))); nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); - pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; + pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + static_cast<unsigned int>(pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime)))) / 2; if (pMasterKey.second.nDeriveIterations < 25000) pMasterKey.second.nDeriveIterations = 25000; @@ -494,63 +514,6 @@ void CWallet::Flush(bool shutdown) dbw->Flush(shutdown); } -bool CWallet::Verify() -{ - if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) - return true; - - uiInterface.InitMessage(_("Verifying wallet(s)...")); - - // Keep track of each wallet absolute path to detect duplicates. - std::set<fs::path> wallet_paths; - - for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { - if (boost::filesystem::path(walletFile).filename() != walletFile) { - return InitError(strprintf(_("Error loading wallet %s. -wallet parameter must only specify a filename (not a path)."), walletFile)); - } - - if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) { - return InitError(strprintf(_("Error loading wallet %s. Invalid characters in -wallet filename."), walletFile)); - } - - fs::path wallet_path = fs::absolute(walletFile, GetDataDir()); - - if (fs::exists(wallet_path) && (!fs::is_regular_file(wallet_path) || fs::is_symlink(wallet_path))) { - return InitError(strprintf(_("Error loading wallet %s. -wallet filename must be a regular file."), walletFile)); - } - - if (!wallet_paths.insert(wallet_path).second) { - return InitError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), walletFile)); - } - - std::string strError; - if (!CWalletDB::VerifyEnvironment(walletFile, GetDataDir().string(), strError)) { - return InitError(strError); - } - - if (gArgs.GetBoolArg("-salvagewallet", false)) { - // Recover readable keypairs: - CWallet dummyWallet; - std::string backup_filename; - if (!CWalletDB::Recover(walletFile, (void *)&dummyWallet, CWalletDB::RecoverKeysOnlyFilter, backup_filename)) { - return false; - } - } - - std::string strWarning; - bool dbV = CWalletDB::VerifyDatabaseFile(walletFile, GetDataDir().string(), strWarning, strError); - if (!strWarning.empty()) { - InitWarning(strWarning); - } - if (!dbV) { - InitError(strError); - return false; - } - } - - return true; -} - void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> range) { // We want all the wallet transactions in range to have the same metadata as @@ -575,6 +538,7 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran const uint256& hash = it->second; CWalletTx* copyTo = &mapWallet[hash]; if (copyFrom == copyTo) continue; + assert(copyFrom && "Oldest wallet transaction in range assumed to have been found."); if (!copyFrom->IsEquivalentTo(*copyTo)) continue; copyTo->mapValue = copyFrom->mapValue; copyTo->vOrderForm = copyFrom->vOrderForm; @@ -651,11 +615,11 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) CCrypter crypter; int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); - kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime)); + kMasterKey.nDeriveIterations = static_cast<unsigned int>(2500000 / ((double)(GetTimeMillis() - nStartTime))); nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); - kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; + kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + static_cast<unsigned int>(kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime)))) / 2; if (kMasterKey.nDeriveIterations < 25000) kMasterKey.nDeriveIterations = 25000; @@ -970,6 +934,15 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) wtx.fFromMe = wtxIn.fFromMe; fUpdated = true; } + // If we have a witness-stripped version of this transaction, and we + // see a new version with a witness, then we must be upgrading a pre-segwit + // wallet. Store the new version of the transaction with the witness, + // as the stripped-version must be invalid. + // TODO: Store all versions of the transaction, instead of just one. + if (wtxIn.tx->HasWitness() && !wtx.tx->HasWitness()) { + wtx.SetTx(wtxIn.tx); + fUpdated = true; + } } //// debug print @@ -989,7 +962,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) // notify an external script when a wallet transaction comes in or is updated std::string strCmd = gArgs.GetArg("-walletnotify", ""); - if ( !strCmd.empty()) + if (!strCmd.empty()) { boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); boost::thread t(runCommand, strCmd); // thread runs free @@ -2608,17 +2581,6 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC return true; } -static CFeeRate GetDiscardRate(const CBlockPolicyEstimator& estimator) -{ - unsigned int highest_target = estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE); - CFeeRate discard_rate = estimator.estimateSmartFee(highest_target, nullptr /* FeeCalculation */, false /* conservative */); - // Don't let discard_rate be greater than longest possible fee estimate if we get a valid fee estimate - discard_rate = (discard_rate == CFeeRate(0)) ? CWallet::m_discard_rate : std::min(discard_rate, CWallet::m_discard_rate); - // Discard rate must be at least dustRelayFee - discard_rate = std::max(discard_rate, ::dustRelayFee); - return discard_rate; -} - bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign) { @@ -2679,6 +2641,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT assert(txNew.nLockTime <= (unsigned int)chainActive.Height()); assert(txNew.nLockTime < LOCKTIME_THRESHOLD); FeeCalculation feeCalc; + CAmount nFeeNeeded; unsigned int nBytes; { std::set<CInputCoin> setCoins; @@ -2841,7 +2804,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT vin.scriptWitness.SetNull(); } - CAmount nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc); + nFeeNeeded = GetMinimumFee(nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc); // If we made it here and we aren't even able to meet the relay fee on the next pass, give up // because we must be at the maximum allowed fee. @@ -2862,13 +2825,15 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT // new inputs. We now know we only need the smaller fee // (because of reduced tx size) and so we should add a // change output. Only try this once. - CAmount fee_needed_for_change = GetMinimumFee(change_prototype_size, coin_control, ::mempool, ::feeEstimator, nullptr); - CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate); - CAmount max_excess_fee = fee_needed_for_change + minimum_value_for_change; - if (nFeeRet > nFeeNeeded + max_excess_fee && nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) { - pick_new_inputs = false; - nFeeRet = nFeeNeeded + fee_needed_for_change; - continue; + if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) { + unsigned int tx_size_with_change = nBytes + change_prototype_size + 2; // Add 2 as a buffer in case increasing # of outputs changes compact size + CAmount fee_needed_with_change = GetMinimumFee(tx_size_with_change, coin_control, ::mempool, ::feeEstimator, nullptr); + CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate); + if (nFeeRet >= fee_needed_with_change + minimum_value_for_change) { + pick_new_inputs = false; + nFeeRet = fee_needed_with_change; + continue; + } } // If we have change output already, just increase it @@ -2963,8 +2928,8 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT } } - LogPrintf("Fee Calculation: Fee:%d Bytes:%u Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n", - nFeeRet, nBytes, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay, + LogPrintf("Fee Calculation: Fee:%d Bytes:%u Needed:%d Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n", + nFeeRet, nBytes, nFeeNeeded, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay, feeCalc.est.pass.start, feeCalc.est.pass.end, 100 * feeCalc.est.pass.withinTarget / (feeCalc.est.pass.totalConfirmed + feeCalc.est.pass.inMempool + feeCalc.est.pass.leftMempool), feeCalc.est.pass.withinTarget, feeCalc.est.pass.totalConfirmed, feeCalc.est.pass.inMempool, feeCalc.est.pass.leftMempool, @@ -3041,80 +3006,16 @@ bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB *pwa return true; } -CAmount CWallet::GetRequiredFee(unsigned int nTxBytes) -{ - return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes)); -} - -CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc) -{ - /* User control of how to calculate fee uses the following parameter precedence: - 1. coin_control.m_feerate - 2. coin_control.m_confirm_target - 3. payTxFee (user-set global variable) - 4. nTxConfirmTarget (user-set global variable) - The first parameter that is set is used. - */ - CAmount fee_needed; - if (coin_control.m_feerate) { // 1. - fee_needed = coin_control.m_feerate->GetFee(nTxBytes); - if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; - // Allow to override automatic min/max check over coin control instance - if (coin_control.fOverrideFeeRate) return fee_needed; - } - else if (!coin_control.m_confirm_target && ::payTxFee != CFeeRate(0)) { // 3. TODO: remove magic value of 0 for global payTxFee - fee_needed = ::payTxFee.GetFee(nTxBytes); - if (feeCalc) feeCalc->reason = FeeReason::PAYTXFEE; - } - else { // 2. or 4. - // We will use smart fee estimation - unsigned int target = coin_control.m_confirm_target ? *coin_control.m_confirm_target : ::nTxConfirmTarget; - // By default estimates are economical iff we are signaling opt-in-RBF - bool conservative_estimate = !coin_control.signalRbf; - // Allow to override the default fee estimate mode over the CoinControl instance - if (coin_control.m_fee_mode == FeeEstimateMode::CONSERVATIVE) conservative_estimate = true; - else if (coin_control.m_fee_mode == FeeEstimateMode::ECONOMICAL) conservative_estimate = false; - - fee_needed = estimator.estimateSmartFee(target, feeCalc, conservative_estimate).GetFee(nTxBytes); - if (fee_needed == 0) { - // if we don't have enough data for estimateSmartFee, then use fallbackFee - fee_needed = fallbackFee.GetFee(nTxBytes); - if (feeCalc) feeCalc->reason = FeeReason::FALLBACK; - } - // Obey mempool min fee when using smart fee estimation - CAmount min_mempool_fee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nTxBytes); - if (fee_needed < min_mempool_fee) { - fee_needed = min_mempool_fee; - if (feeCalc) feeCalc->reason = FeeReason::MEMPOOL_MIN; - } - } - - // prevent user from paying a fee below minRelayTxFee or minTxFee - CAmount required_fee = GetRequiredFee(nTxBytes); - if (required_fee > fee_needed) { - fee_needed = required_fee; - if (feeCalc) feeCalc->reason = FeeReason::REQUIRED; - } - // But always obey the maximum - if (fee_needed > maxTxFee) { - fee_needed = maxTxFee; - if (feeCalc) feeCalc->reason = FeeReason::MAXTXFEE; - } - return fee_needed; -} - - - - DBErrors CWallet::LoadWallet(bool& fFirstRunRet) { + LOCK2(cs_main, cs_wallet); + fFirstRunRet = false; DBErrors nLoadWalletRet = CWalletDB(*dbw,"cr+").LoadWallet(this); if (nLoadWalletRet == DB_NEED_REWRITE) { if (dbw->Rewrite("\x04pool")) { - LOCK(cs_wallet); setInternalKeyPool.clear(); setExternalKeyPool.clear(); m_pool_key_to_index.clear(); @@ -3201,9 +3102,9 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s } NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO, strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) ); - if (!strPurpose.empty() && !CWalletDB(*dbw).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose)) + if (!strPurpose.empty() && !CWalletDB(*dbw).WritePurpose(EncodeDestination(address), strPurpose)) return false; - return CWalletDB(*dbw).WriteName(CBitcoinAddress(address).ToString(), strName); + return CWalletDB(*dbw).WriteName(EncodeDestination(address), strName); } bool CWallet::DelAddressBook(const CTxDestination& address) @@ -3212,7 +3113,7 @@ bool CWallet::DelAddressBook(const CTxDestination& address) LOCK(cs_wallet); // mapAddressBook // Delete destdata tuples associated with address - std::string strAddress = CBitcoinAddress(address).ToString(); + std::string strAddress = EncodeDestination(address); for (const std::pair<std::string, std::string> &item : mapAddressBook[address].destdata) { CWalletDB(*dbw).EraseDestData(strAddress, item.first); @@ -3222,8 +3123,8 @@ bool CWallet::DelAddressBook(const CTxDestination& address) NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, "", CT_DELETED); - CWalletDB(*dbw).ErasePurpose(CBitcoinAddress(address).ToString()); - return CWalletDB(*dbw).EraseName(CBitcoinAddress(address).ToString()); + CWalletDB(*dbw).ErasePurpose(EncodeDestination(address)); + return CWalletDB(*dbw).EraseName(EncodeDestination(address)); } const std::string& CWallet::GetAccountName(const CScript& scriptPubKey) const @@ -3729,13 +3630,10 @@ void CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) c // map in which we'll infer heights of other keys CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganized; use a 144-block safety margin std::map<CKeyID, CBlockIndex*> mapKeyFirstBlock; - std::set<CKeyID> setKeys; - GetKeys(setKeys); - for (const CKeyID &keyid : setKeys) { + for (const CKeyID &keyid : GetKeys()) { if (mapKeyBirth.count(keyid) == 0) mapKeyFirstBlock[keyid] = pindexMax; } - setKeys.clear(); // if there are no such keys, we're done if (mapKeyFirstBlock.empty()) @@ -3840,14 +3738,14 @@ bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, co return false; mapAddressBook[dest].destdata.insert(std::make_pair(key, value)); - return CWalletDB(*dbw).WriteDestData(CBitcoinAddress(dest).ToString(), key, value); + return CWalletDB(*dbw).WriteDestData(EncodeDestination(dest), key, value); } bool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key) { if (!mapAddressBook[dest].destdata.erase(key)) return false; - return CWalletDB(*dbw).EraseDestData(CBitcoinAddress(dest).ToString(), key); + return CWalletDB(*dbw).EraseDestData(EncodeDestination(dest), key); } bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value) @@ -3886,46 +3784,6 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const return values; } -std::string CWallet::GetWalletHelpString(bool showDebug) -{ - std::string strUsage = HelpMessageGroup(_("Wallet options:")); - strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); - strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE)); - strUsage += HelpMessageOpt("-fallbackfee=<amt>", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"), - CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE))); - strUsage += HelpMessageOpt("-discardfee=<amt>", strprintf(_("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). " - "Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target"), - CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE))); - strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"), - CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); - strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), - CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK()))); - strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup")); - strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup")); - strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); - strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); - strUsage += HelpMessageOpt("-usehd", _("Use hierarchical deterministic key generation (HD) after BIP32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET)); - strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (default: %u)"), DEFAULT_WALLET_RBF)); - strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); - strUsage += HelpMessageOpt("-wallet=<file>", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT)); - strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); - strUsage += HelpMessageOpt("-walletnotify=<cmd>", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); - strUsage += HelpMessageOpt("-zapwallettxes=<mode>", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + - " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)")); - - if (showDebug) - { - strUsage += HelpMessageGroup(_("Wallet debugging/testing options:")); - - strUsage += HelpMessageOpt("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE)); - strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET)); - strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB)); - strUsage += HelpMessageOpt("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS)); - } - - return strUsage; -} - CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) { // needed to restore wallet transaction meta data after -zapwallettxes @@ -3998,17 +3856,17 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) if (fFirstRun) { - // Create new keyUser and set as default key - if (gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) { - - // ensure this wallet.dat can only be opened by clients supporting HD with chain split - walletInstance->SetMinVersion(FEATURE_HD_SPLIT); - - // generate a new master key - CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); - if (!walletInstance->SetHDMasterKey(masterPubKey)) - throw std::runtime_error(std::string(__func__) + ": Storing master key failed"); + // ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key + if (!gArgs.GetBoolArg("-usehd", true)) { + InitError(strprintf(_("Error creating %s: You can't create non-HD wallets with this version."), walletFile)); + return nullptr; } + walletInstance->SetMinVersion(FEATURE_NO_DEFAULT_KEY); + + // generate a new master key + CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); + if (!walletInstance->SetHDMasterKey(masterPubKey)) + throw std::runtime_error(std::string(__func__) + ": Storing master key failed"); // Top up the keypool if (!walletInstance->TopUpKeyPool()) { @@ -4019,7 +3877,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) walletInstance->SetBestChain(chainActive.GetLocator()); } else if (gArgs.IsArgSet("-usehd")) { - bool useHD = gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET); + bool useHD = gArgs.GetBoolArg("-usehd", true); if (walletInstance->IsHDEnabled() && !useHD) { InitError(strprintf(_("Error loading %s: You can't disable HD on an already existing HD wallet"), walletFile)); return nullptr; @@ -4114,24 +3972,6 @@ CWallet* CWallet::CreateWalletFromFile(const std::string walletFile) return walletInstance; } -bool CWallet::InitLoadWallet() -{ - if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { - LogPrintf("Wallet disabled!\n"); - return true; - } - - for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { - CWallet * const pwallet = CreateWalletFromFile(walletFile); - if (!pwallet) { - return false; - } - vpwallets.push_back(pwallet); - } - - return true; -} - std::atomic<bool> CWallet::fFlushScheduled(false); void CWallet::postInitProcess(CScheduler& scheduler) @@ -4146,126 +3986,6 @@ void CWallet::postInitProcess(CScheduler& scheduler) } } -bool CWallet::ParameterInteraction() -{ - gArgs.SoftSetArg("-wallet", DEFAULT_WALLET_DAT); - const bool is_multiwallet = gArgs.GetArgs("-wallet").size() > 1; - - if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) - return true; - - if (gArgs.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && gArgs.SoftSetBoolArg("-walletbroadcast", false)) { - LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\n", __func__); - } - - if (gArgs.GetBoolArg("-salvagewallet", false)) { - if (is_multiwallet) { - return InitError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet")); - } - // Rewrite just private keys: rescan to find transactions - if (gArgs.SoftSetBoolArg("-rescan", true)) { - LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\n", __func__); - } - } - - int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0); - // -zapwallettxes implies dropping the mempool on startup - if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-persistmempool", false)) { - LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0\n", __func__, zapwallettxes); - } - - // -zapwallettxes implies a rescan - if (zapwallettxes != 0) { - if (is_multiwallet) { - return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes")); - } - if (gArgs.SoftSetBoolArg("-rescan", true)) { - LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -rescan=1\n", __func__, zapwallettxes); - } - } - - if (is_multiwallet) { - if (gArgs.GetBoolArg("-upgradewallet", false)) { - return InitError(strprintf("%s is only allowed with a single wallet file", "-upgradewallet")); - } - } - - if (gArgs.GetBoolArg("-sysperms", false)) - return InitError("-sysperms is not allowed in combination with enabled wallet functionality"); - if (gArgs.GetArg("-prune", 0) && gArgs.GetBoolArg("-rescan", false)) - return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.")); - - if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-minrelaytxfee") + " " + - _("The wallet will avoid paying less than the minimum relay fee.")); - - if (gArgs.IsArgSet("-mintxfee")) - { - CAmount n = 0; - if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n) - return InitError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", ""))); - if (n > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-mintxfee") + " " + - _("This is the minimum transaction fee you pay on every transaction.")); - CWallet::minTxFee = CFeeRate(n); - } - if (gArgs.IsArgSet("-fallbackfee")) - { - CAmount nFeePerK = 0; - if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) - return InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), gArgs.GetArg("-fallbackfee", ""))); - if (nFeePerK > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-fallbackfee") + " " + - _("This is the transaction fee you may pay when fee estimates are not available.")); - CWallet::fallbackFee = CFeeRate(nFeePerK); - } - if (gArgs.IsArgSet("-discardfee")) - { - CAmount nFeePerK = 0; - if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK)) - return InitError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), gArgs.GetArg("-discardfee", ""))); - if (nFeePerK > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-discardfee") + " " + - _("This is the transaction fee you may discard if change is smaller than dust at this level")); - CWallet::m_discard_rate = CFeeRate(nFeePerK); - } - if (gArgs.IsArgSet("-paytxfee")) - { - CAmount nFeePerK = 0; - if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) - return InitError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", ""))); - if (nFeePerK > HIGH_TX_FEE_PER_KB) - InitWarning(AmountHighWarn("-paytxfee") + " " + - _("This is the transaction fee you will pay if you send a transaction.")); - - payTxFee = CFeeRate(nFeePerK, 1000); - if (payTxFee < ::minRelayTxFee) - { - return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"), - gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString())); - } - } - if (gArgs.IsArgSet("-maxtxfee")) - { - CAmount nMaxFee = 0; - if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) - return InitError(AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", ""))); - if (nMaxFee > HIGH_MAX_TX_FEE) - InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.")); - maxTxFee = nMaxFee; - if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee) - { - return InitError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), - gArgs.GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString())); - } - } - nTxConfirmTarget = gArgs.GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET); - bSpendZeroConfChange = gArgs.GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE); - fWalletRbf = gArgs.GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF); - - return true; -} - bool CWallet::BackupWallet(const std::string& strDest) { return dbw->Backup(strDest); @@ -4328,5 +4048,6 @@ int CMerkleTx::GetBlocksToMaturity() const bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state) { - return ::AcceptToMemoryPool(mempool, state, tx, true, nullptr, nullptr, false, nAbsurdFee); + return ::AcceptToMemoryPool(mempool, state, tx, nullptr /* pfMissingInputs */, + nullptr /* plTxnReplaced */, false /* bypass_limits */, nAbsurdFee); } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index cfe156e238..c4af192f36 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -65,8 +65,6 @@ static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 6; static const bool DEFAULT_WALLET_RBF = false; static const bool DEFAULT_WALLETBROADCAST = true; static const bool DEFAULT_DISABLE_WALLET = false; -//! if set, all keys will be derived by using BIP32 -static const bool DEFAULT_USE_HD_WALLET = true; extern const char * DEFAULT_WALLET_DAT; @@ -87,7 +85,7 @@ enum class FeeEstimateMode; /** (client) version numbers for particular wallet features */ enum WalletFeature { - FEATURE_BASE = 10500, // the earliest version new wallets supports (only useful for getinfo's clientversion output) + FEATURE_BASE = 10500, // the earliest version new wallets supports (only useful for getwalletinfo's clientversion output) FEATURE_WALLETCRYPT = 40000, // wallet encryption FEATURE_COMPRPUBKEY = 60000, // compressed public keys @@ -96,6 +94,8 @@ enum WalletFeature FEATURE_HD_SPLIT = 139900, // Wallet with HD chain split (change outputs will use m/0'/1'/k) + FEATURE_NO_DEFAULT_KEY = 159900, // Wallet without a default key written + FEATURE_LATEST = FEATURE_COMPRPUBKEY // HD is optional, use FEATURE_COMPRPUBKEY as latest version }; @@ -958,16 +958,6 @@ public: static CFeeRate minTxFee; static CFeeRate fallbackFee; static CFeeRate m_discard_rate; - /** - * Estimate the minimum fee considering user set parameters - * and the required fee - */ - static CAmount GetMinimumFee(unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc); - /** - * Return the minimum required fee taking into account the - * floating relay fee and user set minimum transaction fee - */ - static CAmount GetRequiredFee(unsigned int nTxBytes); bool NewKeyPool(); size_t KeypoolCountExternalKeys(); @@ -1054,11 +1044,6 @@ public: //! Flush wallet (bitdb flush) void Flush(bool shutdown=false); - //! Responsible for reading and validating the -wallet arguments and verifying the wallet database. - // This function will perform salvage on the wallet if requested, as long as only one wallet is - // being loaded (CWallet::ParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). - static bool Verify(); - /** * Address book entry changed. * @note called with lock cs_wallet held. @@ -1095,12 +1080,8 @@ public: /** Mark a transaction as replaced by another transaction (e.g., BIP 125). */ bool MarkReplaced(const uint256& originalHash, const uint256& newHash); - /* Returns the wallets help message */ - static std::string GetWalletHelpString(bool showDebug); - /* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */ static CWallet* CreateWalletFromFile(const std::string walletFile); - static bool InitLoadWallet(); /** * Wallet post-init setup @@ -1108,9 +1089,6 @@ public: */ void postInitProcess(CScheduler& scheduler); - /* Wallets parameter interaction */ - static bool ParameterInteraction(); - bool BackupWallet(const std::string& strDest); /* Set the HD chain model (chain child index counters) */ diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 12da3cce64..b7f873c1e4 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -41,9 +41,9 @@ bool CWalletDB::WritePurpose(const std::string& strAddress, const std::string& s return WriteIC(std::make_pair(std::string("purpose"), strAddress), strPurpose); } -bool CWalletDB::ErasePurpose(const std::string& strPurpose) +bool CWalletDB::ErasePurpose(const std::string& strAddress) { - return EraseIC(std::make_pair(std::string("purpose"), strPurpose)); + return EraseIC(std::make_pair(std::string("purpose"), strAddress)); } bool CWalletDB::WriteTx(const CWalletTx& wtx) @@ -253,13 +253,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, { std::string strAddress; ssKey >> strAddress; - ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].name; + ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].name; } else if (strType == "purpose") { std::string strAddress; ssKey >> strAddress; - ssValue >> pwallet->mapAddressBook[CBitcoinAddress(strAddress).Get()].purpose; + ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].purpose; } else if (strType == "tx") { @@ -493,7 +493,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, ssKey >> strAddress; ssKey >> strKey; ssValue >> strValue; - if (!pwallet->LoadDestData(CBitcoinAddress(strAddress).Get(), strKey, strValue)) + if (!pwallet->LoadDestData(DecodeDestination(strAddress), strKey, strValue)) { strErr = "Error reading wallet database: LoadDestData failed"; return false; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 4f8ea185d5..3a146179af 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -167,6 +167,8 @@ public: m_dbw(dbw) { } + CWalletDB(const CWalletDB&) = delete; + CWalletDB& operator=(const CWalletDB&) = delete; bool WriteName(const std::string& strAddress, const std::string& strName); bool EraseName(const std::string& strAddress); @@ -244,9 +246,6 @@ public: private: CDB batch; CWalletDBWrapper& m_dbw; - - CWalletDB(const CWalletDB&); - void operator=(const CWalletDB&); }; //! Compacts BDB state so that wallet.dat is self-contained (if there are changes) diff --git a/test/functional/README.md b/test/functional/README.md index 44efda33d3..2558bd017d 100644 --- a/test/functional/README.md +++ b/test/functional/README.md @@ -24,8 +24,8 @@ don't have test cases for. - Use a module-level docstring to describe what the test is testing, and how it is testing it. - When subclassing the BitcoinTestFramwork, place overrides for the - `__init__()`, and `setup_xxxx()` methods at the top of the subclass, then - locally-defined helper methods, then the `run_test()` method. + `set_test_params()`, `add_options()` and `setup_xxxx()` methods at the top of + the subclass, then locally-defined helper methods, then the `run_test()` method. #### General test-writing advice @@ -36,7 +36,7 @@ don't have test cases for. - Avoid stop-starting the nodes multiple times during the test if possible. A stop-start takes several seconds, so doing it several times blows up the runtime of the test. -- Set the `self.setup_clean_chain` variable in `__init__()` to control whether +- Set the `self.setup_clean_chain` variable in `set_test_params()` to control whether or not to use the cached data directories. The cached data directories contain a 200-block pre-mined blockchain and wallets for four nodes. Each node has 25 mature blocks (25x50=1250 BTC) in its wallet. diff --git a/test/functional/abandonconflict.py b/test/functional/abandonconflict.py index c87c02492d..e8dbc86469 100755 --- a/test/functional/abandonconflict.py +++ b/test/functional/abandonconflict.py @@ -14,10 +14,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class AbandonConflictTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False self.extra_args = [["-minrelaytxfee=0.00001"], []] def run_test(self): @@ -74,7 +72,7 @@ class AbandonConflictTest(BitcoinTestFramework): # Restart the node with a higher min relay fee so the parent tx is no longer in mempool # TODO: redo with eviction self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-minrelaytxfee=0.0001"]) + self.start_node(0, extra_args=["-minrelaytxfee=0.0001"]) # Verify txs no longer in either node's mempool assert_equal(len(self.nodes[0].getrawmempool()), 0) @@ -101,7 +99,7 @@ class AbandonConflictTest(BitcoinTestFramework): # Verify that even with a low min relay fee, the tx is not reaccepted from wallet on startup once abandoned self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-minrelaytxfee=0.00001"]) + self.start_node(0, extra_args=["-minrelaytxfee=0.00001"]) assert_equal(len(self.nodes[0].getrawmempool()), 0) assert_equal(self.nodes[0].getbalance(), balance) @@ -121,7 +119,7 @@ class AbandonConflictTest(BitcoinTestFramework): # Remove using high relay fee again self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-minrelaytxfee=0.0001"]) + self.start_node(0, extra_args=["-minrelaytxfee=0.0001"]) assert_equal(len(self.nodes[0].getrawmempool()), 0) newbalance = self.nodes[0].getbalance() assert_equal(newbalance, balance - Decimal("24.9996")) diff --git a/test/functional/assumevalid.py b/test/functional/assumevalid.py index 9d17faac51..65685c48b7 100755 --- a/test/functional/assumevalid.py +++ b/test/functional/assumevalid.py @@ -54,20 +54,22 @@ class BaseNode(NodeConnCB): self.send_message(headers_message) class AssumeValidTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 def setup_network(self): + self.add_nodes(3) # Start node0. We don't start the other nodes yet since # we need to pre-mine a block with an invalid transaction # signature so we can pass in the block hash as assumevalid. - self.nodes = [self.start_node(0, self.options.tmpdir)] + self.start_node(0) def send_blocks_until_disconnected(self, node): """Keep sending blocks to the node until we're disconnected.""" for i in range(len(self.blocks)): + if not node.connection: + break try: node.send_message(msg_block(self.blocks[i])) except IOError as e: @@ -162,15 +164,13 @@ class AssumeValidTest(BitcoinTestFramework): height += 1 # Start node1 and node2 with assumevalid so they accept a block with a bad signature. - self.nodes.append(self.start_node(1, self.options.tmpdir, - ["-assumevalid=" + hex(block102.sha256)])) + self.start_node(1, extra_args=["-assumevalid=" + hex(block102.sha256)]) node1 = BaseNode() # connects to node1 connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], node1)) node1.add_connection(connections[1]) node1.wait_for_verack() - self.nodes.append(self.start_node(2, self.options.tmpdir, - ["-assumevalid=" + hex(block102.sha256)])) + self.start_node(2, extra_args=["-assumevalid=" + hex(block102.sha256)]) node2 = BaseNode() # connects to node2 connections.append(NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], node2)) node2.add_connection(connections[2]) diff --git a/test/functional/bip65-cltv-p2p.py b/test/functional/bip65-cltv-p2p.py index 65ae8de554..2cd6df6e37 100755 --- a/test/functional/bip65-cltv-p2p.py +++ b/test/functional/bip65-cltv-p2p.py @@ -60,9 +60,7 @@ def create_transaction(node, coinbase, to_address, amount): return tx class BIP65Test(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 self.extra_args = [['-promiscuousmempoolflags=1', '-whitelist=127.0.0.1']] self.setup_clean_chain = True diff --git a/test/functional/bip68-112-113-p2p.py b/test/functional/bip68-112-113-p2p.py index 5a322e8c0e..7e6a4f4408 100755 --- a/test/functional/bip68-112-113-p2p.py +++ b/test/functional/bip68-112-113-p2p.py @@ -92,9 +92,9 @@ def all_rlt_txs(txarray): return txs class BIP68_112_113Test(ComparisonTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 + self.setup_clean_chain = True self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=4']] def run_test(self): diff --git a/test/functional/bip68-sequence.py b/test/functional/bip68-sequence.py index 87a50692f6..5f8f21701f 100755 --- a/test/functional/bip68-sequence.py +++ b/test/functional/bip68-sequence.py @@ -17,10 +17,8 @@ SEQUENCE_LOCKTIME_MASK = 0x0000ffff NOT_FINAL_ERROR = "64: non-BIP68-final" class BIP68Test(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False self.extra_args = [[], ["-acceptnonstdtxn=0"]] def run_test(self): @@ -85,7 +83,7 @@ class BIP68Test(BitcoinTestFramework): tx2.vout = [CTxOut(int(value-self.relayfee*COIN), CScript([b'a']))] tx2.rehash() - assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx2)) + assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx2)) # Setting the version back down to 1 should disable the sequence lock, # so this should be accepted. @@ -182,7 +180,7 @@ class BIP68Test(BitcoinTestFramework): if (using_sequence_locks and not should_pass): # This transaction should be rejected - assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, rawtx) + assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, rawtx) else: # This raw transaction should be accepted self.nodes[0].sendrawtransaction(rawtx) @@ -229,7 +227,7 @@ class BIP68Test(BitcoinTestFramework): if (orig_tx.hash in node.getrawmempool()): # sendrawtransaction should fail if the tx is in the mempool - assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, node.sendrawtransaction, ToHex(tx)) + assert_raises_rpc_error(-26, NOT_FINAL_ERROR, node.sendrawtransaction, ToHex(tx)) else: # sendrawtransaction should succeed if the tx is not in the mempool node.sendrawtransaction(ToHex(tx)) @@ -282,7 +280,7 @@ class BIP68Test(BitcoinTestFramework): tx5.vout[0].nValue += int(utxos[0]["amount"]*COIN) raw_tx5 = self.nodes[0].signrawtransaction(ToHex(tx5))["hex"] - assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5) + assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, raw_tx5) # Test mempool-BIP68 consistency after reorg # @@ -355,7 +353,7 @@ class BIP68Test(BitcoinTestFramework): tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee*COIN), CScript([b'a']))] tx3.rehash() - assert_raises_jsonrpc(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3)) + assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3)) # make a block that violates bip68; ensure that the tip updates tip = int(self.nodes[0].getbestblockhash(), 16) @@ -371,11 +369,14 @@ class BIP68Test(BitcoinTestFramework): def activateCSV(self): # activation should happen at block height 432 (3 periods) + # getblockchaininfo will show CSV as active at block 431 (144 * 3 -1) since it's returning whether CSV is active for the next block. min_activation_height = 432 height = self.nodes[0].getblockcount() - assert(height < min_activation_height) - self.nodes[0].generate(min_activation_height-height) - assert(get_bip9_status(self.nodes[0], 'csv')['status'] == 'active') + assert_greater_than(min_activation_height - height, 2) + self.nodes[0].generate(min_activation_height - height - 2) + assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], "locked_in") + self.nodes[0].generate(1) + assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], "active") sync_blocks(self.nodes) # Use self.nodes[1] to test that version 2 transactions are standard. @@ -387,7 +388,7 @@ class BIP68Test(BitcoinTestFramework): tx = FromHex(CTransaction(), rawtxfund) tx.nVersion = 2 tx_signed = self.nodes[1].signrawtransaction(ToHex(tx))["hex"] - tx_id = self.nodes[1].sendrawtransaction(tx_signed) + self.nodes[1].sendrawtransaction(tx_signed) if __name__ == '__main__': BIP68Test().main() diff --git a/test/functional/bip9-softforks.py b/test/functional/bip9-softforks.py index f00232c9ff..904789301a 100755 --- a/test/functional/bip9-softforks.py +++ b/test/functional/bip9-softforks.py @@ -28,11 +28,10 @@ from test_framework.comptool import TestInstance, TestManager from test_framework.script import CScript, OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP class BIP9SoftForksTest(ComparisonTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 self.extra_args = [['-whitelist=127.0.0.1']] + self.setup_clean_chain = True def run_test(self): self.test = TestManager(self, self.options.tmpdir) @@ -241,6 +240,7 @@ class BIP9SoftForksTest(ComparisonTestFramework): # Restart all self.test.clear_all_connections() self.stop_nodes() + self.nodes = [] shutil.rmtree(self.options.tmpdir + "/node0") self.setup_chain() self.setup_network() diff --git a/test/functional/bipdersig-p2p.py b/test/functional/bipdersig-p2p.py index 9775970893..c620d3e155 100755 --- a/test/functional/bipdersig-p2p.py +++ b/test/functional/bipdersig-p2p.py @@ -48,9 +48,7 @@ def create_transaction(node, coinbase, to_address, amount): return tx class BIP66Test(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 self.extra_args = [['-promiscuousmempoolflags=1', '-whitelist=127.0.0.1']] self.setup_clean_chain = True diff --git a/test/functional/bitcoin_cli.py b/test/functional/bitcoin_cli.py index 1033202092..996cbb8a12 100755 --- a/test/functional/bitcoin_cli.py +++ b/test/functional/bitcoin_cli.py @@ -4,23 +4,59 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test bitcoin-cli""" from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal +from test_framework.util import assert_equal, assert_raises_process_error, get_auth_cookie class TestBitcoinCli(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 def run_test(self): """Main test logic""" - self.log.info("Compare responses from getinfo RPC and `bitcoin-cli getinfo`") - cli_get_info = self.nodes[0].cli.getinfo() - rpc_get_info = self.nodes[0].getinfo() + self.log.info("Compare responses from gewalletinfo RPC and `bitcoin-cli getwalletinfo`") + cli_response = self.nodes[0].cli.getwalletinfo() + rpc_response = self.nodes[0].getwalletinfo() + assert_equal(cli_response, rpc_response) - assert_equal(cli_get_info, rpc_get_info) + self.log.info("Compare responses from getblockchaininfo RPC and `bitcoin-cli getblockchaininfo`") + cli_response = self.nodes[0].cli.getblockchaininfo() + rpc_response = self.nodes[0].getblockchaininfo() + assert_equal(cli_response, rpc_response) + + user, password = get_auth_cookie(self.nodes[0].datadir) + + self.log.info("Test -stdinrpcpass option") + assert_equal(0, self.nodes[0].cli('-rpcuser=%s' % user, '-stdinrpcpass', input=password).getblockcount()) + assert_raises_process_error(1, "incorrect rpcuser or rpcpassword", self.nodes[0].cli('-rpcuser=%s' % user, '-stdinrpcpass', input="foo").echo) + + self.log.info("Test -stdin and -stdinrpcpass") + assert_equal(["foo", "bar"], self.nodes[0].cli('-rpcuser=%s' % user, '-stdin', '-stdinrpcpass', input=password + "\nfoo\nbar").echo()) + assert_raises_process_error(1, "incorrect rpcuser or rpcpassword", self.nodes[0].cli('-rpcuser=%s' % user, '-stdin', '-stdinrpcpass', input="foo").echo) + + self.log.info("Compare responses from `bitcoin-cli -getinfo` and the RPCs data is retrieved from.") + cli_get_info = self.nodes[0].cli('-getinfo').help() + wallet_info = self.nodes[0].getwalletinfo() + network_info = self.nodes[0].getnetworkinfo() + blockchain_info = self.nodes[0].getblockchaininfo() + + assert_equal(cli_get_info['version'], network_info['version']) + assert_equal(cli_get_info['protocolversion'], network_info['protocolversion']) + assert_equal(cli_get_info['walletversion'], wallet_info['walletversion']) + assert_equal(cli_get_info['balance'], wallet_info['balance']) + assert_equal(cli_get_info['blocks'], blockchain_info['blocks']) + assert_equal(cli_get_info['timeoffset'], network_info['timeoffset']) + assert_equal(cli_get_info['connections'], network_info['connections']) + assert_equal(cli_get_info['proxy'], network_info['networks'][0]['proxy']) + assert_equal(cli_get_info['difficulty'], blockchain_info['difficulty']) + assert_equal(cli_get_info['testnet'], blockchain_info['chain'] == "test") + assert_equal(cli_get_info['balance'], wallet_info['balance']) + assert_equal(cli_get_info['keypoololdest'], wallet_info['keypoololdest']) + assert_equal(cli_get_info['keypoolsize'], wallet_info['keypoolsize']) + assert_equal(cli_get_info['paytxfee'], wallet_info['paytxfee']) + assert_equal(cli_get_info['relayfee'], network_info['relayfee']) + # unlocked_until is not tested because the wallet is not encrypted if __name__ == '__main__': TestBitcoinCli().main() diff --git a/test/functional/blockchain.py b/test/functional/blockchain.py index 0812e1b0df..4576cb036a 100755 --- a/test/functional/blockchain.py +++ b/test/functional/blockchain.py @@ -21,25 +21,24 @@ from decimal import Decimal import http.client import subprocess -from test_framework.test_framework import (BitcoinTestFramework, BITCOIND_PROC_WAIT_TIMEOUT) +from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, + assert_greater_than, + assert_greater_than_or_equal, assert_raises, - assert_raises_jsonrpc, + assert_raises_rpc_error, assert_is_hex_string, assert_is_hash_string, ) - class BlockchainTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = False + def set_test_params(self): self.num_nodes = 1 - self.extra_args = [['-stopatheight=207']] + self.extra_args = [['-stopatheight=207', '-prune=1']] def run_test(self): + self._test_getblockchaininfo() self._test_getchaintxstats() self._test_gettxoutsetinfo() self._test_getblockheader() @@ -48,6 +47,56 @@ class BlockchainTest(BitcoinTestFramework): self._test_stopatheight() assert self.nodes[0].verifychain(4, 0) + def _test_getblockchaininfo(self): + self.log.info("Test getblockchaininfo") + + keys = [ + 'bestblockhash', + 'bip9_softforks', + 'blocks', + 'chain', + 'chainwork', + 'difficulty', + 'headers', + 'mediantime', + 'pruned', + 'size_on_disk', + 'softforks', + 'verificationprogress', + 'warnings', + ] + res = self.nodes[0].getblockchaininfo() + + # result should have these additional pruning keys if manual pruning is enabled + assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning'] + keys)) + + # size_on_disk should be > 0 + assert_greater_than(res['size_on_disk'], 0) + + # pruneheight should be greater or equal to 0 + assert_greater_than_or_equal(res['pruneheight'], 0) + + # check other pruning fields given that prune=1 + assert res['pruned'] + assert not res['automatic_pruning'] + + self.restart_node(0, ['-stopatheight=207']) + res = self.nodes[0].getblockchaininfo() + # should have exact keys + assert_equal(sorted(res.keys()), keys) + + self.restart_node(0, ['-stopatheight=207', '-prune=550']) + res = self.nodes[0].getblockchaininfo() + # result should have these additional pruning keys if prune=550 + assert_equal(sorted(res.keys()), sorted(['pruneheight', 'automatic_pruning', 'prune_target_size'] + keys)) + + # check related fields + assert res['pruned'] + assert_equal(res['pruneheight'], 0) + assert res['automatic_pruning'] + assert_equal(res['prune_target_size'], 576716800) + assert_greater_than(res['size_on_disk'], 0) + def _test_getchaintxstats(self): chaintxstats = self.nodes[0].getchaintxstats(1) # 200 txs plus genesis tx @@ -56,6 +105,28 @@ class BlockchainTest(BitcoinTestFramework): # we have to round because of binary math assert_equal(round(chaintxstats['txrate'] * 600, 10), Decimal(1)) + b1 = self.nodes[0].getblock(self.nodes[0].getblockhash(1)) + b200 = self.nodes[0].getblock(self.nodes[0].getblockhash(200)) + time_diff = b200['mediantime'] - b1['mediantime'] + + chaintxstats = self.nodes[0].getchaintxstats() + assert_equal(chaintxstats['time'], b200['time']) + assert_equal(chaintxstats['txcount'], 201) + assert_equal(chaintxstats['window_block_count'], 199) + assert_equal(chaintxstats['window_tx_count'], 199) + assert_equal(chaintxstats['window_interval'], time_diff) + assert_equal(round(chaintxstats['txrate'] * time_diff, 10), Decimal(199)) + + chaintxstats = self.nodes[0].getchaintxstats(blockhash=b1['hash']) + assert_equal(chaintxstats['time'], b1['time']) + assert_equal(chaintxstats['txcount'], 2) + assert_equal(chaintxstats['window_block_count'], 0) + assert('window_tx_count' not in chaintxstats) + assert('window_interval' not in chaintxstats) + assert('txrate' not in chaintxstats) + + assert_raises_rpc_error(-8, "Invalid block count: should be between 0 and the block's height - 1", self.nodes[0].getchaintxstats, 201) + def _test_gettxoutsetinfo(self): node = self.nodes[0] res = node.gettxoutsetinfo() @@ -100,7 +171,7 @@ class BlockchainTest(BitcoinTestFramework): def _test_getblockheader(self): node = self.nodes[0] - assert_raises_jsonrpc(-5, "Block not found", + assert_raises_rpc_error(-5, "Block not found", node.getblockheader, "nonsense") besthash = node.getbestblockhash() @@ -145,8 +216,8 @@ class BlockchainTest(BitcoinTestFramework): except (ConnectionError, http.client.BadStatusLine): pass # The node already shut down before response self.log.debug('Node should stop at this height...') - self.nodes[0].process.wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) - self.nodes[0] = self.start_node(0, self.options.tmpdir) + self.nodes[0].wait_until_stopped() + self.start_node(0) assert_equal(self.nodes[0].getblockcount(), 207) diff --git a/test/functional/bumpfee.py b/test/functional/bumpfee.py index 9633ffdebb..008e83d5b2 100755 --- a/test/functional/bumpfee.py +++ b/test/functional/bumpfee.py @@ -30,25 +30,21 @@ WALLET_PASSPHRASE_TIMEOUT = 3600 class BumpFeeTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True + self.extra_args = [["-prematurewitness", "-walletprematurewitness", "-walletrbf={}".format(i)] + for i in range(self.num_nodes)] - def setup_network(self, split=False): - extra_args = [["-prematurewitness", "-walletprematurewitness", "-walletrbf={}".format(i)] - for i in range(self.num_nodes)] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args) - + def run_test(self): # Encrypt wallet for test_locked_wallet_fails test self.nodes[1].node_encrypt_wallet(WALLET_PASSPHRASE) - self.nodes[1] = self.start_node(1, self.options.tmpdir, extra_args[1]) + self.start_node(1) self.nodes[1].walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) connect_nodes_bi(self.nodes, 0, 1) self.sync_all() - def run_test(self): peer_node, rbf_node = self.nodes rbf_node_address = rbf_node.getnewaddress() @@ -137,7 +133,7 @@ def test_segwit_bumpfee_succeeds(rbf_node, dest_address): def test_nonrbf_bumpfee_fails(peer_node, dest_address): # cannot replace a non RBF transaction (from node which did not enable RBF) not_rbfid = peer_node.sendtoaddress(dest_address, Decimal("0.00090000")) - assert_raises_jsonrpc(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid) + assert_raises_rpc_error(-4, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid) def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address): @@ -157,7 +153,7 @@ def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address): signedtx = rbf_node.signrawtransaction(rawtx) signedtx = peer_node.signrawtransaction(signedtx["hex"]) rbfid = rbf_node.sendrawtransaction(signedtx["hex"]) - assert_raises_jsonrpc(-4, "Transaction contains inputs that don't belong to this wallet", + assert_raises_rpc_error(-4, "Transaction contains inputs that don't belong to this wallet", rbf_node.bumpfee, rbfid) @@ -167,8 +163,8 @@ def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address) parent_id = spend_one_input(rbf_node, rbf_node_address) tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000}) tx = rbf_node.signrawtransaction(tx) - txid = rbf_node.sendrawtransaction(tx["hex"]) - assert_raises_jsonrpc(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id) + rbf_node.sendrawtransaction(tx["hex"]) + assert_raises_rpc_error(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id) def test_small_output_fails(rbf_node, dest_address): @@ -177,7 +173,7 @@ def test_small_output_fails(rbf_node, dest_address): rbf_node.bumpfee(rbfid, {"totalFee": 50000}) rbfid = spend_one_input(rbf_node, dest_address) - assert_raises_jsonrpc(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 50001}) + assert_raises_rpc_error(-4, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 50001}) def test_dust_to_fee(rbf_node, dest_address): @@ -209,7 +205,7 @@ def test_rebumping(rbf_node, dest_address): # check that re-bumping the original tx fails, but bumping the bumper succeeds rbfid = spend_one_input(rbf_node, dest_address) bumped = rbf_node.bumpfee(rbfid, {"totalFee": 2000}) - assert_raises_jsonrpc(-4, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 3000}) + assert_raises_rpc_error(-4, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 3000}) rbf_node.bumpfee(bumped["txid"], {"totalFee": 3000}) @@ -217,7 +213,7 @@ def test_rebumping_not_replaceable(rbf_node, dest_address): # check that re-bumping a non-replaceable bump tx fails rbfid = spend_one_input(rbf_node, dest_address) bumped = rbf_node.bumpfee(rbfid, {"totalFee": 10000, "replaceable": False}) - assert_raises_jsonrpc(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"], + assert_raises_rpc_error(-4, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"], {"totalFee": 20000}) @@ -268,7 +264,7 @@ def test_bumpfee_metadata(rbf_node, dest_address): def test_locked_wallet_fails(rbf_node, dest_address): rbfid = spend_one_input(rbf_node, dest_address) rbf_node.walletlock() - assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first.", + assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first.", rbf_node.bumpfee, rbfid) diff --git a/test/functional/create_cache.py b/test/functional/create_cache.py index 39c4c0f47e..7d4d1a529b 100755 --- a/test/functional/create_cache.py +++ b/test/functional/create_cache.py @@ -12,13 +12,10 @@ tests are being run in parallel. from test_framework.test_framework import BitcoinTestFramework class CreateCache(BitcoinTestFramework): + # Test network and test nodes are not required: - def __init__(self): - super().__init__() - - # Test network and test nodes are not required: + def set_test_params(self): self.num_nodes = 0 - self.nodes = [] def setup_network(self): pass diff --git a/test/functional/dbcrash.py b/test/functional/dbcrash.py index a7fcc411c3..24b9765b4e 100755 --- a/test/functional/dbcrash.py +++ b/test/functional/dbcrash.py @@ -43,8 +43,7 @@ except AttributeError: pass class ChainstateWriteCrashTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 4 self.setup_clean_chain = False @@ -65,7 +64,8 @@ class ChainstateWriteCrashTest(BitcoinTestFramework): def setup_network(self): # Need a bit of extra time for the nodes to start up for this test - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args, timewait=90) + self.add_nodes(self.num_nodes, extra_args=self.extra_args, timewait=90) + self.start_nodes() # Leave them unconnected, we'll use submitblock directly in this test def restart_node(self, node_index, expected_tip): @@ -78,7 +78,7 @@ class ChainstateWriteCrashTest(BitcoinTestFramework): while time.time() - time_start < 120: try: # Any of these RPC calls could throw due to node crash - self.nodes[node_index] = self.start_node(node_index, self.options.tmpdir, self.extra_args[node_index], timewait=90) + self.start_node(node_index) self.nodes[node_index].waitforblock(expected_tip) utxo_hash = self.nodes[node_index].gettxoutsetinfo()['hash_serialized_2'] return utxo_hash diff --git a/test/functional/decodescript.py b/test/functional/decodescript.py index 21a9f1223f..6611da8831 100755 --- a/test/functional/decodescript.py +++ b/test/functional/decodescript.py @@ -10,9 +10,7 @@ from test_framework.mininode import * from io import BytesIO class DecodeScriptTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/deprecated_rpc.py b/test/functional/deprecated_rpc.py new file mode 100755 index 0000000000..19fd24edb9 --- /dev/null +++ b/test/functional/deprecated_rpc.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test deprecation of RPC calls.""" +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_raises_rpc_error + +class DeprecatedRpcTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 2 + self.setup_clean_chain = True + self.extra_args = [[], ["-deprecatedrpc=estimatefee"]] + + def run_test(self): + self.log.info("estimatefee: Shows deprecated message") + assert_raises_rpc_error(-32, 'estimatefee is deprecated', self.nodes[0].estimatefee, 1) + + self.log.info("Using -deprecatedrpc=estimatefee bypasses the error") + self.nodes[1].estimatefee(1) + +if __name__ == '__main__': + DeprecatedRpcTest().main() diff --git a/test/functional/disablewallet.py b/test/functional/disablewallet.py index d344513414..c75ef9b9f1 100755 --- a/test/functional/disablewallet.py +++ b/test/functional/disablewallet.py @@ -11,18 +11,15 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * - class DisableWalletTest (BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [["-disablewallet"]] def run_test (self): # Make sure wallet is really disabled - assert_raises_jsonrpc(-32601, 'Method not found', self.nodes[0].getwalletinfo) + assert_raises_rpc_error(-32601, 'Method not found', self.nodes[0].getwalletinfo) x = self.nodes[0].validateaddress('3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy') assert(x['isvalid'] == False) x = self.nodes[0].validateaddress('mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ') @@ -31,7 +28,7 @@ class DisableWalletTest (BitcoinTestFramework): # Checking mining to an address without a wallet. Generating to a valid address should succeed # but generating to an invalid address will fail. self.nodes[0].generatetoaddress(1, 'mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ') - assert_raises_jsonrpc(-5, "Invalid address", self.nodes[0].generatetoaddress, 1, '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy') + assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].generatetoaddress, 1, '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy') if __name__ == '__main__': DisableWalletTest ().main () diff --git a/test/functional/disconnect_ban.py b/test/functional/disconnect_ban.py index 19723226d3..59655d37fb 100755 --- a/test/functional/disconnect_ban.py +++ b/test/functional/disconnect_ban.py @@ -8,17 +8,14 @@ import time from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, - assert_raises_jsonrpc, + assert_raises_rpc_error, connect_nodes_bi, wait_until, ) class DisconnectBanTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False def run_test(self): self.log.info("Test setban and listbanned RPCs") @@ -37,14 +34,14 @@ class DisconnectBanTest(BitcoinTestFramework): self.log.info("setban: fail to ban an already banned subnet") assert_equal(len(self.nodes[1].listbanned()), 1) - assert_raises_jsonrpc(-23, "IP/Subnet already banned", self.nodes[1].setban, "127.0.0.1", "add") + assert_raises_rpc_error(-23, "IP/Subnet already banned", self.nodes[1].setban, "127.0.0.1", "add") self.log.info("setban: fail to ban an invalid subnet") - assert_raises_jsonrpc(-30, "Error: Invalid IP/Subnet", self.nodes[1].setban, "127.0.0.1/42", "add") + assert_raises_rpc_error(-30, "Error: Invalid IP/Subnet", self.nodes[1].setban, "127.0.0.1/42", "add") assert_equal(len(self.nodes[1].listbanned()), 1) # still only one banned ip because 127.0.0.1 is within the range of 127.0.0.0/24 self.log.info("setban remove: fail to unban a non-banned subnet") - assert_raises_jsonrpc(-30, "Error: Unban failed", self.nodes[1].setban, "127.0.0.1", "remove") + assert_raises_rpc_error(-30, "Error: Unban failed", self.nodes[1].setban, "127.0.0.1", "remove") assert_equal(len(self.nodes[1].listbanned()), 1) self.log.info("setban remove: successfully unban subnet") @@ -68,8 +65,8 @@ class DisconnectBanTest(BitcoinTestFramework): assert_equal(len(self.nodes[1].listbanned()), 3) self.stop_node(1) + self.start_node(1) - self.nodes[1] = self.start_node(1, self.options.tmpdir) listAfterShutdown = self.nodes[1].listbanned() assert_equal("127.0.0.0/24", listAfterShutdown[0]['address']) assert_equal("127.0.0.0/32", listAfterShutdown[1]['address']) @@ -84,10 +81,10 @@ class DisconnectBanTest(BitcoinTestFramework): self.log.info("disconnectnode: fail to disconnect when calling with address and nodeid") address1 = self.nodes[0].getpeerinfo()[0]['addr'] node1 = self.nodes[0].getpeerinfo()[0]['addr'] - assert_raises_jsonrpc(-32602, "Only one of address and nodeid should be provided.", self.nodes[0].disconnectnode, address=address1, nodeid=node1) + assert_raises_rpc_error(-32602, "Only one of address and nodeid should be provided.", self.nodes[0].disconnectnode, address=address1, nodeid=node1) self.log.info("disconnectnode: fail to disconnect when calling with junk address") - assert_raises_jsonrpc(-29, "Node not found in connected nodes", self.nodes[0].disconnectnode, address="221B Baker Street") + assert_raises_rpc_error(-29, "Node not found in connected nodes", self.nodes[0].disconnectnode, address="221B Baker Street") self.log.info("disconnectnode: successfully disconnect node by address") address1 = self.nodes[0].getpeerinfo()[0]['addr'] diff --git a/test/functional/example_test.py b/test/functional/example_test.py index 4f9e0a7dd2..87d73ad14a 100755 --- a/test/functional/example_test.py +++ b/test/functional/example_test.py @@ -73,21 +73,19 @@ def custom_function(): class ExampleTest(BitcoinTestFramework): # Each functional test is a subclass of the BitcoinTestFramework class. - # Override the __init__(), add_options(), setup_chain(), setup_network() + # Override the set_test_params(), add_options(), setup_chain(), setup_network() # and setup_nodes() methods to customize the test setup as required. - def __init__(self): - """Initialize the test + def set_test_params(self): + """Override test parameters for your individual test. - Call super().__init__() first, and then override any test parameters - for your individual test.""" - super().__init__() + This method must be overridden and num_nodes must be exlicitly set.""" self.setup_clean_chain = True self.num_nodes = 3 # Use self.extra_args to change command-line arguments for the nodes self.extra_args = [[], ["-logips"], []] - # self.log.info("I've finished __init__") # Oops! Can't run self.log before run_test() + # self.log.info("I've finished set_test_params") # Oops! Can't run self.log before run_test() # Use add_options() to add specific command-line options for your test. # In practice this is not used very much, since the tests are mostly written diff --git a/test/functional/forknotify.py b/test/functional/forknotify.py deleted file mode 100755 index 3bcf0a6795..0000000000 --- a/test/functional/forknotify.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python3 -# 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. -"""Test the -alertnotify option.""" -import os -import time - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * - -class ForkNotifyTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.num_nodes = 2 - self.setup_clean_chain = False - - def setup_network(self): - self.nodes = [] - self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt") - with open(self.alert_filename, 'w', encoding='utf8'): - pass # Just open then close to create zero-length file - self.nodes.append(self.start_node(0, self.options.tmpdir, - ["-blockversion=2", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""])) - # Node1 mines block.version=211 blocks - self.nodes.append(self.start_node(1, self.options.tmpdir, - ["-blockversion=211"])) - connect_nodes(self.nodes[1], 0) - - self.sync_all() - - def run_test(self): - # Mine 51 up-version blocks - self.nodes[1].generate(51) - self.sync_all() - # -alertnotify should trigger on the 51'st, - # but mine and sync another to give - # -alertnotify time to write - self.nodes[1].generate(1) - self.sync_all() - - # Give bitcoind 10 seconds to write the alert notification - timeout = 10.0 - while timeout > 0: - if os.path.exists(self.alert_filename) and os.path.getsize(self.alert_filename): - break - time.sleep(0.1) - timeout -= 0.1 - else: - assert False, "-alertnotify did not warn of up-version blocks" - - with open(self.alert_filename, 'r', encoding='utf8') as f: - alert_text = f.read() - - # Mine more up-version blocks, should not get more alerts: - self.nodes[1].generate(1) - self.sync_all() - self.nodes[1].generate(1) - self.sync_all() - - with open(self.alert_filename, 'r', encoding='utf8') as f: - alert_text2 = f.read() - - if alert_text != alert_text2: - raise AssertionError("-alertnotify excessive warning of up-version blocks") - -if __name__ == '__main__': - ForkNotifyTest().main() diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py index f2f4efcf28..d446f56d0e 100755 --- a/test/functional/fundrawtransaction.py +++ b/test/functional/fundrawtransaction.py @@ -4,7 +4,7 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the fundrawtransaction RPC.""" -from test_framework.test_framework import BitcoinTestFramework, BITCOIND_PROC_WAIT_TIMEOUT +from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -14,13 +14,10 @@ def get_unspent(listunspent, amount): return utx raise AssertionError('Could not find unspent with amount={}'.format(amount)) - class RawTransactionsTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = True + def set_test_params(self): self.num_nodes = 4 + self.setup_clean_chain = True def setup_network(self, split=False): self.setup_nodes() @@ -182,7 +179,7 @@ class RawTransactionsTest(BitcoinTestFramework): dec_tx = self.nodes[2].decoderawtransaction(rawtx) assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) - assert_raises_jsonrpc(-3, "Unexpected key foo", self.nodes[2].fundrawtransaction, rawtx, {'foo':'bar'}) + assert_raises_rpc_error(-3, "Unexpected key foo", self.nodes[2].fundrawtransaction, rawtx, {'foo':'bar'}) ############################################################ # test a fundrawtransaction with an invalid change address # @@ -195,7 +192,7 @@ class RawTransactionsTest(BitcoinTestFramework): dec_tx = self.nodes[2].decoderawtransaction(rawtx) assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) - assert_raises_jsonrpc(-5, "changeAddress must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'}) + assert_raises_rpc_error(-5, "changeAddress must be a valid bitcoin address", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':'foobar'}) ############################################################ # test a fundrawtransaction with a provided change address # @@ -209,7 +206,7 @@ class RawTransactionsTest(BitcoinTestFramework): assert_equal(utx['txid'], dec_tx['vin'][0]['txid']) change = self.nodes[2].getnewaddress() - assert_raises_jsonrpc(-8, "changePosition out of bounds", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':change, 'changePosition':2}) + assert_raises_rpc_error(-8, "changePosition out of bounds", self.nodes[2].fundrawtransaction, rawtx, {'changeAddress':change, 'changePosition':2}) rawtxfund = self.nodes[2].fundrawtransaction(rawtx, {'changeAddress': change, 'changePosition': 0}) dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) out = dec_tx['vout'][0] @@ -312,13 +309,12 @@ class RawTransactionsTest(BitcoinTestFramework): ############################################## # test a fundrawtransaction with invalid vin # ############################################## - listunspent = self.nodes[2].listunspent() inputs = [ {'txid' : "1c7f966dab21119bac53213a2bc7532bff1fa844c124fd750a7d0b1332440bd1", 'vout' : 0} ] #invalid vin! outputs = { self.nodes[0].getnewaddress() : 1.0} rawtx = self.nodes[2].createrawtransaction(inputs, outputs) dec_tx = self.nodes[2].decoderawtransaction(rawtx) - assert_raises_jsonrpc(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx) + assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].fundrawtransaction, rawtx) ############################################################ #compare fee of a standard pubkeyhash transaction @@ -449,11 +445,11 @@ class RawTransactionsTest(BitcoinTestFramework): ############################################################ # locked wallet test self.stop_node(0) + self.nodes[1].node_encrypt_wallet("test") self.stop_node(2) self.stop_node(3) - self.nodes[1].node_encrypt_wallet("test") - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir) + self.start_nodes() # This test is not meant to test fee estimation and we'd like # to be sure all txs are sent at a consistent desired feerate for node in self.nodes: @@ -473,14 +469,14 @@ class RawTransactionsTest(BitcoinTestFramework): rawtx = self.nodes[1].createrawtransaction(inputs, outputs) # fund a transaction that requires a new key for the change output # creating the key must be impossible because the wallet is locked - assert_raises_jsonrpc(-4, "Keypool ran out, please call keypoolrefill first", self.nodes[1].fundrawtransaction, rawtx) + assert_raises_rpc_error(-4, "Keypool ran out, please call keypoolrefill first", self.nodes[1].fundrawtransaction, rawtx) #refill the keypool self.nodes[1].walletpassphrase("test", 100) self.nodes[1].keypoolrefill(8) #need to refill the keypool to get an internal change address self.nodes[1].walletlock() - assert_raises_jsonrpc(-13, "walletpassphrase", self.nodes[1].sendtoaddress, self.nodes[0].getnewaddress(), 1.2) + assert_raises_rpc_error(-13, "walletpassphrase", self.nodes[1].sendtoaddress, self.nodes[0].getnewaddress(), 1.2) oldBalance = self.nodes[0].getbalance() diff --git a/test/functional/getblocktemplate_longpoll.py b/test/functional/getblocktemplate_longpoll.py index cca30e2688..89768bd2fb 100755 --- a/test/functional/getblocktemplate_longpoll.py +++ b/test/functional/getblocktemplate_longpoll.py @@ -23,10 +23,8 @@ class LongpollThread(threading.Thread): self.node.getblocktemplate({'longpollid':self.longpollid}) class GetBlockTemplateLPTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - self.num_nodes = 4 - self.setup_clean_chain = False + def set_test_params(self): + self.num_nodes = 2 def run_test(self): self.log.info("Warning: this test will take about 70 seconds in the best case. Be patient.") diff --git a/test/functional/getchaintips.py b/test/functional/getchaintips.py index 15f96c565f..21b67bfc64 100755 --- a/test/functional/getchaintips.py +++ b/test/functional/getchaintips.py @@ -14,13 +14,10 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal class GetChainTipsTest (BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 4 - self.setup_clean_chain = False def run_test (self): - tips = self.nodes[0].getchaintips () assert_equal (len (tips), 1) assert_equal (tips[0]['branchlen'], 0) diff --git a/test/functional/httpbasics.py b/test/functional/httpbasics.py index 4b32e8d9ca..c7682cb49d 100755 --- a/test/functional/httpbasics.py +++ b/test/functional/httpbasics.py @@ -11,10 +11,8 @@ import http.client import urllib.parse class HTTPBasicsTest (BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 3 - self.setup_clean_chain = False def setup_network(self): self.setup_nodes() diff --git a/test/functional/import-rescan.py b/test/functional/import-rescan.py index 4fc5078217..6807fa6696 100755 --- a/test/functional/import-rescan.py +++ b/test/functional/import-rescan.py @@ -19,9 +19,8 @@ importing nodes pick up the new transactions regardless of whether rescans happened previously. """ -from test_framework.authproxy import JSONRPCException from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import (connect_nodes, sync_blocks, assert_equal, set_node_times) +from test_framework.util import (assert_raises_rpc_error, connect_nodes, sync_blocks, assert_equal, set_node_times) import collections import enum @@ -35,21 +34,26 @@ Rescan = enum.Enum("Rescan", "no yes late_timestamp") class Variant(collections.namedtuple("Variant", "call data rescan prune")): """Helper for importing one key and verifying scanned transactions.""" + def try_rpc(self, func, *args, **kwargs): + if self.expect_disabled: + assert_raises_rpc_error(-4, "Rescan is disabled in pruned mode", func, *args, **kwargs) + else: + return func(*args, **kwargs) + def do_import(self, timestamp): """Call one key import RPC.""" if self.call == Call.single: if self.data == Data.address: - response, error = try_rpc(self.node.importaddress, self.address["address"], self.label, - self.rescan == Rescan.yes) + response = self.try_rpc(self.node.importaddress, self.address["address"], self.label, + self.rescan == Rescan.yes) elif self.data == Data.pub: - response, error = try_rpc(self.node.importpubkey, self.address["pubkey"], self.label, - self.rescan == Rescan.yes) + response = self.try_rpc(self.node.importpubkey, self.address["pubkey"], self.label, + self.rescan == Rescan.yes) elif self.data == Data.priv: - response, error = try_rpc(self.node.importprivkey, self.key, self.label, self.rescan == Rescan.yes) + response = self.try_rpc(self.node.importprivkey, self.key, self.label, self.rescan == Rescan.yes) assert_equal(response, None) - assert_equal(error, {'message': 'Rescan is disabled in pruned mode', - 'code': -4} if self.expect_disabled else None) + elif self.call == Call.multi: response = self.node.importmulti([{ "scriptPubKey": { @@ -111,8 +115,7 @@ TIMESTAMP_WINDOW = 2 * 60 * 60 class ImportRescanTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 + len(IMPORT_NODES) def setup_network(self): @@ -121,7 +124,8 @@ class ImportRescanTest(BitcoinTestFramework): if import_node.prune: extra_args[i] += ["-prune=1"] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args) + self.add_nodes(self.num_nodes, extra_args) + self.start_nodes() for i in range(1, self.num_nodes): connect_nodes(self.nodes[i], 0) @@ -161,7 +165,6 @@ class ImportRescanTest(BitcoinTestFramework): variant.check() # Create new transactions sending to each address. - fee = self.nodes[0].getnetworkinfo()["relayfee"] for i, variant in enumerate(IMPORT_VARIANTS): variant.sent_amount = 10 - (2 * i + 1) / 8.0 variant.sent_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.sent_amount) @@ -180,13 +183,5 @@ class ImportRescanTest(BitcoinTestFramework): else: variant.check() - -def try_rpc(func, *args, **kwargs): - try: - return func(*args, **kwargs), None - except JSONRPCException as e: - return None, e.error - - if __name__ == "__main__": ImportRescanTest().main() diff --git a/test/functional/importmulti.py b/test/functional/importmulti.py index e83e85de13..c1a42870ec 100755 --- a/test/functional/importmulti.py +++ b/test/functional/importmulti.py @@ -7,8 +7,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class ImportMultiTest (BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True @@ -21,16 +20,7 @@ class ImportMultiTest (BitcoinTestFramework): self.nodes[1].generate(1) timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime'] - # keyword definition - PRIV_KEY = 'privkey' - PUB_KEY = 'pubkey' - ADDRESS_KEY = 'address' - SCRIPT_KEY = 'script' - - node0_address1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - node0_address2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - node0_address3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) #Check only one address assert_equal(node0_address1['ismine'], True) @@ -230,7 +220,6 @@ class ImportMultiTest (BitcoinTestFramework): transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) self.nodes[1].generate(1) timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime'] - transaction = self.nodes[1].gettransaction(transactionid) self.log.info("Should import a p2sh") result = self.nodes[1].importmulti([{ @@ -258,7 +247,6 @@ class ImportMultiTest (BitcoinTestFramework): transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) self.nodes[1].generate(1) timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime'] - transaction = self.nodes[1].gettransaction(transactionid) self.log.info("Should import a p2sh with respective redeem script") result = self.nodes[1].importmulti([{ @@ -286,7 +274,6 @@ class ImportMultiTest (BitcoinTestFramework): transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) self.nodes[1].generate(1) timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime'] - transaction = self.nodes[1].gettransaction(transactionid) self.log.info("Should import a p2sh with respective redeem script and private keys") result = self.nodes[1].importmulti([{ @@ -314,7 +301,6 @@ class ImportMultiTest (BitcoinTestFramework): transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) self.nodes[1].generate(1) timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime'] - transaction = self.nodes[1].gettransaction(transactionid) self.log.info("Should import a p2sh with respective redeem script and private keys") result = self.nodes[1].importmulti([{ @@ -429,7 +415,7 @@ class ImportMultiTest (BitcoinTestFramework): # restart nodes to check for proper serialization/deserialization of watch only address self.stop_nodes() - self.nodes = self.start_nodes(2, self.options.tmpdir) + self.start_nodes() address_assert = self.nodes[1].validateaddress(watchonly_address) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) @@ -437,11 +423,11 @@ class ImportMultiTest (BitcoinTestFramework): # Bad or missing timestamps self.log.info("Should throw on invalid or missing timestamp values") - assert_raises_message(JSONRPCException, 'Missing required timestamp field for key', + assert_raises_rpc_error(-3, 'Missing required timestamp field for key', self.nodes[1].importmulti, [{ "scriptPubKey": address['scriptPubKey'], }]) - assert_raises_message(JSONRPCException, 'Expected number or "now" timestamp value for key. got type string', + assert_raises_rpc_error(-3, 'Expected number or "now" timestamp value for key. got type string', self.nodes[1].importmulti, [{ "scriptPubKey": address['scriptPubKey'], "timestamp": "", diff --git a/test/functional/importprunedfunds.py b/test/functional/importprunedfunds.py index 94753fe431..068052409a 100755 --- a/test/functional/importprunedfunds.py +++ b/test/functional/importprunedfunds.py @@ -6,11 +6,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * - class ImportPrunedFundsTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 @@ -24,7 +21,6 @@ class ImportPrunedFundsTest(BitcoinTestFramework): address1 = self.nodes[0].getnewaddress() # pubkey address2 = self.nodes[0].getnewaddress() - address2_pubkey = self.nodes[0].validateaddress(address2)['pubkey'] # Using pubkey # privkey address3 = self.nodes[0].getnewaddress() address3_privkey = self.nodes[0].dumpprivkey(address3) # Using privkey @@ -70,20 +66,20 @@ class ImportPrunedFundsTest(BitcoinTestFramework): self.sync_all() #Import with no affiliated address - assert_raises_jsonrpc(-5, "No addresses", self.nodes[1].importprunedfunds, rawtxn1, proof1) + assert_raises_rpc_error(-5, "No addresses", self.nodes[1].importprunedfunds, rawtxn1, proof1) balance1 = self.nodes[1].getbalance("", 0, True) assert_equal(balance1, Decimal(0)) #Import with affiliated address with no rescan self.nodes[1].importaddress(address2, "add2", False) - result2 = self.nodes[1].importprunedfunds(rawtxn2, proof2) + self.nodes[1].importprunedfunds(rawtxn2, proof2) balance2 = self.nodes[1].getbalance("add2", 0, True) assert_equal(balance2, Decimal('0.05')) #Import with private key with no rescan - self.nodes[1].importprivkey(address3_privkey, "add3", False) - result3 = self.nodes[1].importprunedfunds(rawtxn3, proof3) + self.nodes[1].importprivkey(privkey=address3_privkey, label="add3", rescan=False) + self.nodes[1].importprunedfunds(rawtxn3, proof3) balance3 = self.nodes[1].getbalance("add3", 0, False) assert_equal(balance3, Decimal('0.025')) balance3 = self.nodes[1].getbalance("*", 0, True) @@ -101,7 +97,7 @@ class ImportPrunedFundsTest(BitcoinTestFramework): assert_equal(address_info['ismine'], True) #Remove transactions - assert_raises_jsonrpc(-8, "Transaction does not exist in wallet.", self.nodes[1].removeprunedfunds, txnid1) + assert_raises_rpc_error(-8, "Transaction does not exist in wallet.", self.nodes[1].removeprunedfunds, txnid1) balance1 = self.nodes[1].getbalance("*", 0, True) assert_equal(balance1, Decimal('0.075')) diff --git a/test/functional/invalidateblock.py b/test/functional/invalidateblock.py index c499d57b90..dd3daf1e07 100755 --- a/test/functional/invalidateblock.py +++ b/test/functional/invalidateblock.py @@ -8,9 +8,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class InvalidateTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 diff --git a/test/functional/invalidblockrequest.py b/test/functional/invalidblockrequest.py index eabc0db8df..9f44b44927 100755 --- a/test/functional/invalidblockrequest.py +++ b/test/functional/invalidblockrequest.py @@ -23,9 +23,9 @@ class InvalidBlockRequestTest(ComparisonTestFramework): ''' Can either run this test as 1 node with expected answers, or two and compare them. Change the "outcome" variable from each TestInstance object to only do the comparison. ''' - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 + self.setup_clean_chain = True def run_test(self): test = TestManager(self, self.options.tmpdir) diff --git a/test/functional/invalidtxrequest.py b/test/functional/invalidtxrequest.py index a9ac231f09..a22bd8f8cd 100755 --- a/test/functional/invalidtxrequest.py +++ b/test/functional/invalidtxrequest.py @@ -19,9 +19,9 @@ class InvalidTxRequestTest(ComparisonTestFramework): ''' Can either run this test as 1 node with expected answers, or two and compare them. Change the "outcome" variable from each TestInstance object to only do the comparison. ''' - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 + self.setup_clean_chain = True def run_test(self): test = TestManager(self, self.options.tmpdir) diff --git a/test/functional/keypool-topup.py b/test/functional/keypool-topup.py index da29f697e3..160a0f7ae5 100755 --- a/test/functional/keypool-topup.py +++ b/test/functional/keypool-topup.py @@ -20,11 +20,10 @@ from test_framework.util import ( ) class KeypoolRestoreTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 - self.extra_args = [['-usehd=0'], ['-usehd=1', '-keypool=100', '-keypoolmin=20']] + self.extra_args = [[], ['-keypool=100', '-keypoolmin=20']] def run_test(self): self.tmpdir = self.options.tmpdir @@ -35,7 +34,7 @@ class KeypoolRestoreTest(BitcoinTestFramework): self.stop_node(1) shutil.copyfile(self.tmpdir + "/node1/regtest/wallet.dat", self.tmpdir + "/wallet.bak") - self.nodes[1] = self.start_node(1, self.tmpdir, self.extra_args[1]) + self.start_node(1, self.extra_args[1]) connect_nodes_bi(self.nodes, 0, 1) self.log.info("Generate keys for wallet") @@ -61,7 +60,7 @@ class KeypoolRestoreTest(BitcoinTestFramework): self.log.info("Verify keypool is restored and balance is correct") - self.nodes[1] = self.start_node(1, self.tmpdir, self.extra_args[1]) + self.start_node(1, self.extra_args[1]) connect_nodes_bi(self.nodes, 0, 1) self.sync_all() diff --git a/test/functional/keypool.py b/test/functional/keypool.py index 3e7bb0ee07..f2701c36bd 100755 --- a/test/functional/keypool.py +++ b/test/functional/keypool.py @@ -8,6 +8,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class KeyPoolTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 def run_test(self): nodes = self.nodes @@ -19,14 +21,14 @@ class KeyPoolTest(BitcoinTestFramework): # Encrypt wallet and wait to terminate nodes[0].node_encrypt_wallet('test') # Restart node 0 - nodes[0] = self.start_node(0, self.options.tmpdir) + self.start_node(0) # Keep creating keys addr = nodes[0].getnewaddress() addr_data = nodes[0].validateaddress(addr) wallet_info = nodes[0].getwalletinfo() assert(addr_before_encrypting_data['hdmasterkeyid'] != wallet_info['hdmasterkeyid']) assert(addr_data['hdmasterkeyid'] == wallet_info['hdmasterkeyid']) - assert_raises_jsonrpc(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress) + assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress) # put six (plus 2) new keys in the keypool (100% external-, +100% internal-keys, 1 in min) nodes[0].walletpassphrase('test', 12000) @@ -45,7 +47,7 @@ class KeyPoolTest(BitcoinTestFramework): nodes[0].getrawchangeaddress() addr = set() # the next one should fail - assert_raises_jsonrpc(-12, "Keypool ran out", nodes[0].getrawchangeaddress) + assert_raises_rpc_error(-12, "Keypool ran out", nodes[0].getrawchangeaddress) # drain the external keys addr.add(nodes[0].getnewaddress()) @@ -56,7 +58,7 @@ class KeyPoolTest(BitcoinTestFramework): addr.add(nodes[0].getnewaddress()) assert(len(addr) == 6) # the next one should fail - assert_raises_jsonrpc(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress) + assert_raises_rpc_error(-12, "Error: Keypool ran out, please call keypoolrefill first", nodes[0].getnewaddress) # refill keypool with three new addresses nodes[0].walletpassphrase('test', 1) @@ -70,7 +72,7 @@ class KeyPoolTest(BitcoinTestFramework): nodes[0].generate(1) nodes[0].generate(1) nodes[0].generate(1) - assert_raises_jsonrpc(-12, "Keypool ran out", nodes[0].generate, 1) + assert_raises_rpc_error(-12, "Keypool ran out", nodes[0].generate, 1) nodes[0].walletpassphrase('test', 100) nodes[0].keypoolrefill(100) @@ -78,10 +80,5 @@ class KeyPoolTest(BitcoinTestFramework): assert_equal(wi['keypoolsize_hd_internal'], 100) assert_equal(wi['keypoolsize'], 100) - def __init__(self): - super().__init__() - self.setup_clean_chain = False - self.num_nodes = 1 - if __name__ == '__main__': KeyPoolTest().main() diff --git a/test/functional/listsinceblock.py b/test/functional/listsinceblock.py index ce2d556ef0..6f428388ec 100755 --- a/test/functional/listsinceblock.py +++ b/test/functional/listsinceblock.py @@ -8,11 +8,9 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal class ListSinceBlockTest (BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = True + def set_test_params(self): self.num_nodes = 4 + self.setup_clean_chain = True def run_test(self): self.nodes[2].generate(101) diff --git a/test/functional/listtransactions.py b/test/functional/listtransactions.py index f75a8e29cc..e4522cc3b5 100755 --- a/test/functional/listtransactions.py +++ b/test/functional/listtransactions.py @@ -16,15 +16,9 @@ def txFromHex(hexstring): return tx class ListTransactionsTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - self.num_nodes = 4 - self.setup_clean_chain = False - - def setup_nodes(self): - #This test requires mocktime + def set_test_params(self): + self.num_nodes = 2 self.enable_mocktime() - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir) def run_test(self): # Simple send, 0 to 1: diff --git a/test/functional/maxuploadtarget.py b/test/functional/maxuploadtarget.py index 66e5bd29e6..1f402798e7 100755 --- a/test/functional/maxuploadtarget.py +++ b/test/functional/maxuploadtarget.py @@ -31,8 +31,7 @@ class TestNode(NodeConnCB): class MaxUploadTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [["-maxuploadtarget=800", "-blockmaxsize=999000"]] @@ -147,7 +146,7 @@ class MaxUploadTest(BitcoinTestFramework): #stop and start node 0 with 1MB maxuploadtarget, whitelist 127.0.0.1 self.log.info("Restarting nodes with -whitelist=127.0.0.1") self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-whitelist=127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000"]) + self.start_node(0, ["-whitelist=127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000"]) #recreate/reconnect a test node test_nodes = [TestNode()] diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py index 2777291dd0..e24dc5a464 100755 --- a/test/functional/mempool_limit.py +++ b/test/functional/mempool_limit.py @@ -8,9 +8,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class MempoolLimitTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [["-maxmempool=5", "-spendzeroconfchange=0"]] diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py index e225493816..b845c75681 100755 --- a/test/functional/mempool_packages.py +++ b/test/functional/mempool_packages.py @@ -12,10 +12,8 @@ MAX_ANCESTORS = 25 MAX_DESCENDANTS = 25 class MempoolPackagesTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False self.extra_args = [["-maxorphantx=1000"], ["-maxorphantx=1000", "-limitancestorcount=5"]] # Build a transaction that spends parent_txid:vout @@ -117,7 +115,7 @@ class MempoolPackagesTest(BitcoinTestFramework): assert_equal(mempool[x]['descendantfees'], descendant_fees * COIN + 1000) # Adding one more transaction on to the chain should fail. - assert_raises_jsonrpc(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], txid, vout, value, fee, 1) + assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], txid, vout, value, fee, 1) # Check that prioritising a tx before it's added to the mempool works # First clear the mempool by mining a block. @@ -169,7 +167,7 @@ class MempoolPackagesTest(BitcoinTestFramework): # Sending one more chained transaction will fail utxo = transaction_package.pop(0) - assert_raises_jsonrpc(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10) + assert_raises_rpc_error(-26, "too-long-mempool-chain", self.chain_transaction, self.nodes[0], utxo['txid'], utxo['vout'], utxo['amount'], fee, 10) # TODO: check that node1's mempool is as expected @@ -213,7 +211,7 @@ class MempoolPackagesTest(BitcoinTestFramework): value = send_value # Create tx1 - (tx1_id, tx1_value) = self.chain_transaction(self.nodes[0], tx0_id, 0, value, fee, 1) + tx1_id, _ = self.chain_transaction(self.nodes[0], tx0_id, 0, value, fee, 1) # Create tx2-7 vout = 1 diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py index 807edeb7a8..92f66be2ff 100755 --- a/test/functional/mempool_persist.py +++ b/test/functional/mempool_persist.py @@ -28,20 +28,22 @@ Test is as follows: - Restart node0 with -persistmempool. Verify that it has 5 transactions in its mempool. This tests that -persistmempool=0 does not overwrite a previously valid mempool stored on disk. + - Remove node0 mempool.dat and verify savemempool RPC recreates it + and verify that node1 can load it and has 5 transaction in its + mempool. + - Verify that savemempool throws when the RPC is called if + node1 can't write to disk. """ +import os import time from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class MempoolPersistTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - # We need 3 nodes for this test. Node1 does not have a persistent mempool. + def set_test_params(self): self.num_nodes = 3 - self.setup_clean_chain = False self.extra_args = [[], ["-persistmempool=0"], []] def run_test(self): @@ -63,9 +65,8 @@ class MempoolPersistTest(BitcoinTestFramework): self.log.debug("Stop-start node0 and node1. Verify that node0 has the transactions in its mempool and node1 does not.") self.stop_nodes() - self.nodes = [] - self.nodes.append(self.start_node(0, self.options.tmpdir)) - self.nodes.append(self.start_node(1, self.options.tmpdir)) + self.start_node(0) + self.start_node(1) # Give bitcoind a second to reload the mempool time.sleep(1) wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5) @@ -73,17 +74,37 @@ class MempoolPersistTest(BitcoinTestFramework): self.log.debug("Stop-start node0 with -persistmempool=0. Verify that it doesn't load its mempool.dat file.") self.stop_nodes() - self.nodes = [] - self.nodes.append(self.start_node(0, self.options.tmpdir, ["-persistmempool=0"])) + self.start_node(0, extra_args=["-persistmempool=0"]) # Give bitcoind a second to reload the mempool time.sleep(1) assert_equal(len(self.nodes[0].getrawmempool()), 0) self.log.debug("Stop-start node0. Verify that it has the transactions in its mempool.") self.stop_nodes() - self.nodes = [] - self.nodes.append(self.start_node(0, self.options.tmpdir)) + self.start_node(0) wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5) + mempooldat0 = os.path.join(self.options.tmpdir, 'node0', 'regtest', 'mempool.dat') + mempooldat1 = os.path.join(self.options.tmpdir, 'node1', 'regtest', 'mempool.dat') + self.log.debug("Remove the mempool.dat file. Verify that savemempool to disk via RPC re-creates it") + os.remove(mempooldat0) + self.nodes[0].savemempool() + assert os.path.isfile(mempooldat0) + + self.log.debug("Stop nodes, make node1 use mempool.dat from node0. Verify it has 5 transactions") + os.rename(mempooldat0, mempooldat1) + self.stop_nodes() + self.start_node(1, extra_args=[]) + wait_until(lambda: len(self.nodes[1].getrawmempool()) == 5) + + self.log.debug("Prevent bitcoind from writing mempool.dat to disk. Verify that `savemempool` fails") + # to test the exception we are setting bad permissions on a tmp file called mempool.dat.new + # which is an implementation detail that could change and break this test + mempooldotnew1 = mempooldat1 + '.new' + with os.fdopen(os.open(mempooldotnew1, os.O_CREAT, 0o000), 'w'): + pass + assert_raises_rpc_error(-1, "Unable to dump mempool to disk", self.nodes[1].savemempool) + os.remove(mempooldotnew1) + if __name__ == '__main__': MempoolPersistTest().main() diff --git a/test/functional/mempool_reorg.py b/test/functional/mempool_reorg.py index 937bf4bab5..2803371f5b 100755 --- a/test/functional/mempool_reorg.py +++ b/test/functional/mempool_reorg.py @@ -13,10 +13,8 @@ from test_framework.util import * # Create one-input, one-output, no-fee transaction: class MempoolCoinbaseTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False self.extra_args = [["-checkmempool"]] * 2 alert_filename = None # Set by setup_network @@ -52,14 +50,14 @@ class MempoolCoinbaseTest(BitcoinTestFramework): timelock_tx = timelock_tx[:-8] + hex(self.nodes[0].getblockcount() + 2)[2:] + "000000" timelock_tx = self.nodes[0].signrawtransaction(timelock_tx)["hex"] # This will raise an exception because the timelock transaction is too immature to spend - assert_raises_jsonrpc(-26, "non-final", self.nodes[0].sendrawtransaction, timelock_tx) + assert_raises_rpc_error(-26, "non-final", self.nodes[0].sendrawtransaction, timelock_tx) # Broadcast and mine spend_102 and 103: spend_102_id = self.nodes[0].sendrawtransaction(spend_102_raw) spend_103_id = self.nodes[0].sendrawtransaction(spend_103_raw) self.nodes[0].generate(1) # Time-locked transaction is still too immature to spend - assert_raises_jsonrpc(-26,'non-final', self.nodes[0].sendrawtransaction, timelock_tx) + assert_raises_rpc_error(-26,'non-final', self.nodes[0].sendrawtransaction, timelock_tx) # Create 102_1 and 103_1: spend_102_1_raw = create_tx(self.nodes[0], spend_102_id, node1_address, 49.98) diff --git a/test/functional/mempool_resurrect_test.py b/test/functional/mempool_resurrect_test.py index a2f6228df9..1263c9306b 100755 --- a/test/functional/mempool_resurrect_test.py +++ b/test/functional/mempool_resurrect_test.py @@ -9,12 +9,8 @@ from test_framework.util import * # Create one-input, one-output, no-fee transaction: class MempoolCoinbaseTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 - self.setup_clean_chain = False - # Just need one node for this test self.extra_args = [["-checkmempool"]] def run_test(self): diff --git a/test/functional/mempool_spendcoinbase.py b/test/functional/mempool_spendcoinbase.py index 277ea45ad5..6e8a635a76 100755 --- a/test/functional/mempool_spendcoinbase.py +++ b/test/functional/mempool_spendcoinbase.py @@ -17,11 +17,8 @@ from test_framework.util import * # Create one-input, one-output, no-fee transaction: class MempoolSpendCoinbaseTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 - self.setup_clean_chain = False self.extra_args = [["-checkmempool"]] def run_test(self): @@ -39,7 +36,7 @@ class MempoolSpendCoinbaseTest(BitcoinTestFramework): spend_101_id = self.nodes[0].sendrawtransaction(spends_raw[0]) # coinbase at height 102 should be too immature to spend - assert_raises_jsonrpc(-26,"bad-txns-premature-spend-of-coinbase", self.nodes[0].sendrawtransaction, spends_raw[1]) + assert_raises_rpc_error(-26,"bad-txns-premature-spend-of-coinbase", self.nodes[0].sendrawtransaction, spends_raw[1]) # mempool should have just spend_101: assert_equal(self.nodes[0].getrawmempool(), [ spend_101_id ]) diff --git a/test/functional/merkle_blocks.py b/test/functional/merkle_blocks.py index bcc65c8408..b3989a4c54 100755 --- a/test/functional/merkle_blocks.py +++ b/test/functional/merkle_blocks.py @@ -8,11 +8,9 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class MerkleBlockTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = True + def set_test_params(self): self.num_nodes = 4 + self.setup_clean_chain = True # Nodes 0/1 are "wallet" nodes, Nodes 2/3 are used for testing self.extra_args = [[], [], [], ["-txindex"]] @@ -40,7 +38,7 @@ class MerkleBlockTest(BitcoinTestFramework): tx2 = self.nodes[0].createrawtransaction([node0utxos.pop()], {self.nodes[1].getnewaddress(): 49.99}) txid2 = self.nodes[0].sendrawtransaction(self.nodes[0].signrawtransaction(tx2)["hex"]) # This will raise an exception because the transaction is not yet in a block - assert_raises_jsonrpc(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [txid1]) + assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[0].gettxoutproof, [txid1]) self.nodes[0].generate(1) blockhash = self.nodes[0].getblockhash(chain_height + 1) @@ -65,11 +63,11 @@ class MerkleBlockTest(BitcoinTestFramework): txid_unspent = txid1 if txin_spent["txid"] != txid1 else txid2 # We can't find the block from a fully-spent tx - assert_raises_jsonrpc(-5, "Transaction not yet in block", self.nodes[2].gettxoutproof, [txid_spent]) + assert_raises_rpc_error(-5, "Transaction not yet in block", self.nodes[2].gettxoutproof, [txid_spent]) # We can get the proof if we specify the block assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_spent], blockhash)), [txid_spent]) # We can't get the proof if we specify a non-existent block - assert_raises_jsonrpc(-5, "Block not found", self.nodes[2].gettxoutproof, [txid_spent], "00000000000000000000000000000000") + assert_raises_rpc_error(-5, "Block not found", self.nodes[2].gettxoutproof, [txid_spent], "00000000000000000000000000000000") # We can get the proof if the transaction is unspent assert_equal(self.nodes[2].verifytxoutproof(self.nodes[2].gettxoutproof([txid_unspent])), [txid_unspent]) # We can get the proof if we provide a list of transactions and one of them is unspent. The ordering of the list should not matter. @@ -78,7 +76,7 @@ class MerkleBlockTest(BitcoinTestFramework): # We can always get a proof if we have a -txindex assert_equal(self.nodes[2].verifytxoutproof(self.nodes[3].gettxoutproof([txid_spent])), [txid_spent]) # We can't get a proof if we specify transactions from different blocks - assert_raises_jsonrpc(-5, "Not all transactions found in specified or retrieved block", self.nodes[2].gettxoutproof, [txid1, txid3]) + assert_raises_rpc_error(-5, "Not all transactions found in specified or retrieved block", self.nodes[2].gettxoutproof, [txid1, txid3]) if __name__ == '__main__': diff --git a/test/functional/minchainwork.py b/test/functional/minchainwork.py new file mode 100755 index 0000000000..c7579d2548 --- /dev/null +++ b/test/functional/minchainwork.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test logic for setting nMinimumChainWork on command line. + +Nodes don't consider themselves out of "initial block download" until +their active chain has more work than nMinimumChainWork. + +Nodes don't download blocks from a peer unless the peer's best known block +has more work than nMinimumChainWork. + +While in initial block download, nodes won't relay blocks to their peers, so +test that this parameter functions as intended by verifying that block relay +only succeeds past a given node once its nMinimumChainWork has been exceeded. +""" + +import time + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import sync_blocks, connect_nodes, assert_equal + +# 2 hashes required per regtest block (with no difficulty adjustment) +REGTEST_WORK_PER_BLOCK = 2 + +class MinimumChainWorkTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 3 + self.extra_args = [[], ["-minimumchainwork=0x65"], ["-minimumchainwork=0x65"]] + self.node_min_work = [0, 101, 101] + + def setup_network(self): + # This test relies on the chain setup being: + # node0 <- node1 <- node2 + # Before leaving IBD, nodes prefer to download blocks from outbound + # peers, so ensure that we're mining on an outbound peer and testing + # block relay to inbound peers. + self.setup_nodes() + for i in range(self.num_nodes-1): + connect_nodes(self.nodes[i+1], i) + + def run_test(self): + # Start building a chain on node0. node2 shouldn't be able to sync until node1's + # minchainwork is exceeded + starting_chain_work = REGTEST_WORK_PER_BLOCK # Genesis block's work + self.log.info("Testing relay across node %d (minChainWork = %d)", 1, self.node_min_work[1]) + + starting_blockcount = self.nodes[2].getblockcount() + + num_blocks_to_generate = int((self.node_min_work[1] - starting_chain_work) / REGTEST_WORK_PER_BLOCK) + self.log.info("Generating %d blocks on node0", num_blocks_to_generate) + hashes = self.nodes[0].generate(num_blocks_to_generate) + + self.log.info("Node0 current chain work: %s", self.nodes[0].getblockheader(hashes[-1])['chainwork']) + + # Sleep a few seconds and verify that node2 didn't get any new blocks + # or headers. We sleep, rather than sync_blocks(node0, node1) because + # it's reasonable either way for node1 to get the blocks, or not get + # them (since they're below node1's minchainwork). + time.sleep(3) + + self.log.info("Verifying node 2 has no more blocks than before") + self.log.info("Blockcounts: %s", [n.getblockcount() for n in self.nodes]) + # Node2 shouldn't have any new headers yet, because node1 should not + # have relayed anything. + assert_equal(len(self.nodes[2].getchaintips()), 1) + assert_equal(self.nodes[2].getchaintips()[0]['height'], 0) + + assert self.nodes[1].getbestblockhash() != self.nodes[0].getbestblockhash() + assert_equal(self.nodes[2].getblockcount(), starting_blockcount) + + self.log.info("Generating one more block") + self.nodes[0].generate(1) + + self.log.info("Verifying nodes are all synced") + self.sync_all() + self.log.info("Blockcounts: %s", [n.getblockcount() for n in self.nodes]) + +if __name__ == '__main__': + MinimumChainWorkTest().main() diff --git a/test/functional/mining.py b/test/functional/mining.py index dbd4e29eca..9aee06864e 100755 --- a/test/functional/mining.py +++ b/test/functional/mining.py @@ -4,16 +4,18 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test mining RPCs +- getmininginfo - getblocktemplate proposal mode - submitblock""" -from binascii import b2a_hex import copy +from binascii import b2a_hex +from decimal import Decimal from test_framework.blocktools import create_coinbase -from test_framework.test_framework import BitcoinTestFramework from test_framework.mininode import CBlock -from test_framework.util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal, assert_raises_rpc_error def b2x(b): return b2a_hex(b).decode('ascii') @@ -25,14 +27,23 @@ def assert_template(node, block, expect, rehash=True): assert_equal(rsp, expect) class MiningTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = False def run_test(self): node = self.nodes[0] + + self.log.info('getmininginfo') + mining_info = node.getmininginfo() + assert_equal(mining_info['blocks'], 200) + assert_equal(mining_info['chain'], 'regtest') + assert_equal(mining_info['currentblocktx'], 0) + assert_equal(mining_info['currentblockweight'], 0) + assert_equal(mining_info['difficulty'], Decimal('4.656542373906925E-10')) + assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334')) + assert_equal(mining_info['pooledtx'], 0) + # Mine a block to leave initial block download node.generate(1) tmpl = node.getblocktemplate() @@ -57,7 +68,7 @@ class MiningTest(BitcoinTestFramework): assert_template(node, block, None) self.log.info("submitblock: Test block decode failure") - assert_raises_jsonrpc(-22, "Block decode failed", node.submitblock, b2x(block.serialize()[:-15])) + assert_raises_rpc_error(-22, "Block decode failed", node.submitblock, b2x(block.serialize()[:-15])) self.log.info("getblocktemplate: Test bad input hash for coinbase transaction") bad_block = copy.deepcopy(block) @@ -66,10 +77,10 @@ class MiningTest(BitcoinTestFramework): assert_template(node, bad_block, 'bad-cb-missing') self.log.info("submitblock: Test invalid coinbase transaction") - assert_raises_jsonrpc(-22, "Block does not start with a coinbase", node.submitblock, b2x(bad_block.serialize())) + assert_raises_rpc_error(-22, "Block does not start with a coinbase", node.submitblock, b2x(bad_block.serialize())) self.log.info("getblocktemplate: Test truncated final transaction") - assert_raises_jsonrpc(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(block.serialize()[:-1]), 'mode': 'proposal'}) + assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(block.serialize()[:-1]), 'mode': 'proposal'}) self.log.info("getblocktemplate: Test duplicate transaction") bad_block = copy.deepcopy(block) @@ -96,7 +107,7 @@ class MiningTest(BitcoinTestFramework): bad_block_sn = bytearray(block.serialize()) assert_equal(bad_block_sn[TX_COUNT_OFFSET], 1) bad_block_sn[TX_COUNT_OFFSET] += 1 - assert_raises_jsonrpc(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(bad_block_sn), 'mode': 'proposal'}) + assert_raises_rpc_error(-22, "Block decode failed", node.getblocktemplate, {'data': b2x(bad_block_sn), 'mode': 'proposal'}) self.log.info("getblocktemplate: Test bad bits") bad_block = copy.deepcopy(block) diff --git a/test/functional/multi_rpc.py b/test/functional/multi_rpc.py index a30e15ace9..a2b346f274 100755 --- a/test/functional/multi_rpc.py +++ b/test/functional/multi_rpc.py @@ -12,10 +12,7 @@ import http.client import urllib.parse class HTTPBasicsTest (BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = False + def set_test_params(self): self.num_nodes = 2 def setup_chain(self): diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py index fc6e8e325f..6adcc1fd88 100755 --- a/test/functional/multiwallet.py +++ b/test/functional/multiwallet.py @@ -9,31 +9,31 @@ Verify that a bitcoind node can load multiple wallet files import os from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, assert_raises_jsonrpc +from test_framework.util import assert_equal, assert_raises_rpc_error class MultiWalletTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [['-wallet=w1', '-wallet=w2', '-wallet=w3']] def run_test(self): + assert_equal(set(self.nodes[0].listwallets()), {"w1", "w2", "w3"}) + self.stop_node(0) # should not initialize if there are duplicate wallets - self.assert_start_raises_init_error(0, self.options.tmpdir, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.') + self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.') # should not initialize if wallet file is a directory os.mkdir(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w11')) - self.assert_start_raises_init_error(0, self.options.tmpdir, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.') + self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.') # should not initialize if wallet file is a symlink os.symlink(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w1'), os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w12')) - self.assert_start_raises_init_error(0, self.options.tmpdir, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.') + self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.') - self.nodes[0] = self.start_node(0, self.options.tmpdir, self.extra_args[0]) + self.start_node(0, self.extra_args[0]) w1 = self.nodes[0].get_wallet_rpc("w1") w2 = self.nodes[0].get_wallet_rpc("w2") @@ -43,10 +43,10 @@ class MultiWalletTest(BitcoinTestFramework): w1.generate(1) # accessing invalid wallet fails - assert_raises_jsonrpc(-18, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo) + assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo) # accessing wallet RPC without using wallet endpoint fails - assert_raises_jsonrpc(-19, "Wallet file not specified", self.nodes[0].getwalletinfo) + assert_raises_rpc_error(-19, "Wallet file not specified", self.nodes[0].getwalletinfo) # check w1 wallet balance w1_info = w1.getwalletinfo() diff --git a/test/functional/net.py b/test/functional/net.py index 1e63d38035..16e4f6adb4 100755 --- a/test/functional/net.py +++ b/test/functional/net.py @@ -12,15 +12,13 @@ import time from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, - assert_raises_jsonrpc, + assert_raises_rpc_error, connect_nodes_bi, p2p_port, ) - class NetTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 @@ -86,7 +84,7 @@ class NetTest(BitcoinTestFramework): assert_equal(len(added_nodes), 1) assert_equal(added_nodes[0]['addednode'], ip_port) # check that a non-existent node returns an error - assert_raises_jsonrpc(-24, "Node has not been added", + assert_raises_rpc_error(-24, "Node has not been added", self.nodes[0].getaddednodeinfo, '1.1.1.1') def _test_getpeerinfo(self): diff --git a/test/functional/notifications.py b/test/functional/notifications.py new file mode 100755 index 0000000000..c88972ab91 --- /dev/null +++ b/test/functional/notifications.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +# 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. +"""Test the -alertnotify, -blocknotify and -walletnotify options.""" +import os + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal, wait_until, connect_nodes_bi + +class NotificationsTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 2 + self.setup_clean_chain = True + + def setup_network(self): + self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt") + self.block_filename = os.path.join(self.options.tmpdir, "blocks.txt") + self.tx_filename = os.path.join(self.options.tmpdir, "transactions.txt") + + # -alertnotify and -blocknotify on node0, walletnotify on node1 + self.extra_args = [["-blockversion=2", + "-alertnotify=echo %%s >> %s" % self.alert_filename, + "-blocknotify=echo %%s >> %s" % self.block_filename], + ["-blockversion=211", + "-rescan", + "-walletnotify=echo %%s >> %s" % self.tx_filename]] + super().setup_network() + + def run_test(self): + self.log.info("test -blocknotify") + block_count = 10 + blocks = self.nodes[1].generate(block_count) + + # wait at most 10 seconds for expected file size before reading the content + wait_until(lambda: os.path.isfile(self.block_filename) and os.stat(self.block_filename).st_size >= (block_count * 65), timeout=10) + + # file content should equal the generated blocks hashes + with open(self.block_filename, 'r') as f: + assert_equal(sorted(blocks), sorted(f.read().splitlines())) + + self.log.info("test -walletnotify") + # wait at most 10 seconds for expected file size before reading the content + wait_until(lambda: os.path.isfile(self.tx_filename) and os.stat(self.tx_filename).st_size >= (block_count * 65), timeout=10) + + # file content should equal the generated transaction hashes + txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count))) + with open(self.tx_filename, 'r') as f: + assert_equal(sorted(txids_rpc), sorted(f.read().splitlines())) + os.remove(self.tx_filename) + + self.log.info("test -walletnotify after rescan") + # restart node to rescan to force wallet notifications + self.restart_node(1) + connect_nodes_bi(self.nodes, 0, 1) + + wait_until(lambda: os.path.isfile(self.tx_filename) and os.stat(self.tx_filename).st_size >= (block_count * 65), timeout=10) + + # file content should equal the generated transaction hashes + txids_rpc = list(map(lambda t: t['txid'], self.nodes[1].listtransactions("*", block_count))) + with open(self.tx_filename, 'r') as f: + assert_equal(sorted(txids_rpc), sorted(f.read().splitlines())) + + # Mine another 41 up-version blocks. -alertnotify should trigger on the 51st. + self.log.info("test -alertnotify") + self.nodes[1].generate(41) + self.sync_all() + + # Give bitcoind 10 seconds to write the alert notification + wait_until(lambda: os.path.isfile(self.alert_filename) and os.path.getsize(self.alert_filename), timeout=10) + + with open(self.alert_filename, 'r', encoding='utf8') as f: + alert_text = f.read() + + # Mine more up-version blocks, should not get more alerts: + self.nodes[1].generate(2) + self.sync_all() + + with open(self.alert_filename, 'r', encoding='utf8') as f: + alert_text2 = f.read() + + self.log.info("-alertnotify should not continue notifying for more unknown version blocks") + assert_equal(alert_text, alert_text2) + +if __name__ == '__main__': + NotificationsTest().main() diff --git a/test/functional/nulldummy.py b/test/functional/nulldummy.py index 9717add272..91c4550653 100755 --- a/test/functional/nulldummy.py +++ b/test/functional/nulldummy.py @@ -37,8 +37,7 @@ def trueDummy(tx): class NULLDUMMYTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True self.extra_args = [['-whitelist=127.0.0.1', '-walletprematurewitness']] @@ -72,7 +71,7 @@ class NULLDUMMYTest(BitcoinTestFramework): self.log.info("Test 2: Non-NULLDUMMY base multisig transaction should not be accepted to mempool before activation") test2tx = self.create_transaction(self.nodes[0], txid2, self.ms_address, 47) trueDummy(test2tx) - assert_raises_jsonrpc(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test2tx.serialize_with_witness()), True) + assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test2tx.serialize_with_witness()), True) self.log.info("Test 3: Non-NULLDUMMY base transactions should be accepted in a block before activation [431]") self.block_submit(self.nodes[0], [test2tx], False, True) @@ -81,14 +80,14 @@ class NULLDUMMYTest(BitcoinTestFramework): test4tx = self.create_transaction(self.nodes[0], test2tx.hash, self.address, 46) test6txs=[CTransaction(test4tx)] trueDummy(test4tx) - assert_raises_jsonrpc(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test4tx.serialize_with_witness()), True) + assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test4tx.serialize_with_witness()), True) self.block_submit(self.nodes[0], [test4tx]) self.log.info("Test 5: Non-NULLDUMMY P2WSH multisig transaction invalid after activation") test5tx = self.create_transaction(self.nodes[0], txid3, self.wit_address, 48) test6txs.append(CTransaction(test5tx)) test5tx.wit.vtxinwit[0].scriptWitness.stack[0] = b'\x01' - assert_raises_jsonrpc(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test5tx.serialize_with_witness()), True) + assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, bytes_to_hex_str(test5tx.serialize_with_witness()), True) self.block_submit(self.nodes[0], [test5tx], True) self.log.info("Test 6: NULLDUMMY compliant base/witness transactions should be accepted to mempool and in block after activation [432]") diff --git a/test/functional/p2p-acceptblock.py b/test/functional/p2p-acceptblock.py index 322cb767db..5b6429b410 100755 --- a/test/functional/p2p-acceptblock.py +++ b/test/functional/p2p-acceptblock.py @@ -60,8 +60,7 @@ class AcceptBlockTest(BitcoinTestFramework): default=os.getenv("BITCOIND", "bitcoind"), help="bitcoind binary to test") - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 self.extra_args = [[], ["-whitelist=127.0.0.1"]] @@ -172,7 +171,7 @@ class AcceptBlockTest(BitcoinTestFramework): # Blocks 1-287 should be accepted, block 288 should be ignored because it's too far ahead for x in all_blocks[:-1]: self.nodes[0].getblock(x.hash) - assert_raises_jsonrpc(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[-1].hash) + assert_raises_rpc_error(-1, "Block not found on disk", self.nodes[0].getblock, all_blocks[-1].hash) headers_message.headers.pop() # Ensure the last block is unrequested white_node.send_message(headers_message) # Send headers leading to tip diff --git a/test/functional/p2p-compactblocks.py b/test/functional/p2p-compactblocks.py index c5c264765a..94513d3f43 100755 --- a/test/functional/p2p-compactblocks.py +++ b/test/functional/p2p-compactblocks.py @@ -89,8 +89,7 @@ class TestNode(NodeConnCB): wait_until(lambda: not self.connected, timeout=timeout, lock=mininode_lock) class CompactBlocksTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True # Node0 = pre-segwit, node1 = segwit-aware self.num_nodes = 2 @@ -286,7 +285,8 @@ class CompactBlocksTest(BitcoinTestFramework): # Store the raw block in our internal format. block = FromHex(CBlock(), node.getblock("%02x" % block_hash, False)) - [tx.calc_sha256() for tx in block.vtx] + for tx in block.vtx: + tx.calc_sha256() block.rehash() # Wait until the block was announced (via compact blocks) diff --git a/test/functional/p2p-feefilter.py b/test/functional/p2p-feefilter.py index dbccb633a5..8c92365ced 100755 --- a/test/functional/p2p-feefilter.py +++ b/test/functional/p2p-feefilter.py @@ -37,11 +37,8 @@ class TestNode(NodeConnCB): self.txinvs = [] class FeeFilterTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False def run_test(self): node1 = self.nodes[1] diff --git a/test/functional/p2p-fingerprint.py b/test/functional/p2p-fingerprint.py new file mode 100755 index 0000000000..fe60c6cd46 --- /dev/null +++ b/test/functional/p2p-fingerprint.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test various fingerprinting protections. + +If an stale block more than a month old or its header are requested by a peer, +the node should pretend that it does not have it to avoid fingerprinting. +""" + +import time + +from test_framework.blocktools import (create_block, create_coinbase) +from test_framework.mininode import ( + CInv, + NetworkThread, + NodeConn, + NodeConnCB, + msg_headers, + msg_block, + msg_getdata, + msg_getheaders, + wait_until, +) +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + p2p_port, +) + +class P2PFingerprintTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 1 + + # Build a chain of blocks on top of given one + def build_chain(self, nblocks, prev_hash, prev_height, prev_median_time): + blocks = [] + for _ in range(nblocks): + coinbase = create_coinbase(prev_height + 1) + block_time = prev_median_time + 1 + block = create_block(int(prev_hash, 16), coinbase, block_time) + block.solve() + + blocks.append(block) + prev_hash = block.hash + prev_height += 1 + prev_median_time = block_time + return blocks + + # Send a getdata request for a given block hash + def send_block_request(self, block_hash, node): + msg = msg_getdata() + msg.inv.append(CInv(2, block_hash)) # 2 == "Block" + node.send_message(msg) + + # Send a getheaders request for a given single block hash + def send_header_request(self, block_hash, node): + msg = msg_getheaders() + msg.hashstop = block_hash + node.send_message(msg) + + # Check whether last block received from node has a given hash + def last_block_equals(self, expected_hash, node): + block_msg = node.last_message.get("block") + return block_msg and block_msg.block.rehash() == expected_hash + + # Check whether last block header received from node has a given hash + def last_header_equals(self, expected_hash, node): + headers_msg = node.last_message.get("headers") + return (headers_msg and + headers_msg.headers and + headers_msg.headers[0].rehash() == expected_hash) + + # Checks that stale blocks timestamped more than a month ago are not served + # by the node while recent stale blocks and old active chain blocks are. + # This does not currently test that stale blocks timestamped within the + # last month but that have over a month's worth of work are also withheld. + def run_test(self): + node0 = NodeConnCB() + + connections = [] + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0)) + node0.add_connection(connections[0]) + + NetworkThread().start() + node0.wait_for_verack() + + # Set node time to 60 days ago + self.nodes[0].setmocktime(int(time.time()) - 60 * 24 * 60 * 60) + + # Generating a chain of 10 blocks + block_hashes = self.nodes[0].generate(nblocks=10) + + # Create longer chain starting 2 blocks before current tip + height = len(block_hashes) - 2 + block_hash = block_hashes[height - 1] + block_time = self.nodes[0].getblockheader(block_hash)["mediantime"] + 1 + new_blocks = self.build_chain(5, block_hash, height, block_time) + + # Force reorg to a longer chain + node0.send_message(msg_headers(new_blocks)) + node0.wait_for_getdata() + for block in new_blocks: + node0.send_and_ping(msg_block(block)) + + # Check that reorg succeeded + assert_equal(self.nodes[0].getblockcount(), 13) + + stale_hash = int(block_hashes[-1], 16) + + # Check that getdata request for stale block succeeds + self.send_block_request(stale_hash, node0) + test_function = lambda: self.last_block_equals(stale_hash, node0) + wait_until(test_function, timeout=3) + + # Check that getheader request for stale block header succeeds + self.send_header_request(stale_hash, node0) + test_function = lambda: self.last_header_equals(stale_hash, node0) + wait_until(test_function, timeout=3) + + # Longest chain is extended so stale is much older than chain tip + self.nodes[0].setmocktime(0) + tip = self.nodes[0].generate(nblocks=1)[0] + assert_equal(self.nodes[0].getblockcount(), 14) + + # Send getdata & getheaders to refresh last received getheader message + block_hash = int(tip, 16) + self.send_block_request(block_hash, node0) + self.send_header_request(block_hash, node0) + node0.sync_with_ping() + + # Request for very old stale block should now fail + self.send_block_request(stale_hash, node0) + time.sleep(3) + assert not self.last_block_equals(stale_hash, node0) + + # Request for very old stale block header should now fail + self.send_header_request(stale_hash, node0) + time.sleep(3) + assert not self.last_header_equals(stale_hash, node0) + + # Verify we can fetch very old blocks and headers on the active chain + block_hash = int(block_hashes[2], 16) + self.send_block_request(block_hash, node0) + self.send_header_request(block_hash, node0) + node0.sync_with_ping() + + self.send_block_request(block_hash, node0) + test_function = lambda: self.last_block_equals(block_hash, node0) + wait_until(test_function, timeout=3) + + self.send_header_request(block_hash, node0) + test_function = lambda: self.last_header_equals(block_hash, node0) + wait_until(test_function, timeout=3) + +if __name__ == '__main__': + P2PFingerprintTest().main() diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py index e7fe7372c8..1d969fc7c1 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -49,12 +49,11 @@ class CBrokenBlock(CBlock): return r class FullBlockTest(ComparisonTestFramework): - # Can either run this test as 1 node with expected answers, or two and compare them. # Change the "outcome" variable from each TestInstance object to only do the comparison. - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 + self.setup_clean_chain = True self.block_heights = {} self.coinbase_key = CECKey() self.coinbase_key.set_secretbytes(b"horsebattery") @@ -397,7 +396,7 @@ class FullBlockTest(ComparisonTestFramework): yield rejected(RejectResult(16, b'bad-cb-length')) # Extend the b26 chain to make sure bitcoind isn't accepting b26 - b27 = block(27, spend=out[7]) + block(27, spend=out[7]) yield rejected(False) # Now try a too-large-coinbase script @@ -409,7 +408,7 @@ class FullBlockTest(ComparisonTestFramework): yield rejected(RejectResult(16, b'bad-cb-length')) # Extend the b28 chain to make sure bitcoind isn't accepting b28 - b29 = block(29, spend=out[7]) + block(29, spend=out[7]) yield rejected(False) # b30 has a max-sized coinbase scriptSig. @@ -581,7 +580,7 @@ class FullBlockTest(ComparisonTestFramework): # same as b40, but one less sigop tip(39) - b41 = block(41, spend=None) + block(41, spend=None) update_block(41, b40.vtx[1:-1]) b41_sigops_to_fill = b40_sigops_to_fill - 1 tx = CTransaction() @@ -927,7 +926,7 @@ class FullBlockTest(ComparisonTestFramework): # -> b42 (12) -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) # tip(64) - b65 = block(65) + block(65) tx1 = create_and_sign_tx(out[19].tx, out[19].n, out[19].tx.vout[0].nValue) tx2 = create_and_sign_tx(tx1, 0, 0) update_block(65, [tx1, tx2]) @@ -939,7 +938,7 @@ class FullBlockTest(ComparisonTestFramework): # -> b43 (13) -> b53 (14) -> b55 (15) -> b57 (16) -> b60 (17) -> b64 (18) -> b65 (19) # \-> b66 (20) tip(65) - b66 = block(66) + block(66) tx1 = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue) tx2 = create_and_sign_tx(tx1, 0, 1) update_block(66, [tx2, tx1]) @@ -952,7 +951,7 @@ class FullBlockTest(ComparisonTestFramework): # # tip(65) - b67 = block(67) + block(67) tx1 = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue) tx2 = create_and_sign_tx(tx1, 0, 1) tx3 = create_and_sign_tx(tx1, 0, 2) @@ -972,7 +971,7 @@ class FullBlockTest(ComparisonTestFramework): # this succeeds # tip(65) - b68 = block(68, additional_coinbase_value=10) + block(68, additional_coinbase_value=10) tx = create_and_sign_tx(out[20].tx, out[20].n, out[20].tx.vout[0].nValue-9) update_block(68, [tx]) yield rejected(RejectResult(16, b'bad-cb-amount')) @@ -1175,7 +1174,7 @@ class FullBlockTest(ComparisonTestFramework): # # -> b81 (26) -> b82 (27) -> b83 (28) # - b83 = block(83) + block(83) op_codes = [OP_IF, OP_INVALIDOPCODE, OP_ELSE, OP_TRUE, OP_ENDIF] script = CScript(op_codes) tx1 = create_and_sign_tx(out[28].tx, out[28].n, out[28].tx.vout[0].nValue, script) @@ -1195,7 +1194,7 @@ class FullBlockTest(ComparisonTestFramework): # \-> b85 (29) -> b86 (30) \-> b89a (32) # # - b84 = block(84) + block(84) tx1 = create_tx(out[29].tx, out[29].n, 0, CScript([OP_RETURN])) tx1.vout.append(CTxOut(0, CScript([OP_TRUE]))) tx1.vout.append(CTxOut(0, CScript([OP_TRUE]))) diff --git a/test/functional/p2p-leaktests.py b/test/functional/p2p-leaktests.py index f0d4d9a8b8..1dc8f72cd6 100755 --- a/test/functional/p2p-leaktests.py +++ b/test/functional/p2p-leaktests.py @@ -92,8 +92,7 @@ class CNodeNoVerackIdle(CLazyNode): conn.send_message(msg_getaddr()) class P2PLeakTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 self.extra_args = [['-banscore='+str(banscore)]] @@ -140,6 +139,9 @@ class P2PLeakTest(BitcoinTestFramework): [conn.disconnect_node() for conn in connections] + # Wait until all connections are closed + wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 0) + # Make sure no unexpected messages came in assert(no_version_bannode.unexpected_msg == False) assert(no_version_idlenode.unexpected_msg == False) @@ -158,8 +160,10 @@ class P2PLeakTest(BitcoinTestFramework): allowed_service_bit5_node.add_connection(connections[5]) allowed_service_bit7_node.add_connection(connections[6]) - wait_until(lambda: allowed_service_bit5_node.message_count["verack"], timeout=10, lock=mininode_lock) - wait_until(lambda: allowed_service_bit7_node.message_count["verack"], timeout=10, lock=mininode_lock) + NetworkThread().start() # Network thread stopped when all previous NodeConnCBs disconnected. Restart it + + wait_until(lambda: allowed_service_bit5_node.message_count["verack"], lock=mininode_lock) + wait_until(lambda: allowed_service_bit7_node.message_count["verack"], lock=mininode_lock) if __name__ == '__main__': P2PLeakTest().main() diff --git a/test/functional/p2p-mempool.py b/test/functional/p2p-mempool.py index 34ef249eea..40fcde2605 100755 --- a/test/functional/p2p-mempool.py +++ b/test/functional/p2p-mempool.py @@ -13,9 +13,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class P2PMempoolTests(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [["-peerbloomfilters=0"]] diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py index 63dfbb8ae6..a9ef47559b 100755 --- a/test/functional/p2p-segwit.py +++ b/test/functional/p2p-segwit.py @@ -17,7 +17,6 @@ from binascii import hexlify # The versionbit bit used to signal activation of SegWit VB_WITNESS_BIT = 1 VB_PERIOD = 144 -VB_ACTIVATION_THRESHOLD = 108 VB_TOP_BITS = 0x20000000 MAX_SIGOP_COST = 80000 @@ -109,9 +108,7 @@ def sign_P2PK_witness_input(script, txTo, inIdx, hashtype, value, key): class SegWitTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 self.extra_args = [["-whitelist=127.0.0.1"], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0"], ["-whitelist=127.0.0.1", "-vbparams=segwit:0:0"]] @@ -1496,7 +1493,7 @@ class SegWitTest(BitcoinTestFramework): # Restart with the new binary self.stop_node(node_id) - self.nodes[node_id] = self.start_node(node_id, self.options.tmpdir) + self.start_node(node_id, extra_args=[]) connect_nodes(self.nodes[0], node_id) sync_blocks(self.nodes) diff --git a/test/functional/p2p-timeouts.py b/test/functional/p2p-timeouts.py index c3b29c215b..51d4769efc 100755 --- a/test/functional/p2p-timeouts.py +++ b/test/functional/p2p-timeouts.py @@ -33,8 +33,7 @@ class TestNode(NodeConnCB): pass class TimeoutsTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/p2p-versionbits-warning.py b/test/functional/p2p-versionbits-warning.py index df7e8ce5c1..f9bef2580a 100755 --- a/test/functional/p2p-versionbits-warning.py +++ b/test/functional/p2p-versionbits-warning.py @@ -28,8 +28,7 @@ class TestNode(NodeConnCB): pass class VersionBitsWarningTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 @@ -88,8 +87,7 @@ class VersionBitsWarningTest(BitcoinTestFramework): self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD + 1) # Check that we're not getting any versionbit-related errors in # get*info() - assert(not VB_PATTERN.match(self.nodes[0].getinfo()["errors"])) - assert(not VB_PATTERN.match(self.nodes[0].getmininginfo()["errors"])) + assert(not VB_PATTERN.match(self.nodes[0].getmininginfo()["warnings"])) assert(not VB_PATTERN.match(self.nodes[0].getnetworkinfo()["warnings"])) # 3. Now build one period of blocks with >= VB_THRESHOLD blocks signaling @@ -100,8 +98,7 @@ class VersionBitsWarningTest(BitcoinTestFramework): # have gotten a different alert due to more than 51/100 blocks # being of unexpected version. # Check that get*info() shows some kind of error. - assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getinfo()["errors"]) - assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getmininginfo()["errors"]) + assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getmininginfo()["warnings"]) assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getnetworkinfo()["warnings"]) # Mine a period worth of expected blocks so the generic block-version warning @@ -112,18 +109,17 @@ class VersionBitsWarningTest(BitcoinTestFramework): # Empty out the alert file with open(self.alert_filename, 'w', encoding='utf8') as _: pass - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args) + self.start_nodes() # Connecting one block should be enough to generate an error. self.nodes[0].generate(1) - assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getinfo()["errors"]) - assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getmininginfo()["errors"]) + assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getmininginfo()["warnings"]) assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getnetworkinfo()["warnings"]) self.stop_nodes() self.test_versionbits_in_alert_file() # Test framework expects the node to still be running... - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args) + self.start_nodes() if __name__ == '__main__': VersionBitsWarningTest().main() diff --git a/test/functional/preciousblock.py b/test/functional/preciousblock.py index 04b41e76ba..1466f901c0 100755 --- a/test/functional/preciousblock.py +++ b/test/functional/preciousblock.py @@ -35,8 +35,7 @@ def node_sync_via_rpc(nodes): unidirectional_node_sync_via_rpc(node_src, node_dest) class PreciousTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 @@ -47,16 +46,16 @@ class PreciousTest(BitcoinTestFramework): self.log.info("Ensure submitblock can in principle reorg to a competing chain") self.nodes[0].generate(1) assert_equal(self.nodes[0].getblockcount(), 1) - (hashY, hashZ) = self.nodes[1].generate(2) + hashZ = self.nodes[1].generate(2)[-1] assert_equal(self.nodes[1].getblockcount(), 2) node_sync_via_rpc(self.nodes[0:3]) assert_equal(self.nodes[0].getbestblockhash(), hashZ) self.log.info("Mine blocks A-B-C on Node 0") - (hashA, hashB, hashC) = self.nodes[0].generate(3) + hashC = self.nodes[0].generate(3)[-1] assert_equal(self.nodes[0].getblockcount(), 5) self.log.info("Mine competing blocks E-F-G on Node 1") - (hashE, hashF, hashG) = self.nodes[1].generate(3) + hashG = self.nodes[1].generate(3)[-1] assert_equal(self.nodes[1].getblockcount(), 5) assert(hashC != hashG) self.log.info("Connect nodes and check no reorg occurs") diff --git a/test/functional/prioritise_transaction.py b/test/functional/prioritise_transaction.py index 4fc03d2547..bb56db9b40 100755 --- a/test/functional/prioritise_transaction.py +++ b/test/functional/prioritise_transaction.py @@ -9,9 +9,7 @@ from test_framework.util import * from test_framework.mininode import COIN, MAX_BLOCK_BASE_SIZE class PrioritiseTransactionTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 self.extra_args = [["-printpriority=1"], ["-printpriority=1"]] @@ -103,7 +101,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework): tx_id = self.nodes[0].decoderawtransaction(tx_hex)["txid"] # This will raise an exception due to min relay fee not being met - assert_raises_jsonrpc(-26, "66: min relay fee not met", self.nodes[0].sendrawtransaction, tx_hex) + assert_raises_rpc_error(-26, "66: min relay fee not met", self.nodes[0].sendrawtransaction, tx_hex) assert(tx_id not in self.nodes[0].getrawmempool()) # This is a less than 1000-byte transaction, so just set the fee diff --git a/test/functional/proxy_test.py b/test/functional/proxy_test.py index ae6f843ddc..81b99d1bf4 100755 --- a/test/functional/proxy_test.py +++ b/test/functional/proxy_test.py @@ -41,12 +41,9 @@ from test_framework.netutil import test_ipv6_local RANGE_BEGIN = PORT_MIN + 2 * PORT_RANGE # Start after p2p and rpc ports - class ProxyTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 4 - self.setup_clean_chain = False def setup_nodes(self): self.have_ipv6 = test_ipv6_local() @@ -89,7 +86,8 @@ class ProxyTest(BitcoinTestFramework): ] if self.have_ipv6: args[3] = ['-listen', '-proxy=[%s]:%i' % (self.conf3.addr),'-proxyrandomize=0', '-noonion'] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args=args) + self.add_nodes(self.num_nodes, extra_args=args) + self.start_nodes() def node_test(self, node, proxies, auth, test_onion=True): rv = [] diff --git a/test/functional/pruning.py b/test/functional/pruning.py index 3e00a34ac4..0101f61185 100755 --- a/test/functional/pruning.py +++ b/test/functional/pruning.py @@ -26,9 +26,7 @@ def calc_usage(blockdir): return sum(os.path.getsize(blockdir+f) for f in os.listdir(blockdir) if os.path.isfile(blockdir+f)) / (1024. * 1024.) class PruneTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 6 @@ -56,6 +54,10 @@ class PruneTest(BitcoinTestFramework): connect_nodes(self.nodes[0], 4) sync_blocks(self.nodes[0:5]) + def setup_nodes(self): + self.add_nodes(self.num_nodes, self.extra_args, timewait=900) + self.start_nodes() + def create_big_chain(self): # Start by creating some coinbases we can spend later self.nodes[1].generate(200) @@ -98,7 +100,7 @@ class PruneTest(BitcoinTestFramework): # Node 2 stays connected, so it hears about the stale blocks and then reorg's when node0 reconnects # Stopping node 0 also clears its mempool, so it doesn't have node1's transactions to accidentally mine self.stop_node(0) - self.nodes[0]=self.start_node(0, self.options.tmpdir, self.full_node_default_args, timewait=900) + self.start_node(0, extra_args=self.full_node_default_args) # Mine 24 blocks in node 1 for i in range(24): if j == 0: @@ -126,7 +128,7 @@ class PruneTest(BitcoinTestFramework): # Reboot node 1 to clear its mempool (hopefully make the invalidate faster) # Lower the block max size so we don't keep mining all our big mempool transactions (from disconnected blocks) self.stop_node(1) - self.nodes[1] = self.start_node(1, self.options.tmpdir, ["-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"], timewait=900) + self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"]) height = self.nodes[1].getblockcount() self.log.info("Current block height: %d" % height) @@ -149,7 +151,7 @@ class PruneTest(BitcoinTestFramework): # Reboot node1 to clear those giant tx's from mempool self.stop_node(1) - self.nodes[1] = self.start_node(1, self.options.tmpdir, ["-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"], timewait=900) + self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"]) self.log.info("Generating new longer chain of 300 more blocks") self.nodes[1].generate(300) @@ -183,7 +185,7 @@ class PruneTest(BitcoinTestFramework): def reorg_back(self): # Verify that a block on the old main chain fork has been pruned away - assert_raises_jsonrpc(-1, "Block not available (pruned data)", self.nodes[2].getblock, self.forkhash) + assert_raises_rpc_error(-1, "Block not available (pruned data)", self.nodes[2].getblock, self.forkhash) self.log.info("Will need to redownload block %d" % self.forkheight) # Verify that we have enough history to reorg back to the fork point @@ -227,13 +229,15 @@ class PruneTest(BitcoinTestFramework): def manual_test(self, node_number, use_timestamp): # at this point, node has 995 blocks and has not yet run in prune mode - node = self.nodes[node_number] = self.start_node(node_number, self.options.tmpdir, timewait=900) + self.start_node(node_number) + node = self.nodes[node_number] assert_equal(node.getblockcount(), 995) - assert_raises_jsonrpc(-1, "not in prune mode", node.pruneblockchain, 500) - self.stop_node(node_number) + assert_raises_rpc_error(-1, "not in prune mode", node.pruneblockchain, 500) # now re-start in manual pruning mode - node = self.nodes[node_number] = self.start_node(node_number, self.options.tmpdir, ["-prune=1"], timewait=900) + self.stop_node(node_number) + self.start_node(node_number, extra_args=["-prune=1"]) + node = self.nodes[node_number] assert_equal(node.getblockcount(), 995) def height(index): @@ -261,14 +265,14 @@ class PruneTest(BitcoinTestFramework): return os.path.isfile(self.options.tmpdir + "/node{}/regtest/blocks/blk{:05}.dat".format(node_number, index)) # should not prune because chain tip of node 3 (995) < PruneAfterHeight (1000) - assert_raises_jsonrpc(-1, "Blockchain is too short for pruning", node.pruneblockchain, height(500)) + assert_raises_rpc_error(-1, "Blockchain is too short for pruning", node.pruneblockchain, height(500)) # mine 6 blocks so we are at height 1001 (i.e., above PruneAfterHeight) node.generate(6) assert_equal(node.getblockchaininfo()["blocks"], 1001) # negative heights should raise an exception - assert_raises_jsonrpc(-8, "Negative", node.pruneblockchain, -10) + assert_raises_rpc_error(-8, "Negative", node.pruneblockchain, -10) # height=100 too low to prune first block file so this is a no-op prune(100) @@ -307,7 +311,7 @@ class PruneTest(BitcoinTestFramework): # stop node, start back up with auto-prune at 550MB, make sure still runs self.stop_node(node_number) - self.nodes[node_number] = self.start_node(node_number, self.options.tmpdir, ["-prune=550"], timewait=900) + self.start_node(node_number, extra_args=["-prune=550"]) self.log.info("Success") @@ -315,7 +319,7 @@ class PruneTest(BitcoinTestFramework): # check that the pruning node's wallet is still in good shape self.log.info("Stop and start pruning node to trigger wallet rescan") self.stop_node(2) - self.nodes[2] = self.start_node(2, self.options.tmpdir, ["-prune=550"]) + self.start_node(2, extra_args=["-prune=550"]) self.log.info("Success") # check that wallet loads successfully when restarting a pruned node after IBD. @@ -325,7 +329,7 @@ class PruneTest(BitcoinTestFramework): nds = [self.nodes[0], self.nodes[5]] sync_blocks(nds, wait=5, timeout=300) self.stop_node(5) #stop and start to trigger rescan - self.nodes[5] = self.start_node(5, self.options.tmpdir, ["-prune=550"]) + self.start_node(5, extra_args=["-prune=550"]) self.log.info("Success") def run_test(self): diff --git a/test/functional/rawtransactions.py b/test/functional/rawtransactions.py index b6b90d6781..2777cb9693 100755 --- a/test/functional/rawtransactions.py +++ b/test/functional/rawtransactions.py @@ -17,9 +17,7 @@ from test_framework.util import * # Create one-input, one-output, no-fee transaction: class RawTransactionsTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 @@ -50,7 +48,7 @@ class RawTransactionsTest(BitcoinTestFramework): rawtx = self.nodes[2].signrawtransaction(rawtx) # This will raise an exception since there are missing inputs - assert_raises_jsonrpc(-25, "Missing inputs", self.nodes[2].sendrawtransaction, rawtx['hex']) + assert_raises_rpc_error(-25, "Missing inputs", self.nodes[2].sendrawtransaction, rawtx['hex']) ######################### # RAW TX MULTISIG TESTS # @@ -63,7 +61,6 @@ class RawTransactionsTest(BitcoinTestFramework): addr2Obj = self.nodes[2].validateaddress(addr2) mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']]) - mSigObjValid = self.nodes[2].validateaddress(mSigObj) #use balance deltas instead of absolute values bal = self.nodes[2].getbalance() @@ -87,7 +84,6 @@ class RawTransactionsTest(BitcoinTestFramework): addr3Obj = self.nodes[2].validateaddress(addr3) mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey']]) - mSigObjValid = self.nodes[2].validateaddress(mSigObj) txId = self.nodes[0].sendtoaddress(mSigObj, 2.2) decTx = self.nodes[0].gettransaction(txId) @@ -192,13 +188,13 @@ class RawTransactionsTest(BitcoinTestFramework): assert_equal(self.nodes[0].getrawtransaction(txHash, True)["hex"], rawTxSigned['hex']) # 6. invalid parameters - supply txid and string "Flase" - assert_raises_jsonrpc(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, "Flase") + assert_raises_rpc_error(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, "Flase") # 7. invalid parameters - supply txid and empty array - assert_raises_jsonrpc(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, []) + assert_raises_rpc_error(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, []) # 8. invalid parameters - supply txid and empty dict - assert_raises_jsonrpc(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, {}) + assert_raises_rpc_error(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, {}) inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 1000}] outputs = { self.nodes[0].getnewaddress() : 1 } @@ -209,12 +205,12 @@ class RawTransactionsTest(BitcoinTestFramework): # 9. invalid parameters - sequence number out of range inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : -1}] outputs = { self.nodes[0].getnewaddress() : 1 } - assert_raises_jsonrpc(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) + assert_raises_rpc_error(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) # 10. invalid parameters - sequence number out of range inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 4294967296}] outputs = { self.nodes[0].getnewaddress() : 1 } - assert_raises_jsonrpc(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) + assert_raises_rpc_error(-8, 'Invalid parameter, sequence number is out of range', self.nodes[0].createrawtransaction, inputs, outputs) inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 4294967294}] outputs = { self.nodes[0].getnewaddress() : 1 } diff --git a/test/functional/receivedby.py b/test/functional/receivedby.py index 19d99c9c9e..db6fc86b82 100755 --- a/test/functional/receivedby.py +++ b/test/functional/receivedby.py @@ -23,16 +23,9 @@ def get_sub_array_from_array(object_array, to_match): return [] class ReceivedByTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.num_nodes = 4 - self.setup_clean_chain = False - - def setup_nodes(self): - #This test requires mocktime + def set_test_params(self): + self.num_nodes = 2 self.enable_mocktime() - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir) def run_test(self): ''' diff --git a/test/functional/reindex.py b/test/functional/reindex.py index b446baa04d..1f684a1afe 100755 --- a/test/functional/reindex.py +++ b/test/functional/reindex.py @@ -15,8 +15,7 @@ import time class ReindexTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 @@ -25,7 +24,7 @@ class ReindexTest(BitcoinTestFramework): blockcount = self.nodes[0].getblockcount() self.stop_nodes() extra_args = [["-reindex-chainstate" if justchainstate else "-reindex", "-checkblockindex=1"]] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args) + self.start_nodes(extra_args) while self.nodes[0].getblockcount() < blockcount: time.sleep(0.1) assert_equal(self.nodes[0].getblockcount(), blockcount) diff --git a/test/functional/replace-by-fee.py b/test/functional/replace-by-fee.py index bc67654987..269d57775c 100755 --- a/test/functional/replace-by-fee.py +++ b/test/functional/replace-by-fee.py @@ -61,16 +61,15 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])): class ReplaceByFeeTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - self.num_nodes = 1 - self.setup_clean_chain = False + def set_test_params(self): + self.num_nodes = 2 self.extra_args= [["-maxorphantx=1000", "-whitelist=127.0.0.1", "-limitancestorcount=50", "-limitancestorsize=101", "-limitdescendantcount=200", - "-limitdescendantsize=101"]] + "-limitdescendantsize=101"], + ["-mempoolreplacement=0"]] def run_test(self): make_utxo(self.nodes[0], 1*COIN) @@ -117,6 +116,8 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) + self.sync_all([self.nodes]) + # Should fail because we haven't changed the fee tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] @@ -124,13 +125,18 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1b_hex = txToHex(tx1b) # This will raise an exception due to insufficient fee - assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) + assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) + # This will raise an exception due to transaction replacement being disabled + assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True) # Extra 0.1 BTC fee tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] tx1b_hex = txToHex(tx1b) + # Replacement still disabled even with "enough fee" + assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True) + # Works when enabled tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) mempool = self.nodes[0].getrawmempool() @@ -140,6 +146,11 @@ class ReplaceByFeeTest(BitcoinTestFramework): assert_equal(tx1b_hex, self.nodes[0].getrawtransaction(tx1b_txid)) + # Second node is running mempoolreplacement=0, will not replace originally-seen txn + mempool = self.nodes[1].getrawmempool() + assert tx1a_txid in mempool + assert tx1b_txid not in mempool + def test_doublespend_chain(self): """Doublespend of a long chain""" @@ -167,7 +178,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): dbl_tx_hex = txToHex(dbl_tx) # This will raise an exception due to insufficient fee - assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) + assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) # Accepted with sufficient fee dbl_tx = CTransaction() @@ -228,7 +239,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): dbl_tx.vout = [CTxOut(initial_nValue - fee*n, CScript([1]))] dbl_tx_hex = txToHex(dbl_tx) # This will raise an exception due to insufficient fee - assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) + assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) # 1 BTC fee is enough dbl_tx = CTransaction() @@ -256,7 +267,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): dbl_tx.vout = [CTxOut(initial_nValue - 2*fee*n, CScript([1]))] dbl_tx_hex = txToHex(dbl_tx) # This will raise an exception - assert_raises_jsonrpc(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) + assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) for tx in tree_txs: tx.rehash() @@ -270,7 +281,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] tx1a_hex = txToHex(tx1a) - tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) + self.nodes[0].sendrawtransaction(tx1a_hex, True) # Higher fee, but the fee per KB is much lower, so the replacement is # rejected. @@ -280,7 +291,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1b_hex = txToHex(tx1b) # This will raise an exception due to insufficient fee - assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) + assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) def test_spends_of_conflicting_outputs(self): """Replacements that spend conflicting tx outputs are rejected""" @@ -303,7 +314,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx2_hex = txToHex(tx2) # This will raise an exception - assert_raises_jsonrpc(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, True) + assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, True) # Spend tx1a's output to test the indirect case. tx1b = CTransaction() @@ -320,7 +331,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx2_hex = txToHex(tx2) # This will raise an exception - assert_raises_jsonrpc(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, True) + assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, True) def test_new_unconfirmed_inputs(self): """Replacements that add new unconfirmed inputs are rejected""" @@ -331,7 +342,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1.vin = [CTxIn(confirmed_utxo)] tx1.vout = [CTxOut(1*COIN, CScript([b'a']))] tx1_hex = txToHex(tx1) - tx1_txid = self.nodes[0].sendrawtransaction(tx1_hex, True) + self.nodes[0].sendrawtransaction(tx1_hex, True) tx2 = CTransaction() tx2.vin = [CTxIn(confirmed_utxo), CTxIn(unconfirmed_utxo)] @@ -339,7 +350,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx2_hex = txToHex(tx2) # This will raise an exception - assert_raises_jsonrpc(-26, "replacement-adds-unconfirmed", self.nodes[0].sendrawtransaction, tx2_hex, True) + assert_raises_rpc_error(-26, "replacement-adds-unconfirmed", self.nodes[0].sendrawtransaction, tx2_hex, True) def test_too_many_replacements(self): """Replacements that evict too many transactions are rejected""" @@ -385,7 +396,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): double_tx_hex = txToHex(double_tx) # This will raise an exception - assert_raises_jsonrpc(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, double_tx_hex, True) + assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, double_tx_hex, True) # If we remove an input, it should pass double_tx = CTransaction() @@ -412,7 +423,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1b_hex = txToHex(tx1b) # This will raise an exception - assert_raises_jsonrpc(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx1b_hex, True) + assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx1b_hex, True) tx1_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) @@ -430,7 +441,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx2b_hex = txToHex(tx2b) # This will raise an exception - assert_raises_jsonrpc(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx2b_hex, True) + assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx2b_hex, True) # Now create a new transaction that spends from tx1a and tx2a # opt-in on one of the inputs @@ -482,7 +493,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1b_hex = txToHex(tx1b) # Verify tx1b cannot replace tx1a. - assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) + assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) # Use prioritisetransaction to set tx1a's fee to 0. self.nodes[0].prioritisetransaction(txid=tx1a_txid, fee_delta=int(-0.1*COIN)) @@ -499,7 +510,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0)] tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))] tx2a_hex = txToHex(tx2a) - tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True) + self.nodes[0].sendrawtransaction(tx2a_hex, True) # Lower fee, but we'll prioritise it tx2b = CTransaction() @@ -509,7 +520,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx2b_hex = txToHex(tx2b) # Verify tx2b cannot replace tx2a. - assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b_hex, True) + assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b_hex, True) # Now prioritise tx2b to have a higher modified fee self.nodes[0].prioritisetransaction(txid=tx2b.hash, fee_delta=int(0.1*COIN)) diff --git a/test/functional/resendwallettransactions.py b/test/functional/resendwallettransactions.py index 5059aa106e..d959bb4c38 100755 --- a/test/functional/resendwallettransactions.py +++ b/test/functional/resendwallettransactions.py @@ -5,22 +5,20 @@ """Test resendwallettransactions RPC.""" from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, assert_raises_jsonrpc +from test_framework.util import assert_equal, assert_raises_rpc_error class ResendWalletTransactionsTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.extra_args = [['--walletbroadcast=false']] + def set_test_params(self): self.num_nodes = 1 + self.extra_args = [['--walletbroadcast=false']] def run_test(self): # Should raise RPC_WALLET_ERROR (-4) if walletbroadcast is disabled. - assert_raises_jsonrpc(-4, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast", self.nodes[0].resendwallettransactions) + assert_raises_rpc_error(-4, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast", self.nodes[0].resendwallettransactions) # Should return an empty array if there aren't unconfirmed wallet transactions. self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir) + self.start_node(0, extra_args=[]) assert_equal(self.nodes[0].resendwallettransactions(), []) # Should return an array with the unconfirmed wallet transaction. diff --git a/test/functional/rest.py b/test/functional/rest.py index a69dbb5013..437111a4d7 100755 --- a/test/functional/rest.py +++ b/test/functional/rest.py @@ -43,8 +43,7 @@ def http_post_call(host, port, path, requestdata = '', response_object = 0): class RESTTest (BitcoinTestFramework): FORMAT_SEPARATOR = "." - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 diff --git a/test/functional/rpcbind_test.py b/test/functional/rpcbind_test.py index 20808207b2..0e8c3fa209 100755 --- a/test/functional/rpcbind_test.py +++ b/test/functional/rpcbind_test.py @@ -11,19 +11,13 @@ from test_framework.test_framework import BitcoinTestFramework, SkipTest from test_framework.util import * from test_framework.netutil import * - class RPCBindTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 def setup_network(self): - pass - - def setup_nodes(self): - pass + self.add_nodes(self.num_nodes, None) def run_bind_test(self, allow_ips, connect_to, addresses, expected): ''' @@ -31,12 +25,14 @@ class RPCBindTest(BitcoinTestFramework): then try to connect, and check if the set of bound addresses matches the expected set. ''' + self.log.info("Bind test for %s" % str(addresses)) expected = [(addr_to_hex(addr), port) for (addr, port) in expected] base_args = ['-disablewallet', '-nolisten'] if allow_ips: base_args += ['-rpcallowip=' + x for x in allow_ips] binds = ['-rpcbind='+addr for addr in addresses] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, [base_args + binds], connect_to) + self.nodes[0].rpchost = connect_to + self.start_node(0, base_args + binds) pid = self.nodes[0].process.pid assert_equal(set(get_bind_addrs(pid)), set(expected)) self.stop_nodes() @@ -46,8 +42,10 @@ class RPCBindTest(BitcoinTestFramework): Start a node with rpcallow IP, and request getnetworkinfo at a non-localhost IP. ''' + self.log.info("Allow IP test for %s:%d" % (rpchost, rpcport)) base_args = ['-disablewallet', '-nolisten'] + ['-rpcallowip='+x for x in allow_ips] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, [base_args]) + self.nodes[0].rpchost = None + self.start_nodes([base_args]) # connect to node through non-loopback interface node = get_rpc_proxy(rpc_url(get_datadir_path(self.options.tmpdir, 0), 0, "%s:%d" % (rpchost, rpcport)), 0, coveragedir=self.options.coveragedir) node.getnetworkinfo() @@ -103,7 +101,7 @@ class RPCBindTest(BitcoinTestFramework): # Check that with invalid rpcallowip, we are denied self.run_allowip_test([non_loopback_ip], non_loopback_ip, defaultport) - assert_raises_jsonrpc(-342, "non-JSON HTTP response with '403 Forbidden' from server", self.run_allowip_test, ['1.1.1.1'], non_loopback_ip, defaultport) + assert_raises_rpc_error(-342, "non-JSON HTTP response with '403 Forbidden' from server", self.run_allowip_test, ['1.1.1.1'], non_loopback_ip, defaultport) if __name__ == '__main__': RPCBindTest().main() diff --git a/test/functional/rpcnamedargs.py b/test/functional/rpcnamedargs.py index 3b286000a1..c47212bddb 100755 --- a/test/functional/rpcnamedargs.py +++ b/test/functional/rpcnamedargs.py @@ -7,26 +7,19 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, - assert_raises_jsonrpc, + assert_raises_rpc_error, ) - class NamedArgumentTest(BitcoinTestFramework): - """ - Test named arguments on RPC calls. - """ - - def __init__(self): - super().__init__() - self.setup_clean_chain = False + def set_test_params(self): self.num_nodes = 1 def run_test(self): node = self.nodes[0] - h = node.help(command='getinfo') - assert(h.startswith('getinfo\n')) + h = node.help(command='getblockchaininfo') + assert(h.startswith('getblockchaininfo\n')) - assert_raises_jsonrpc(-8, 'Unknown named parameter', node.help, random='getinfo') + assert_raises_rpc_error(-8, 'Unknown named parameter', node.help, random='getblockchaininfo') h = node.getblockhash(height=0) node.getblock(blockhash=h) diff --git a/test/functional/segwit.py b/test/functional/segwit.py index 51eaa34a54..6ecade7cb6 100755 --- a/test/functional/segwit.py +++ b/test/functional/segwit.py @@ -7,12 +7,11 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * from test_framework.mininode import sha256, CTransaction, CTxIn, COutPoint, CTxOut, COIN, ToHex, FromHex -from test_framework.address import script_to_p2sh, key_to_p2pkh +from test_framework.address import script_to_p2sh, key_to_p2pkh, key_to_p2sh_p2wpkh, key_to_p2wpkh, script_to_p2sh_p2wsh, script_to_p2wsh, program_to_witness from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_1, OP_2, OP_CHECKMULTISIG, OP_TRUE from io import BytesIO NODE_0 = 0 -NODE_1 = 1 NODE_2 = 2 WIT_V0 = 0 WIT_V1 = 1 @@ -34,15 +33,15 @@ def witness_script(use_p2wsh, pubkey): # Return a transaction (in hex) that spends the given utxo to a segwit output, # optionally wrapping the segwit output using P2SH. -def create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount): - pkscript = hex_str_to_bytes(witness_script(use_p2wsh, pubkey)) - if (encode_p2sh): - p2sh_hash = hash160(pkscript) - pkscript = CScript([OP_HASH160, p2sh_hash, OP_EQUAL]) - tx = CTransaction() - tx.vin.append(CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), b"")) - tx.vout.append(CTxOut(int(amount*COIN), pkscript)) - return ToHex(tx) +def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount): + if use_p2wsh: + program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG]) + addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program) + else: + addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey) + if not encode_p2sh: + assert_equal(node.validateaddress(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey)) + return node.createrawtransaction([utxo], {addr: amount}) # Create a transaction spending a given utxo to a segwit output corresponding # to the given pubkey: use_p2wsh determines whether to use P2WPKH or P2WSH; @@ -50,7 +49,7 @@ def create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount): # sign=True will have the given node sign the transaction. # insert_redeem_script will be added to the scriptSig, if given. def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""): - tx_to_witness = create_witnessprogram(use_p2wsh, utxo, pubkey, encode_p2sh, amount) + tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount) if (sign): signed = node.signrawtransaction(tx_to_witness) assert("errors" not in signed or len(["errors"]) == 0) @@ -75,9 +74,7 @@ def find_unspent(node, min_value): return utxo class SegWitTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 self.extra_args = [["-walletprematurewitness", "-rpcserialversion=0"], @@ -102,11 +99,11 @@ class SegWitTest(BitcoinTestFramework): sync_blocks(self.nodes) def fail_accept(self, node, error_msg, txid, sign, redeem_script=""): - assert_raises_jsonrpc(-26, error_msg, send_to_witness, 1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script) + assert_raises_rpc_error(-26, error_msg, send_to_witness, 1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script) def fail_mine(self, node, txid, sign, redeem_script=""): send_to_witness(1, node, getutxo(txid), self.pubkey[0], False, Decimal("49.998"), sign, redeem_script) - assert_raises_jsonrpc(-1, "CreateNewBlock: TestBlockValidity failed", node.generate, 1) + assert_raises_rpc_error(-1, "CreateNewBlock: TestBlockValidity failed", node.generate, 1) sync_blocks(self.nodes) def run_test(self): @@ -136,8 +133,15 @@ class SegWitTest(BitcoinTestFramework): newaddress = self.nodes[i].getnewaddress() self.pubkey.append(self.nodes[i].validateaddress(newaddress)["pubkey"]) multiaddress = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]]) - self.nodes[i].addwitnessaddress(newaddress) - self.nodes[i].addwitnessaddress(multiaddress) + multiscript = CScript([OP_1, hex_str_to_bytes(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG]) + p2sh_addr = self.nodes[i].addwitnessaddress(newaddress, True) + bip173_addr = self.nodes[i].addwitnessaddress(newaddress, False) + p2sh_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, True) + bip173_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, False) + assert_equal(p2sh_addr, key_to_p2sh_p2wpkh(self.pubkey[-1])) + assert_equal(bip173_addr, key_to_p2wpkh(self.pubkey[-1])) + assert_equal(p2sh_ms_addr, script_to_p2sh_p2wsh(multiscript)) + assert_equal(bip173_ms_addr, script_to_p2wsh(multiscript)) p2sh_ids.append([]) wit_ids.append([]) for v in range(2): @@ -282,6 +286,9 @@ class SegWitTest(BitcoinTestFramework): assert(txid2 in template_txids) assert(txid3 in template_txids) + # Check that wtxid is properly reported in mempool entry + assert_equal(int(self.nodes[0].getmempoolentry(txid3)["wtxid"], 16), tx.calc_sha256(True)) + # Mine a block to clear the gbt cache again. self.nodes[0].generate(1) @@ -445,11 +452,7 @@ class SegWitTest(BitcoinTestFramework): for i in importlist: # import all generated addresses. The wallet already has the private keys for some of these, so catch JSON RPC # exceptions and continue. - try: - self.nodes[0].importaddress(i,"",False,True) - except JSONRPCException as exp: - assert_equal(exp.error["message"], "The wallet already contains the private key for this address or script") - assert_equal(exp.error["code"], -4) + try_rpc(-4, "The wallet already contains the private key for this address or script", self.nodes[0].importaddress, i, "", False, True) self.nodes[0].importaddress(script_to_p2sh(op0)) # import OP_0 as address only self.nodes[0].importaddress(multisig_without_privkey_address) # Test multisig_without_privkey @@ -462,7 +465,7 @@ class SegWitTest(BitcoinTestFramework): # addwitnessaddress should refuse to return a witness address if an uncompressed key is used # note that no witness address should be returned by unsolvable addresses for i in uncompressed_spendable_address + uncompressed_solvable_address + unknown_address + unsolvable_address: - assert_raises_jsonrpc(-4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i) + assert_raises_rpc_error(-4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i) # addwitnessaddress should return a witness addresses even if keys are not in the wallet self.nodes[0].addwitnessaddress(multisig_without_privkey_address) @@ -545,7 +548,7 @@ class SegWitTest(BitcoinTestFramework): # premature_witaddress are not accepted until the script is added with addwitnessaddress first for i in uncompressed_spendable_address + uncompressed_solvable_address + premature_witaddress: # This will raise an exception - assert_raises_jsonrpc(-4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i) + assert_raises_rpc_error(-4, "Public key or redeemscript not known to wallet, or the key is uncompressed", self.nodes[0].addwitnessaddress, i) # after importaddress it should pass addwitnessaddress v = self.nodes[0].validateaddress(compressed_solvable_address[1]) @@ -558,6 +561,13 @@ class SegWitTest(BitcoinTestFramework): solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress, 1)) self.mine_and_test_listunspent(unseen_anytime, 0) + # Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works + v1_addr = program_to_witness(1, [3,5]) + v1_tx = self.nodes[0].createrawtransaction([getutxo(spendable_txid[0])],{v1_addr: 1}) + v1_decoded = self.nodes[1].decoderawtransaction(v1_tx) + assert_equal(v1_decoded['vout'][0]['scriptPubKey']['addresses'][0], v1_addr) + assert_equal(v1_decoded['vout'][0]['scriptPubKey']['hex'], "51020305") + # Check that spendable outputs are really spendable self.create_and_mine_tx_from_txids(spendable_txid) @@ -570,6 +580,29 @@ class SegWitTest(BitcoinTestFramework): self.nodes[0].importprivkey("cTW5mR5M45vHxXkeChZdtSPozrFwFgmEvTNnanCW6wrqwaCZ1X7K") self.create_and_mine_tx_from_txids(solvable_txid) + # Test that importing native P2WPKH/P2WSH scripts works + for use_p2wsh in [False, True]: + if use_p2wsh: + scriptPubKey = "00203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a" + transaction = "01000000000100e1f505000000002200203a59f3f56b713fdcf5d1a57357f02c44342cbf306ffe0c4741046837bf90561a00000000" + else: + scriptPubKey = "a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d87" + transaction = "01000000000100e1f5050000000017a9142f8c469c2f0084c48e11f998ffbe7efa7549f26d8700000000" + + self.nodes[1].importaddress(scriptPubKey, "", False) + rawtxfund = self.nodes[1].fundrawtransaction(transaction)['hex'] + rawtxfund = self.nodes[1].signrawtransaction(rawtxfund)["hex"] + txid = self.nodes[1].sendrawtransaction(rawtxfund) + + assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid) + assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid) + + # Assert it is properly saved + self.stop_node(1) + self.start_node(1) + assert_equal(self.nodes[1].gettransaction(txid, True)["txid"], txid) + assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid) + def mine_and_test_listunspent(self, script_list, ismine): utxo = find_unspent(self.nodes[0], 50) tx = CTransaction() diff --git a/test/functional/sendheaders.py b/test/functional/sendheaders.py index 6451b097c0..fe577dc20a 100755 --- a/test/functional/sendheaders.py +++ b/test/functional/sendheaders.py @@ -174,8 +174,7 @@ class TestNode(NodeConnCB): self.send_message(getblocks_message) class SendHeadersTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 diff --git a/test/functional/signmessages.py b/test/functional/signmessages.py index 42f6a9daaf..52ba6a5ad7 100755 --- a/test/functional/signmessages.py +++ b/test/functional/signmessages.py @@ -5,31 +5,34 @@ """Test RPC commands for signing and verifying messages.""" from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal class SignMessagesTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 def run_test(self): message = 'This is just a test message' - # Test the signing with a privkey - privKey = 'cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N' + self.log.info('test signing with priv_key') + priv_key = 'cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N' address = 'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB' - signature = self.nodes[0].signmessagewithprivkey(privKey, message) - - # Verify the message + expected_signature = 'INbVnW4e6PeRmsv2Qgu8NuopvrVjkcxob+sX8OcZG0SALhWybUjzMLPdAsXI46YZGb0KQTRii+wWIQzRpG/U+S0=' + signature = self.nodes[0].signmessagewithprivkey(priv_key, message) + assert_equal(expected_signature, signature) assert(self.nodes[0].verifymessage(address, signature, message)) - # Test the signing with an address with wallet + self.log.info('test signing with an address with wallet') address = self.nodes[0].getnewaddress() signature = self.nodes[0].signmessage(address, message) - - # Verify the message assert(self.nodes[0].verifymessage(address, signature, message)) + self.log.info('test verifying with another address should not work') + other_address = self.nodes[0].getnewaddress() + other_signature = self.nodes[0].signmessage(other_address, message) + assert(not self.nodes[0].verifymessage(other_address, signature, message)) + assert(not self.nodes[0].verifymessage(address, other_signature, message)) + if __name__ == '__main__': SignMessagesTest().main() diff --git a/test/functional/signrawtransactions.py b/test/functional/signrawtransactions.py index 415727268a..9a45d53cb8 100755 --- a/test/functional/signrawtransactions.py +++ b/test/functional/signrawtransactions.py @@ -9,8 +9,7 @@ from test_framework.util import * class SignRawTransactionsTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 @@ -83,7 +82,7 @@ class SignRawTransactionsTest(BitcoinTestFramework): assert_equal(decodedRawTx["vin"][i]["vout"], inp["vout"]) # Make sure decoderawtransaction throws if there is extra data - assert_raises(JSONRPCException, self.nodes[0].decoderawtransaction, rawTx + "00") + assert_raises_rpc_error(-22, "TX decode failed", self.nodes[0].decoderawtransaction, rawTx + "00") rawTxSigned = self.nodes[0].signrawtransaction(rawTx, scripts, privKeys) diff --git a/test/functional/smartfees.py b/test/functional/smartfees.py index bc42a319df..986f4546a8 100755 --- a/test/functional/smartfees.py +++ b/test/functional/smartfees.py @@ -141,11 +141,8 @@ def check_estimates(node, fees_seen, max_invalid, print_estimates = True): class EstimateFeeTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 3 - self.setup_clean_chain = False def setup_network(self): """ @@ -153,57 +150,16 @@ class EstimateFeeTest(BitcoinTestFramework): But first we need to use one node to create a lot of outputs which we will use to generate our transactions. """ - self.nodes = [] + self.add_nodes(3, extra_args=[["-maxorphantx=1000", "-whitelist=127.0.0.1"], + ["-blockmaxsize=17000", "-maxorphantx=1000", "-deprecatedrpc=estimatefee"], + ["-blockmaxsize=8000", "-maxorphantx=1000"]]) # Use node0 to mine blocks for input splitting - self.nodes.append(self.start_node(0, self.options.tmpdir, ["-maxorphantx=1000", - "-whitelist=127.0.0.1"])) - - self.log.info("This test is time consuming, please be patient") - self.log.info("Splitting inputs so we can generate tx's") - self.txouts = [] - self.txouts2 = [] - # Split a coinbase into two transaction puzzle outputs - split_inputs(self.nodes[0], self.nodes[0].listunspent(0), self.txouts, True) - - # Mine - while (len(self.nodes[0].getrawmempool()) > 0): - self.nodes[0].generate(1) - - # Repeatedly split those 2 outputs, doubling twice for each rep - # Use txouts to monitor the available utxo, since these won't be tracked in wallet - reps = 0 - while (reps < 5): - #Double txouts to txouts2 - while (len(self.txouts)>0): - split_inputs(self.nodes[0], self.txouts, self.txouts2) - while (len(self.nodes[0].getrawmempool()) > 0): - self.nodes[0].generate(1) - #Double txouts2 to txouts - while (len(self.txouts2)>0): - split_inputs(self.nodes[0], self.txouts2, self.txouts) - while (len(self.nodes[0].getrawmempool()) > 0): - self.nodes[0].generate(1) - reps += 1 - self.log.info("Finished splitting") - - # Now we can connect the other nodes, didn't want to connect them earlier - # so the estimates would not be affected by the splitting transactions # Node1 mines small blocks but that are bigger than the expected transaction rate. # NOTE: the CreateNewBlock code starts counting block size at 1,000 bytes, # (17k is room enough for 110 or so transactions) - self.nodes.append(self.start_node(1, self.options.tmpdir, - ["-blockmaxsize=17000", "-maxorphantx=1000"])) - connect_nodes(self.nodes[1], 0) - # Node2 is a stingy miner, that # produces too small blocks (room for only 55 or so transactions) - node2args = ["-blockmaxsize=8000", "-maxorphantx=1000"] - self.nodes.append(self.start_node(2, self.options.tmpdir, node2args)) - connect_nodes(self.nodes[0], 2) - connect_nodes(self.nodes[2], 1) - - self.sync_all() def transact_and_mine(self, numblocks, mining_node): min_fee = Decimal("0.00001") @@ -232,9 +188,51 @@ class EstimateFeeTest(BitcoinTestFramework): self.memutxo = newmem def run_test(self): + self.log.info("This test is time consuming, please be patient") + self.log.info("Splitting inputs so we can generate tx's") + # Make log handler available to helper functions global log log = self.log + + # Start node0 + self.start_node(0) + self.txouts = [] + self.txouts2 = [] + # Split a coinbase into two transaction puzzle outputs + split_inputs(self.nodes[0], self.nodes[0].listunspent(0), self.txouts, True) + + # Mine + while (len(self.nodes[0].getrawmempool()) > 0): + self.nodes[0].generate(1) + + # Repeatedly split those 2 outputs, doubling twice for each rep + # Use txouts to monitor the available utxo, since these won't be tracked in wallet + reps = 0 + while (reps < 5): + #Double txouts to txouts2 + while (len(self.txouts)>0): + split_inputs(self.nodes[0], self.txouts, self.txouts2) + while (len(self.nodes[0].getrawmempool()) > 0): + self.nodes[0].generate(1) + #Double txouts2 to txouts + while (len(self.txouts2)>0): + split_inputs(self.nodes[0], self.txouts2, self.txouts) + while (len(self.nodes[0].getrawmempool()) > 0): + self.nodes[0].generate(1) + reps += 1 + self.log.info("Finished splitting") + + # Now we can connect the other nodes, didn't want to connect them earlier + # so the estimates would not be affected by the splitting transactions + self.start_node(1) + self.start_node(2) + connect_nodes(self.nodes[1], 0) + connect_nodes(self.nodes[0], 2) + connect_nodes(self.nodes[2], 1) + + self.sync_all() + self.fees_per_kb = [] self.memutxo = [] self.confutxo = self.txouts # Start with the set of confirmed txouts after splitting diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py index 96bebe1ea1..2e2db5ffb2 100644 --- a/test/functional/test_framework/address.py +++ b/test/functional/test_framework/address.py @@ -7,6 +7,8 @@ from .script import hash256, hash160, sha256, CScript, OP_0 from .util import bytes_to_hex_str, hex_str_to_bytes +from . import segwit_addr + chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' def byte_to_base58(b, version): @@ -49,6 +51,22 @@ def key_to_p2sh_p2wpkh(key, main = False): p2shscript = CScript([OP_0, hash160(key)]) return script_to_p2sh(p2shscript, main) +def program_to_witness(version, program, main = False): + if (type(program) is str): + program = hex_str_to_bytes(program) + assert 0 <= version <= 16 + assert 2 <= len(program) <= 40 + assert version > 0 or len(program) in [20, 32] + return segwit_addr.encode("bc" if main else "bcrt", version, program) + +def script_to_p2wsh(script, main = False): + script = check_script(script) + return program_to_witness(0, sha256(script), main) + +def key_to_p2wpkh(key, main = False): + key = check_key(key) + return program_to_witness(0, hash160(key), main) + def script_to_p2sh_p2wsh(script, main = False): script = check_script(script) p2shscript = CScript([OP_0, sha256(script)]) diff --git a/test/functional/test_framework/bignum.py b/test/functional/test_framework/bignum.py index 024611da6e..db5ccd62c2 100644 --- a/test/functional/test_framework/bignum.py +++ b/test/functional/test_framework/bignum.py @@ -26,12 +26,6 @@ def bn2bin(v): i -= 1 return s -def bin2bn(s): - l = 0 - for ch in s: - l = (l << 8) | ch - return l - def bn2mpi(v): have_ext = False if v.bit_length() > 0: @@ -54,30 +48,6 @@ def bn2mpi(v): v_bin[0] |= 0x80 return s + ext + v_bin -def mpi2bn(s): - if len(s) < 4: - return None - s_size = bytes(s[:4]) - v_len = struct.unpack(b">I", s_size)[0] - if len(s) != (v_len + 4): - return None - if v_len == 0: - return 0 - - v_str = bytearray(s[4:]) - neg = False - i = v_str[0] - if i & 0x80: - neg = True - i &= ~0x80 - v_str[0] = i - - v = bin2bn(v_str) - - if neg: - return -v - return v - # bitcoin-specific little endian format, with implicit size def mpi2vch(s): r = s[4:] # strip size @@ -86,12 +56,3 @@ def mpi2vch(s): def bn2vch(v): return bytes(mpi2vch(bn2mpi(v))) - -def vch2mpi(s): - r = struct.pack(b">I", len(s)) # size - r += s[::-1] # reverse string, converting LE->BE - return r - -def vch2bn(s): - return mpi2bn(vch2mpi(s)) - diff --git a/test/functional/test_framework/blockstore.py b/test/functional/test_framework/blockstore.py index 4cfd682bb5..4b2170a03f 100644 --- a/test/functional/test_framework/blockstore.py +++ b/test/functional/test_framework/blockstore.py @@ -143,16 +143,6 @@ class TxStore(object): return None return value - def get_transaction(self, txhash): - ret = None - serialized_tx = self.get(txhash) - if serialized_tx is not None: - f = BytesIO(serialized_tx) - ret = CTransaction() - ret.deserialize(f) - ret.calc_sha256() - return ret - def add_transaction(self, tx): tx.calc_sha256() try: diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py index d0753276db..c6f596156a 100755 --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -48,8 +48,8 @@ MAX_BLOCK_BASE_SIZE = 1000000 COIN = 100000000 # 1 btc in satoshis NODE_NETWORK = (1 << 0) -NODE_GETUTXO = (1 << 1) -NODE_BLOOM = (1 << 2) +# NODE_GETUTXO = (1 << 1) +# NODE_BLOOM = (1 << 2) NODE_WITNESS = (1 << 3) NODE_UNSUPPORTED_SERVICE_BIT_5 = (1 << 5) NODE_UNSUPPORTED_SERVICE_BIT_7 = (1 << 7) @@ -1310,8 +1310,8 @@ class msg_getheaders(object): class msg_headers(object): command = b"headers" - def __init__(self): - self.headers = [] + def __init__(self, headers=None): + self.headers = headers if headers is not None else [] def deserialize(self, f): # comment in bitcoind indicates these should be deserialized as blocks @@ -1479,9 +1479,6 @@ class NodeConnCB(object): # before acquiring the global lock and delivering the next message. self.deliver_sleep_time = None - # Remember the services our peer has advertised - self.peer_services = None - # Message receiving methods def deliver(self, conn, message): @@ -1505,10 +1502,7 @@ class NodeConnCB(object): except: print("ERROR delivering %s (%s)" % (repr(message), sys.exc_info()[0])) - - def set_deliver_sleep_time(self, value): - with mininode_lock: - self.deliver_sleep_time = value + raise def get_deliver_sleep_time(self): with mininode_lock: @@ -1661,6 +1655,7 @@ class NodeConn(asyncore.dispatcher): self.dstaddr = dstaddr self.dstport = dstport self.create_socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) self.sendbuf = b"" self.recvbuf = b"" self.ver_send = 209 @@ -1708,13 +1703,10 @@ class NodeConn(asyncore.dispatcher): self.cb.on_close(self) def handle_read(self): - try: - t = self.recv(8192) - if len(t) > 0: - self.recvbuf += t - self.got_data() - except: - pass + t = self.recv(8192) + if len(t) > 0: + self.recvbuf += t + self.got_data() def readable(self): return True @@ -1780,8 +1772,10 @@ class NodeConn(asyncore.dispatcher): self.got_message(t) else: logger.warning("Received unknown command from %s:%d: '%s' %s" % (self.dstaddr, self.dstport, command, repr(msg))) + raise ValueError("Unknown command: '%s'" % (command)) except Exception as e: logger.exception('got_data:', repr(e)) + raise def send_message(self, message, pushbuf=False): if self.state != "connected" and not pushbuf: @@ -1799,7 +1793,14 @@ class NodeConn(asyncore.dispatcher): tmsg += h[:4] tmsg += data with mininode_lock: - self.sendbuf += tmsg + if (len(self.sendbuf) == 0 and not pushbuf): + try: + sent = self.send(tmsg) + self.sendbuf = tmsg[sent:] + except BlockingIOError: + self.sendbuf = tmsg + else: + self.sendbuf += tmsg self.last_sent = time.time() def got_message(self, message): @@ -1837,6 +1838,7 @@ class NetworkThread(Thread): disconnected.append(obj) [ obj.handle_close() for obj in disconnected ] asyncore.loop(0.1, use_poll=True, map=mininode_socket_map, count=1) + logger.debug("Network thread closing") # An exception we can raise if we detect a potential disconnect diff --git a/test/functional/test_framework/netutil.py b/test/functional/test_framework/netutil.py index 45d8e22d22..e5d415788f 100644 --- a/test/functional/test_framework/netutil.py +++ b/test/functional/test_framework/netutil.py @@ -15,17 +15,17 @@ import array import os from binascii import unhexlify, hexlify -STATE_ESTABLISHED = '01' -STATE_SYN_SENT = '02' -STATE_SYN_RECV = '03' -STATE_FIN_WAIT1 = '04' -STATE_FIN_WAIT2 = '05' -STATE_TIME_WAIT = '06' -STATE_CLOSE = '07' -STATE_CLOSE_WAIT = '08' -STATE_LAST_ACK = '09' +# STATE_ESTABLISHED = '01' +# STATE_SYN_SENT = '02' +# STATE_SYN_RECV = '03' +# STATE_FIN_WAIT1 = '04' +# STATE_FIN_WAIT2 = '05' +# STATE_TIME_WAIT = '06' +# STATE_CLOSE = '07' +# STATE_CLOSE_WAIT = '08' +# STATE_LAST_ACK = '09' STATE_LISTEN = '0A' -STATE_CLOSING = '0B' +# STATE_CLOSING = '0B' def get_socket_inodes(pid): ''' diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index 3d9572788e..8f5339a02a 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -23,9 +23,7 @@ import struct from .bignum import bn2vch -MAX_SCRIPT_SIZE = 10000 MAX_SCRIPT_ELEMENT_SIZE = 520 -MAX_SCRIPT_OPCODES = 201 OPCODE_NAMES = {} @@ -242,131 +240,6 @@ OP_PUBKEY = CScriptOp(0xfe) OP_INVALIDOPCODE = CScriptOp(0xff) -VALID_OPCODES = { - OP_1NEGATE, - OP_RESERVED, - OP_1, - OP_2, - OP_3, - OP_4, - OP_5, - OP_6, - OP_7, - OP_8, - OP_9, - OP_10, - OP_11, - OP_12, - OP_13, - OP_14, - OP_15, - OP_16, - - OP_NOP, - OP_VER, - OP_IF, - OP_NOTIF, - OP_VERIF, - OP_VERNOTIF, - OP_ELSE, - OP_ENDIF, - OP_VERIFY, - OP_RETURN, - - OP_TOALTSTACK, - OP_FROMALTSTACK, - OP_2DROP, - OP_2DUP, - OP_3DUP, - OP_2OVER, - OP_2ROT, - OP_2SWAP, - OP_IFDUP, - OP_DEPTH, - OP_DROP, - OP_DUP, - OP_NIP, - OP_OVER, - OP_PICK, - OP_ROLL, - OP_ROT, - OP_SWAP, - OP_TUCK, - - OP_CAT, - OP_SUBSTR, - OP_LEFT, - OP_RIGHT, - OP_SIZE, - - OP_INVERT, - OP_AND, - OP_OR, - OP_XOR, - OP_EQUAL, - OP_EQUALVERIFY, - OP_RESERVED1, - OP_RESERVED2, - - OP_1ADD, - OP_1SUB, - OP_2MUL, - OP_2DIV, - OP_NEGATE, - OP_ABS, - OP_NOT, - OP_0NOTEQUAL, - - OP_ADD, - OP_SUB, - OP_MUL, - OP_DIV, - OP_MOD, - OP_LSHIFT, - OP_RSHIFT, - - OP_BOOLAND, - OP_BOOLOR, - OP_NUMEQUAL, - OP_NUMEQUALVERIFY, - OP_NUMNOTEQUAL, - OP_LESSTHAN, - OP_GREATERTHAN, - OP_LESSTHANOREQUAL, - OP_GREATERTHANOREQUAL, - OP_MIN, - OP_MAX, - - OP_WITHIN, - - OP_RIPEMD160, - OP_SHA1, - OP_SHA256, - OP_HASH160, - OP_HASH256, - OP_CODESEPARATOR, - OP_CHECKSIG, - OP_CHECKSIGVERIFY, - OP_CHECKMULTISIG, - OP_CHECKMULTISIGVERIFY, - - OP_NOP1, - OP_CHECKLOCKTIMEVERIFY, - OP_CHECKSEQUENCEVERIFY, - OP_NOP4, - OP_NOP5, - OP_NOP6, - OP_NOP7, - OP_NOP8, - OP_NOP9, - OP_NOP10, - - OP_SMALLINTEGER, - OP_PUBKEYS, - OP_PUBKEYHASH, - OP_PUBKEY, -} - OPCODE_NAMES.update({ OP_0 : 'OP_0', OP_PUSHDATA1 : 'OP_PUSHDATA1', @@ -486,124 +359,6 @@ OPCODE_NAMES.update({ OP_INVALIDOPCODE : 'OP_INVALIDOPCODE', }) -OPCODES_BY_NAME = { - 'OP_0' : OP_0, - 'OP_PUSHDATA1' : OP_PUSHDATA1, - 'OP_PUSHDATA2' : OP_PUSHDATA2, - 'OP_PUSHDATA4' : OP_PUSHDATA4, - 'OP_1NEGATE' : OP_1NEGATE, - 'OP_RESERVED' : OP_RESERVED, - 'OP_1' : OP_1, - 'OP_2' : OP_2, - 'OP_3' : OP_3, - 'OP_4' : OP_4, - 'OP_5' : OP_5, - 'OP_6' : OP_6, - 'OP_7' : OP_7, - 'OP_8' : OP_8, - 'OP_9' : OP_9, - 'OP_10' : OP_10, - 'OP_11' : OP_11, - 'OP_12' : OP_12, - 'OP_13' : OP_13, - 'OP_14' : OP_14, - 'OP_15' : OP_15, - 'OP_16' : OP_16, - 'OP_NOP' : OP_NOP, - 'OP_VER' : OP_VER, - 'OP_IF' : OP_IF, - 'OP_NOTIF' : OP_NOTIF, - 'OP_VERIF' : OP_VERIF, - 'OP_VERNOTIF' : OP_VERNOTIF, - 'OP_ELSE' : OP_ELSE, - 'OP_ENDIF' : OP_ENDIF, - 'OP_VERIFY' : OP_VERIFY, - 'OP_RETURN' : OP_RETURN, - 'OP_TOALTSTACK' : OP_TOALTSTACK, - 'OP_FROMALTSTACK' : OP_FROMALTSTACK, - 'OP_2DROP' : OP_2DROP, - 'OP_2DUP' : OP_2DUP, - 'OP_3DUP' : OP_3DUP, - 'OP_2OVER' : OP_2OVER, - 'OP_2ROT' : OP_2ROT, - 'OP_2SWAP' : OP_2SWAP, - 'OP_IFDUP' : OP_IFDUP, - 'OP_DEPTH' : OP_DEPTH, - 'OP_DROP' : OP_DROP, - 'OP_DUP' : OP_DUP, - 'OP_NIP' : OP_NIP, - 'OP_OVER' : OP_OVER, - 'OP_PICK' : OP_PICK, - 'OP_ROLL' : OP_ROLL, - 'OP_ROT' : OP_ROT, - 'OP_SWAP' : OP_SWAP, - 'OP_TUCK' : OP_TUCK, - 'OP_CAT' : OP_CAT, - 'OP_SUBSTR' : OP_SUBSTR, - 'OP_LEFT' : OP_LEFT, - 'OP_RIGHT' : OP_RIGHT, - 'OP_SIZE' : OP_SIZE, - 'OP_INVERT' : OP_INVERT, - 'OP_AND' : OP_AND, - 'OP_OR' : OP_OR, - 'OP_XOR' : OP_XOR, - 'OP_EQUAL' : OP_EQUAL, - 'OP_EQUALVERIFY' : OP_EQUALVERIFY, - 'OP_RESERVED1' : OP_RESERVED1, - 'OP_RESERVED2' : OP_RESERVED2, - 'OP_1ADD' : OP_1ADD, - 'OP_1SUB' : OP_1SUB, - 'OP_2MUL' : OP_2MUL, - 'OP_2DIV' : OP_2DIV, - 'OP_NEGATE' : OP_NEGATE, - 'OP_ABS' : OP_ABS, - 'OP_NOT' : OP_NOT, - 'OP_0NOTEQUAL' : OP_0NOTEQUAL, - 'OP_ADD' : OP_ADD, - 'OP_SUB' : OP_SUB, - 'OP_MUL' : OP_MUL, - 'OP_DIV' : OP_DIV, - 'OP_MOD' : OP_MOD, - 'OP_LSHIFT' : OP_LSHIFT, - 'OP_RSHIFT' : OP_RSHIFT, - 'OP_BOOLAND' : OP_BOOLAND, - 'OP_BOOLOR' : OP_BOOLOR, - 'OP_NUMEQUAL' : OP_NUMEQUAL, - 'OP_NUMEQUALVERIFY' : OP_NUMEQUALVERIFY, - 'OP_NUMNOTEQUAL' : OP_NUMNOTEQUAL, - 'OP_LESSTHAN' : OP_LESSTHAN, - 'OP_GREATERTHAN' : OP_GREATERTHAN, - 'OP_LESSTHANOREQUAL' : OP_LESSTHANOREQUAL, - 'OP_GREATERTHANOREQUAL' : OP_GREATERTHANOREQUAL, - 'OP_MIN' : OP_MIN, - 'OP_MAX' : OP_MAX, - 'OP_WITHIN' : OP_WITHIN, - 'OP_RIPEMD160' : OP_RIPEMD160, - 'OP_SHA1' : OP_SHA1, - 'OP_SHA256' : OP_SHA256, - 'OP_HASH160' : OP_HASH160, - 'OP_HASH256' : OP_HASH256, - 'OP_CODESEPARATOR' : OP_CODESEPARATOR, - 'OP_CHECKSIG' : OP_CHECKSIG, - 'OP_CHECKSIGVERIFY' : OP_CHECKSIGVERIFY, - 'OP_CHECKMULTISIG' : OP_CHECKMULTISIG, - 'OP_CHECKMULTISIGVERIFY' : OP_CHECKMULTISIGVERIFY, - 'OP_NOP1' : OP_NOP1, - 'OP_CHECKLOCKTIMEVERIFY' : OP_CHECKLOCKTIMEVERIFY, - 'OP_CHECKSEQUENCEVERIFY' : OP_CHECKSEQUENCEVERIFY, - 'OP_NOP4' : OP_NOP4, - 'OP_NOP5' : OP_NOP5, - 'OP_NOP6' : OP_NOP6, - 'OP_NOP7' : OP_NOP7, - 'OP_NOP8' : OP_NOP8, - 'OP_NOP9' : OP_NOP9, - 'OP_NOP10' : OP_NOP10, - 'OP_SMALLINTEGER' : OP_SMALLINTEGER, - 'OP_PUBKEYS' : OP_PUBKEYS, - 'OP_PUBKEYHASH' : OP_PUBKEYHASH, - 'OP_PUBKEY' : OP_PUBKEY, -} - class CScriptInvalidError(Exception): """Base class for CScript exceptions""" pass diff --git a/test/functional/test_framework/segwit_addr.py b/test/functional/test_framework/segwit_addr.py new file mode 100644 index 0000000000..02368e938f --- /dev/null +++ b/test/functional/test_framework/segwit_addr.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 Pieter Wuille +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Reference implementation for Bech32 and segwit addresses.""" + + +CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" + + +def bech32_polymod(values): + """Internal function that computes the Bech32 checksum.""" + generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] + chk = 1 + for value in values: + top = chk >> 25 + chk = (chk & 0x1ffffff) << 5 ^ value + for i in range(5): + chk ^= generator[i] if ((top >> i) & 1) else 0 + return chk + + +def bech32_hrp_expand(hrp): + """Expand the HRP into values for checksum computation.""" + return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp] + + +def bech32_verify_checksum(hrp, data): + """Verify a checksum given HRP and converted data characters.""" + return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1 + + +def bech32_create_checksum(hrp, data): + """Compute the checksum values given HRP and data.""" + values = bech32_hrp_expand(hrp) + data + polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1 + return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)] + + +def bech32_encode(hrp, data): + """Compute a Bech32 string given HRP and data values.""" + combined = data + bech32_create_checksum(hrp, data) + return hrp + '1' + ''.join([CHARSET[d] for d in combined]) + + +def bech32_decode(bech): + """Validate a Bech32 string, and determine HRP and data.""" + if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or + (bech.lower() != bech and bech.upper() != bech)): + return (None, None) + bech = bech.lower() + pos = bech.rfind('1') + if pos < 1 or pos + 7 > len(bech) or len(bech) > 90: + return (None, None) + if not all(x in CHARSET for x in bech[pos+1:]): + return (None, None) + hrp = bech[:pos] + data = [CHARSET.find(x) for x in bech[pos+1:]] + if not bech32_verify_checksum(hrp, data): + return (None, None) + return (hrp, data[:-6]) + + +def convertbits(data, frombits, tobits, pad=True): + """General power-of-2 base conversion.""" + acc = 0 + bits = 0 + ret = [] + maxv = (1 << tobits) - 1 + max_acc = (1 << (frombits + tobits - 1)) - 1 + for value in data: + if value < 0 or (value >> frombits): + return None + acc = ((acc << frombits) | value) & max_acc + bits += frombits + while bits >= tobits: + bits -= tobits + ret.append((acc >> bits) & maxv) + if pad: + if bits: + ret.append((acc << (tobits - bits)) & maxv) + elif bits >= frombits or ((acc << (tobits - bits)) & maxv): + return None + return ret + + +def decode(hrp, addr): + """Decode a segwit address.""" + hrpgot, data = bech32_decode(addr) + if hrpgot != hrp: + return (None, None) + decoded = convertbits(data[1:], 5, 8, False) + if decoded is None or len(decoded) < 2 or len(decoded) > 40: + return (None, None) + if data[0] > 16: + return (None, None) + if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32: + return (None, None) + return (data[0], decoded) + + +def encode(hrp, witver, witprog): + """Encode a segwit address.""" + ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5)) + if decode(hrp, ret) == (None, None): + return None + return ret diff --git a/test/functional/test_framework/socks5.py b/test/functional/test_framework/socks5.py index a08b03ed24..0070844168 100644 --- a/test/functional/test_framework/socks5.py +++ b/test/functional/test_framework/socks5.py @@ -91,7 +91,7 @@ class Socks5Connection(object): self.conn.sendall(bytearray([0x01, 0x00])) # Read connect request - (ver,cmd,rsv,atyp) = recvall(self.conn, 4) + ver, cmd, _, atyp = recvall(self.conn, 4) if ver != 0x05: raise IOError('Invalid socks version %i in connect request' % ver) if cmd != Command.CONNECT: diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 7903bb0045..381513ab9e 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -43,62 +43,33 @@ TEST_EXIT_PASSED = 0 TEST_EXIT_FAILED = 1 TEST_EXIT_SKIPPED = 77 -BITCOIND_PROC_WAIT_TIMEOUT = 60 - class BitcoinTestFramework(object): """Base class for a bitcoin test script. - Individual bitcoin test scripts should subclass this class and override the following methods: + Individual bitcoin test scripts should subclass this class and override the set_test_params() and run_test() methods. + + Individual tests can also override the following methods to customize the test setup: - - __init__() - add_options() - setup_chain() - setup_network() - - run_test() + - setup_nodes() - The main() method should not be overridden. + The __init__() and main() methods should not be overridden. This class also contains various public and private helper methods.""" - # Methods to override in subclass test scripts. def __init__(self): - self.num_nodes = 4 + """Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method""" self.setup_clean_chain = False self.nodes = [] self.mocktime = 0 + self.set_test_params() - def add_options(self, parser): - pass - - def setup_chain(self): - self.log.info("Initializing test directory " + self.options.tmpdir) - if self.setup_clean_chain: - self._initialize_chain_clean(self.options.tmpdir, self.num_nodes) - else: - self._initialize_chain(self.options.tmpdir, self.num_nodes, self.options.cachedir) - - def setup_network(self): - self.setup_nodes() - - # Connect the nodes as a "chain". This allows us - # to split the network between nodes 1 and 2 to get - # two halves that can work on competing chains. - for i in range(self.num_nodes - 1): - connect_nodes_bi(self.nodes, i, i + 1) - self.sync_all() - - def setup_nodes(self): - extra_args = None - if hasattr(self, "extra_args"): - extra_args = self.extra_args - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args) - - def run_test(self): - raise NotImplementedError - - # Main function. This should not be overridden by the subclass test scripts. + assert hasattr(self, "num_nodes"), "Test must set self.num_nodes in set_test_params()" def main(self): + """Main function. This should not be overridden by the subclass test scripts.""" parser = optparse.OptionParser(usage="%prog [options]") parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true", @@ -202,26 +173,50 @@ class BitcoinTestFramework(object): logging.shutdown() sys.exit(TEST_EXIT_FAILED) - # Public helper methods. These can be accessed by the subclass test scripts. + # Methods to override in subclass test scripts. + def set_test_params(self): + """Tests must this method to change default values for number of nodes, topology, etc""" + raise NotImplementedError - def start_node(self, i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None, stderr=None): - """Start a bitcoind and return RPC connection to it""" + def add_options(self, parser): + """Override this method to add command-line options to the test""" + pass - if extra_args is None: - extra_args = [] - if binary is None: - binary = os.getenv("BITCOIND", "bitcoind") - node = TestNode(i, dirname, extra_args, rpchost, timewait, binary, stderr, self.mocktime, coverage_dir=self.options.coveragedir) - node.start() - node.wait_for_rpc_connection() + def setup_chain(self): + """Override this method to customize blockchain setup""" + self.log.info("Initializing test directory " + self.options.tmpdir) + if self.setup_clean_chain: + self._initialize_chain_clean() + else: + self._initialize_chain() - if self.options.coveragedir is not None: - coverage.write_all_rpc_commands(self.options.coveragedir, node.rpc) + def setup_network(self): + """Override this method to customize test network topology""" + self.setup_nodes() + + # Connect the nodes as a "chain". This allows us + # to split the network between nodes 1 and 2 to get + # two halves that can work on competing chains. + for i in range(self.num_nodes - 1): + connect_nodes_bi(self.nodes, i, i + 1) + self.sync_all() + + def setup_nodes(self): + """Override this method to customize test node setup""" + extra_args = None + if hasattr(self, "extra_args"): + extra_args = self.extra_args + self.add_nodes(self.num_nodes, extra_args) + self.start_nodes() + + def run_test(self): + """Tests must override this method to define test logic""" + raise NotImplementedError - return node + # Public helper methods. These can be accessed by the subclass test scripts. - def start_nodes(self, num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None): - """Start multiple bitcoinds, return RPC connections to them""" + def add_nodes(self, num_nodes, extra_args=None, rpchost=None, timewait=None, binary=None): + """Instantiate TestNode objects""" if extra_args is None: extra_args = [[]] * num_nodes @@ -229,12 +224,30 @@ class BitcoinTestFramework(object): binary = [None] * num_nodes assert_equal(len(extra_args), num_nodes) assert_equal(len(binary), num_nodes) - nodes = [] + for i in range(num_nodes): + self.nodes.append(TestNode(i, self.options.tmpdir, extra_args[i], rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir)) + + def start_node(self, i, extra_args=None, stderr=None): + """Start a bitcoind""" + + node = self.nodes[i] + + node.start(extra_args, stderr) + node.wait_for_rpc_connection() + + if self.options.coveragedir is not None: + coverage.write_all_rpc_commands(self.options.coveragedir, node.rpc) + + def start_nodes(self, extra_args=None): + """Start multiple bitcoinds""" + + if extra_args is None: + extra_args = [None] * self.num_nodes + assert_equal(len(extra_args), self.num_nodes) try: - for i in range(num_nodes): - nodes.append(TestNode(i, dirname, extra_args[i], rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir)) - nodes[i].start() - for node in nodes: + for i, node in enumerate(self.nodes): + node.start(extra_args[i]) + for node in self.nodes: node.wait_for_rpc_connection() except: # If one node failed to start, stop the others @@ -242,16 +255,13 @@ class BitcoinTestFramework(object): raise if self.options.coveragedir is not None: - for node in nodes: + for node in self.nodes: coverage.write_all_rpc_commands(self.options.coveragedir, node.rpc) - return nodes - def stop_node(self, i): """Stop a bitcoind test node""" self.nodes[i].stop_node() - while not self.nodes[i].is_node_stopped(): - time.sleep(0.1) + self.nodes[i].wait_until_stopped() def stop_nodes(self): """Stop multiple bitcoind test nodes""" @@ -261,13 +271,17 @@ class BitcoinTestFramework(object): for node in self.nodes: # Wait for nodes to stop - while not node.is_node_stopped(): - time.sleep(0.1) + node.wait_until_stopped() - def assert_start_raises_init_error(self, i, dirname, extra_args=None, expected_msg=None): + def restart_node(self, i, extra_args=None): + """Stop and start a test node""" + self.stop_node(i) + self.start_node(i, extra_args) + + def assert_start_raises_init_error(self, i, extra_args=None, expected_msg=None): with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr: try: - self.start_node(i, dirname, extra_args, stderr=log_stderr) + self.start_node(i, extra_args, stderr=log_stderr) self.stop_node(i) except Exception as e: assert 'bitcoind exited' in str(e) # node must have shutdown @@ -356,16 +370,16 @@ class BitcoinTestFramework(object): rpc_handler.setLevel(logging.DEBUG) rpc_logger.addHandler(rpc_handler) - def _initialize_chain(self, test_dir, num_nodes, cachedir): + def _initialize_chain(self): """Initialize a pre-mined blockchain for use by the test. Create a cache of a 200-block-long chain (with wallet) for MAX_NODES Afterward, create num_nodes copies from the cache.""" - assert num_nodes <= MAX_NODES + assert self.num_nodes <= MAX_NODES create_cache = False for i in range(MAX_NODES): - if not os.path.isdir(os.path.join(cachedir, 'node' + str(i))): + if not os.path.isdir(os.path.join(self.options.cachedir, 'node' + str(i))): create_cache = True break @@ -374,18 +388,18 @@ class BitcoinTestFramework(object): # find and delete old cache directories if any exist for i in range(MAX_NODES): - if os.path.isdir(os.path.join(cachedir, "node" + str(i))): - shutil.rmtree(os.path.join(cachedir, "node" + str(i))) + if os.path.isdir(os.path.join(self.options.cachedir, "node" + str(i))): + shutil.rmtree(os.path.join(self.options.cachedir, "node" + str(i))) # Create cache directories, run bitcoinds: for i in range(MAX_NODES): - datadir = initialize_datadir(cachedir, i) + datadir = initialize_datadir(self.options.cachedir, i) args = [os.getenv("BITCOIND", "bitcoind"), "-server", "-keypool=1", "-datadir=" + datadir, "-discover=0"] if i > 0: args.append("-connect=127.0.0.1:" + str(p2p_port(0))) - self.nodes.append(TestNode(i, cachedir, extra_args=[], rpchost=None, timewait=None, binary=None, stderr=None, mocktime=self.mocktime, coverage_dir=None)) + self.nodes.append(TestNode(i, self.options.cachedir, extra_args=[], rpchost=None, timewait=None, binary=None, stderr=None, mocktime=self.mocktime, coverage_dir=None)) self.nodes[i].args = args - self.nodes[i].start() + self.start_node(i) # Wait for RPC connections to be ready for node in self.nodes: @@ -414,24 +428,24 @@ class BitcoinTestFramework(object): self.nodes = [] self.disable_mocktime() for i in range(MAX_NODES): - os.remove(log_filename(cachedir, i, "debug.log")) - os.remove(log_filename(cachedir, i, "db.log")) - os.remove(log_filename(cachedir, i, "peers.dat")) - os.remove(log_filename(cachedir, i, "fee_estimates.dat")) - - for i in range(num_nodes): - from_dir = os.path.join(cachedir, "node" + str(i)) - to_dir = os.path.join(test_dir, "node" + str(i)) + os.remove(log_filename(self.options.cachedir, i, "debug.log")) + os.remove(log_filename(self.options.cachedir, i, "db.log")) + os.remove(log_filename(self.options.cachedir, i, "peers.dat")) + os.remove(log_filename(self.options.cachedir, i, "fee_estimates.dat")) + + for i in range(self.num_nodes): + from_dir = os.path.join(self.options.cachedir, "node" + str(i)) + to_dir = os.path.join(self.options.tmpdir, "node" + str(i)) shutil.copytree(from_dir, to_dir) - initialize_datadir(test_dir, i) # Overwrite port/rpcport in bitcoin.conf + initialize_datadir(self.options.tmpdir, i) # Overwrite port/rpcport in bitcoin.conf - def _initialize_chain_clean(self, test_dir, num_nodes): + def _initialize_chain_clean(self): """Initialize empty blockchain for use by the test. Create an empty blockchain and num_nodes wallets. Useful if a test case wants complete control over initialization.""" - for i in range(num_nodes): - initialize_datadir(test_dir, i) + for i in range(self.num_nodes): + initialize_datadir(self.options.tmpdir, i) class ComparisonTestFramework(BitcoinTestFramework): """Test framework for doing p2p comparison testing @@ -441,8 +455,7 @@ class ComparisonTestFramework(BitcoinTestFramework): - 2 binaries: 1 test binary, 1 ref binary - n>2 binaries: 1 test binary, n-1 ref binaries""" - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True @@ -455,13 +468,13 @@ class ComparisonTestFramework(BitcoinTestFramework): help="bitcoind binary to use for reference nodes (if any)") def setup_network(self): - extra_args = [['-whitelist=127.0.0.1']]*self.num_nodes + extra_args = [['-whitelist=127.0.0.1']] * self.num_nodes if hasattr(self, "extra_args"): extra_args = self.extra_args - self.nodes = self.start_nodes( - self.num_nodes, self.options.tmpdir, extra_args, - binary=[self.options.testbinary] + - [self.options.refbinary] * (self.num_nodes - 1)) + self.add_nodes(self.num_nodes, extra_args, + binary=[self.options.testbinary] + + [self.options.refbinary] * (self.num_nodes - 1)) + self.start_nodes() class SkipTest(Exception): """This exception is raised to skip a test""" diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 7c1325e691..12dab57a02 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -17,9 +17,12 @@ from .util import ( assert_equal, get_rpc_proxy, rpc_url, + wait_until, ) from .authproxy import JSONRPCException +BITCOIND_PROC_WAIT_TIMEOUT = 60 + class TestNode(): """A class for representing a bitcoind node under test. @@ -65,9 +68,13 @@ class TestNode(): assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection" return self.rpc.__getattr__(*args, **kwargs) - def start(self): + def start(self, extra_args=None, stderr=None): """Start the node.""" - self.process = subprocess.Popen(self.args + self.extra_args, stderr=self.stderr) + if extra_args is None: + extra_args = self.extra_args + if stderr is None: + stderr = self.stderr + self.process = subprocess.Popen(self.args + extra_args, stderr=stderr) self.running = True self.log.debug("bitcoind started, waiting for RPC to come up") @@ -78,7 +85,7 @@ class TestNode(): for _ in range(poll_per_s * self.rpc_timeout): assert self.process.poll() is None, "bitcoind exited with status %i during initialization" % self.process.returncode try: - self.rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, coveragedir=self.coverage_dir) + self.rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir) self.rpc.getblockcount() # If the call to getblockcount() succeeds then the RPC connection is up self.rpc_connected = True @@ -121,14 +128,20 @@ class TestNode(): if not self.running: return True return_code = self.process.poll() - if return_code is not None: - # process has stopped. Assert that it didn't return an error code. - assert_equal(return_code, 0) - self.running = False - self.process = None - self.log.debug("Node stopped") - return True - return False + if return_code is None: + return False + + # process has stopped. Assert that it didn't return an error code. + assert_equal(return_code, 0) + self.running = False + self.process = None + self.rpc_connected = False + self.rpc = None + self.log.debug("Node stopped") + return True + + def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT): + wait_until(self.is_node_stopped, timeout=timeout) def node_encrypt_wallet(self, passphrase): """"Encrypts the wallet. @@ -136,17 +149,22 @@ class TestNode(): This causes bitcoind to shutdown, so this method takes care of cleaning up resources.""" self.encryptwallet(passphrase) - while not self.is_node_stopped(): - time.sleep(0.1) - self.rpc = None - self.rpc_connected = False + self.wait_until_stopped() class TestNodeCLI(): """Interface to bitcoin-cli for an individual node""" def __init__(self, binary, datadir): + self.args = [] self.binary = binary self.datadir = datadir + self.input = None + + def __call__(self, *args, input=None): + # TestNodeCLI is callable with bitcoin-cli command-line args + self.args = [str(arg) for arg in args] + self.input = input + return self def __getattr__(self, command): def dispatcher(*args, **kwargs): @@ -159,9 +177,14 @@ class TestNodeCLI(): pos_args = [str(arg) for arg in args] named_args = [str(key) + "=" + str(value) for (key, value) in kwargs.items()] assert not (pos_args and named_args), "Cannot use positional arguments and named arguments in the same bitcoin-cli call" - p_args = [self.binary, "-datadir=" + self.datadir] + p_args = [self.binary, "-datadir=" + self.datadir] + self.args if named_args: p_args += ["-named"] p_args += [command] + pos_args + named_args - cli_output = subprocess.check_output(p_args, universal_newlines=True) - return json.loads(cli_output, parse_float=decimal.Decimal) + process = subprocess.Popen(p_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + cli_stdout, cli_stderr = process.communicate(input=self.input) + returncode = process.poll() + if returncode: + # Ignore cli_stdout, raise with cli_stderr + raise subprocess.CalledProcessError(returncode, self.binary, output=cli_stderr) + return json.loads(cli_stdout, parse_float=decimal.Decimal) diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index a14cda07d0..102c903018 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -7,11 +7,13 @@ from base64 import b64encode from binascii import hexlify, unhexlify from decimal import Decimal, ROUND_DOWN +import hashlib import json import logging import os import random import re +from subprocess import CalledProcessError import time from . import coverage @@ -49,6 +51,8 @@ def assert_raises(exc, fun, *args, **kwds): def assert_raises_message(exc, message, fun, *args, **kwds): try: fun(*args, **kwds) + except JSONRPCException: + raise AssertionError("Use assert_raises_rpc_error() to test RPC failures") except exc as e: if message is not None and message not in e.error['message']: raise AssertionError("Expected substring not found:" + e.error['message']) @@ -57,22 +61,53 @@ def assert_raises_message(exc, message, fun, *args, **kwds): else: raise AssertionError("No exception raised") -def assert_raises_jsonrpc(code, message, fun, *args, **kwds): +def assert_raises_process_error(returncode, output, fun, *args, **kwds): + """Execute a process and asserts the process return code and output. + + Calls function `fun` with arguments `args` and `kwds`. Catches a CalledProcessError + and verifies that the return code and output are as expected. Throws AssertionError if + no CalledProcessError was raised or if the return code and output are not as expected. + + Args: + returncode (int): the process return code. + output (string): [a substring of] the process output. + fun (function): the function to call. This should execute a process. + args*: positional arguments for the function. + kwds**: named arguments for the function. + """ + try: + fun(*args, **kwds) + except CalledProcessError as e: + if returncode != e.returncode: + raise AssertionError("Unexpected returncode %i" % e.returncode) + if output not in e.output: + raise AssertionError("Expected substring not found:" + e.output) + else: + raise AssertionError("No exception raised") + +def assert_raises_rpc_error(code, message, fun, *args, **kwds): """Run an RPC and verify that a specific JSONRPC exception code and message is raised. Calls function `fun` with arguments `args` and `kwds`. Catches a JSONRPCException and verifies that the error code and message are as expected. Throws AssertionError if - no JSONRPCException was returned or if the error code/message are not as expected. + no JSONRPCException was raised or if the error code/message are not as expected. Args: code (int), optional: the error code returned by the RPC call (defined in src/rpc/protocol.h). Set to None if checking the error code is not required. message (string), optional: [a substring of] the error string returned by the - RPC call. Set to None if checking the error string is not required + RPC call. Set to None if checking the error string is not required. fun (function): the function to call. This should be the name of an RPC. args*: positional arguments for the function. kwds**: named arguments for the function. """ + assert try_rpc(code, message, fun, *args, **kwds), "No exception raised" + +def try_rpc(code, message, fun, *args, **kwds): + """Tries to run an rpc command. + + Test against error code and message if the rpc fails. + Returns whether a JSONRPCException was raised.""" try: fun(*args, **kwds) except JSONRPCException as e: @@ -81,10 +116,11 @@ def assert_raises_jsonrpc(code, message, fun, *args, **kwds): raise AssertionError("Unexpected JSONRPC error code %i" % e.error["code"]) if (message is not None) and (message not in e.error['message']): raise AssertionError("Expected substring not found:" + e.error['message']) + return True except Exception as e: raise AssertionError("Unexpected exception raised: " + type(e).__name__) else: - raise AssertionError("No exception raised") + return False def assert_is_hex_string(string): try: @@ -148,6 +184,13 @@ def count_bytes(hex_string): def bytes_to_hex_str(byte_str): return hexlify(byte_str).decode('ascii') +def hash256(byte_str): + sha256 = hashlib.sha256() + sha256.update(byte_str) + sha256d = hashlib.sha256() + sha256d.update(sha256.digest()) + return sha256d.digest()[::-1] + def hex_str_to_bytes(hex_str): return unhexlify(hex_str.encode('ascii')) diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 59c236ce15..8c4651f6e0 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -98,6 +98,7 @@ BASE_SCRIPTS= [ 'disconnect_ban.py', 'decodescript.py', 'blockchain.py', + 'deprecated_rpc.py', 'disablewallet.py', 'net.py', 'keypool.py', @@ -121,6 +122,8 @@ BASE_SCRIPTS= [ 'bip65-cltv-p2p.py', 'uptime.py', 'resendwallettransactions.py', + 'minchainwork.py', + 'p2p-fingerprint.py', ] EXTENDED_SCRIPTS = [ @@ -146,7 +149,7 @@ EXTENDED_SCRIPTS = [ 'example_test.py', 'txn_doublespend.py', 'txn_clone.py --mineblock', - 'forknotify.py', + 'notifications.py', 'invalidateblock.py', 'p2p-acceptblock.py', 'replace-by-fee.py', diff --git a/test/functional/txn_clone.py b/test/functional/txn_clone.py index 9b81af96cf..740bb2d4c5 100755 --- a/test/functional/txn_clone.py +++ b/test/functional/txn_clone.py @@ -8,11 +8,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class TxnMallTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 4 - self.setup_clean_chain = False def add_options(self, parser): parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true", diff --git a/test/functional/txn_doublespend.py b/test/functional/txn_doublespend.py index 1bd3b3271c..69629ef951 100755 --- a/test/functional/txn_doublespend.py +++ b/test/functional/txn_doublespend.py @@ -8,11 +8,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class TxnMallTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 4 - self.setup_clean_chain = False def add_options(self, parser): parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true", diff --git a/test/functional/uptime.py b/test/functional/uptime.py index b20d6f5cb6..78236b2393 100755 --- a/test/functional/uptime.py +++ b/test/functional/uptime.py @@ -13,9 +13,7 @@ from test_framework.test_framework import BitcoinTestFramework class UptimeTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - + def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True diff --git a/test/functional/wallet-accounts.py b/test/functional/wallet-accounts.py index 158aa9ae89..40726d2a76 100755 --- a/test/functional/wallet-accounts.py +++ b/test/functional/wallet-accounts.py @@ -17,9 +17,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal class WalletAccountsTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [[]] diff --git a/test/functional/wallet-dump.py b/test/functional/wallet-dump.py index 61ad00330b..47de8777a6 100755 --- a/test/functional/wallet-dump.py +++ b/test/functional/wallet-dump.py @@ -7,7 +7,7 @@ import os from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal +from test_framework.util import (assert_equal, assert_raises_rpc_error) def read_dump(file_name, addrs, hd_master_addr_old): @@ -56,10 +56,7 @@ def read_dump(file_name, addrs, hd_master_addr_old): class WalletDumpTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = False + def set_test_params(self): self.num_nodes = 1 self.extra_args = [["-keypool=90"]] @@ -68,7 +65,8 @@ class WalletDumpTest(BitcoinTestFramework): # longer than the default 30 seconds due to an expensive # CWallet::TopUpKeyPool call, and the encryptwallet RPC made later in # the test often takes even longer. - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args, timewait=60) + self.add_nodes(self.num_nodes, self.extra_args, timewait=60) + self.start_nodes() def run_test (self): tmpdir = self.options.tmpdir @@ -95,17 +93,20 @@ class WalletDumpTest(BitcoinTestFramework): #encrypt wallet, restart, unlock and dump self.nodes[0].node_encrypt_wallet('test') - self.nodes[0] = self.start_node(0, self.options.tmpdir, self.extra_args[0]) + self.start_node(0) self.nodes[0].walletpassphrase('test', 10) # Should be a no-op: self.nodes[0].keypoolrefill() self.nodes[0].dumpwallet(tmpdir + "/node0/wallet.encrypted.dump") - found_addr, found_addr_chg, found_addr_rsv, hd_master_addr_enc = \ + found_addr, found_addr_chg, found_addr_rsv, _ = \ read_dump(tmpdir + "/node0/wallet.encrypted.dump", addrs, hd_master_addr_unenc) assert_equal(found_addr, test_addr_count) assert_equal(found_addr_chg, 90*2 + 50) # old reserve keys are marked as change now assert_equal(found_addr_rsv, 90*2) + # Overwriting should fail + assert_raises_rpc_error(-8, "already exists", self.nodes[0].dumpwallet, tmpdir + "/node0/wallet.unencrypted.dump") + if __name__ == '__main__': WalletDumpTest().main () diff --git a/test/functional/wallet-encryption.py b/test/functional/wallet-encryption.py index 8fea4140db..db62e1e30f 100755 --- a/test/functional/wallet-encryption.py +++ b/test/functional/wallet-encryption.py @@ -6,16 +6,14 @@ import time -from test_framework.test_framework import BitcoinTestFramework, BITCOIND_PROC_WAIT_TIMEOUT +from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, - assert_raises_jsonrpc, + assert_raises_rpc_error, ) class WalletEncryptionTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 @@ -31,10 +29,10 @@ class WalletEncryptionTest(BitcoinTestFramework): # Encrypt the wallet self.nodes[0].node_encrypt_wallet(passphrase) - self.nodes[0] = self.start_node(0, self.options.tmpdir) + self.start_node(0) # Test that the wallet is encrypted - assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address) + assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address) # Check that walletpassphrase works self.nodes[0].walletpassphrase(passphrase, 2) @@ -42,20 +40,20 @@ class WalletEncryptionTest(BitcoinTestFramework): # Check that the timeout is right time.sleep(2) - assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address) + assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address) # Test wrong passphrase - assert_raises_jsonrpc(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase + "wrong", 10) + assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase + "wrong", 10) # Test walletlock self.nodes[0].walletpassphrase(passphrase, 84600) assert_equal(privkey, self.nodes[0].dumpprivkey(address)) self.nodes[0].walletlock() - assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address) + assert_raises_rpc_error(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address) # Test passphrase changes self.nodes[0].walletpassphrasechange(passphrase, passphrase2) - assert_raises_jsonrpc(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase, 10) + assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase, 10) self.nodes[0].walletpassphrase(passphrase2, 10) assert_equal(privkey, self.nodes[0].dumpprivkey(address)) diff --git a/test/functional/wallet-hd.py b/test/functional/wallet-hd.py index 751512301e..5ef3bf5bff 100755 --- a/test/functional/wallet-hd.py +++ b/test/functional/wallet-hd.py @@ -11,22 +11,19 @@ from test_framework.util import ( ) import shutil - class WalletHDTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 - self.extra_args = [['-usehd=0'], ['-usehd=1', '-keypool=0']] + self.extra_args = [[], ['-keypool=0']] def run_test (self): tmpdir = self.options.tmpdir # Make sure can't switch off usehd after wallet creation self.stop_node(1) - self.assert_start_raises_init_error(1, self.options.tmpdir, ['-usehd=0'], 'already existing HD wallet') - self.nodes[1] = self.start_node(1, self.options.tmpdir, self.extra_args[1]) + self.assert_start_raises_init_error(1, ['-usehd=0'], 'already existing HD wallet') + self.start_node(1) connect_nodes_bi(self.nodes, 0, 1) # Make sure we use hd, keep masterkeyid @@ -76,7 +73,7 @@ class WalletHDTest(BitcoinTestFramework): shutil.rmtree(tmpdir + "/node1/regtest/blocks") shutil.rmtree(tmpdir + "/node1/regtest/chainstate") shutil.copyfile(tmpdir + "/hd.bak", tmpdir + "/node1/regtest/wallet.dat") - self.nodes[1] = self.start_node(1, self.options.tmpdir, self.extra_args[1]) + self.start_node(1) # Assert that derivation is deterministic hd_add_2 = None @@ -91,7 +88,7 @@ class WalletHDTest(BitcoinTestFramework): # Needs rescan self.stop_node(1) - self.nodes[1] = self.start_node(1, self.options.tmpdir, self.extra_args[1] + ['-rescan']) + self.start_node(1, extra_args=self.extra_args[1] + ['-rescan']) assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1) # send a tx and make sure its using the internal chain for the changeoutput diff --git a/test/functional/wallet.py b/test/functional/wallet.py index 3e3e8fcddb..9d8ae50354 100755 --- a/test/functional/wallet.py +++ b/test/functional/wallet.py @@ -7,28 +7,27 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class WalletTest(BitcoinTestFramework): - - def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size): - """Return curr_balance after asserting the fee was in range""" - fee = balance_with_fee - curr_balance - assert_fee_amount(fee, tx_size, fee_per_byte * 1000) - return curr_balance - - def __init__(self): - super().__init__() - self.setup_clean_chain = True + def set_test_params(self): self.num_nodes = 4 - self.extra_args = [['-usehd={:d}'.format(i%2==0)] for i in range(4)] + self.setup_clean_chain = True def setup_network(self): - self.nodes = self.start_nodes(3, self.options.tmpdir, self.extra_args[:3]) + self.add_nodes(4) + self.start_node(0) + self.start_node(1) + self.start_node(2) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) - self.sync_all() + self.sync_all([self.nodes[0:3]]) - def run_test(self): + def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size): + """Return curr_balance after asserting the fee was in range""" + fee = balance_with_fee - curr_balance + assert_fee_amount(fee, tx_size, fee_per_byte * 1000) + return curr_balance + def run_test(self): # Check that there's no UTXO on none of the nodes assert_equal(len(self.nodes[0].listunspent()), 0) assert_equal(len(self.nodes[1].listunspent()), 0) @@ -42,9 +41,9 @@ class WalletTest(BitcoinTestFramework): assert_equal(walletinfo['immature_balance'], 50) assert_equal(walletinfo['balance'], 0) - self.sync_all() + self.sync_all([self.nodes[0:3]]) self.nodes[1].generate(101) - self.sync_all() + self.sync_all([self.nodes[0:3]]) assert_equal(self.nodes[0].getbalance(), 50) assert_equal(self.nodes[1].getbalance(), 50) @@ -56,6 +55,15 @@ class WalletTest(BitcoinTestFramework): assert_equal(len(self.nodes[1].listunspent()), 1) assert_equal(len(self.nodes[2].listunspent()), 0) + self.log.info("test gettxout") + confirmed_txid, confirmed_index = utxos[0]["txid"], utxos[0]["vout"] + # First, outputs that are unspent both in the chain and in the + # mempool should appear with or without include_mempool + txout = self.nodes[0].gettxout(txid=confirmed_txid, n=confirmed_index, include_mempool=False) + assert_equal(txout['value'], 50) + txout = self.nodes[0].gettxout(txid=confirmed_txid, n=confirmed_index, include_mempool=True) + assert_equal(txout['value'], 50) + # Send 21 BTC from 0 to 2 using sendtoaddress call. # Locked memory should use at least 32 bytes to sign each transaction self.log.info("test getmemoryinfo") @@ -65,10 +73,9 @@ class WalletTest(BitcoinTestFramework): memory_after = self.nodes[0].getmemoryinfo() assert(memory_before['locked']['used'] + 64 <= memory_after['locked']['used']) - self.log.info("test gettxout") + self.log.info("test gettxout (second part)") # utxo spent in mempool should be visible if you exclude mempool # but invisible if you include mempool - confirmed_txid, confirmed_index = utxos[0]["txid"], utxos[0]["vout"] txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, False) assert_equal(txout['value'], 50) txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, True) @@ -88,20 +95,20 @@ class WalletTest(BitcoinTestFramework): # Have node0 mine a block, thus it will collect its own fee. self.nodes[0].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) # Exercise locking of unspent outputs unspent_0 = self.nodes[2].listunspent()[0] unspent_0 = {"txid": unspent_0["txid"], "vout": unspent_0["vout"]} self.nodes[2].lockunspent(False, [unspent_0]) - assert_raises_jsonrpc(-4, "Insufficient funds", self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), 20) + assert_raises_rpc_error(-4, "Insufficient funds", self.nodes[2].sendtoaddress, self.nodes[2].getnewaddress(), 20) assert_equal([unspent_0], self.nodes[2].listlockunspent()) self.nodes[2].lockunspent(True, [unspent_0]) assert_equal(len(self.nodes[2].listlockunspent()), 0) # Have node1 generate 100 blocks (so node0 can recover the fee) self.nodes[1].generate(100) - self.sync_all() + self.sync_all([self.nodes[0:3]]) # node0 should end up with 100 btc in block rewards plus fees, but # minus the 21 plus fees sent to node2 @@ -130,7 +137,7 @@ class WalletTest(BitcoinTestFramework): # Have node1 mine a block to confirm transactions: self.nodes[1].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) assert_equal(self.nodes[0].getbalance(), 0) assert_equal(self.nodes[2].getbalance(), 94) @@ -142,14 +149,14 @@ class WalletTest(BitcoinTestFramework): self.nodes[2].settxfee(fee_per_byte * 1000) txid = self.nodes[2].sendtoaddress(address, 10, "", "", False) self.nodes[2].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), Decimal('84'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) assert_equal(self.nodes[0].getbalance(), Decimal('10')) # Send 10 BTC with subtract fee from amount txid = self.nodes[2].sendtoaddress(address, 10, "", "", True) self.nodes[2].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) node_2_bal -= Decimal('10') assert_equal(self.nodes[2].getbalance(), node_2_bal) node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) @@ -157,7 +164,7 @@ class WalletTest(BitcoinTestFramework): # Sendmany 10 BTC txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", []) self.nodes[2].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) node_0_bal += Decimal('10') node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) assert_equal(self.nodes[0].getbalance(), node_0_bal) @@ -165,7 +172,7 @@ class WalletTest(BitcoinTestFramework): # Sendmany 10 BTC with subtract fee from amount txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", [address]) self.nodes[2].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) node_2_bal -= Decimal('10') assert_equal(self.nodes[2].getbalance(), node_2_bal) node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) @@ -176,9 +183,9 @@ class WalletTest(BitcoinTestFramework): # EXPECT: nodes[3] should have those transactions in its mempool. txid1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1) txid2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1) - sync_mempools(self.nodes) + sync_mempools(self.nodes[0:2]) - self.nodes.append(self.start_node(3, self.options.tmpdir, self.extra_args[3])) + self.start_node(3) connect_nodes_bi(self.nodes, 0, 3) sync_blocks(self.nodes) @@ -206,7 +213,7 @@ class WalletTest(BitcoinTestFramework): signedRawTx = self.nodes[1].signrawtransaction(rawTx) decRawTx = self.nodes[1].decoderawtransaction(signedRawTx['hex']) zeroValueTxid= decRawTx['txid'] - sendResp = self.nodes[1].sendrawtransaction(signedRawTx['hex']) + self.nodes[1].sendrawtransaction(signedRawTx['hex']) self.sync_all() self.nodes[1].generate(1) #mine a block @@ -222,22 +229,24 @@ class WalletTest(BitcoinTestFramework): #do some -walletbroadcast tests self.stop_nodes() - self.nodes = self.start_nodes(3, self.options.tmpdir, [["-walletbroadcast=0"],["-walletbroadcast=0"],["-walletbroadcast=0"]]) + self.start_node(0, ["-walletbroadcast=0"]) + self.start_node(1, ["-walletbroadcast=0"]) + self.start_node(2, ["-walletbroadcast=0"]) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) - self.sync_all() + self.sync_all([self.nodes[0:3]]) txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted) self.nodes[1].generate(1) #mine a block, tx should not be in there - self.sync_all() + self.sync_all([self.nodes[0:3]]) assert_equal(self.nodes[2].getbalance(), node_2_bal) #should not be changed because tx was not broadcasted #now broadcast from another node, mine a block, sync, and check the balance self.nodes[1].sendrawtransaction(txObjNotBroadcasted['hex']) self.nodes[1].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) node_2_bal += 2 txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted) assert_equal(self.nodes[2].getbalance(), node_2_bal) @@ -247,14 +256,16 @@ class WalletTest(BitcoinTestFramework): #restart the nodes with -walletbroadcast=1 self.stop_nodes() - self.nodes = self.start_nodes(3, self.options.tmpdir) + self.start_node(0) + self.start_node(1) + self.start_node(2) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) - sync_blocks(self.nodes) + sync_blocks(self.nodes[0:3]) self.nodes[0].generate(1) - sync_blocks(self.nodes) + sync_blocks(self.nodes[0:3]) node_2_bal += 2 #tx should be added to balance because after restarting the nodes tx should be broadcastet @@ -275,17 +286,17 @@ class WalletTest(BitcoinTestFramework): assert_equal(txObj['amount'], Decimal('-0.0001')) # This will raise an exception because the amount type is wrong - assert_raises_jsonrpc(-3, "Invalid amount", self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4") + assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4") # This will raise an exception since generate does not accept a string - assert_raises_jsonrpc(-1, "not an integer", self.nodes[0].generate, "2") + assert_raises_rpc_error(-1, "not an integer", self.nodes[0].generate, "2") # Import address and private key to check correct behavior of spendable unspents # 1. Send some coins to generate new UTXO address_to_import = self.nodes[2].getnewaddress() txid = self.nodes[0].sendtoaddress(address_to_import, 1) self.nodes[0].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) # 2. Import address from node2 to node1 self.nodes[1].importaddress(address_to_import) @@ -311,15 +322,15 @@ class WalletTest(BitcoinTestFramework): cbAddr = self.nodes[1].getnewaddress() blkHash = self.nodes[0].generatetoaddress(1, cbAddr)[0] cbTxId = self.nodes[0].getblock(blkHash)['tx'][0] - self.sync_all() + self.sync_all([self.nodes[0:3]]) # Check that the txid and balance is found by node1 self.nodes[1].gettransaction(cbTxId) # check if wallet or blockchain maintenance changes the balance - self.sync_all() + self.sync_all([self.nodes[0:3]]) blocks = self.nodes[0].generate(2) - self.sync_all() + self.sync_all([self.nodes[0:3]]) balance_nodes = [self.nodes[i].getbalance() for i in range(3)] block_count = self.nodes[0].getblockcount() @@ -350,7 +361,9 @@ class WalletTest(BitcoinTestFramework): self.log.info("check " + m) self.stop_nodes() # set lower ancestor limit for later - self.nodes = self.start_nodes(3, self.options.tmpdir, [[m, "-limitancestorcount="+str(chainlimit)]] * 3) + self.start_node(0, [m, "-limitancestorcount="+str(chainlimit)]) + self.start_node(1, [m, "-limitancestorcount="+str(chainlimit)]) + self.start_node(2, [m, "-limitancestorcount="+str(chainlimit)]) while m == '-reindex' and [block_count] * 3 != [self.nodes[i].getblockcount() for i in range(3)]: # reindex will leave rpc warm up "early"; Wait for it to finish time.sleep(0.1) @@ -398,7 +411,7 @@ class WalletTest(BitcoinTestFramework): # Try with walletrejectlongchains # Double chain limit but require combining inputs, so we pass SelectCoinsMinConf self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-walletrejectlongchains", "-limitancestorcount="+str(2*chainlimit)]) + self.start_node(0, extra_args=["-walletrejectlongchains", "-limitancestorcount="+str(2*chainlimit)]) # wait for loadmempool timeout = 10 @@ -409,7 +422,7 @@ class WalletTest(BitcoinTestFramework): node0_balance = self.nodes[0].getbalance() # With walletrejectlongchains we will not create the tx and store it in our wallet. - assert_raises_jsonrpc(-4, "Transaction has too long of a mempool chain", self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('0.01')) + assert_raises_rpc_error(-4, "Transaction has too long of a mempool chain", self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('0.01')) # Verify nothing new in wallet assert_equal(total_txs, len(self.nodes[0].listtransactions("*",99999))) diff --git a/test/functional/walletbackup.py b/test/functional/walletbackup.py index ff51cba4b3..15ea26afa1 100755 --- a/test/functional/walletbackup.py +++ b/test/functional/walletbackup.py @@ -37,11 +37,9 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class WalletBackupTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = True + def set_test_params(self): self.num_nodes = 4 + self.setup_clean_chain = True # nodes 1, 2,3 are spenders, let's give them a keypool=100 self.extra_args = [["-keypool=100"], ["-keypool=100"], ["-keypool=100"], []] @@ -78,9 +76,9 @@ class WalletBackupTest(BitcoinTestFramework): # As above, this mirrors the original bash test. def start_three(self): - self.nodes[0] = self.start_node(0, self.options.tmpdir) - self.nodes[1] = self.start_node(1, self.options.tmpdir) - self.nodes[2] = self.start_node(2, self.options.tmpdir) + self.start_node(0) + self.start_node(1) + self.start_node(2) connect_nodes(self.nodes[0], 3) connect_nodes(self.nodes[1], 3) connect_nodes(self.nodes[2], 3) diff --git a/test/functional/zapwallettxes.py b/test/functional/zapwallettxes.py index af867d7a52..8cd622dc8e 100755 --- a/test/functional/zapwallettxes.py +++ b/test/functional/zapwallettxes.py @@ -15,14 +15,14 @@ been zapped. """ from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import (assert_equal, - assert_raises_jsonrpc, - ) +from test_framework.util import ( + assert_equal, + assert_raises_rpc_error, + wait_until, +) class ZapWalletTXesTest (BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 @@ -48,7 +48,7 @@ class ZapWalletTXesTest (BitcoinTestFramework): # Stop-start node0. Both confirmed and unconfirmed transactions remain in the wallet. self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir) + self.start_node(0) assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1) assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2) @@ -56,7 +56,9 @@ class ZapWalletTXesTest (BitcoinTestFramework): # Stop node0 and restart with zapwallettxes and persistmempool. The unconfirmed # transaction is zapped from the wallet, but is re-added when the mempool is reloaded. self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-persistmempool=1", "-zapwallettxes=2"]) + self.start_node(0, ["-persistmempool=1", "-zapwallettxes=2"]) + + wait_until(lambda: self.nodes[0].getmempoolinfo()['size'] == 1, timeout=3) assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1) assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2) @@ -64,13 +66,13 @@ class ZapWalletTXesTest (BitcoinTestFramework): # Stop node0 and restart with zapwallettxes, but not persistmempool. # The unconfirmed transaction is zapped and is no longer in the wallet. self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-zapwallettxes=2"]) + self.start_node(0, ["-zapwallettxes=2"]) # tx1 is still be available because it was confirmed assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1) # This will raise an exception because the unconfirmed transaction has been zapped - assert_raises_jsonrpc(-5, 'Invalid or non-wallet transaction id', self.nodes[0].gettransaction, txid2) + assert_raises_rpc_error(-5, 'Invalid or non-wallet transaction id', self.nodes[0].gettransaction, txid2) if __name__ == '__main__': ZapWalletTXesTest().main() diff --git a/test/functional/zmq_test.py b/test/functional/zmq_test.py index 26c946d215..382ef5bae2 100755 --- a/test/functional/zmq_test.py +++ b/test/functional/zmq_test.py @@ -10,12 +10,11 @@ import struct from test_framework.test_framework import BitcoinTestFramework, SkipTest from test_framework.util import (assert_equal, bytes_to_hex_str, - ) + hash256, + ) class ZMQTest (BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 def setup_nodes(self): @@ -39,10 +38,14 @@ class ZMQTest (BitcoinTestFramework): self.zmqSubSocket.set(zmq.RCVTIMEO, 60000) self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashblock") self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtx") + self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawblock") + self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawtx") ip_address = "tcp://127.0.0.1:28332" self.zmqSubSocket.connect(ip_address) - extra_args = [['-zmqpubhashtx=%s' % ip_address, '-zmqpubhashblock=%s' % ip_address], []] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args) + self.extra_args = [['-zmqpubhashblock=%s' % ip_address, '-zmqpubhashtx=%s' % ip_address, + '-zmqpubrawblock=%s' % ip_address, '-zmqpubrawtx=%s' % ip_address], []] + self.add_nodes(self.num_nodes, self.extra_args) + self.start_nodes() def run_test(self): try: @@ -60,28 +63,51 @@ class ZMQTest (BitcoinTestFramework): msg = self.zmqSubSocket.recv_multipart() topic = msg[0] assert_equal(topic, b"hashtx") - body = msg[1] + txhash = msg[1] msgSequence = struct.unpack('<I', msg[-1])[-1] assert_equal(msgSequence, 0) # must be sequence 0 on hashtx + # rawtx + msg = self.zmqSubSocket.recv_multipart() + topic = msg[0] + assert_equal(topic, b"rawtx") + body = msg[1] + msgSequence = struct.unpack('<I', msg[-1])[-1] + assert_equal(msgSequence, 0) # must be sequence 0 on rawtx + + # Check that the rawtx hashes to the hashtx + assert_equal(hash256(body), txhash) + self.log.info("Wait for block") msg = self.zmqSubSocket.recv_multipart() topic = msg[0] + assert_equal(topic, b"hashblock") body = msg[1] msgSequence = struct.unpack('<I', msg[-1])[-1] assert_equal(msgSequence, 0) # must be sequence 0 on hashblock blkhash = bytes_to_hex_str(body) - assert_equal(genhashes[0], blkhash) # blockhash from generate must be equal to the hash received over zmq + # rawblock + msg = self.zmqSubSocket.recv_multipart() + topic = msg[0] + assert_equal(topic, b"rawblock") + body = msg[1] + msgSequence = struct.unpack('<I', msg[-1])[-1] + assert_equal(msgSequence, 0) #must be sequence 0 on rawblock + + # Check the hash of the rawblock's header matches generate + assert_equal(genhashes[0], bytes_to_hex_str(hash256(body[:80]))) + self.log.info("Generate 10 blocks (and 10 coinbase txes)") n = 10 genhashes = self.nodes[1].generate(n) self.sync_all() zmqHashes = [] + zmqRawHashed = [] blockcount = 0 - for x in range(n * 2): + for x in range(n * 4): msg = self.zmqSubSocket.recv_multipart() topic = msg[0] body = msg[1] @@ -90,9 +116,14 @@ class ZMQTest (BitcoinTestFramework): msgSequence = struct.unpack('<I', msg[-1])[-1] assert_equal(msgSequence, blockcount + 1) blockcount += 1 + if topic == b"rawblock": + zmqRawHashed.append(bytes_to_hex_str(hash256(body[:80]))) + msgSequence = struct.unpack('<I', msg[-1])[-1] + assert_equal(msgSequence, blockcount) for x in range(n): assert_equal(genhashes[x], zmqHashes[x]) # blockhash from generate must be equal to the hash received over zmq + assert_equal(genhashes[x], zmqRawHashed[x]) self.log.info("Wait for tx from second node") # test tx from a second node @@ -102,13 +133,21 @@ class ZMQTest (BitcoinTestFramework): # now we should receive a zmq msg because the tx was broadcast msg = self.zmqSubSocket.recv_multipart() topic = msg[0] - body = msg[1] assert_equal(topic, b"hashtx") + body = msg[1] hashZMQ = bytes_to_hex_str(body) msgSequence = struct.unpack('<I', msg[-1])[-1] assert_equal(msgSequence, blockcount + 1) + msg = self.zmqSubSocket.recv_multipart() + topic = msg[0] + assert_equal(topic, b"rawtx") + body = msg[1] + hashedZMQ = bytes_to_hex_str(hash256(body)) + msgSequence = struct.unpack('<I', msg[-1])[-1] + assert_equal(msgSequence, blockcount+1) assert_equal(hashRPC, hashZMQ) # txid from sendtoaddress must be equal to the hash received over zmq + assert_equal(hashRPC, hashedZMQ) if __name__ == '__main__': ZMQTest().main() diff --git a/test/util/bitcoin-util-test.py b/test/util/bitcoin-util-test.py index d15d6a6011..ef34955d90 100755 --- a/test/util/bitcoin-util-test.py +++ b/test/util/bitcoin-util-test.py @@ -9,9 +9,14 @@ Runs automatically during `make check`. Can also be run manually.""" +from __future__ import division,print_function,unicode_literals + import argparse import binascii -import configparser +try: + import configparser +except ImportError: + import ConfigParser as configparser import difflib import json import logging @@ -22,7 +27,9 @@ import sys def main(): config = configparser.ConfigParser() - config.read_file(open(os.path.dirname(__file__) + "/../config.ini")) + config.optionxform = str + config.readfp(open(os.path.join(os.path.dirname(__file__), "../config.ini"))) + env_conf = dict(config.items('environment')) parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-v', '--verbose', action='store_true') @@ -37,7 +44,7 @@ def main(): # Add the format/level to the logger logging.basicConfig(format=formatter, level=level) - bctester(config["environment"]["SRCDIR"] + "/test/util/data", "bitcoin-util-test.json", config["environment"]) + bctester(os.path.join(env_conf["SRCDIR"], "test/util/data"), "bitcoin-util-test.json", env_conf) def bctester(testDir, input_basename, buildenv): """ Loads and parses the input file, runs all tests and reports results""" diff --git a/test/util/data/bitcoin-util-test.json b/test/util/data/bitcoin-util-test.json index b61a4f7f8f..89b28bba6c 100644 --- a/test/util/data/bitcoin-util-test.json +++ b/test/util/data/bitcoin-util-test.json @@ -263,6 +263,13 @@ }, { "exec": "./bitcoin-tx", "args": + ["-json", "-create", "outpubkey=0:047d1368ba7ae01c94bc32293efd70bd7e3be7aa7912d07d0b1c659c1008d179b8642f5fb90f47580feb29f045e216ff5a4716d3a0fed36da414d332046303c44a:WS", "nversion=1"], + "return_code": 1, + "error_txt": "error: Uncompressed pubkeys are not useable for SegWit outputs", + "description": "Creates a new transaction with a single pay-to-pub-key output, wrapped in P2SH (output as json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-create", "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0", "outdata=4:badhexdata"], @@ -388,5 +395,16 @@ "args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:02df2089105c77f266fa11a9d33f05c735234075f2e8780824c6b709415f9fb485:WS", "nversion=1"], "output_cmp": "txcreatemultisig4.json", "description": "Creates a new transaction with a single 2-of-3 multisig in a P2WSH output, wrapped in P2SH (output in json)" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:047d1368ba7ae01c94bc32293efd70bd7e3be7aa7912d07d0b1c659c1008d179b8642f5fb90f47580feb29f045e216ff5a4716d3a0fed36da414d332046303c44a:S"], + "output_cmp": "txcreatemultisig5.json", + "description": "Uncompressed pubkeys should work just fine for non-witness outputs" + }, + { "exec": "./bitcoin-tx", + "args": ["-json", "-create", "outmultisig=1:2:3:02a5613bd857b7048924264d1e70e08fb2a7e6527d32b7ab1bb993ac59964ff397:021ac43c7ff740014c3b33737ede99c967e4764553d1b2b83db77c83b8715fa72d:047d1368ba7ae01c94bc32293efd70bd7e3be7aa7912d07d0b1c659c1008d179b8642f5fb90f47580feb29f045e216ff5a4716d3a0fed36da414d332046303c44a:WS"], + "return_code": 1, + "error_txt": "error: Uncompressed pubkeys are not useable for SegWit outputs", + "description": "Ensure adding witness outputs with uncompressed pubkeys fails" } ] diff --git a/test/util/data/tt-delin1-out.json b/test/util/data/tt-delin1-out.json index 0b3235dd4a..de647f98b6 100644 --- a/test/util/data/tt-delin1-out.json +++ b/test/util/data/tt-delin1-out.json @@ -14,7 +14,7 @@ "hex": "493046022100b4251ecd63778a3dde0155abe4cd162947620ae9ee45a874353551092325b116022100db307baf4ff3781ec520bd18f387948cedd15dc27bafe17c894b0fe6ffffcafa012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc" }, "sequence": 4294967295 - }, + }, { "txid": "752f7f69b915637dc1c2f7aed1466ad676f6f3e24cf922809705f664e97ab3c1", "vout": 1, @@ -23,7 +23,7 @@ "hex": "473044022079bd62ee09621a3be96b760c39e8ef78170101d46313923c6b07ae60a95c90670220238e51ea29fc70b04b65508450523caedbb11cb4dd5aa608c81487de798925ba0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34" }, "sequence": 4294967295 - }, + }, { "txid": "b0ac9cca2e69cd02410e31b1f4402a25758e71abd1ab06c265ef9077dc05d0ed", "vout": 209, @@ -32,7 +32,7 @@ "hex": "48304502207722d6f9038673c86a1019b1c4de2d687ae246477cd4ca7002762be0299de385022100e594a11e3a313942595f7666dcf7078bcb14f1330f4206b95c917e7ec0e82fac012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc" }, "sequence": 4294967295 - }, + }, { "txid": "a135eafb595eaf4c1ea59ccb111cdc0eae1b2c979b226a1e5aa8b76fe2d628df", "vout": 0, @@ -41,7 +41,7 @@ "hex": "483045022100a63a4788027b79b65c6f9d9e054f68cf3b4eed19efd82a2d53f70dcbe64683390220526f243671425b2bd05745fcf2729361f985cfe84ea80c7cfc817b93d8134374012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52" }, "sequence": 4294967295 - }, + }, { "txid": "a5d6bf53ba21140b8a4d554feb00fe8bb9a62430ff9e4624aa2f58a120232aae", "vout": 1, @@ -50,7 +50,7 @@ "hex": "493046022100b200ac6db16842f76dab9abe807ce423c992805879bc50abd46ed8275a59d9cf022100c0d518e85dd345b3c29dd4dc47b9a420d3ce817b18720e94966d2fe23413a408012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc" }, "sequence": 4294967295 - }, + }, { "txid": "1b299cf14f1a22e81ea56d71b7affbd7cf386807bf2b4d4b79a18a54125accb3", "vout": 0, @@ -59,7 +59,7 @@ "hex": "483045022100ededc441c3103a6f2bd6cab7639421af0f6ec5e60503bce1e603cf34f00aee1c02205cb75f3f519a13fb348783b21db3085cb5ec7552c59e394fdbc3e1feea43f967012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52" }, "sequence": 4294967295 - }, + }, { "txid": "071df1cdcb3f0070f9d6af7b0274f02d0be2324a274727cfd288383167531485", "vout": 21, @@ -68,7 +68,7 @@ "hex": "483045022100d9eed5413d2a4b4b98625aa6e3169edc4fb4663e7862316d69224454e70cd8ca022061e506521d5ced51dd0ea36496e75904d756a4c4f9fb111568555075d5f68d9a012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "b012e500eb7adf7a13ed332dd6ece849f94f7a62bb3eac5babab356d1fc19282", "vout": 9, @@ -77,7 +77,7 @@ "hex": "48304502207e84b27139c4c19c828cb1e30c349bba88e4d9b59be97286960793b5ddc0a2af0221008cdc7a951e7f31c20953ed5635fbabf228e80b7047f32faaa0313e7693005177012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "58840fee9c833f2f2d40575842f30f4b8d2553094d06ad88b03d06869acf3d88", "vout": 30, @@ -86,7 +86,7 @@ "hex": "4730440220426540dfed9c4ab5812e5f06df705b8bcf307dd7d20f7fa6512298b2a6314f420220064055096e3ca62f6c7352c66a5447767c53f946acdf35025ab3807ddb2fa404012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "e69f9cd16946e570a665245354428a3f507ea69f4568b581e4af98edb3db9766", "vout": 114, @@ -95,7 +95,7 @@ "hex": "47304402200a5e673996f2fc88e21cc8613611f08a650bc0370338803591d85d0ec5663764022040b6664a0d1ec83a7f01975b8fde5232992b8ca58bf48af6725d2f92a936ab2e012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "595d1257f654ed2cbe5a65421e8aefd2b4d70b5b6c89a03f1d7e518221fc3f02", "vout": 103, @@ -104,7 +104,7 @@ "hex": "493046022100d93b30219c5735f673be5c3b4688366d96f545561c74cb62c6958c00f6960806022100ec8200adcb028f2184fa2a4f6faac7f8bb57cb4503bb7584ac11051fece31b3d012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc" }, "sequence": 4294967295 - }, + }, { "txid": "06fc818f9555a261248ecd7aad0993eafb5a82ceb2b5c87c3ddfb06671c7f816", "vout": 1, @@ -113,7 +113,7 @@ "hex": "483045022100a13934e68d3f5b22b130c4cb33f4da468cffc52323a47fbfbe06b64858162246022047081e0a70ff770e64a2e2d31e5d520d9102268b57a47009a72fe73ec766901801210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd" }, "sequence": 4294967295 - }, + }, { "txid": "fb416c8155d6bb1d43f9395466ca90a638a7c2dd3ff617aadf3a7ac8f3967b19", "vout": 0, @@ -122,7 +122,7 @@ "hex": "49304602210097f1f35d5bdc1a3a60390a1b015b8e7c4f916aa3847aafd969e04975e15bbe70022100a9052eb25517d481f1fda1b129eb1b534da50ea1a51f3ee012dca3601c11b86a0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34" }, "sequence": 4294967295 - }, + }, { "txid": "3940b9683bd6104ad24c978e640ba4095993cafdb27d2ed91baa27ee61a2d920", "vout": 221, @@ -131,7 +131,7 @@ "hex": "483045022012b3138c591bf7154b6fef457f2c4a3c7162225003788ac0024a99355865ff13022100b71b125ae1ffb2e1d1571f580cd3ebc8cd049a2d7a8a41f138ba94aeb982106f012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc" }, "sequence": 4294967295 - }, + }, { "txid": "711b5714d3b5136147c02194cd95bde94a4648c4263ca6f972d86cd1d579f150", "vout": 1, @@ -140,7 +140,7 @@ "hex": "483045022100f834ccc8b22ee72712a3e5e6ef4acb8b2fb791b5385b70e2cd4332674d6667f4022024fbda0a997e0c253503f217501f508a4d56edce2c813ecdd9ad796dbeba907401210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd" }, "sequence": 4294967295 - }, + }, { "txid": "6364b5c5efe018430789e7fb4e338209546cae5d9c5f5e300aac68155d861b55", "vout": 27, @@ -149,7 +149,7 @@ "hex": "48304502203b2fd1e39ae0e469d7a15768f262661b0de41470daf0fe8c4fd0c26542a0870002210081c57e331f9a2d214457d953e3542904727ee412c63028113635d7224da3dccc012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "0bb57f6e38012c86d4c5a28c904f2675082859147921a707d48961015a3e5057", "vout": 1095, @@ -158,7 +158,7 @@ "hex": "48304502206947a9c54f0664ece4430fd4ae999891dc50bb6126bc36b6a15a3189f29d25e9022100a86cfc4e2fdd9e39a20e305cfd1b76509c67b3e313e0f118229105caa0e823c9012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "9b34274814a2540bb062107117f8f3e75ef85d953e9372d8261a3e9dfbc1163f", "vout": 37, @@ -167,7 +167,7 @@ "hex": "483045022100c7128fe10b2d38744ae8177776054c29fc8ec13f07207723e70766ab7164847402201d2cf09009b9596de74c0183d1ab832e5edddb7a9965880bb400097e850850f8012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "b86b5cc0d8a7374d94e277850b0a249cb26a7b42ddf014f28a49b8859da64241", "vout": 20, @@ -176,7 +176,7 @@ "hex": "48304502203b89a71628a28cc3703d170ca3be77786cff6b867e38a18b719705f8a326578f022100b2a9879e1acf621faa6466c207746a7f3eb4c8514c1482969aba3f2a957f1321012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "3d0a2353eeec44d3c10aed259038db321912122cd4150048f7bfa4c0ecfee236", "vout": 242, @@ -200,7 +200,7 @@ "1E7SGgAZFCHDnVZLuRViX3gUmxpMfdvd2o" ] } - }, + }, { "value": 0.01000001, "n": 1, diff --git a/test/util/data/tt-delout1-out.json b/test/util/data/tt-delout1-out.json index 5b69d0cd86..067ffe74e7 100644 --- a/test/util/data/tt-delout1-out.json +++ b/test/util/data/tt-delout1-out.json @@ -14,7 +14,7 @@ "hex": "493046022100b4251ecd63778a3dde0155abe4cd162947620ae9ee45a874353551092325b116022100db307baf4ff3781ec520bd18f387948cedd15dc27bafe17c894b0fe6ffffcafa012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc" }, "sequence": 4294967295 - }, + }, { "txid": "a72ec96bd0d022d1b0c2f9078cdd46b3725b8eecdd001e17b21e3ababad14ecb", "vout": 0, @@ -23,7 +23,7 @@ "hex": "493046022100a9b617843b68c284715d3e02fd120479cd0d96a6c43bf01e697fb0a460a21a3a022100ba0a12fbe8b993d4e7911fa3467615765dbe421ddf5c51b57a9c1ee19dcc00ba012103e633b4fa4ceb705c2da712390767199be8ef2448b3095dc01652e11b2b751505" }, "sequence": 4294967295 - }, + }, { "txid": "752f7f69b915637dc1c2f7aed1466ad676f6f3e24cf922809705f664e97ab3c1", "vout": 1, @@ -32,7 +32,7 @@ "hex": "473044022079bd62ee09621a3be96b760c39e8ef78170101d46313923c6b07ae60a95c90670220238e51ea29fc70b04b65508450523caedbb11cb4dd5aa608c81487de798925ba0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34" }, "sequence": 4294967295 - }, + }, { "txid": "b0ac9cca2e69cd02410e31b1f4402a25758e71abd1ab06c265ef9077dc05d0ed", "vout": 209, @@ -41,7 +41,7 @@ "hex": "48304502207722d6f9038673c86a1019b1c4de2d687ae246477cd4ca7002762be0299de385022100e594a11e3a313942595f7666dcf7078bcb14f1330f4206b95c917e7ec0e82fac012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc" }, "sequence": 4294967295 - }, + }, { "txid": "a135eafb595eaf4c1ea59ccb111cdc0eae1b2c979b226a1e5aa8b76fe2d628df", "vout": 0, @@ -50,7 +50,7 @@ "hex": "483045022100a63a4788027b79b65c6f9d9e054f68cf3b4eed19efd82a2d53f70dcbe64683390220526f243671425b2bd05745fcf2729361f985cfe84ea80c7cfc817b93d8134374012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52" }, "sequence": 4294967295 - }, + }, { "txid": "a5d6bf53ba21140b8a4d554feb00fe8bb9a62430ff9e4624aa2f58a120232aae", "vout": 1, @@ -59,7 +59,7 @@ "hex": "493046022100b200ac6db16842f76dab9abe807ce423c992805879bc50abd46ed8275a59d9cf022100c0d518e85dd345b3c29dd4dc47b9a420d3ce817b18720e94966d2fe23413a408012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc" }, "sequence": 4294967295 - }, + }, { "txid": "1b299cf14f1a22e81ea56d71b7affbd7cf386807bf2b4d4b79a18a54125accb3", "vout": 0, @@ -68,7 +68,7 @@ "hex": "483045022100ededc441c3103a6f2bd6cab7639421af0f6ec5e60503bce1e603cf34f00aee1c02205cb75f3f519a13fb348783b21db3085cb5ec7552c59e394fdbc3e1feea43f967012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52" }, "sequence": 4294967295 - }, + }, { "txid": "071df1cdcb3f0070f9d6af7b0274f02d0be2324a274727cfd288383167531485", "vout": 21, @@ -77,7 +77,7 @@ "hex": "483045022100d9eed5413d2a4b4b98625aa6e3169edc4fb4663e7862316d69224454e70cd8ca022061e506521d5ced51dd0ea36496e75904d756a4c4f9fb111568555075d5f68d9a012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "b012e500eb7adf7a13ed332dd6ece849f94f7a62bb3eac5babab356d1fc19282", "vout": 9, @@ -86,7 +86,7 @@ "hex": "48304502207e84b27139c4c19c828cb1e30c349bba88e4d9b59be97286960793b5ddc0a2af0221008cdc7a951e7f31c20953ed5635fbabf228e80b7047f32faaa0313e7693005177012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "58840fee9c833f2f2d40575842f30f4b8d2553094d06ad88b03d06869acf3d88", "vout": 30, @@ -95,7 +95,7 @@ "hex": "4730440220426540dfed9c4ab5812e5f06df705b8bcf307dd7d20f7fa6512298b2a6314f420220064055096e3ca62f6c7352c66a5447767c53f946acdf35025ab3807ddb2fa404012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "e69f9cd16946e570a665245354428a3f507ea69f4568b581e4af98edb3db9766", "vout": 114, @@ -104,7 +104,7 @@ "hex": "47304402200a5e673996f2fc88e21cc8613611f08a650bc0370338803591d85d0ec5663764022040b6664a0d1ec83a7f01975b8fde5232992b8ca58bf48af6725d2f92a936ab2e012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "595d1257f654ed2cbe5a65421e8aefd2b4d70b5b6c89a03f1d7e518221fc3f02", "vout": 103, @@ -113,7 +113,7 @@ "hex": "493046022100d93b30219c5735f673be5c3b4688366d96f545561c74cb62c6958c00f6960806022100ec8200adcb028f2184fa2a4f6faac7f8bb57cb4503bb7584ac11051fece31b3d012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc" }, "sequence": 4294967295 - }, + }, { "txid": "06fc818f9555a261248ecd7aad0993eafb5a82ceb2b5c87c3ddfb06671c7f816", "vout": 1, @@ -122,7 +122,7 @@ "hex": "483045022100a13934e68d3f5b22b130c4cb33f4da468cffc52323a47fbfbe06b64858162246022047081e0a70ff770e64a2e2d31e5d520d9102268b57a47009a72fe73ec766901801210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd" }, "sequence": 4294967295 - }, + }, { "txid": "fb416c8155d6bb1d43f9395466ca90a638a7c2dd3ff617aadf3a7ac8f3967b19", "vout": 0, @@ -131,7 +131,7 @@ "hex": "49304602210097f1f35d5bdc1a3a60390a1b015b8e7c4f916aa3847aafd969e04975e15bbe70022100a9052eb25517d481f1fda1b129eb1b534da50ea1a51f3ee012dca3601c11b86a0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34" }, "sequence": 4294967295 - }, + }, { "txid": "3940b9683bd6104ad24c978e640ba4095993cafdb27d2ed91baa27ee61a2d920", "vout": 221, @@ -140,7 +140,7 @@ "hex": "483045022012b3138c591bf7154b6fef457f2c4a3c7162225003788ac0024a99355865ff13022100b71b125ae1ffb2e1d1571f580cd3ebc8cd049a2d7a8a41f138ba94aeb982106f012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc" }, "sequence": 4294967295 - }, + }, { "txid": "711b5714d3b5136147c02194cd95bde94a4648c4263ca6f972d86cd1d579f150", "vout": 1, @@ -149,7 +149,7 @@ "hex": "483045022100f834ccc8b22ee72712a3e5e6ef4acb8b2fb791b5385b70e2cd4332674d6667f4022024fbda0a997e0c253503f217501f508a4d56edce2c813ecdd9ad796dbeba907401210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd" }, "sequence": 4294967295 - }, + }, { "txid": "6364b5c5efe018430789e7fb4e338209546cae5d9c5f5e300aac68155d861b55", "vout": 27, @@ -158,7 +158,7 @@ "hex": "48304502203b2fd1e39ae0e469d7a15768f262661b0de41470daf0fe8c4fd0c26542a0870002210081c57e331f9a2d214457d953e3542904727ee412c63028113635d7224da3dccc012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "0bb57f6e38012c86d4c5a28c904f2675082859147921a707d48961015a3e5057", "vout": 1095, @@ -167,7 +167,7 @@ "hex": "48304502206947a9c54f0664ece4430fd4ae999891dc50bb6126bc36b6a15a3189f29d25e9022100a86cfc4e2fdd9e39a20e305cfd1b76509c67b3e313e0f118229105caa0e823c9012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "9b34274814a2540bb062107117f8f3e75ef85d953e9372d8261a3e9dfbc1163f", "vout": 37, @@ -176,7 +176,7 @@ "hex": "483045022100c7128fe10b2d38744ae8177776054c29fc8ec13f07207723e70766ab7164847402201d2cf09009b9596de74c0183d1ab832e5edddb7a9965880bb400097e850850f8012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "b86b5cc0d8a7374d94e277850b0a249cb26a7b42ddf014f28a49b8859da64241", "vout": 20, @@ -185,7 +185,7 @@ "hex": "48304502203b89a71628a28cc3703d170ca3be77786cff6b867e38a18b719705f8a326578f022100b2a9879e1acf621faa6466c207746a7f3eb4c8514c1482969aba3f2a957f1321012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "3d0a2353eeec44d3c10aed259038db321912122cd4150048f7bfa4c0ecfee236", "vout": 242, diff --git a/test/util/data/tt-locktime317000-out.json b/test/util/data/tt-locktime317000-out.json index cf1ebcdf38..af7903d1dd 100644 --- a/test/util/data/tt-locktime317000-out.json +++ b/test/util/data/tt-locktime317000-out.json @@ -14,7 +14,7 @@ "hex": "493046022100b4251ecd63778a3dde0155abe4cd162947620ae9ee45a874353551092325b116022100db307baf4ff3781ec520bd18f387948cedd15dc27bafe17c894b0fe6ffffcafa012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc" }, "sequence": 4294967295 - }, + }, { "txid": "a72ec96bd0d022d1b0c2f9078cdd46b3725b8eecdd001e17b21e3ababad14ecb", "vout": 0, @@ -23,7 +23,7 @@ "hex": "493046022100a9b617843b68c284715d3e02fd120479cd0d96a6c43bf01e697fb0a460a21a3a022100ba0a12fbe8b993d4e7911fa3467615765dbe421ddf5c51b57a9c1ee19dcc00ba012103e633b4fa4ceb705c2da712390767199be8ef2448b3095dc01652e11b2b751505" }, "sequence": 4294967295 - }, + }, { "txid": "752f7f69b915637dc1c2f7aed1466ad676f6f3e24cf922809705f664e97ab3c1", "vout": 1, @@ -32,7 +32,7 @@ "hex": "473044022079bd62ee09621a3be96b760c39e8ef78170101d46313923c6b07ae60a95c90670220238e51ea29fc70b04b65508450523caedbb11cb4dd5aa608c81487de798925ba0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34" }, "sequence": 4294967295 - }, + }, { "txid": "b0ac9cca2e69cd02410e31b1f4402a25758e71abd1ab06c265ef9077dc05d0ed", "vout": 209, @@ -41,7 +41,7 @@ "hex": "48304502207722d6f9038673c86a1019b1c4de2d687ae246477cd4ca7002762be0299de385022100e594a11e3a313942595f7666dcf7078bcb14f1330f4206b95c917e7ec0e82fac012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc" }, "sequence": 4294967295 - }, + }, { "txid": "a135eafb595eaf4c1ea59ccb111cdc0eae1b2c979b226a1e5aa8b76fe2d628df", "vout": 0, @@ -50,7 +50,7 @@ "hex": "483045022100a63a4788027b79b65c6f9d9e054f68cf3b4eed19efd82a2d53f70dcbe64683390220526f243671425b2bd05745fcf2729361f985cfe84ea80c7cfc817b93d8134374012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52" }, "sequence": 4294967295 - }, + }, { "txid": "a5d6bf53ba21140b8a4d554feb00fe8bb9a62430ff9e4624aa2f58a120232aae", "vout": 1, @@ -59,7 +59,7 @@ "hex": "493046022100b200ac6db16842f76dab9abe807ce423c992805879bc50abd46ed8275a59d9cf022100c0d518e85dd345b3c29dd4dc47b9a420d3ce817b18720e94966d2fe23413a408012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc" }, "sequence": 4294967295 - }, + }, { "txid": "1b299cf14f1a22e81ea56d71b7affbd7cf386807bf2b4d4b79a18a54125accb3", "vout": 0, @@ -68,7 +68,7 @@ "hex": "483045022100ededc441c3103a6f2bd6cab7639421af0f6ec5e60503bce1e603cf34f00aee1c02205cb75f3f519a13fb348783b21db3085cb5ec7552c59e394fdbc3e1feea43f967012103a621f08be22d1bbdcbe4e527ee4927006aa555fc65e2aafa767d4ea2fe9dfa52" }, "sequence": 4294967295 - }, + }, { "txid": "071df1cdcb3f0070f9d6af7b0274f02d0be2324a274727cfd288383167531485", "vout": 21, @@ -77,7 +77,7 @@ "hex": "483045022100d9eed5413d2a4b4b98625aa6e3169edc4fb4663e7862316d69224454e70cd8ca022061e506521d5ced51dd0ea36496e75904d756a4c4f9fb111568555075d5f68d9a012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "b012e500eb7adf7a13ed332dd6ece849f94f7a62bb3eac5babab356d1fc19282", "vout": 9, @@ -86,7 +86,7 @@ "hex": "48304502207e84b27139c4c19c828cb1e30c349bba88e4d9b59be97286960793b5ddc0a2af0221008cdc7a951e7f31c20953ed5635fbabf228e80b7047f32faaa0313e7693005177012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "58840fee9c833f2f2d40575842f30f4b8d2553094d06ad88b03d06869acf3d88", "vout": 30, @@ -95,7 +95,7 @@ "hex": "4730440220426540dfed9c4ab5812e5f06df705b8bcf307dd7d20f7fa6512298b2a6314f420220064055096e3ca62f6c7352c66a5447767c53f946acdf35025ab3807ddb2fa404012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "e69f9cd16946e570a665245354428a3f507ea69f4568b581e4af98edb3db9766", "vout": 114, @@ -104,7 +104,7 @@ "hex": "47304402200a5e673996f2fc88e21cc8613611f08a650bc0370338803591d85d0ec5663764022040b6664a0d1ec83a7f01975b8fde5232992b8ca58bf48af6725d2f92a936ab2e012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "595d1257f654ed2cbe5a65421e8aefd2b4d70b5b6c89a03f1d7e518221fc3f02", "vout": 103, @@ -113,7 +113,7 @@ "hex": "493046022100d93b30219c5735f673be5c3b4688366d96f545561c74cb62c6958c00f6960806022100ec8200adcb028f2184fa2a4f6faac7f8bb57cb4503bb7584ac11051fece31b3d012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc" }, "sequence": 4294967295 - }, + }, { "txid": "06fc818f9555a261248ecd7aad0993eafb5a82ceb2b5c87c3ddfb06671c7f816", "vout": 1, @@ -122,7 +122,7 @@ "hex": "483045022100a13934e68d3f5b22b130c4cb33f4da468cffc52323a47fbfbe06b64858162246022047081e0a70ff770e64a2e2d31e5d520d9102268b57a47009a72fe73ec766901801210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd" }, "sequence": 4294967295 - }, + }, { "txid": "fb416c8155d6bb1d43f9395466ca90a638a7c2dd3ff617aadf3a7ac8f3967b19", "vout": 0, @@ -131,7 +131,7 @@ "hex": "49304602210097f1f35d5bdc1a3a60390a1b015b8e7c4f916aa3847aafd969e04975e15bbe70022100a9052eb25517d481f1fda1b129eb1b534da50ea1a51f3ee012dca3601c11b86a0121027a759be8df971a6a04fafcb4f6babf75dc811c5cdaa0734cddbe9b942ce75b34" }, "sequence": 4294967295 - }, + }, { "txid": "3940b9683bd6104ad24c978e640ba4095993cafdb27d2ed91baa27ee61a2d920", "vout": 221, @@ -140,7 +140,7 @@ "hex": "483045022012b3138c591bf7154b6fef457f2c4a3c7162225003788ac0024a99355865ff13022100b71b125ae1ffb2e1d1571f580cd3ebc8cd049a2d7a8a41f138ba94aeb982106f012103091137f3ef23f4acfc19a5953a68b2074fae942ad3563ef28c33b0cac9a93adc" }, "sequence": 4294967295 - }, + }, { "txid": "711b5714d3b5136147c02194cd95bde94a4648c4263ca6f972d86cd1d579f150", "vout": 1, @@ -149,7 +149,7 @@ "hex": "483045022100f834ccc8b22ee72712a3e5e6ef4acb8b2fb791b5385b70e2cd4332674d6667f4022024fbda0a997e0c253503f217501f508a4d56edce2c813ecdd9ad796dbeba907401210234b9d9413f247bb78cd3293b7b65a2c38018ba5621ea9ee737f3a6a3523fb4cd" }, "sequence": 4294967295 - }, + }, { "txid": "6364b5c5efe018430789e7fb4e338209546cae5d9c5f5e300aac68155d861b55", "vout": 27, @@ -158,7 +158,7 @@ "hex": "48304502203b2fd1e39ae0e469d7a15768f262661b0de41470daf0fe8c4fd0c26542a0870002210081c57e331f9a2d214457d953e3542904727ee412c63028113635d7224da3dccc012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "0bb57f6e38012c86d4c5a28c904f2675082859147921a707d48961015a3e5057", "vout": 1095, @@ -167,7 +167,7 @@ "hex": "48304502206947a9c54f0664ece4430fd4ae999891dc50bb6126bc36b6a15a3189f29d25e9022100a86cfc4e2fdd9e39a20e305cfd1b76509c67b3e313e0f118229105caa0e823c9012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "9b34274814a2540bb062107117f8f3e75ef85d953e9372d8261a3e9dfbc1163f", "vout": 37, @@ -176,7 +176,7 @@ "hex": "483045022100c7128fe10b2d38744ae8177776054c29fc8ec13f07207723e70766ab7164847402201d2cf09009b9596de74c0183d1ab832e5edddb7a9965880bb400097e850850f8012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "b86b5cc0d8a7374d94e277850b0a249cb26a7b42ddf014f28a49b8859da64241", "vout": 20, @@ -185,7 +185,7 @@ "hex": "48304502203b89a71628a28cc3703d170ca3be77786cff6b867e38a18b719705f8a326578f022100b2a9879e1acf621faa6466c207746a7f3eb4c8514c1482969aba3f2a957f1321012103f1575d6124ac78be398c25b31146d08313c6072d23a4d7df5ac6a9f87346c64c" }, "sequence": 4294967295 - }, + }, { "txid": "3d0a2353eeec44d3c10aed259038db321912122cd4150048f7bfa4c0ecfee236", "vout": 242, @@ -209,7 +209,7 @@ "1E7SGgAZFCHDnVZLuRViX3gUmxpMfdvd2o" ] } - }, + }, { "value": 0.01000001, "n": 1, diff --git a/test/util/data/txcreate1.json b/test/util/data/txcreate1.json index edb091f946..83a86649e0 100644 --- a/test/util/data/txcreate1.json +++ b/test/util/data/txcreate1.json @@ -14,7 +14,7 @@ "hex": "" }, "sequence": 4294967295 - }, + }, { "txid": "bf829c6bcf84579331337659d31f89dfd138f7f7785802d5501c92333145ca7c", "vout": 18, @@ -23,7 +23,7 @@ "hex": "" }, "sequence": 4294967295 - }, + }, { "txid": "22a6f904655d53ae2ff70e701a0bbd90aa3975c0f40bfc6cc996a9049e31cdfc", "vout": 1, @@ -47,7 +47,7 @@ "13tuJJDR2RgArmgfv6JScSdreahzgc4T6o" ] } - }, + }, { "value": 4.00000000, "n": 1, diff --git a/test/util/data/txcreatedata1.json b/test/util/data/txcreatedata1.json index e66a6bb9a5..15a4246ae5 100644 --- a/test/util/data/txcreatedata1.json +++ b/test/util/data/txcreatedata1.json @@ -29,7 +29,7 @@ "13tuJJDR2RgArmgfv6JScSdreahzgc4T6o" ] } - }, + }, { "value": 4.00000000, "n": 1, diff --git a/test/util/data/txcreatedata2.json b/test/util/data/txcreatedata2.json index 0f8edcafdd..cb93c27971 100644 --- a/test/util/data/txcreatedata2.json +++ b/test/util/data/txcreatedata2.json @@ -29,7 +29,7 @@ "13tuJJDR2RgArmgfv6JScSdreahzgc4T6o" ] } - }, + }, { "value": 0.00000000, "n": 1, diff --git a/test/util/data/txcreatedata_seq1.json b/test/util/data/txcreatedata_seq1.json index 771ff1bb10..dea48ba373 100644 --- a/test/util/data/txcreatedata_seq1.json +++ b/test/util/data/txcreatedata_seq1.json @@ -14,7 +14,7 @@ "hex": "" }, "sequence": 4294967293 - }, + }, { "txid": "5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f", "vout": 0, diff --git a/test/util/data/txcreatemultisig1.json b/test/util/data/txcreatemultisig1.json index 7c814dad83..72e20c8691 100644 --- a/test/util/data/txcreatemultisig1.json +++ b/test/util/data/txcreatemultisig1.json @@ -17,8 +17,8 @@ "reqSigs": 2, "type": "multisig", "addresses": [ - "1FoG2386FG2tAJS9acMuiDsKy67aGg9MKz", - "1FXtz9KU8JNmQDyHdiEm5HDiALuP3zdHvV", + "1FoG2386FG2tAJS9acMuiDsKy67aGg9MKz", + "1FXtz9KU8JNmQDyHdiEm5HDiALuP3zdHvV", "14LuavcBbXZYJ6Tsz3cAUQj9SuQoL2xCQX" ] } diff --git a/test/util/data/txcreatemultisig3.json b/test/util/data/txcreatemultisig3.json index 06e093e224..6c5b49d876 100644 --- a/test/util/data/txcreatemultisig3.json +++ b/test/util/data/txcreatemultisig3.json @@ -14,7 +14,11 @@ "scriptPubKey": { "asm": "0 e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05", "hex": "0020e15a86a23178f433d514dbbce042e87d72662b8b5edcacfd2e37ab7a2d135f05", - "type": "witness_v0_scripthash" + "reqSigs": 1, + "type": "witness_v0_scripthash", + "addresses": [ + "bc1qu9dgdg330r6r84g5mw7wqshg04exv2uttmw2elfwx74h5tgntuzs44gyfg" + ] } } ], diff --git a/test/util/data/txcreatemultisig5.json b/test/util/data/txcreatemultisig5.json new file mode 100644 index 0000000000..20e9bb077b --- /dev/null +++ b/test/util/data/txcreatemultisig5.json @@ -0,0 +1,26 @@ +{ + "txid": "813cf75e1f08debd242ef7c8192b7d478fb651355209369499a0de779ba7eb2f", + "hash": "813cf75e1f08debd242ef7c8192b7d478fb651355209369499a0de779ba7eb2f", + "version": 2, + "size": 42, + "vsize": 42, + "locktime": 0, + "vin": [ + ], + "vout": [ + { + "value": 1.00000000, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 a4051c02398868af83f28f083208fae99a769263 OP_EQUAL", + "hex": "a914a4051c02398868af83f28f083208fae99a76926387", + "reqSigs": 1, + "type": "scripthash", + "addresses": [ + "3GeGs1eHUxPz5YyuFe9WPpXid2UsUb5Jos" + ] + } + } + ], + "hex": "02000000000100e1f5050000000017a914a4051c02398868af83f28f083208fae99a7692638700000000" +} diff --git a/test/util/data/txcreateoutpubkey2.json b/test/util/data/txcreateoutpubkey2.json index 5144722230..4ba5dcb282 100644 --- a/test/util/data/txcreateoutpubkey2.json +++ b/test/util/data/txcreateoutpubkey2.json @@ -14,7 +14,11 @@ "scriptPubKey": { "asm": "0 a2516e770582864a6a56ed21a102044e388c62e3", "hex": "0014a2516e770582864a6a56ed21a102044e388c62e3", - "type": "witness_v0_keyhash" + "reqSigs": 1, + "type": "witness_v0_keyhash", + "addresses": [ + "bc1q5fgkuac9s2ry56jka5s6zqsyfcugcchry5cwu0" + ] } } ], diff --git a/test/util/data/txcreatescript3.json b/test/util/data/txcreatescript3.json index 980da2fb31..31b6459214 100644 --- a/test/util/data/txcreatescript3.json +++ b/test/util/data/txcreatescript3.json @@ -14,7 +14,11 @@ "scriptPubKey": { "asm": "0 0bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6", "hex": "00200bfe935e70c321c7ca3afc75ce0d0ca2f98b5422e008bb31c00c6d7f1f1c0ad6", - "type": "witness_v0_scripthash" + "reqSigs": 1, + "type": "witness_v0_scripthash", + "addresses": [ + "bc1qp0lfxhnscvsu0j36l36uurgv5tuck4pzuqytkvwqp3kh78cupttqyf705v" + ] } } ], |