diff options
87 files changed, 1628 insertions, 1106 deletions
diff --git a/.travis.yml b/.travis.yml index 48bcdf601b..af5553f3b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,11 +8,13 @@ cache: - depends/built - depends/sdk-sources - $HOME/.ccache +stages: + - lint + - test env: global: - MAKEJOBS=-j3 - RUN_TESTS=false - - CHECK_DOC=0 - BOOST_TEST_RANDOM=1$TRAVIS_BUILD_ID - CCACHE_SIZE=100M - CCACHE_TEMPDIR=/tmp/.ccache-temp @@ -22,7 +24,7 @@ env: - WINEDEBUG=fixme-all matrix: # ARM - - HOST=arm-linux-gnueabihf PACKAGES="g++-arm-linux-gnueabihf python3-pip shellcheck" DEP_OPTS="NO_QT=1" CHECK_DOC=1 GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports" + - HOST=arm-linux-gnueabihf PACKAGES="g++-arm-linux-gnueabihf" DEP_OPTS="NO_QT=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" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-reduce-exports" # Win64 @@ -44,17 +46,7 @@ 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 - - if [ "$CHECK_DOC" = 1 -a "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then travis_retry pip3 install flake8 --user; fi before_script: - - if [ "$CHECK_DOC" = 1 ]; then git fetch --unshallow; fi - - 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/git-subtree-check.sh src/crypto/ctaes; fi - - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/git-subtree-check.sh src/secp256k1; fi - - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/git-subtree-check.sh src/univalue; fi - - if [ "$CHECK_DOC" = 1 ]; then contrib/devtools/git-subtree-check.sh src/leveldb; 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 -a "$TRAVIS_EVENT_TYPE" = "pull_request" ]; 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 @@ -63,8 +55,6 @@ before_script: # 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_EVENT_TYPE" = "cron" ]; then travis_wait 30 contrib/verify-commits/verify-commits.sh; fi - export TRAVIS_COMMIT_LOG=`git log --format=fuller -1` - if [ -n "$USE_SHELL" ]; then export CONFIG_SHELL="$USE_SHELL"; fi - OUTDIR=$BASE_OUTDIR/$TRAVIS_PULL_REQUEST/$TRAVIS_JOB_NUMBER-$HOST @@ -84,3 +74,28 @@ script: after_script: - echo $TRAVIS_COMMIT_RANGE - echo $TRAVIS_COMMIT_LOG + +jobs: + include: + - stage: lint + sudo: false + addons: + apt: + packages: + - python3-pip + - shellcheck + install: + - if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then travis_retry pip3 install flake8 --user; fi + before_script: + - git fetch --unshallow + - if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/commit-script-check.sh $TRAVIS_COMMIT_RANGE; fi + - contrib/devtools/git-subtree-check.sh src/crypto/ctaes + - contrib/devtools/git-subtree-check.sh src/secp256k1 + - contrib/devtools/git-subtree-check.sh src/univalue + - contrib/devtools/git-subtree-check.sh src/leveldb + - contrib/devtools/check-doc.py + - contrib/devtools/check-rpc-mappings.py . + - if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then contrib/devtools/lint-all.sh; fi + script: + - if [ "$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 [ "$TRAVIS_REPO_SLUG" = "bitcoin/bitcoin" -a "$TRAVIS_EVENT_TYPE" = "cron" ]; then travis_wait 30 contrib/verify-commits/verify-commits.sh; fi diff --git a/configure.ac b/configure.ac index ce9e683eda..95acd2bd86 100644 --- a/configure.ac +++ b/configure.ac @@ -243,14 +243,22 @@ AC_LANG_PUSH([C++]) AX_CHECK_COMPILE_FLAG([-Werror],[CXXFLAG_WERROR="-Werror"],[CXXFLAG_WERROR=""]) if test "x$enable_debug" = xyes; then - CPPFLAGS="$CPPFLAGS -DDEBUG -DDEBUG_LOCKORDER" - if test "x$GCC" = xyes; then - CFLAGS="$CFLAGS -g3 -O0" - fi + # Prefer -Og, fall back to -O0 if that is unavailable. + AX_CHECK_COMPILE_FLAG( + [-Og], + [[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -Og"]], + [AX_CHECK_COMPILE_FLAG([-O0],[[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -O0"]],,[[$CXXFLAG_WERROR]])], + [[$CXXFLAG_WERROR]]) - if test "x$GXX" = xyes; then - CXXFLAGS="$CXXFLAGS -g3 -O0" - fi + # Prefer -g3, fall back to -g if that is unavailable. + AX_CHECK_COMPILE_FLAG( + [-g3], + [[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -g3"]], + [AX_CHECK_COMPILE_FLAG([-g],[[DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -g"]],,[[$CXXFLAG_WERROR]])], + [[$CXXFLAG_WERROR]]) + + AX_CHECK_PREPROC_FLAG([-DDEBUG],[[DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DDEBUG"]],,[[$CXXFLAG_WERROR]]) + AX_CHECK_PREPROC_FLAG([-DDEBUG_LOCKORDER],[[DEBUG_CPPFLAGS="$DEBUG_CPPFLAGS -DDEBUG_LOCKORDER"]],,[[$CXXFLAG_WERROR]]) fi if test x$use_sanitizers != x; then @@ -1272,6 +1280,8 @@ AC_SUBST(BITCOIN_CLI_NAME) AC_SUBST(BITCOIN_TX_NAME) AC_SUBST(RELDFLAGS) +AC_SUBST(DEBUG_CPPFLAGS) +AC_SUBST(DEBUG_CXXFLAGS) AC_SUBST(ERROR_CXXFLAGS) AC_SUBST(GPROF_CXXFLAGS) AC_SUBST(GPROF_LDFLAGS) @@ -1380,9 +1390,9 @@ echo " build os = $BUILD_OS" echo echo " CC = $CC" echo " CFLAGS = $CFLAGS" -echo " CPPFLAGS = $CPPFLAGS" +echo " CPPFLAGS = $DEBUG_CPPFLAGS $HARDENED_CPPFLAGS $CPPFLAGS" echo " CXX = $CXX" -echo " CXXFLAGS = $CXXFLAGS" -echo " LDFLAGS = $LDFLAGS" +echo " CXXFLAGS = $DEBUG_CXXFLAGS $HARDENED_CXXFLAGS $ERROR_CXXFLAGS $GPROF_CXXFLAGS $CXXFLAGS" +echo " LDFLAGS = $PTHREAD_CFLAGS $HARDENED_LDFLAGS $GPROF_LDFLAGS $LDFLAGS" echo " ARFLAGS = $ARFLAGS" echo diff --git a/contrib/devtools/check-doc.py b/contrib/devtools/check-doc.py index 0c2e1a24be..de5719eb29 100755 --- a/contrib/devtools/check-doc.py +++ b/contrib/devtools/check-doc.py @@ -17,7 +17,7 @@ import sys FOLDER_GREP = 'src' FOLDER_TEST = 'src/test/' REGEX_ARG = '(?:ForceSet|SoftSet|Get|Is)(?:Bool)?Args?(?:Set)?\("(-[^"]+)"' -REGEX_DOC = 'HelpMessageOpt\("(-[^"=]+?)(?:=|")' +REGEX_DOC = 'AddArg\("(-[^"=]+?)(?:=|")' CMD_ROOT_DIR = '`git rev-parse --show-toplevel`/{}'.format(FOLDER_GREP) CMD_GREP_ARGS = r"git grep --perl-regexp '{}' -- {} ':(exclude){}'".format(REGEX_ARG, CMD_ROOT_DIR, FOLDER_TEST) CMD_GREP_DOCS = r"git grep --perl-regexp '{}' {}".format(REGEX_DOC, CMD_ROOT_DIR) diff --git a/contrib/devtools/copyright_header.py b/contrib/devtools/copyright_header.py index e7cccaab03..82d3c19683 100755 --- a/contrib/devtools/copyright_header.py +++ b/contrib/devtools/copyright_header.py @@ -506,7 +506,7 @@ def file_has_hashbang(file_lines): def insert_python_header(filename, file_lines, start_year, end_year): if file_has_hashbang(file_lines): - insert_idx = 1 + insert_idx = 1 else: insert_idx = 0 header_lines = get_python_header_lines_to_insert(start_year, end_year) @@ -571,7 +571,7 @@ def insert_cmd(argv): if extension not in ['.h', '.cpp', '.cc', '.c', '.py']: sys.exit("*** cannot insert for file extension %s" % extension) - if extension == '.py': + if extension == '.py': style = 'python' else: style = 'cpp' diff --git a/contrib/devtools/lint-python.sh b/contrib/devtools/lint-python.sh index 239337000d..7d3555b6d4 100755 --- a/contrib/devtools/lint-python.sh +++ b/contrib/devtools/lint-python.sh @@ -6,11 +6,13 @@ # # Check for specified flake8 warnings in python files. +# E101 indentation contains mixed spaces and tabs # E112 expected an indented block # E113 unexpected indentation # E115 expected an indented block (comment) # E116 unexpected indentation (comment) # E125 continuation line with same indent as next logical line +# E129 visually indented line with same indent as next logical line # E131 continuation line unaligned for hanging indent # E133 closing bracket is missing indentation # E223 tab before operator @@ -61,6 +63,8 @@ # F823 local variable name … referenced before assignment # F831 duplicate argument name in function definition # F841 local variable 'foo' is assigned to but never used +# W191 indentation contains tabs +# W291 trailing whitespace # W292 no newline at end of file # W293 blank line contains whitespace # W504 line break after binary operator @@ -71,4 +75,4 @@ # W605 invalid escape sequence "x" # W606 'async' and 'await' are reserved keywords starting with Python 3.7 -flake8 --ignore=B,C,E,F,I,N,W --select=E112,E113,E115,E116,E125,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,F401,E901,E902,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W292,W293,W504,W601,W602,W603,W604,W605,W606 . +flake8 --ignore=B,C,E,F,I,N,W --select=E101,E112,E113,E115,E116,E125,E129,E131,E133,E223,E224,E242,E266,E271,E272,E273,E274,E275,E304,E306,E401,E402,E502,E701,E702,E703,E714,E721,E741,E742,E743,F401,E901,E902,F402,F404,F406,F407,F601,F602,F621,F622,F631,F701,F702,F703,F704,F705,F706,F707,F811,F812,F821,F822,F823,F831,F841,W191,W291,W292,W293,W504,W601,W602,W603,W604,W605,W606 . diff --git a/contrib/devtools/security-check.py b/contrib/devtools/security-check.py index 0f2099953f..c9516ef83f 100755 --- a/contrib/devtools/security-check.py +++ b/contrib/devtools/security-check.py @@ -150,7 +150,7 @@ def check_PE_DYNAMIC_BASE(executable): def check_PE_HIGH_ENTROPY_VA(executable): '''PIE: DllCharacteristics bit 0x20 signifies high-entropy ASLR''' (arch,bits) = get_PE_dll_characteristics(executable) - if arch == 'i386:x86-64': + if arch == 'i386:x86-64': reqbits = IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA else: # Unnecessary on 32-bit assert(arch == 'i386') diff --git a/contrib/devtools/test-security-check.py b/contrib/devtools/test-security-check.py index 37a895872f..307e057736 100755 --- a/contrib/devtools/test-security-check.py +++ b/contrib/devtools/test-security-check.py @@ -32,15 +32,15 @@ class TestSecurityChecks(unittest.TestCase): cc = 'gcc' write_testcode(source) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-zexecstack','-fno-stack-protector','-Wl,-znorelro']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-zexecstack','-fno-stack-protector','-Wl,-znorelro']), (1, executable+': failed PIE NX RELRO Canary')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fno-stack-protector','-Wl,-znorelro']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fno-stack-protector','-Wl,-znorelro']), (1, executable+': failed PIE RELRO Canary')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro']), (1, executable+': failed PIE RELRO')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-pie','-fPIE']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-znorelro','-pie','-fPIE']), (1, executable+': failed RELRO')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack','-fstack-protector-all','-Wl,-zrelro','-Wl,-z,now','-pie','-fPIE']), (0, '')) def test_32bit_PE(self): @@ -49,11 +49,11 @@ class TestSecurityChecks(unittest.TestCase): cc = 'i686-w64-mingw32-gcc' write_testcode(source) - self.assertEqual(call_security_check(cc, source, executable, []), + self.assertEqual(call_security_check(cc, source, executable, []), (1, executable+': failed DYNAMIC_BASE NX')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat']), (1, executable+': failed DYNAMIC_BASE')) - self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase']), + self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase']), (0, '')) def test_64bit_PE(self): source = 'test1.c' diff --git a/contrib/linearize/linearize-data.py b/contrib/linearize/linearize-data.py index c609e9b336..f8aea27342 100755 --- a/contrib/linearize/linearize-data.py +++ b/contrib/linearize/linearize-data.py @@ -22,300 +22,300 @@ from binascii import hexlify, unhexlify settings = {} def hex_switchEndian(s): - """ Switches the endianness of a hex string (in pairs of hex chars) """ - pairList = [s[i:i+2].encode() for i in range(0, len(s), 2)] - return b''.join(pairList[::-1]).decode() + """ Switches the endianness of a hex string (in pairs of hex chars) """ + pairList = [s[i:i+2].encode() for i in range(0, len(s), 2)] + return b''.join(pairList[::-1]).decode() def uint32(x): - return x & 0xffffffff + return x & 0xffffffff def bytereverse(x): - return uint32(( ((x) << 24) | (((x) << 8) & 0x00ff0000) | - (((x) >> 8) & 0x0000ff00) | ((x) >> 24) )) + return uint32(( ((x) << 24) | (((x) << 8) & 0x00ff0000) | + (((x) >> 8) & 0x0000ff00) | ((x) >> 24) )) def bufreverse(in_buf): - out_words = [] - for i in range(0, len(in_buf), 4): - word = struct.unpack('@I', in_buf[i:i+4])[0] - out_words.append(struct.pack('@I', bytereverse(word))) - return b''.join(out_words) + out_words = [] + for i in range(0, len(in_buf), 4): + word = struct.unpack('@I', in_buf[i:i+4])[0] + out_words.append(struct.pack('@I', bytereverse(word))) + return b''.join(out_words) def wordreverse(in_buf): - out_words = [] - for i in range(0, len(in_buf), 4): - out_words.append(in_buf[i:i+4]) - out_words.reverse() - return b''.join(out_words) + out_words = [] + for i in range(0, len(in_buf), 4): + out_words.append(in_buf[i:i+4]) + out_words.reverse() + return b''.join(out_words) def calc_hdr_hash(blk_hdr): - hash1 = hashlib.sha256() - hash1.update(blk_hdr) - hash1_o = hash1.digest() + hash1 = hashlib.sha256() + hash1.update(blk_hdr) + hash1_o = hash1.digest() - hash2 = hashlib.sha256() - hash2.update(hash1_o) - hash2_o = hash2.digest() + hash2 = hashlib.sha256() + hash2.update(hash1_o) + hash2_o = hash2.digest() - return hash2_o + return hash2_o def calc_hash_str(blk_hdr): - hash = calc_hdr_hash(blk_hdr) - hash = bufreverse(hash) - hash = wordreverse(hash) - hash_str = hexlify(hash).decode('utf-8') - return hash_str + hash = calc_hdr_hash(blk_hdr) + hash = bufreverse(hash) + hash = wordreverse(hash) + hash_str = hexlify(hash).decode('utf-8') + return hash_str def get_blk_dt(blk_hdr): - members = struct.unpack("<I", blk_hdr[68:68+4]) - nTime = members[0] - dt = datetime.datetime.fromtimestamp(nTime) - dt_ym = datetime.datetime(dt.year, dt.month, 1) - return (dt_ym, nTime) + members = struct.unpack("<I", blk_hdr[68:68+4]) + nTime = members[0] + dt = datetime.datetime.fromtimestamp(nTime) + dt_ym = datetime.datetime(dt.year, dt.month, 1) + return (dt_ym, nTime) # When getting the list of block hashes, undo any byte reversals. def get_block_hashes(settings): - blkindex = [] - f = open(settings['hashlist'], "r") - for line in f: - line = line.rstrip() - if settings['rev_hash_bytes'] == 'true': - line = hex_switchEndian(line) - blkindex.append(line) + blkindex = [] + f = open(settings['hashlist'], "r") + for line in f: + line = line.rstrip() + if settings['rev_hash_bytes'] == 'true': + line = hex_switchEndian(line) + blkindex.append(line) - print("Read " + str(len(blkindex)) + " hashes") + print("Read " + str(len(blkindex)) + " hashes") - return blkindex + return blkindex # The block map shouldn't give or receive byte-reversed hashes. def mkblockmap(blkindex): - blkmap = {} - for height,hash in enumerate(blkindex): - blkmap[hash] = height - return blkmap + blkmap = {} + for height,hash in enumerate(blkindex): + blkmap[hash] = height + return blkmap # Block header and extent on disk BlockExtent = namedtuple('BlockExtent', ['fn', 'offset', 'inhdr', 'blkhdr', 'size']) class BlockDataCopier: - def __init__(self, settings, blkindex, blkmap): - self.settings = settings - self.blkindex = blkindex - self.blkmap = blkmap - - self.inFn = 0 - self.inF = None - self.outFn = 0 - self.outsz = 0 - self.outF = None - self.outFname = None - self.blkCountIn = 0 - self.blkCountOut = 0 - - self.lastDate = datetime.datetime(2000, 1, 1) - self.highTS = 1408893517 - 315360000 - self.timestampSplit = False - self.fileOutput = True - self.setFileTime = False - self.maxOutSz = settings['max_out_sz'] - if 'output' in settings: - self.fileOutput = False - if settings['file_timestamp'] != 0: - self.setFileTime = True - if settings['split_timestamp'] != 0: - self.timestampSplit = True - # Extents and cache for out-of-order blocks - self.blockExtents = {} - self.outOfOrderData = {} - self.outOfOrderSize = 0 # running total size for items in outOfOrderData - - def writeBlock(self, inhdr, blk_hdr, rawblock): - blockSizeOnDisk = len(inhdr) + len(blk_hdr) + len(rawblock) - if not self.fileOutput and ((self.outsz + blockSizeOnDisk) > self.maxOutSz): - self.outF.close() - if self.setFileTime: - os.utime(self.outFname, (int(time.time()), self.highTS)) - self.outF = None - self.outFname = None - self.outFn = self.outFn + 1 - self.outsz = 0 - - (blkDate, blkTS) = get_blk_dt(blk_hdr) - if self.timestampSplit and (blkDate > self.lastDate): - print("New month " + blkDate.strftime("%Y-%m") + " @ " + self.hash_str) - self.lastDate = blkDate - if self.outF: - self.outF.close() - if self.setFileTime: - os.utime(self.outFname, (int(time.time()), self.highTS)) - self.outF = None - self.outFname = None - self.outFn = self.outFn + 1 - self.outsz = 0 - - if not self.outF: - if self.fileOutput: - self.outFname = self.settings['output_file'] - else: - self.outFname = os.path.join(self.settings['output'], "blk%05d.dat" % self.outFn) - print("Output file " + self.outFname) - self.outF = open(self.outFname, "wb") - - self.outF.write(inhdr) - self.outF.write(blk_hdr) - self.outF.write(rawblock) - self.outsz = self.outsz + len(inhdr) + len(blk_hdr) + len(rawblock) - - self.blkCountOut = self.blkCountOut + 1 - if blkTS > self.highTS: - self.highTS = blkTS - - if (self.blkCountOut % 1000) == 0: - print('%i blocks scanned, %i blocks written (of %i, %.1f%% complete)' % - (self.blkCountIn, self.blkCountOut, len(self.blkindex), 100.0 * self.blkCountOut / len(self.blkindex))) - - def inFileName(self, fn): - return os.path.join(self.settings['input'], "blk%05d.dat" % fn) - - def fetchBlock(self, extent): - '''Fetch block contents from disk given extents''' - with open(self.inFileName(extent.fn), "rb") as f: - f.seek(extent.offset) - return f.read(extent.size) - - def copyOneBlock(self): - '''Find the next block to be written in the input, and copy it to the output.''' - extent = self.blockExtents.pop(self.blkCountOut) - if self.blkCountOut in self.outOfOrderData: - # If the data is cached, use it from memory and remove from the cache - rawblock = self.outOfOrderData.pop(self.blkCountOut) - self.outOfOrderSize -= len(rawblock) - else: # Otherwise look up data on disk - rawblock = self.fetchBlock(extent) - - self.writeBlock(extent.inhdr, extent.blkhdr, rawblock) - - def run(self): - while self.blkCountOut < len(self.blkindex): - if not self.inF: - fname = self.inFileName(self.inFn) - print("Input file " + fname) - try: - self.inF = open(fname, "rb") - except IOError: - print("Premature end of block data") - return - - inhdr = self.inF.read(8) - if (not inhdr or (inhdr[0] == "\0")): - self.inF.close() - self.inF = None - self.inFn = self.inFn + 1 - continue - - inMagic = inhdr[:4] - if (inMagic != self.settings['netmagic']): - print("Invalid magic: " + hexlify(inMagic).decode('utf-8')) - return - inLenLE = inhdr[4:] - su = struct.unpack("<I", inLenLE) - inLen = su[0] - 80 # length without header - blk_hdr = self.inF.read(80) - inExtent = BlockExtent(self.inFn, self.inF.tell(), inhdr, blk_hdr, inLen) - - self.hash_str = calc_hash_str(blk_hdr) - if not self.hash_str in blkmap: - # Because blocks can be written to files out-of-order as of 0.10, the script - # may encounter blocks it doesn't know about. Treat as debug output. - if settings['debug_output'] == 'true': - print("Skipping unknown block " + self.hash_str) - self.inF.seek(inLen, os.SEEK_CUR) - continue - - blkHeight = self.blkmap[self.hash_str] - self.blkCountIn += 1 - - if self.blkCountOut == blkHeight: - # If in-order block, just copy - rawblock = self.inF.read(inLen) - self.writeBlock(inhdr, blk_hdr, rawblock) - - # See if we can catch up to prior out-of-order blocks - while self.blkCountOut in self.blockExtents: - self.copyOneBlock() - - else: # If out-of-order, skip over block data for now - self.blockExtents[blkHeight] = inExtent - if self.outOfOrderSize < self.settings['out_of_order_cache_sz']: - # If there is space in the cache, read the data - # Reading the data in file sequence instead of seeking and fetching it later is preferred, - # but we don't want to fill up memory - self.outOfOrderData[blkHeight] = self.inF.read(inLen) - self.outOfOrderSize += inLen - else: # If no space in cache, seek forward - self.inF.seek(inLen, os.SEEK_CUR) - - print("Done (%i blocks written)" % (self.blkCountOut)) + def __init__(self, settings, blkindex, blkmap): + self.settings = settings + self.blkindex = blkindex + self.blkmap = blkmap + + self.inFn = 0 + self.inF = None + self.outFn = 0 + self.outsz = 0 + self.outF = None + self.outFname = None + self.blkCountIn = 0 + self.blkCountOut = 0 + + self.lastDate = datetime.datetime(2000, 1, 1) + self.highTS = 1408893517 - 315360000 + self.timestampSplit = False + self.fileOutput = True + self.setFileTime = False + self.maxOutSz = settings['max_out_sz'] + if 'output' in settings: + self.fileOutput = False + if settings['file_timestamp'] != 0: + self.setFileTime = True + if settings['split_timestamp'] != 0: + self.timestampSplit = True + # Extents and cache for out-of-order blocks + self.blockExtents = {} + self.outOfOrderData = {} + self.outOfOrderSize = 0 # running total size for items in outOfOrderData + + def writeBlock(self, inhdr, blk_hdr, rawblock): + blockSizeOnDisk = len(inhdr) + len(blk_hdr) + len(rawblock) + if not self.fileOutput and ((self.outsz + blockSizeOnDisk) > self.maxOutSz): + self.outF.close() + if self.setFileTime: + os.utime(self.outFname, (int(time.time()), self.highTS)) + self.outF = None + self.outFname = None + self.outFn = self.outFn + 1 + self.outsz = 0 + + (blkDate, blkTS) = get_blk_dt(blk_hdr) + if self.timestampSplit and (blkDate > self.lastDate): + print("New month " + blkDate.strftime("%Y-%m") + " @ " + self.hash_str) + self.lastDate = blkDate + if self.outF: + self.outF.close() + if self.setFileTime: + os.utime(self.outFname, (int(time.time()), self.highTS)) + self.outF = None + self.outFname = None + self.outFn = self.outFn + 1 + self.outsz = 0 + + if not self.outF: + if self.fileOutput: + self.outFname = self.settings['output_file'] + else: + self.outFname = os.path.join(self.settings['output'], "blk%05d.dat" % self.outFn) + print("Output file " + self.outFname) + self.outF = open(self.outFname, "wb") + + self.outF.write(inhdr) + self.outF.write(blk_hdr) + self.outF.write(rawblock) + self.outsz = self.outsz + len(inhdr) + len(blk_hdr) + len(rawblock) + + self.blkCountOut = self.blkCountOut + 1 + if blkTS > self.highTS: + self.highTS = blkTS + + if (self.blkCountOut % 1000) == 0: + print('%i blocks scanned, %i blocks written (of %i, %.1f%% complete)' % + (self.blkCountIn, self.blkCountOut, len(self.blkindex), 100.0 * self.blkCountOut / len(self.blkindex))) + + def inFileName(self, fn): + return os.path.join(self.settings['input'], "blk%05d.dat" % fn) + + def fetchBlock(self, extent): + '''Fetch block contents from disk given extents''' + with open(self.inFileName(extent.fn), "rb") as f: + f.seek(extent.offset) + return f.read(extent.size) + + def copyOneBlock(self): + '''Find the next block to be written in the input, and copy it to the output.''' + extent = self.blockExtents.pop(self.blkCountOut) + if self.blkCountOut in self.outOfOrderData: + # If the data is cached, use it from memory and remove from the cache + rawblock = self.outOfOrderData.pop(self.blkCountOut) + self.outOfOrderSize -= len(rawblock) + else: # Otherwise look up data on disk + rawblock = self.fetchBlock(extent) + + self.writeBlock(extent.inhdr, extent.blkhdr, rawblock) + + def run(self): + while self.blkCountOut < len(self.blkindex): + if not self.inF: + fname = self.inFileName(self.inFn) + print("Input file " + fname) + try: + self.inF = open(fname, "rb") + except IOError: + print("Premature end of block data") + return + + inhdr = self.inF.read(8) + if (not inhdr or (inhdr[0] == "\0")): + self.inF.close() + self.inF = None + self.inFn = self.inFn + 1 + continue + + inMagic = inhdr[:4] + if (inMagic != self.settings['netmagic']): + print("Invalid magic: " + hexlify(inMagic).decode('utf-8')) + return + inLenLE = inhdr[4:] + su = struct.unpack("<I", inLenLE) + inLen = su[0] - 80 # length without header + blk_hdr = self.inF.read(80) + inExtent = BlockExtent(self.inFn, self.inF.tell(), inhdr, blk_hdr, inLen) + + self.hash_str = calc_hash_str(blk_hdr) + if not self.hash_str in blkmap: + # Because blocks can be written to files out-of-order as of 0.10, the script + # may encounter blocks it doesn't know about. Treat as debug output. + if settings['debug_output'] == 'true': + print("Skipping unknown block " + self.hash_str) + self.inF.seek(inLen, os.SEEK_CUR) + continue + + blkHeight = self.blkmap[self.hash_str] + self.blkCountIn += 1 + + if self.blkCountOut == blkHeight: + # If in-order block, just copy + rawblock = self.inF.read(inLen) + self.writeBlock(inhdr, blk_hdr, rawblock) + + # See if we can catch up to prior out-of-order blocks + while self.blkCountOut in self.blockExtents: + self.copyOneBlock() + + else: # If out-of-order, skip over block data for now + self.blockExtents[blkHeight] = inExtent + if self.outOfOrderSize < self.settings['out_of_order_cache_sz']: + # If there is space in the cache, read the data + # Reading the data in file sequence instead of seeking and fetching it later is preferred, + # but we don't want to fill up memory + self.outOfOrderData[blkHeight] = self.inF.read(inLen) + self.outOfOrderSize += inLen + else: # If no space in cache, seek forward + self.inF.seek(inLen, os.SEEK_CUR) + + print("Done (%i blocks written)" % (self.blkCountOut)) if __name__ == '__main__': - if len(sys.argv) != 2: - print("Usage: linearize-data.py CONFIG-FILE") - sys.exit(1) - - f = open(sys.argv[1]) - for line in f: - # skip comment lines - m = re.search('^\s*#', line) - if m: - continue - - # parse key=value lines - m = re.search('^(\w+)\s*=\s*(\S.*)$', line) - if m is None: - continue - settings[m.group(1)] = m.group(2) - f.close() - - # Force hash byte format setting to be lowercase to make comparisons easier. - # Also place upfront in case any settings need to know about it. - if 'rev_hash_bytes' not in settings: - settings['rev_hash_bytes'] = 'false' - settings['rev_hash_bytes'] = settings['rev_hash_bytes'].lower() - - if 'netmagic' not in settings: - settings['netmagic'] = 'f9beb4d9' - if 'genesis' not in settings: - settings['genesis'] = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f' - if 'input' not in settings: - settings['input'] = 'input' - if 'hashlist' not in settings: - settings['hashlist'] = 'hashlist.txt' - if 'file_timestamp' not in settings: - settings['file_timestamp'] = 0 - if 'split_timestamp' not in settings: - settings['split_timestamp'] = 0 - if 'max_out_sz' not in settings: - settings['max_out_sz'] = 1000 * 1000 * 1000 - if 'out_of_order_cache_sz' not in settings: - settings['out_of_order_cache_sz'] = 100 * 1000 * 1000 - if 'debug_output' not in settings: - settings['debug_output'] = 'false' - - settings['max_out_sz'] = int(settings['max_out_sz']) - settings['split_timestamp'] = int(settings['split_timestamp']) - settings['file_timestamp'] = int(settings['file_timestamp']) - settings['netmagic'] = unhexlify(settings['netmagic'].encode('utf-8')) - settings['out_of_order_cache_sz'] = int(settings['out_of_order_cache_sz']) - settings['debug_output'] = settings['debug_output'].lower() - - if 'output_file' not in settings and 'output' not in settings: - print("Missing output file / directory") - sys.exit(1) - - blkindex = get_block_hashes(settings) - blkmap = mkblockmap(blkindex) - - # Block hash map won't be byte-reversed. Neither should the genesis hash. - if not settings['genesis'] in blkmap: - print("Genesis block not found in hashlist") - else: - BlockDataCopier(settings, blkindex, blkmap).run() + if len(sys.argv) != 2: + print("Usage: linearize-data.py CONFIG-FILE") + sys.exit(1) + + f = open(sys.argv[1]) + for line in f: + # skip comment lines + m = re.search('^\s*#', line) + if m: + continue + + # parse key=value lines + m = re.search('^(\w+)\s*=\s*(\S.*)$', line) + if m is None: + continue + settings[m.group(1)] = m.group(2) + f.close() + + # Force hash byte format setting to be lowercase to make comparisons easier. + # Also place upfront in case any settings need to know about it. + if 'rev_hash_bytes' not in settings: + settings['rev_hash_bytes'] = 'false' + settings['rev_hash_bytes'] = settings['rev_hash_bytes'].lower() + + if 'netmagic' not in settings: + settings['netmagic'] = 'f9beb4d9' + if 'genesis' not in settings: + settings['genesis'] = '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f' + if 'input' not in settings: + settings['input'] = 'input' + if 'hashlist' not in settings: + settings['hashlist'] = 'hashlist.txt' + if 'file_timestamp' not in settings: + settings['file_timestamp'] = 0 + if 'split_timestamp' not in settings: + settings['split_timestamp'] = 0 + if 'max_out_sz' not in settings: + settings['max_out_sz'] = 1000 * 1000 * 1000 + if 'out_of_order_cache_sz' not in settings: + settings['out_of_order_cache_sz'] = 100 * 1000 * 1000 + if 'debug_output' not in settings: + settings['debug_output'] = 'false' + + settings['max_out_sz'] = int(settings['max_out_sz']) + settings['split_timestamp'] = int(settings['split_timestamp']) + settings['file_timestamp'] = int(settings['file_timestamp']) + settings['netmagic'] = unhexlify(settings['netmagic'].encode('utf-8')) + settings['out_of_order_cache_sz'] = int(settings['out_of_order_cache_sz']) + settings['debug_output'] = settings['debug_output'].lower() + + if 'output_file' not in settings and 'output' not in settings: + print("Missing output file / directory") + sys.exit(1) + + blkindex = get_block_hashes(settings) + blkmap = mkblockmap(blkindex) + + # Block hash map won't be byte-reversed. Neither should the genesis hash. + if not settings['genesis'] in blkmap: + print("Genesis block not found in hashlist") + else: + BlockDataCopier(settings, blkindex, blkmap).run() diff --git a/contrib/linearize/linearize-hashes.py b/contrib/linearize/linearize-hashes.py index e1304e26d0..8e1266ae0b 100755 --- a/contrib/linearize/linearize-hashes.py +++ b/contrib/linearize/linearize-hashes.py @@ -22,135 +22,135 @@ import os.path settings = {} def hex_switchEndian(s): - """ Switches the endianness of a hex string (in pairs of hex chars) """ - pairList = [s[i:i+2].encode() for i in range(0, len(s), 2)] - return b''.join(pairList[::-1]).decode() + """ Switches the endianness of a hex string (in pairs of hex chars) """ + pairList = [s[i:i+2].encode() for i in range(0, len(s), 2)] + return b''.join(pairList[::-1]).decode() class BitcoinRPC: - def __init__(self, host, port, username, password): - authpair = "%s:%s" % (username, password) - authpair = authpair.encode('utf-8') - self.authhdr = b"Basic " + base64.b64encode(authpair) - self.conn = httplib.HTTPConnection(host, port=port, timeout=30) - - def execute(self, obj): - try: - self.conn.request('POST', '/', json.dumps(obj), - { 'Authorization' : self.authhdr, - 'Content-type' : 'application/json' }) - except ConnectionRefusedError: - print('RPC connection refused. Check RPC settings and the server status.', - file=sys.stderr) - return None - - resp = self.conn.getresponse() - if resp is None: - print("JSON-RPC: no response", file=sys.stderr) - return None - - body = resp.read().decode('utf-8') - resp_obj = json.loads(body) - return resp_obj - - @staticmethod - def build_request(idx, method, params): - obj = { 'version' : '1.1', - 'method' : method, - 'id' : idx } - if params is None: - obj['params'] = [] - else: - obj['params'] = params - return obj - - @staticmethod - def response_is_error(resp_obj): - return 'error' in resp_obj and resp_obj['error'] is not None + def __init__(self, host, port, username, password): + authpair = "%s:%s" % (username, password) + authpair = authpair.encode('utf-8') + self.authhdr = b"Basic " + base64.b64encode(authpair) + self.conn = httplib.HTTPConnection(host, port=port, timeout=30) + + def execute(self, obj): + try: + self.conn.request('POST', '/', json.dumps(obj), + { 'Authorization' : self.authhdr, + 'Content-type' : 'application/json' }) + except ConnectionRefusedError: + print('RPC connection refused. Check RPC settings and the server status.', + file=sys.stderr) + return None + + resp = self.conn.getresponse() + if resp is None: + print("JSON-RPC: no response", file=sys.stderr) + return None + + body = resp.read().decode('utf-8') + resp_obj = json.loads(body) + return resp_obj + + @staticmethod + def build_request(idx, method, params): + obj = { 'version' : '1.1', + 'method' : method, + 'id' : idx } + if params is None: + obj['params'] = [] + else: + obj['params'] = params + return obj + + @staticmethod + def response_is_error(resp_obj): + return 'error' in resp_obj and resp_obj['error'] is not None def get_block_hashes(settings, max_blocks_per_call=10000): - rpc = BitcoinRPC(settings['host'], settings['port'], - settings['rpcuser'], settings['rpcpassword']) - - height = settings['min_height'] - while height < settings['max_height']+1: - num_blocks = min(settings['max_height']+1-height, max_blocks_per_call) - batch = [] - for x in range(num_blocks): - batch.append(rpc.build_request(x, 'getblockhash', [height + x])) - - reply = rpc.execute(batch) - if reply is None: - print('Cannot continue. Program will halt.') - return None - - 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) - 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']) - print(resp_obj['result']) - - height += num_blocks + rpc = BitcoinRPC(settings['host'], settings['port'], + settings['rpcuser'], settings['rpcpassword']) + + height = settings['min_height'] + while height < settings['max_height']+1: + num_blocks = min(settings['max_height']+1-height, max_blocks_per_call) + batch = [] + for x in range(num_blocks): + batch.append(rpc.build_request(x, 'getblockhash', [height + x])) + + reply = rpc.execute(batch) + if reply is None: + print('Cannot continue. Program will halt.') + return None + + 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) + 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']) + print(resp_obj['result']) + + height += num_blocks def get_rpc_cookie(): - # Open the cookie file - with open(os.path.join(os.path.expanduser(settings['datadir']), '.cookie'), 'r') as f: - combined = f.readline() - combined_split = combined.split(":") - settings['rpcuser'] = combined_split[0] - settings['rpcpassword'] = combined_split[1] + # Open the cookie file + with open(os.path.join(os.path.expanduser(settings['datadir']), '.cookie'), 'r') as f: + combined = f.readline() + combined_split = combined.split(":") + settings['rpcuser'] = combined_split[0] + settings['rpcpassword'] = combined_split[1] if __name__ == '__main__': - if len(sys.argv) != 2: - print("Usage: linearize-hashes.py CONFIG-FILE") - sys.exit(1) - - f = open(sys.argv[1]) - for line in f: - # skip comment lines - m = re.search('^\s*#', line) - if m: - continue - - # parse key=value lines - m = re.search('^(\w+)\s*=\s*(\S.*)$', line) - if m is None: - continue - settings[m.group(1)] = m.group(2) - f.close() - - if 'host' not in settings: - settings['host'] = '127.0.0.1' - if 'port' not in settings: - settings['port'] = 8332 - if 'min_height' not in settings: - settings['min_height'] = 0 - if 'max_height' not in settings: - settings['max_height'] = 313000 - if 'rev_hash_bytes' not in settings: - settings['rev_hash_bytes'] = 'false' - - use_userpass = True - use_datadir = False - if 'rpcuser' not in settings or 'rpcpassword' not in settings: - use_userpass = False - 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=sys.stderr) - sys.exit(1) - - settings['port'] = int(settings['port']) - settings['min_height'] = int(settings['min_height']) - settings['max_height'] = int(settings['max_height']) - - # Force hash byte format setting to be lowercase to make comparisons easier. - settings['rev_hash_bytes'] = settings['rev_hash_bytes'].lower() - - # Get the rpc user and pass from the cookie if the datadir is set - if use_datadir: - get_rpc_cookie() - - get_block_hashes(settings) + if len(sys.argv) != 2: + print("Usage: linearize-hashes.py CONFIG-FILE") + sys.exit(1) + + f = open(sys.argv[1]) + for line in f: + # skip comment lines + m = re.search('^\s*#', line) + if m: + continue + + # parse key=value lines + m = re.search('^(\w+)\s*=\s*(\S.*)$', line) + if m is None: + continue + settings[m.group(1)] = m.group(2) + f.close() + + if 'host' not in settings: + settings['host'] = '127.0.0.1' + if 'port' not in settings: + settings['port'] = 8332 + if 'min_height' not in settings: + settings['min_height'] = 0 + if 'max_height' not in settings: + settings['max_height'] = 313000 + if 'rev_hash_bytes' not in settings: + settings['rev_hash_bytes'] = 'false' + + use_userpass = True + use_datadir = False + if 'rpcuser' not in settings or 'rpcpassword' not in settings: + use_userpass = False + 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=sys.stderr) + sys.exit(1) + + settings['port'] = int(settings['port']) + settings['min_height'] = int(settings['min_height']) + settings['max_height'] = int(settings['max_height']) + + # Force hash byte format setting to be lowercase to make comparisons easier. + settings['rev_hash_bytes'] = settings['rev_hash_bytes'].lower() + + # Get the rpc user and pass from the cookie if the datadir is set + if use_datadir: + get_rpc_cookie() + + get_block_hashes(settings) diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py index 72eb7255f3..ace7d3534f 100755 --- a/contrib/seeds/generate-seeds.py +++ b/contrib/seeds/generate-seeds.py @@ -11,7 +11,7 @@ argument: nodes_main.txt nodes_test.txt -These files must consist of lines in the format +These files must consist of lines in the format <ip> <ip>:<port> diff --git a/doc/release-notes-pr10267.md b/doc/release-notes-pr10267.md new file mode 100644 index 0000000000..7e1967daf0 --- /dev/null +++ b/doc/release-notes-pr10267.md @@ -0,0 +1,13 @@ +Changed command-line options +---------------------------- + +- `-includeconf=<file>` can be used to include additional configuration files. + Only works inside the `bitcoin.conf` file, not inside included files or from + command-line. Multiple files may be included. Can be disabled from command- + line via `-noincludeconf`. Note that multi-argument commands like + `-includeconf` will override preceding `-noincludeconf`, i.e. + + noincludeconf=1 + includeconf=relative.conf + + as bitcoin.conf will still include `relative.conf`. diff --git a/src/Makefile.am b/src/Makefile.am index 04bd75a2a5..5dbf4bef8c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,8 +5,8 @@ DIST_SUBDIRS = secp256k1 univalue AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(GPROF_LDFLAGS) $(SANITIZER_LDFLAGS) -AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) $(GPROF_CXXFLAGS) $(SANITIZER_CXXFLAGS) -AM_CPPFLAGS = $(HARDENED_CPPFLAGS) +AM_CXXFLAGS = $(DEBUG_CXXFLAGS) $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) $(GPROF_CXXFLAGS) $(SANITIZER_CXXFLAGS) +AM_CPPFLAGS = $(DEBUG_CPPFLAGS) $(HARDENED_CPPFLAGS) AM_LIBTOOLFLAGS = --preserve-dup-deps EXTRA_LIBRARIES = diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp index c1f3339830..81648f534c 100644 --- a/src/bench/bench_bitcoin.cpp +++ b/src/bench/bench_bitcoin.cpp @@ -22,22 +22,27 @@ static const char* DEFAULT_PLOT_PLOTLYURL = "https://cdn.plot.ly/plotly-latest.m static const int64_t DEFAULT_PLOT_WIDTH = 1024; static const int64_t DEFAULT_PLOT_HEIGHT = 768; +static void SetupBenchArgs() +{ + gArgs.AddArg("-?", _("Print this help message and exit"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-list", _("List benchmarks without executing them. Can be combined with -scaling and -filter"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-evals=<n>", strprintf(_("Number of measurement evaluations to perform. (default: %u)"), DEFAULT_BENCH_EVALUATIONS), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-filter=<regex>", strprintf(_("Regular expression filter to select benchmark by name (default: %s)"), DEFAULT_BENCH_FILTER), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-scaling=<n>", strprintf(_("Scaling factor for benchmark's runtime (default: %u)"), DEFAULT_BENCH_SCALING), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-printer=(console|plot)", strprintf(_("Choose printer format. console: print data to console. plot: Print results as HTML graph (default: %s)"), DEFAULT_BENCH_PRINTER), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-plot-plotlyurl=<uri>", strprintf(_("URL to use for plotly.js (default: %s)"), DEFAULT_PLOT_PLOTLYURL), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-plot-width=<x>", strprintf(_("Plot width in pixel (default: %u)"), DEFAULT_PLOT_WIDTH), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-plot-height=<x>", strprintf(_("Plot height in pixel (default: %u)"), DEFAULT_PLOT_HEIGHT), false, OptionsCategory::OPTIONS); +} + int main(int argc, char** argv) { + SetupBenchArgs(); gArgs.ParseParameters(argc, argv); if (HelpRequested(gArgs)) { - std::cout << HelpMessageGroup(_("Options:")) - << HelpMessageOpt("-?", _("Print this help message and exit")) - << HelpMessageOpt("-list", _("List benchmarks without executing them. Can be combined with -scaling and -filter")) - << HelpMessageOpt("-evals=<n>", strprintf(_("Number of measurement evaluations to perform. (default: %u)"), DEFAULT_BENCH_EVALUATIONS)) - << HelpMessageOpt("-filter=<regex>", strprintf(_("Regular expression filter to select benchmark by name (default: %s)"), DEFAULT_BENCH_FILTER)) - << HelpMessageOpt("-scaling=<n>", strprintf(_("Scaling factor for benchmark's runtime (default: %u)"), DEFAULT_BENCH_SCALING)) - << HelpMessageOpt("-printer=(console|plot)", strprintf(_("Choose printer format. console: print data to console. plot: Print results as HTML graph (default: %s)"), DEFAULT_BENCH_PRINTER)) - << HelpMessageOpt("-plot-plotlyurl=<uri>", strprintf(_("URL to use for plotly.js (default: %s)"), DEFAULT_PLOT_PLOTLYURL)) - << HelpMessageOpt("-plot-width=<x>", strprintf(_("Plot width in pixel (default: %u)"), DEFAULT_PLOT_WIDTH)) - << HelpMessageOpt("-plot-height=<x>", strprintf(_("Plot height in pixel (default: %u)"), DEFAULT_PLOT_HEIGHT)); + std::cout << gArgs.GetHelpMessage(); return 0; } diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 34472a0e61..b26b172305 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -29,29 +29,26 @@ static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900; static const bool DEFAULT_NAMED=false; static const int CONTINUE_EXECUTION=-1; -static std::string HelpMessageCli() +static void SetupCliArgs() { const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN); const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET); - std::string strUsage; - strUsage += HelpMessageGroup(_("Options:")); - strUsage += HelpMessageOpt("-?", _("This help message")); - strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file. Relative paths will be prefixed by datadir location. (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("-rpcclienttimeout=<n>", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT)); - strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT)); - strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections")); - strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort())); - strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections")); - strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start")); - strUsage += HelpMessageOpt("-rpcwallet=<walletname>", _("Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind)")); - strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.")); - strUsage += HelpMessageOpt("-stdinrpcpass", strprintf(_("Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password."))); - - return strUsage; + + gArgs.AddArg("-?", _("This help message"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-conf=<file>", strprintf(_("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)"), BITCOIN_CONF_FILENAME), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-datadir=<dir>", _("Specify data directory"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-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)"), false, OptionsCategory::OPTIONS); + SetupChainParamsBaseOptions(); + gArgs.AddArg("-named", strprintf(_("Pass named instead of positional arguments (default: %s)"), DEFAULT_NAMED), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpcclienttimeout=<n>", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpcpassword=<pw>", _("Password for JSON-RPC connections"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpcuser=<user>", _("Username for JSON-RPC connections"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpcwait", _("Wait for RPC server to start"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpcwallet=<walletname>", _("Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind)"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password."), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-stdinrpcpass", strprintf(_("Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password.")), false, OptionsCategory::OPTIONS); } ////////////////////////////////////////////////////////////////////////////// @@ -82,6 +79,7 @@ static int AppInitRPC(int argc, char* argv[]) // // Parameters // + SetupCliArgs(); gArgs.ParseParameters(argc, argv); if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { std::string strUsage = strprintf(_("%s RPC client version"), _(PACKAGE_NAME)) + " " + FormatFullVersion() + "\n"; @@ -92,7 +90,7 @@ static int AppInitRPC(int argc, char* argv[]) " bitcoin-cli [options] help " + _("List commands") + "\n" + " bitcoin-cli [options] help <command> " + _("Get help for a command") + "\n"; - strUsage += "\n" + HelpMessageCli(); + strUsage += "\n" + gArgs.GetHelpMessage(); } fprintf(stdout, "%s", strUsage.c_str()); @@ -107,7 +105,7 @@ static int AppInitRPC(int argc, char* argv[]) return EXIT_FAILURE; } try { - gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); + gArgs.ReadConfigFiles(); } catch (const std::exception& e) { fprintf(stderr,"Error reading configuration file: %s\n", e.what()); return EXIT_FAILURE; diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 07ad09ea7b..bf42307df5 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -31,6 +31,41 @@ static bool fCreateBlank; static std::map<std::string,UniValue> registers; static const int CONTINUE_EXECUTION=-1; +static void SetupBitcoinTxArgs() +{ + gArgs.AddArg("-?", _("This help message"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-create", _("Create new, empty TX."), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-json", _("Select JSON output"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-txid", _("Output only the hex-encoded transaction id of the resultant transaction."), false, OptionsCategory::OPTIONS); + SetupChainParamsBaseOptions(); + + gArgs.AddArg("delin=N", _("Delete input N from TX"), false, OptionsCategory::COMMANDS); + gArgs.AddArg("delout=N", _("Delete output N from TX"), false, OptionsCategory::COMMANDS); + gArgs.AddArg("in=TXID:VOUT(:SEQUENCE_NUMBER)", _("Add input to TX"), false, OptionsCategory::COMMANDS); + gArgs.AddArg("locktime=N", _("Set TX lock time to N"), false, OptionsCategory::COMMANDS); + gArgs.AddArg("nversion=N", _("Set TX version to N"), false, OptionsCategory::COMMANDS); + gArgs.AddArg("outaddr=VALUE:ADDRESS", _("Add address-based output to TX"), false, OptionsCategory::COMMANDS); + gArgs.AddArg("outdata=[VALUE:]DATA", _("Add data-based output to TX"), false, OptionsCategory::COMMANDS); + gArgs.AddArg("outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", _("Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS") + ". " + + _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " + + _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash."), false, OptionsCategory::COMMANDS); + gArgs.AddArg("outpubkey=VALUE:PUBKEY[:FLAGS]", _("Add pay-to-pubkey output to TX") + ". " + + _("Optionally add the \"W\" flag to produce a pay-to-witness-pubkey-hash output") + ". " + + _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash."), false, OptionsCategory::COMMANDS); + gArgs.AddArg("outscript=VALUE:SCRIPT[:FLAGS]", _("Add raw script output to TX") + ". " + + _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " + + _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash."), false, OptionsCategory::COMMANDS); + gArgs.AddArg("replaceable(=N)", _("Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)"), false, OptionsCategory::COMMANDS); + gArgs.AddArg("sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " + + _("This command requires JSON registers:") + + _("prevtxs=JSON object") + ", " + + _("privatekeys=JSON object") + ". " + + _("See signrawtransaction docs for format of sighash flags, JSON objects."), false, OptionsCategory::COMMANDS); + + gArgs.AddArg("load=NAME:FILENAME", _("Load JSON file FILENAME into register NAME"), false, OptionsCategory::REGISTER_COMMANDS); + gArgs.AddArg("set=NAME:JSON-STRING", _("Set register NAME to given JSON-STRING"), false, OptionsCategory::REGISTER_COMMANDS); +} + // // This function returns either one of EXIT_ codes when it's expected to stop the process or // CONTINUE_EXECUTION when it's expected to continue further. @@ -40,6 +75,7 @@ static int AppInitRawTx(int argc, char* argv[]) // // Parameters // + SetupBitcoinTxArgs(); gArgs.ParseParameters(argc, argv); // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) @@ -59,48 +95,10 @@ static int AppInitRawTx(int argc, char* argv[]) " bitcoin-tx [options] <hex-tx> [commands] " + _("Update hex-encoded bitcoin transaction") + "\n" + " bitcoin-tx [options] -create [commands] " + _("Create hex-encoded bitcoin transaction") + "\n" + "\n"; + strUsage += gArgs.GetHelpMessage(); fprintf(stdout, "%s", strUsage.c_str()); - strUsage = HelpMessageGroup(_("Options:")); - strUsage += HelpMessageOpt("-?", _("This help message")); - strUsage += HelpMessageOpt("-create", _("Create new, empty TX.")); - strUsage += HelpMessageOpt("-json", _("Select JSON output")); - strUsage += HelpMessageOpt("-txid", _("Output only the hex-encoded transaction id of the resultant transaction.")); - AppendParamsHelpMessages(strUsage); - - fprintf(stdout, "%s", strUsage.c_str()); - - strUsage = HelpMessageGroup(_("Commands:")); - strUsage += HelpMessageOpt("delin=N", _("Delete input N from TX")); - strUsage += HelpMessageOpt("delout=N", _("Delete output N from TX")); - strUsage += HelpMessageOpt("in=TXID:VOUT(:SEQUENCE_NUMBER)", _("Add input to TX")); - strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N")); - strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N")); - strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX")); - strUsage += HelpMessageOpt("outdata=[VALUE:]DATA", _("Add data-based output to TX")); - strUsage += HelpMessageOpt("outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", _("Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = PUBKEYS") + ". " + - _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " + - _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.")); - strUsage += HelpMessageOpt("outpubkey=VALUE:PUBKEY[:FLAGS]", _("Add pay-to-pubkey output to TX") + ". " + - _("Optionally add the \"W\" flag to produce a pay-to-witness-pubkey-hash output") + ". " + - _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.")); - strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT[:FLAGS]", _("Add raw script output to TX") + ". " + - _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " + - _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.")); - strUsage += HelpMessageOpt("replaceable(=N)", _("Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)")); - strUsage += HelpMessageOpt("sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " + - _("This command requires JSON registers:") + - _("prevtxs=JSON object") + ", " + - _("privatekeys=JSON object") + ". " + - _("See signrawtransaction docs for format of sighash flags, JSON objects.")); - fprintf(stdout, "%s", strUsage.c_str()); - - strUsage = HelpMessageGroup(_("Register Commands:")); - strUsage += HelpMessageOpt("load=NAME:FILENAME", _("Load JSON file FILENAME into register NAME")); - strUsage += HelpMessageOpt("set=NAME:JSON-STRING", _("Set register NAME to given JSON-STRING")); - fprintf(stdout, "%s", strUsage.c_str()); - if (argc < 2) { fprintf(stderr, "Error: too few parameters\n"); return EXIT_FAILURE; diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 69de1a1666..1539e4025a 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -61,6 +61,10 @@ static bool AppInit(int argc, char* argv[]) // Parameters // // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main() + SetupServerArgs(); +#if HAVE_DECL_DAEMON + gArgs.AddArg("-daemon", _("Run in the background as a daemon and accept commands"), false, OptionsCategory::OPTIONS); +#endif gArgs.ParseParameters(argc, argv); // Process help and version before taking care about datadir @@ -76,7 +80,7 @@ static bool AppInit(int argc, char* argv[]) strUsage += "\n" + _("Usage:") + "\n" + " bitcoind [options] " + strprintf(_("Start %s Daemon"), _(PACKAGE_NAME)) + "\n"; - strUsage += "\n" + HelpMessage(HelpMessageMode::BITCOIND); + strUsage += "\n" + gArgs.GetHelpMessage(); } fprintf(stdout, "%s", strUsage.c_str()); @@ -92,7 +96,7 @@ static bool AppInit(int argc, char* argv[]) } try { - gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); + gArgs.ReadConfigFiles(); } catch (const std::exception& e) { fprintf(stderr,"Error reading configuration file: %s\n", e.what()); return false; diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 3ef9c2cfe5..91ba3ac9d9 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -14,14 +14,11 @@ const std::string CBaseChainParams::MAIN = "main"; const std::string CBaseChainParams::TESTNET = "test"; const std::string CBaseChainParams::REGTEST = "regtest"; -void AppendParamsHelpMessages(std::string& strUsage, bool debugHelp) +void SetupChainParamsBaseOptions() { - strUsage += HelpMessageGroup(_("Chain selection options:")); - if (debugHelp) { - strUsage += HelpMessageOpt("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. " - "This is intended for regression testing tools and app development."); - } - strUsage += HelpMessageOpt("-testnet", _("Use the test chain")); + gArgs.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. " + "This is intended for regression testing tools and app development.", true, OptionsCategory::CHAINPARAMS); + gArgs.AddArg("-testnet", _("Use the test chain"), false, OptionsCategory::CHAINPARAMS); } static std::unique_ptr<CBaseChainParams> globalChainBaseParams; diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index 5b11f36770..9f8bbafcd5 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -40,10 +40,9 @@ private: std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain); /** - * Append the help messages for the chainparams options to the - * parameter string. + *Set the arguments for chainparams */ -void AppendParamsHelpMessages(std::string& strUsage, bool debugHelp=true); +void SetupChainParamsBaseOptions(); /** * Return the currently selected parameters. This won't change after app diff --git a/src/init.cpp b/src/init.cpp index 7d8f462ef1..4ad68b73f2 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -76,7 +76,7 @@ std::unique_ptr<PeerLogicValidation> peerLogic; class DummyWalletInit : public WalletInitInterface { public: - std::string GetHelpString(bool showDebug) const override {return std::string{};} + void AddWalletOptions() const override {} bool ParameterInteraction() const override {return true;} void RegisterRPC(CRPCTable &) const override {} bool Verify() const override {return true;} @@ -341,197 +341,164 @@ static void OnRPCStopped() LogPrint(BCLog::RPC, "RPC stopped.\n"); } -std::string HelpMessage(HelpMessageMode mode) +void SetupServerArgs() { const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN); const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET); const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN); const auto testnetChainParams = CreateChainParams(CBaseChainParams::TESTNET); - const bool showDebug = gArgs.GetBoolArg("-help-debug", false); + // Set all of the args and their help // When adding new options to the categories, please keep and ensure alphabetical ordering. // Do not translate _(...) -help-debug options, Many technical terms, and only a very small audience, so is unnecessary stress to translators. - std::string strUsage = HelpMessageGroup(_("Options:")); - strUsage += HelpMessageOpt("-?", _("Print this help message and exit")); - strUsage += HelpMessageOpt("-version", _("Print version and exit")); - strUsage += HelpMessageOpt("-alertnotify=<cmd>", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)")); - strUsage +=HelpMessageOpt("-assumevalid=<hex>", strprintf(_("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: %s, testnet: %s)"), defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex())); - strUsage += HelpMessageOpt("-blocksdir=<dir>", _("Specify blocks directory (default: <datadir>/blocks)")); - strUsage += HelpMessageOpt("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash)")); - strUsage += HelpMessageOpt("-blockreconstructionextratxn=<n>", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN)); - if (showDebug) - strUsage += HelpMessageOpt("-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %u)"), DEFAULT_BLOCKSONLY)); - strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)"), BITCOIN_CONF_FILENAME)); - if (mode == HelpMessageMode::BITCOIND) - { -#if HAVE_DECL_DAEMON - strUsage += HelpMessageOpt("-daemon", _("Run in the background as a daemon and accept commands")); -#endif - } - strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory")); - if (showDebug) { - strUsage += HelpMessageOpt("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize)); - } - strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache)); - strUsage += HelpMessageOpt("-debuglogfile=<file>", strprintf(_("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), DEFAULT_DEBUGLOGFILE)); - if (showDebug) - strUsage += HelpMessageOpt("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER)); - strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file on startup")); - strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE)); - strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); - 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("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), - -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS)); - strUsage += HelpMessageOpt("-persistmempool", strprintf(_("Whether to save the mempool on shutdown and load on restart (default: %u)"), DEFAULT_PERSIST_MEMPOOL)); + gArgs.AddArg("-?", _("Print this help message and exit"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-version", _("Print version and exit"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-alertnotify=<cmd>", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-assumevalid=<hex>", strprintf(_("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: %s, testnet: %s)"), defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-blocksdir=<dir>", _("Specify blocks directory (default: <datadir>/blocks)"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash)"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-blockreconstructionextratxn=<n>", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %u)"), DEFAULT_BLOCKSONLY), true, OptionsCategory::OPTIONS); + gArgs.AddArg("-conf=<file>", strprintf(_("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)"), BITCOIN_CONF_FILENAME), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-datadir=<dir>", _("Specify data directory"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), true, OptionsCategory::OPTIONS); + gArgs.AddArg("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-debuglogfile=<file>", strprintf(_("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), DEFAULT_DEBUGLOGFILE), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER), true, OptionsCategory::OPTIONS); + gArgs.AddArg("-includeconf=<file>", _("Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-loadblock=<file>", _("Imports blocks from external blk000??.dat file on startup"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-mempoolexpiry=<n>", strprintf(_("Do not keep transactions in the mempool longer than <n> hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-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()), true, OptionsCategory::OPTIONS); + gArgs.AddArg("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), + -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-persistmempool", strprintf(_("Whether to save the mempool on shutdown and load on restart (default: %u)"), DEFAULT_PERSIST_MEMPOOL), false, OptionsCategory::OPTIONS); #ifndef WIN32 - strUsage += HelpMessageOpt("-pid=<file>", strprintf(_("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), BITCOIN_PID_FILENAME)); + gArgs.AddArg("-pid=<file>", strprintf(_("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), BITCOIN_PID_FILENAME), false, OptionsCategory::OPTIONS); #endif - strUsage += HelpMessageOpt("-prune=<n>", strprintf(_("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. " + gArgs.AddArg("-prune=<n>", strprintf(_("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. " "Warning: Reverting this setting requires re-downloading the entire blockchain. " - "(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); - strUsage += HelpMessageOpt("-reindex", _("Rebuild chain state and block index from the blk*.dat files on disk")); - strUsage += HelpMessageOpt("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks")); + "(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-reindex", _("Rebuild chain state and block index from the blk*.dat files on disk"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks"), false, OptionsCategory::OPTIONS); #ifndef WIN32 - strUsage += HelpMessageOpt("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)")); + gArgs.AddArg("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)"), false, OptionsCategory::OPTIONS); #endif - strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), DEFAULT_TXINDEX)); - - strUsage += HelpMessageGroup(_("Connection options:")); - strUsage += HelpMessageOpt("-addnode=<ip>", _("Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info)")); - strUsage += HelpMessageOpt("-banscore=<n>", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD)); - strUsage += HelpMessageOpt("-bantime=<n>", strprintf(_("Number of seconds to keep misbehaving peers from reconnecting (default: %u)"), DEFAULT_MISBEHAVING_BANTIME)); - strUsage += HelpMessageOpt("-bind=<addr>", _("Bind to given address and always listen on it. Use [host]:port notation for IPv6")); - strUsage += HelpMessageOpt("-connect=<ip>", _("Connect only to the specified node(s); -connect=0 disables automatic connections (the rules for this peer are the same as for -addnode)")); - strUsage += HelpMessageOpt("-discover", _("Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)")); - strUsage += HelpMessageOpt("-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + strprintf(_("(default: %u)"), DEFAULT_NAME_LOOKUP)); - strUsage += HelpMessageOpt("-dnsseed", _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)")); - strUsage += HelpMessageOpt("-externalip=<ip>", _("Specify your own public address")); - strUsage += HelpMessageOpt("-forcednsseed", strprintf(_("Always query for peer addresses via DNS lookup (default: %u)"), DEFAULT_FORCEDNSSEED)); - strUsage += HelpMessageOpt("-listen", _("Accept connections from outside (default: 1 if no -proxy or -connect)")); - strUsage += HelpMessageOpt("-listenonion", strprintf(_("Automatically create Tor hidden service (default: %d)"), DEFAULT_LISTEN_ONION)); - strUsage += HelpMessageOpt("-maxconnections=<n>", strprintf(_("Maintain at most <n> connections to peers (default: %u)"), DEFAULT_MAX_PEER_CONNECTIONS)); - strUsage += HelpMessageOpt("-maxreceivebuffer=<n>", strprintf(_("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)"), DEFAULT_MAXRECEIVEBUFFER)); - strUsage += HelpMessageOpt("-maxsendbuffer=<n>", strprintf(_("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)"), DEFAULT_MAXSENDBUFFER)); - strUsage += HelpMessageOpt("-maxtimeadjustment", strprintf(_("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)"), DEFAULT_MAX_TIME_ADJUSTMENT)); - 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)); - strUsage += HelpMessageOpt("-onion=<ip:port>", strprintf(_("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)"), "-proxy")); - strUsage += HelpMessageOpt("-onlynet=<net>", _("Only connect to nodes in network <net> (ipv4, ipv6 or onion)")); - strUsage += HelpMessageOpt("-peerbloomfilters", strprintf(_("Support filtering of blocks and transaction with bloom filters (default: %u)"), DEFAULT_PEERBLOOMFILTERS)); - strUsage += HelpMessageOpt("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), DEFAULT_PERMIT_BAREMULTISIG)); - strUsage += HelpMessageOpt("-port=<port>", strprintf(_("Listen for connections on <port> (default: %u or testnet: %u)"), defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort())); - strUsage += HelpMessageOpt("-proxy=<ip:port>", _("Connect through SOCKS5 proxy")); - strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), DEFAULT_PROXYRANDOMIZE)); - strUsage += HelpMessageOpt("-seednode=<ip>", _("Connect to a node to retrieve peer addresses, and disconnect")); - strUsage += HelpMessageOpt("-timeout=<n>", strprintf(_("Specify connection timeout in milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT)); - strUsage += HelpMessageOpt("-torcontrol=<ip>:<port>", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL)); - strUsage += HelpMessageOpt("-torpassword=<pass>", _("Tor control port password (default: empty)")); + gArgs.AddArg("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), DEFAULT_TXINDEX), false, OptionsCategory::OPTIONS); + + gArgs.AddArg("-addnode=<ip>", _("Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info)"), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-banscore=<n>", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-bantime=<n>", strprintf(_("Number of seconds to keep misbehaving peers from reconnecting (default: %u)"), DEFAULT_MISBEHAVING_BANTIME), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-bind=<addr>", _("Bind to given address and always listen on it. Use [host]:port notation for IPv6"), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-connect=<ip>", _("Connect only to the specified node(s); -connect=0 disables automatic connections (the rules for this peer are the same as for -addnode)"), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-discover", _("Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)"), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + strprintf(_("(default: %u)"), DEFAULT_NAME_LOOKUP), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-dnsseed", _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)"), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-externalip=<ip>", _("Specify your own public address"), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-forcednsseed", strprintf(_("Always query for peer addresses via DNS lookup (default: %u)"), DEFAULT_FORCEDNSSEED), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-listen", _("Accept connections from outside (default: 1 if no -proxy or -connect)"), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-listenonion", strprintf(_("Automatically create Tor hidden service (default: %d)"), DEFAULT_LISTEN_ONION), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-maxconnections=<n>", strprintf(_("Maintain at most <n> connections to peers (default: %u)"), DEFAULT_MAX_PEER_CONNECTIONS), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-maxreceivebuffer=<n>", strprintf(_("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)"), DEFAULT_MAXRECEIVEBUFFER), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-maxsendbuffer=<n>", strprintf(_("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)"), DEFAULT_MAXSENDBUFFER), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-maxtimeadjustment", strprintf(_("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)"), DEFAULT_MAX_TIME_ADJUSTMENT), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-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), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-onion=<ip:port>", strprintf(_("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)"), "-proxy"), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-onlynet=<net>", _("Only connect to nodes in network <net> (ipv4, ipv6 or onion)"), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-peerbloomfilters", strprintf(_("Support filtering of blocks and transaction with bloom filters (default: %u)"), DEFAULT_PEERBLOOMFILTERS), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), DEFAULT_PERMIT_BAREMULTISIG), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-port=<port>", strprintf(_("Listen for connections on <port> (default: %u or testnet: %u)"), defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort()), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-proxy=<ip:port>", _("Connect through SOCKS5 proxy"), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), DEFAULT_PROXYRANDOMIZE), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-seednode=<ip>", _("Connect to a node to retrieve peer addresses, and disconnect"), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-timeout=<n>", strprintf(_("Specify connection timeout in milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-torcontrol=<ip>:<port>", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-torpassword=<pass>", _("Tor control port password (default: empty)"), false, OptionsCategory::CONNECTION); #ifdef USE_UPNP #if USE_UPNP - strUsage += HelpMessageOpt("-upnp", _("Use UPnP to map the listening port (default: 1 when listening and no -proxy)")); + gArgs.AddArg("-upnp", _("Use UPnP to map the listening port (default: 1 when listening and no -proxy)"), false, OptionsCategory::CONNECTION); #else - strUsage += HelpMessageOpt("-upnp", strprintf(_("Use UPnP to map the listening port (default: %u)"), 0)); + gArgs.AddArg("-upnp", strprintf(_("Use UPnP to map the listening port (default: %u)"), 0), false, OptionsCategory::CONNECTION); #endif #endif - strUsage += HelpMessageOpt("-whitebind=<addr>", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6")); - strUsage += HelpMessageOpt("-whitelist=<IP address or network>", _("Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple 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")); + gArgs.AddArg("-whitebind=<addr>", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6"), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-whitelist=<IP address or network>", _("Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple 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"), false, OptionsCategory::CONNECTION); - strUsage += g_wallet_init_interface.GetHelpString(showDebug); + g_wallet_init_interface.AddWalletOptions(); #if ENABLE_ZMQ - strUsage += HelpMessageGroup(_("ZeroMQ notification options:")); - strUsage += HelpMessageOpt("-zmqpubhashblock=<address>", _("Enable publish hash block in <address>")); - strUsage += HelpMessageOpt("-zmqpubhashtx=<address>", _("Enable publish hash transaction in <address>")); - strUsage += HelpMessageOpt("-zmqpubrawblock=<address>", _("Enable publish raw block in <address>")); - strUsage += HelpMessageOpt("-zmqpubrawtx=<address>", _("Enable publish raw transaction in <address>")); + gArgs.AddArg("-zmqpubhashblock=<address>", _("Enable publish hash block in <address>"), false, OptionsCategory::ZMQ); + gArgs.AddArg("-zmqpubhashtx=<address>", _("Enable publish hash transaction in <address>"), false, OptionsCategory::ZMQ); + gArgs.AddArg("-zmqpubrawblock=<address>", _("Enable publish raw block in <address>"), false, OptionsCategory::ZMQ); + gArgs.AddArg("-zmqpubrawtx=<address>", _("Enable publish raw transaction in <address>"), false, OptionsCategory::ZMQ); #endif - strUsage += HelpMessageGroup(_("Debugging/Testing options:")); - if (showDebug) { - strUsage += HelpMessageOpt("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS)); - strUsage += HelpMessageOpt("-checklevel=<n>", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL)); - strUsage += HelpMessageOpt("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. (default: %u)", defaultChainParams->DefaultConsistencyChecks())); - 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("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used"); - strUsage += HelpMessageOpt("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages"); - strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT)); - strUsage += HelpMessageOpt("-stopatheight", strprintf("Stop running after reaching the given height in the main chain (default: %u)", DEFAULT_STOPATHEIGHT)); - - strUsage += HelpMessageOpt("-limitancestorcount=<n>", strprintf("Do not accept transactions if number of in-mempool ancestors is <n> or more (default: %u)", DEFAULT_ANCESTOR_LIMIT)); - strUsage += HelpMessageOpt("-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT)); - strUsage += HelpMessageOpt("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT)); - strUsage += HelpMessageOpt("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT)); - strUsage += HelpMessageOpt("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)"); - strUsage += HelpMessageOpt("-addrmantest", "Allows to test address relay on localhost"); - } - strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " + - _("If <category> is not supplied or if <category> = 1, output all debugging information.") + " " + _("<category> can be:") + " " + ListLogCategories() + "."); - strUsage += HelpMessageOpt("-debugexclude=<category>", strprintf(_("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories."))); - strUsage += HelpMessageOpt("-help-debug", _("Show all debugging options (usage: --help -help-debug)")); - strUsage += HelpMessageOpt("-logips", strprintf(_("Include IP addresses in debug output (default: %u)"), DEFAULT_LOGIPS)); - strUsage += HelpMessageOpt("-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %u)"), DEFAULT_LOGTIMESTAMPS)); - if (showDebug) - { - strUsage += HelpMessageOpt("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS)); - strUsage += HelpMessageOpt("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)"); - strUsage += HelpMessageOpt("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE)); - strUsage += HelpMessageOpt("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE)); - } - strUsage += HelpMessageOpt("-maxtxfee=<amt>", strprintf(_("Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)"), - CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE))); - if (showDebug) - { - strUsage += HelpMessageOpt("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY)); - } - strUsage += HelpMessageOpt("-printtoconsole", _("Send trace/debug info to console instead of debug.log file")); - strUsage += HelpMessageOpt("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when no -debug)")); - strUsage += HelpMessageOpt("-uacomment=<cmt>", _("Append comment to the user agent string")); - - AppendParamsHelpMessages(strUsage, showDebug); - - strUsage += HelpMessageGroup(_("Node relay options:")); - if (showDebug) { - strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard())); - strUsage += HelpMessageOpt("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE))); - strUsage += HelpMessageOpt("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE))); - } - strUsage += HelpMessageOpt("-bytespersigop", strprintf(_("Equivalent bytes per sigop in transactions for relay and mining (default: %u)"), DEFAULT_BYTES_PER_SIGOP)); - strUsage += HelpMessageOpt("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), DEFAULT_ACCEPT_DATACARRIER)); - strUsage += HelpMessageOpt("-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY)); - strUsage += HelpMessageOpt("-mempoolreplacement", strprintf(_("Enable transaction replacement in the memory pool (default: %u)"), DEFAULT_ENABLE_REPLACEMENT)); - strUsage += HelpMessageOpt("-minrelaytxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)"), - CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE))); - strUsage += HelpMessageOpt("-whitelistforcerelay", strprintf(_("Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d)"), DEFAULT_WHITELISTFORCERELAY)); - strUsage += HelpMessageOpt("-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY)); - - strUsage += HelpMessageGroup(_("Block creation options:")); - strUsage += HelpMessageOpt("-blockmaxweight=<n>", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT)); - 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"); - - strUsage += HelpMessageGroup(_("RPC server options:")); - strUsage += HelpMessageOpt("-rest", strprintf(_("Accept public REST requests (default: %u)"), DEFAULT_REST_ENABLE)); - strUsage += HelpMessageOpt("-rpcallowip=<ip>", _("Allow JSON-RPC connections from specified source. Valid for <ip> are a 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")); - strUsage += HelpMessageOpt("-rpcauth=<userpw>", _("Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times")); - strUsage += HelpMessageOpt("-rpcbind=<addr>[:port]", _("Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. 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 -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)")); - strUsage += HelpMessageOpt("-rpccookiefile=<loc>", _("Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)")); - strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections")); - strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort())); - strUsage += HelpMessageOpt("-rpcserialversion", strprintf(_("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)"), DEFAULT_RPC_SERIALIZE_VERSION)); - if (showDebug) - strUsage += HelpMessageOpt("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT)); - strUsage += HelpMessageOpt("-rpcthreads=<n>", strprintf(_("Set the number of threads to service RPC calls (default: %d)"), DEFAULT_HTTP_THREADS)); - strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections")); - if (showDebug) - strUsage += HelpMessageOpt("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE)); - strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands")); - - return strUsage; + gArgs.AddArg("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-checklevel=<n>", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. (default: %u)", defaultChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", defaultChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used", true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages", true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-stopatheight", strprintf("Stop running after reaching the given height in the main chain (default: %u)", DEFAULT_STOPATHEIGHT), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-limitancestorcount=<n>", strprintf("Do not accept transactions if number of in-mempool ancestors is <n> or more (default: %u)", DEFAULT_ANCESTOR_LIMIT), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-addrmantest", "Allows to test address relay on localhost", true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " + + _("If <category> is not supplied or if <category> = 1, output all debugging information.") + " " + _("<category> can be:") + " " + ListLogCategories() + ".", false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-debugexclude=<category>", strprintf(_("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories.")), false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-help-debug", _("Show all debugging options (usage: --help -help-debug)"), false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-logips", strprintf(_("Include IP addresses in debug output (default: %u)"), DEFAULT_LOGIPS), false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %u)"), DEFAULT_LOGTIMESTAMPS), false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)", true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-maxtxfee=<amt>", strprintf(_("Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)"), + CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-printtoconsole", _("Send trace/debug info to console instead of debug.log file"), false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when no -debug)"), false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-uacomment=<cmt>", _("Append comment to the user agent string"), false, OptionsCategory::DEBUG_TEST); + + SetupChainParamsBaseOptions(); + + gArgs.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard()), true, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), true, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), true, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-bytespersigop", strprintf(_("Equivalent bytes per sigop in transactions for relay and mining (default: %u)"), DEFAULT_BYTES_PER_SIGOP), false, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), DEFAULT_ACCEPT_DATACARRIER), false, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY), false, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-mempoolreplacement", strprintf(_("Enable transaction replacement in the memory pool (default: %u)"), DEFAULT_ENABLE_REPLACEMENT), false, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-minrelaytxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)"), + CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), false, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-whitelistforcerelay", strprintf(_("Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d)"), DEFAULT_WHITELISTFORCERELAY), false, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY), false, OptionsCategory::NODE_RELAY); + + + gArgs.AddArg("-blockmaxweight=<n>", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT), false, OptionsCategory::BLOCK_CREATION); + gArgs.AddArg("-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)), false, OptionsCategory::BLOCK_CREATION); + gArgs.AddArg("-blockversion=<n>", "Override block version to test forking scenarios", true, OptionsCategory::BLOCK_CREATION); + + gArgs.AddArg("-rest", strprintf(_("Accept public REST requests (default: %u)"), DEFAULT_REST_ENABLE), false, OptionsCategory::RPC); + gArgs.AddArg("-rpcallowip=<ip>", _("Allow JSON-RPC connections from specified source. Valid for <ip> are a 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"), false, OptionsCategory::RPC); + gArgs.AddArg("-rpcauth=<userpw>", _("Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times"), false, OptionsCategory::RPC); + gArgs.AddArg("-rpcbind=<addr>[:port]", _("Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. 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 -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)"), false, OptionsCategory::RPC); + gArgs.AddArg("-rpccookiefile=<loc>", _("Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)"), false, OptionsCategory::RPC); + gArgs.AddArg("-rpcpassword=<pw>", _("Password for JSON-RPC connections"), false, OptionsCategory::RPC); + gArgs.AddArg("-rpcport=<port>", strprintf(_("Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()), false, OptionsCategory::RPC); + gArgs.AddArg("-rpcserialversion", strprintf(_("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)"), DEFAULT_RPC_SERIALIZE_VERSION), false, OptionsCategory::RPC); + gArgs.AddArg("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), true, OptionsCategory::RPC); + gArgs.AddArg("-rpcthreads=<n>", strprintf(_("Set the number of threads to service RPC calls (default: %d)"), DEFAULT_HTTP_THREADS), false, OptionsCategory::RPC); + gArgs.AddArg("-rpcuser=<user>", _("Username for JSON-RPC connections"), false, OptionsCategory::RPC); + gArgs.AddArg("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), true, OptionsCategory::RPC); + gArgs.AddArg("-server", _("Accept command line and JSON-RPC commands"), false, OptionsCategory::RPC); } std::string LicenseInfo() diff --git a/src/init.h b/src/init.h index 000c8c95e4..5423a042a6 100644 --- a/src/init.h +++ b/src/init.h @@ -8,6 +8,7 @@ #include <memory> #include <string> +#include <util.h> class CScheduler; class CWallet; @@ -60,14 +61,11 @@ bool AppInitLockDataDirectory(); */ bool AppInitMain(); -/** The help message mode determines what help message to show */ -enum class HelpMessageMode { - BITCOIND, - BITCOIN_QT -}; +/** + * Setup the arguments for gArgs + */ +void SetupServerArgs(); -/** Help for options shared between UI and daemon (for -help) */ -std::string HelpMessage(HelpMessageMode mode); /** Returns licensing information (for -version) */ std::string LicenseInfo(); diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index 53d2359caf..73657d4f5a 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -52,7 +52,7 @@ class NodeImpl : public Node { gArgs.ParseParameters(argc, argv); } - void readConfigFile(const std::string& conf_path) override { gArgs.ReadConfigFile(conf_path); } + void readConfigFiles() override { gArgs.ReadConfigFiles(); } bool softSetArg(const std::string& arg, const std::string& value) override { return gArgs.SoftSetArg(arg, value); } bool softSetBoolArg(const std::string& arg, bool value) override { return gArgs.SoftSetBoolArg(arg, value); } void selectParams(const std::string& network) override { SelectParams(network); } @@ -83,7 +83,7 @@ class NodeImpl : public Node StopMapPort(); } } - std::string helpMessage(HelpMessageMode mode) override { return HelpMessage(mode); } + void setupServerArgs() override { return SetupServerArgs(); } bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); } size_t getNodeCount(CConnman::NumConnections flags) override { diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 3cebe53eb0..2a1a8152df 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -7,7 +7,6 @@ #include <addrdb.h> // For banmap_t #include <amount.h> // For CAmount -#include <init.h> // For HelpMessageMode #include <net.h> // For CConnman::NumConnections #include <netaddress.h> // For Network @@ -48,7 +47,7 @@ public: virtual bool softSetBoolArg(const std::string& arg, bool value) = 0; //! Load settings from configuration file. - virtual void readConfigFile(const std::string& conf_path) = 0; + virtual void readConfigFiles() = 0; //! Choose network parameters. virtual void selectParams(const std::string& network) = 0; @@ -83,8 +82,8 @@ public: //! Return whether shutdown was requested. virtual bool shutdownRequested() = 0; - //! Get help message string. - virtual std::string helpMessage(HelpMessageMode mode) = 0; + //! Setup arguments + virtual void setupServerArgs() = 0; //! Map port. virtual void mapPort(bool use_upnp) = 0; diff --git a/src/keystore.cpp b/src/keystore.cpp index e69d518890..ea93ed69fa 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -195,3 +195,10 @@ CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest) } return CKeyID(); } + +bool HaveKey(const CKeyStore& store, const CKey& key) +{ + CKey key2; + key2.Set(key.begin(), key.end(), !key.IsCompressed()); + return store.HaveKey(key.GetPubKey().GetID()) || store.HaveKey(key2.GetPubKey().GetID()); +} diff --git a/src/keystore.h b/src/keystore.h index c56e4751de..cd5ded9203 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -80,4 +80,7 @@ typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > Crypt /** Return the CKeyID of the key involved in a script (if there is a unique one). */ CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest); +/** Checks if a CKey is in the given CKeyStore compressed or otherwise*/ +bool HaveKey(const CKeyStore& store, const CKey& key); + #endif // BITCOIN_KEYSTORE_H diff --git a/src/miner.cpp b/src/miner.cpp index 0660df928c..d4527a1d67 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -14,7 +14,6 @@ #include <consensus/merkle.h> #include <consensus/validation.h> #include <hash.h> -#include <validation.h> #include <net.h> #include <policy/feerate.h> #include <policy/policy.h> diff --git a/src/miner.h b/src/miner.h index 33a22ba75f..ed1b4434f9 100644 --- a/src/miner.h +++ b/src/miner.h @@ -8,6 +8,7 @@ #include <primitives/block.h> #include <txmempool.h> +#include <validation.h> #include <stdint.h> #include <memory> @@ -169,7 +170,7 @@ private: /** Add transactions based on feerate including unconfirmed ancestors * Increments nPackagesSelected / nDescendantsUpdated with corresponding * statistics from the package selection (for logging statistics). */ - void addPackageTxs(int &nPackagesSelected, int &nDescendantsUpdated); + void addPackageTxs(int &nPackagesSelected, int &nDescendantsUpdated) EXCLUSIVE_LOCKS_REQUIRED(mempool.cs); // helper functions for addPackageTxs() /** Remove confirmed (inBlock) entries from given set */ @@ -183,13 +184,13 @@ private: bool TestPackageTransactions(const CTxMemPool::setEntries& package); /** Return true if given transaction from mapTx has already been evaluated, * or if the transaction's cached data in mapTx is incorrect. */ - bool SkipMapTxEntry(CTxMemPool::txiter it, indexed_modified_transaction_set &mapModifiedTx, CTxMemPool::setEntries &failedTx); + bool SkipMapTxEntry(CTxMemPool::txiter it, indexed_modified_transaction_set &mapModifiedTx, CTxMemPool::setEntries &failedTx) EXCLUSIVE_LOCKS_REQUIRED(mempool.cs); /** Sort the package in an order that is valid to appear in a block */ void SortForBlock(const CTxMemPool::setEntries& package, std::vector<CTxMemPool::txiter>& sortedEntries); /** Add descendants of given transactions to mapModifiedTx with ancestor * state updated assuming given transactions are inBlock. Returns number * of updated descendants. */ - int UpdatePackagesForAdded(const CTxMemPool::setEntries& alreadyAdded, indexed_modified_transaction_set &mapModifiedTx); + int UpdatePackagesForAdded(const CTxMemPool::setEntries& alreadyAdded, indexed_modified_transaction_set &mapModifiedTx) EXCLUSIVE_LOCKS_REQUIRED(mempool.cs); }; /** Modify the extranonce in a block */ diff --git a/src/policy/policy.h b/src/policy/policy.h index e4eda4b635..5ce019df4c 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -22,6 +22,8 @@ static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = MAX_BLOCK_WEIGHT - 4000; static const unsigned int DEFAULT_BLOCK_MIN_TX_FEE = 1000; /** The maximum weight for transactions we're willing to relay/mine */ static const unsigned int MAX_STANDARD_TX_WEIGHT = 400000; +/** The minimum non-witness size for transactions we're willing to relay/mine (1 segwit input + 1 P2WPKH output = 82 bytes) */ +static const unsigned int MIN_STANDARD_TX_NONWITNESS_SIZE = 82; /** Maximum number of signature check operations in an IsStandard() P2SH script */ static const unsigned int MAX_P2SH_SIGOPS = 15; /** The maximum number of sigops we're willing to relay/mine in a single tx */ @@ -63,7 +65,8 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VE SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM | - SCRIPT_VERIFY_WITNESS_PUBKEYTYPE; + SCRIPT_VERIFY_WITNESS_PUBKEYTYPE | + SCRIPT_VERIFY_CONST_SCRIPTCODE; /** For convenience, standard but not mandatory verify flags. */ static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 57fe4552a1..9178317601 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -531,6 +531,20 @@ WId BitcoinApplication::getMainWinId() const return window->winId(); } +static void SetupUIArgs() +{ +#ifdef ENABLE_WALLET + gArgs.AddArg("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS), true, OptionsCategory::GUI); +#endif + gArgs.AddArg("-choosedatadir", strprintf(QObject::tr("Choose data directory on startup (default: %u)").toStdString(), DEFAULT_CHOOSE_DATADIR), false, OptionsCategory::GUI); + gArgs.AddArg("-lang=<lang>", QObject::tr("Set language, for example \"de_DE\" (default: system locale)").toStdString(), false, OptionsCategory::GUI); + gArgs.AddArg("-min", QObject::tr("Start minimized").toStdString(), false, OptionsCategory::GUI); + gArgs.AddArg("-resetguisettings", QObject::tr("Reset all settings changed in the GUI").toStdString(), false, OptionsCategory::GUI); + gArgs.AddArg("-rootcertificates=<file>", QObject::tr("Set SSL root certificates for payment request (default: -system-)").toStdString(), false, OptionsCategory::GUI); + gArgs.AddArg("-splash", strprintf(QObject::tr("Show splash screen on startup (default: %u)").toStdString(), DEFAULT_SPLASHSCREEN), false, OptionsCategory::GUI); + gArgs.AddArg("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM), true, OptionsCategory::GUI); +} + #ifndef BITCOIN_QT_TEST int main(int argc, char *argv[]) { @@ -540,6 +554,8 @@ int main(int argc, char *argv[]) /// 1. Parse command-line options. These take precedence over anything else. // Command-line options take precedence: + node->setupServerArgs(); + SetupUIArgs(); node->parseParameters(argc, argv); // Do not refer to data directory yet, this can be overridden by Intro::pickDataDirectory @@ -616,7 +632,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } try { - node->readConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); + node->readConfigFiles(); } catch (const std::exception& e) { QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), QObject::tr("Error: Cannot parse configuration file: %1. Only use key=value syntax.").arg(e.what())); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 7924840d0b..4032729a84 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -70,6 +70,7 @@ namespace { const QStringList historyFilter = QStringList() << "importprivkey" << "importmulti" + << "sethdseed" << "signmessagewithprivkey" << "signrawtransaction" << "signrawtransactionwithkey" diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index d5b98486ae..993d7454d6 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -78,21 +78,7 @@ HelpMessageDialog::HelpMessageDialog(interfaces::Node& node, QWidget *parent, bo cursor.insertText(header); cursor.insertBlock(); - std::string strUsage = node.helpMessage(HelpMessageMode::BITCOIN_QT); - const bool showDebug = gArgs.GetBoolArg("-help-debug", false); - strUsage += HelpMessageGroup(tr("UI Options:").toStdString()); - if (showDebug) { - strUsage += HelpMessageOpt("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS)); - } - strUsage += HelpMessageOpt("-choosedatadir", strprintf(tr("Choose data directory on startup (default: %u)").toStdString(), DEFAULT_CHOOSE_DATADIR)); - strUsage += HelpMessageOpt("-lang=<lang>", tr("Set language, for example \"de_DE\" (default: system locale)").toStdString()); - strUsage += HelpMessageOpt("-min", tr("Start minimized").toStdString()); - strUsage += HelpMessageOpt("-resetguisettings", tr("Reset all settings changed in the GUI").toStdString()); - strUsage += HelpMessageOpt("-rootcertificates=<file>", tr("Set SSL root certificates for payment request (default: -system-)").toStdString()); - strUsage += HelpMessageOpt("-splash", strprintf(tr("Show splash screen on startup (default: %u)").toStdString(), DEFAULT_SPLASHSCREEN)); - if (showDebug) { - strUsage += HelpMessageOpt("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM)); - } + std::string strUsage = gArgs.GetHelpMessage(); QString coreOptions = QString::fromStdString(strUsage); text = version + "\n" + header + "\n" + coreOptions; diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 34c41b3b6b..475fe1e274 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -38,6 +38,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "sendtoaddress", 5 , "replaceable" }, { "sendtoaddress", 6 , "conf_target" }, { "settxfee", 0, "amount" }, + { "sethdseed", 0, "newkeypool" }, { "getreceivedbyaddress", 1, "minconf" }, { "getreceivedbyaccount", 1, "minconf" }, { "getreceivedbylabel", 1, "minconf" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 45ec501b9d..203fac39e2 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -525,6 +525,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) // Need to update only after we know CreateNewBlock succeeded pindexPrev = pindexPrevNew; } + assert(pindexPrev); CBlock* pblock = &pblocktemplate->block; // pointer for convenience const Consensus::Params& consensusParams = Params().GetConsensus(); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 7edd51d3d7..10040b1255 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -23,10 +23,10 @@ #include <memory> // for unique_ptr #include <unordered_map> -static bool fRPCRunning = false; -static bool fRPCInWarmup = true; -static std::string rpcWarmupStatus("RPC server started"); static CCriticalSection cs_rpcWarmup; +static bool fRPCRunning = false; +static bool fRPCInWarmup GUARDED_BY(cs_rpcWarmup) = true; +static std::string rpcWarmupStatus GUARDED_BY(cs_rpcWarmup) = "RPC server started"; /* Timer-creating functions */ static RPCTimerInterface* timerInterface = nullptr; /* Map of name to timer. */ diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index e0d193fa38..13f41a7cbf 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -336,6 +336,10 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& opcode == OP_RSHIFT) return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes. + // With SCRIPT_VERIFY_CONST_SCRIPTCODE, OP_CODESEPARATOR in non-segwit script is rejected even in an unexecuted branch + if (opcode == OP_CODESEPARATOR && sigversion == SigVersion::BASE && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE)) + return set_error(serror, SCRIPT_ERR_OP_CODESEPARATOR); + if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) { if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) { return set_error(serror, SCRIPT_ERR_MINIMALDATA); @@ -899,6 +903,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& case OP_CODESEPARATOR: { + // If SCRIPT_VERIFY_CONST_SCRIPTCODE flag is set, use of OP_CODESEPARATOR is rejected in pre-segwit + // script, even in an unexecuted branch (this is checked above the opcode case statement). + // Hash starts after the code separator pbegincodehash = pc; } @@ -919,7 +926,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& // Drop the signature in pre-segwit scripts but not segwit scripts if (sigversion == SigVersion::BASE) { - FindAndDelete(scriptCode, CScript(vchSig)); + int found = FindAndDelete(scriptCode, CScript(vchSig)); + if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE)) + return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE); } if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) { @@ -983,7 +992,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& { valtype& vchSig = stacktop(-isig-k); if (sigversion == SigVersion::BASE) { - FindAndDelete(scriptCode, CScript(vchSig)); + int found = FindAndDelete(scriptCode, CScript(vchSig)); + if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE)) + return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE); } } diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 50c747900a..2800473a68 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -111,6 +111,10 @@ enum // Public keys in segregated witness scripts must be compressed // SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15), + + // Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts + // + SCRIPT_VERIFY_CONST_SCRIPTCODE = (1U << 16), }; bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror); diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index dbceb1f740..ceda740580 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -89,6 +89,10 @@ const char* ScriptErrorString(const ScriptError serror) return "Witness provided for non-witness script"; case SCRIPT_ERR_WITNESS_PUBKEYTYPE: return "Using non-compressed keys in segwit"; + case SCRIPT_ERR_OP_CODESEPARATOR: + return "Using OP_CODESEPARATOR in non-witness script"; + case SCRIPT_ERR_SIG_FINDANDDELETE: + return "Signature is found in scriptCode"; case SCRIPT_ERR_UNKNOWN_ERROR: case SCRIPT_ERR_ERROR_COUNT: default: break; diff --git a/src/script/script_error.h b/src/script/script_error.h index 3200e94707..6982a087f4 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -64,6 +64,10 @@ typedef enum ScriptError_t SCRIPT_ERR_WITNESS_UNEXPECTED, SCRIPT_ERR_WITNESS_PUBKEYTYPE, + /* Constant scriptCode */ + SCRIPT_ERR_OP_CODESEPARATOR, + SCRIPT_ERR_SIG_FINDANDDELETE, + SCRIPT_ERR_ERROR_COUNT } ScriptError; diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index abb46fe533..918df6d8d9 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -340,5 +340,53 @@ [[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7500, "0x00 0x20 0x9b66c15b4e0b4eb49fa877982cafded24859fe5b0e2dbfbe4f0df1de7743fd52", 200000]], "010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601010221023cb6055f4b57a1580c5a753e19610cafaedf7e0ff377731c77837fd666eae1712102c1b1db303ac232ffa8e5e7cc2cf5f96c6e40d3e6914061204c0541cb2043a0969552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS"], +["SCRIPT_VERIFY_CONST_SCRIPTCODE tests"], +["All transactions are copied from OP_CODESEPARATOR tests in tx_valid.json"], + +[[["bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224", 0, "CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]], + "01000000012432b60dc72cebc1a27ce0969c0989c895bdd9e62e8234839117f8fc32d17fbc000000004a493046022100a576b52051962c25e642c0fd3d77ee6c92487048e5d90818bcf5b51abaccd7900221008204f8fb121be4ec3b24483b1f92d89b1b0548513a134e345c5442e86e8617a501ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], +[[["83e194f90b6ef21fa2e3a365b63794fb5daa844bdc9b25de30899fcfe7b01047", 0, "CODESEPARATOR CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]], + "01000000014710b0e7cf9f8930de259bdc4b84aa5dfb9437b665a3e3a21ff26e0bf994e183000000004a493046022100a166121a61b4eeb19d8f922b978ff6ab58ead8a5a5552bf9be73dc9c156873ea02210092ad9bc43ee647da4f6652c320800debcf08ec20a094a0aaf085f63ecb37a17201ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + +[[["326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CODESEPARATOR CHECKSIG"]], + "01000000015ebaa001d8e4ec7a88703a3bcf69d98c874bca6299cca0f191512bf2a7826832000000004948304502203bf754d1c6732fbf87c5dcd81258aefd30f2060d7bd8ac4a5696f7927091dad1022100f5bcb726c4cf5ed0ed34cc13dadeedf628ae1045b7cb34421bc60b89f4cecae701ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 1"]], + "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + +["CODESEPARATOR in an unexecuted IF block is still invalid"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], + "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0100ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + +["CODESEPARATOR in an executed IF block is invalid"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], + "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510151ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + + +["Using CHECKSIG with singatures in scriptSigs will trigger FindAndDelete, which is invalid"], +[[["ccf7f4053a02e653c36ac75c891b7496d0dc5ce5214f6c913d9cf8f1329ebee0", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], + "0100000001e0be9e32f1f89c3d916c4f21e55cdcd096741b895cc76ac353e6023a05f4f7cc00000000d86149304602210086e5f736a2c3622ebb62bd9d93d8e5d76508b98be922b97160edc3dcca6d8c47022100b23c312ac232a4473f19d2aeb95ab7bdf2b65518911a0d72d50e38b5dd31dc820121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac4730440220508fa761865c8abd81244a168392876ee1d94e8ed83897066b5e2df2400dad24022043f5ee7538e87e9c6aef7ef55133d3e51da7cc522830a9c4d736977a76ef755c0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + +["OP_CODESEPARATOR in scriptSig is invalid"], +[[["10c9f0effe83e97f80f067de2b11c6a00c3088a4bce42c5ae761519af9306f3c", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], + "01000000013c6f30f99a5161e75a2ce4bca488300ca0c6112bde67f0807fe983feeff0c91001000000e608646561646265656675ab61493046022100ce18d384221a731c993939015e3d1bcebafb16e8c0b5b5d14097ec8177ae6f28022100bcab227af90bab33c3fe0a9abfee03ba976ee25dc6ce542526e9b2e56e14b7f10121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac493046022100c3b93edcc0fd6250eb32f2dd8a0bba1754b0f6c3be8ed4100ed582f3db73eba2022100bf75b5bd2eff4d6bf2bda2e34a40fcc07d4aa3cf862ceaa77b47b81eff829f9a01ab21038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + +["Again, FindAndDelete() in scriptSig"], +[[["6056ebd549003b10cbbd915cea0d82209fe40b8617104be917a26fa92cbe3d6f", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], + "01000000016f3dbe2ca96fa217e94b1017860be49f20820dea5c91bdcb103b0049d5eb566000000000fd1d0147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac47304402203757e937ba807e4a5da8534c17f9d121176056406a6465054bdd260457515c1a02200f02eccf1bec0f3a0d65df37889143c2e88ab7acec61a7b6f5aa264139141a2b0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + +[[["5a6b0021a6042a686b6b94abc36b387bef9109847774e8b1e51eb8cc55c53921", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], + "01000000012139c555ccb81ee5b1e87477840991ef7b386bc3ab946b6b682a04a621006b5a01000000fdb40148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f2204148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390175ac4830450220646b72c35beeec51f4d5bc1cbae01863825750d7f490864af354e6ea4f625e9c022100f04b98432df3a9641719dbced53393022e7249fb59db993af1118539830aab870148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a580039017521038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + +["FindAndDelete() in redeemScript"], +[[["b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"], + ["ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742", 0, "HASH160 0x14 0xd8dacdadb7462ae15cd906f1878706d0da8660e6 EQUAL"]], + "0100000002f9cbafc519425637ba4227f8d0a0b7160b4e65168193d5af39747891de98b5b5000000006b4830450221008dd619c563e527c47d9bd53534a770b102e40faa87f61433580e04e271ef2f960220029886434e18122b53d5decd25f1f4acb2480659fea20aabd856987ba3c3907e0121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffff42e7988254800876b69f24676b3e0205b77be476512ca4d970707dd5c60598ab00000000fd260100483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a53034930460221008431bdfa72bc67f9d41fe72e94c88fb8f359ffa30b33c72c121c5a877d922e1002210089ef5fc22dd8bfc6bf9ffdb01a9862d27687d424d1fefbab9e9c7176844a187a014c9052483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c7153aeffffffff01a08601000000000017a914d8dacdadb7462ae15cd906f1878706d0da8660e68700000000", "P2SH,CONST_SCRIPTCODE"], + +["FindAndDelete() in bare CHECKMULTISIG"], +[[["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"], + ["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 1, "2 0x48 0x3045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 3 CHECKMULTISIG"]], + "0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000", "P2SH,CONST_SCRIPTCODE"], + ["Make diffs cleaner by leaving a comment here without comma at the end"] ] diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index 0bcecc58fe..4a1c77166d 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -477,17 +477,17 @@ ["BIP143 example: P2WSH with OP_CODESEPARATOR and out-of-range SIGHASH_SINGLE."], [[["6eb316926b1c5d567cd6f5e6a84fec606fc53d7b474526d1fff3948020c93dfe", 0, "0x21 0x036d5c20fa14fb2f635474c1dc4ef5909d4568e5569b79fc94d3448486e14685f8 CHECKSIG", 156250000], ["f825690aee1b3dc247da796cacb12687a5e802429fd291cfd63e010f02cf1508", 0, "0x00 0x20 0x5d1b56b63d714eebe542309525f484b7e9d6f686b3781b6f61ef925d66d6f6a0", 4900000000]], -"01000000000102fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e000000004847304402200af4e47c9b9629dbecc21f73af989bdaa911f7e6f6c2e9394588a3aa68f81e9902204f3fcf6ade7e5abb1295b6774c8e0abd94ae62217367096bc02ee5e435b67da201ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac000347304402200de66acf4527789bfda55fc5459e214fa6083f936b430a762c629656216805ac0220396f550692cd347171cbc1ef1f51e15282e837bb2b30860dc77c8f78bc8501e503473044022027dc95ad6b740fe5129e7e62a75dd00f291a2aeb1200b84b09d9e3789406b6c002201a9ecd315dd6a0e632ab20bbb98948bc0c6fb204f2c286963bb48517a7058e27034721026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac00000000", "P2SH,WITNESS"], +"01000000000102fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e000000004847304402200af4e47c9b9629dbecc21f73af989bdaa911f7e6f6c2e9394588a3aa68f81e9902204f3fcf6ade7e5abb1295b6774c8e0abd94ae62217367096bc02ee5e435b67da201ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac000347304402200de66acf4527789bfda55fc5459e214fa6083f936b430a762c629656216805ac0220396f550692cd347171cbc1ef1f51e15282e837bb2b30860dc77c8f78bc8501e503473044022027dc95ad6b740fe5129e7e62a75dd00f291a2aeb1200b84b09d9e3789406b6c002201a9ecd315dd6a0e632ab20bbb98948bc0c6fb204f2c286963bb48517a7058e27034721026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac00000000", "P2SH,WITNESS,CONST_SCRIPTCODE"], ["BIP143 example: P2WSH with unexecuted OP_CODESEPARATOR and SINGLE|ANYONECANPAY"], [[["01c0cf7fba650638e55eb91261b183251fbb466f90dff17f10086817c542b5e9", 0, "0x00 0x20 0xba468eea561b26301e4cf69fa34bde4ad60c81e70f059f045ca9a79931004a4d", 16777215], ["1b2a9a426ba603ba357ce7773cb5805cb9c7c2b386d100d1fc9263513188e680", 0, "0x00 0x20 0xd9bbfbe56af7c4b7f960a70d7ea107156913d9e5a26b0a71429df5e097ca6537", 16777215]], -"01000000000102e9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff80e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffff0280969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac80969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "P2SH,WITNESS"], +"01000000000102e9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff80e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffff0280969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac80969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "P2SH,WITNESS,CONST_SCRIPTCODE"], ["BIP143 example: Same as the previous example with input-output pairs swapped"], [[["1b2a9a426ba603ba357ce7773cb5805cb9c7c2b386d100d1fc9263513188e680", 0, "0x00 0x20 0xd9bbfbe56af7c4b7f960a70d7ea107156913d9e5a26b0a71429df5e097ca6537", 16777215], ["01c0cf7fba650638e55eb91261b183251fbb466f90dff17f10086817c542b5e9", 0, "0x00 0x20 0xba468eea561b26301e4cf69fa34bde4ad60c81e70f059f045ca9a79931004a4d", 16777215]], -"0100000000010280e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffffe9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff0280969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac80969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "P2SH,WITNESS"], +"0100000000010280e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffffe9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff0280969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac80969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "P2SH,WITNESS,CONST_SCRIPTCODE"], ["BIP143 example: P2SH-P2WSH 6-of-6 multisig signed with 6 different SIGHASH types"], [[["6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", 1, "HASH160 0x14 0x9993a429037b5d912407a71c252019287b8d27a5 EQUAL", 987654321]], @@ -504,7 +504,7 @@ "010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0121037a3fb04bcdb09eba90f69961ba1692a3528e45e67c85b200df820212d7594d334aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000", "P2SH,WITNESS"], ["BIP143: correct sighash (without FindAndDelete) = 71c9cd9b2869b9c70b01b1f0360c148f42dee72297db312638df136f43311f23"], [[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7500, "0x00 0x20 0x9e1be07558ea5cc8e02ed1d80c0911048afad949affa36d5c3951e3159dbea19", 200000]], -"0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9781d66b61fb5a7ef00ac5ad5bc6ffc78be7b44a566e3c87870e1079368df4c4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000", "P2SH,WITNESS"], +"0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9781d66b61fb5a7ef00ac5ad5bc6ffc78be7b44a566e3c87870e1079368df4c4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000", "P2SH,WITNESS,CONST_SCRIPTCODE"], ["This is multisig version of the FindAndDelete tests"], ["Script is 2 CHECKMULTISIGVERIFY <sig1> <sig2> DROP"], ["52af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175"], @@ -514,7 +514,7 @@ "01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601522102cd74a2809ffeeed0092bc124fd79836706e41f048db3f6ae9df8708cefb83a1c2102e615999372426e46fd107b76eaf007156a507584aa2cc21de9eee3bdbd26d36c4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000", "P2SH,WITNESS"], ["BIP143: correct sighash (without FindAndDelete) = c1628a1e7c67f14ca0c27c06e4fdeec2e6d1a73c7a91d7c046ff83e835aebb72"], [[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7500, "0x00 0x20 0x9b66c15b4e0b4eb49fa877982cafded24859fe5b0e2dbfbe4f0df1de7743fd52", 200000]], -"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960101022102966f109c54e85d3aee8321301136cedeb9fc710fdef58a9de8a73942f8e567c021034ffc99dd9a79dd3cb31e2ab3e0b09e0e67db41ac068c625cd1f491576016c84e9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS"], +"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960101022102966f109c54e85d3aee8321301136cedeb9fc710fdef58a9de8a73942f8e567c021034ffc99dd9a79dd3cb31e2ab3e0b09e0e67db41ac068c625cd1f491576016c84e9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS,CONST_SCRIPTCODE"], ["Test long outputs, which are streamed using length-prefixed bitcoin strings. This might be surprising."], [[["1111111111111111111111111111111111111111111111111111111111111111", 0, "0x00 0x14 0x751e76e8199196d454941c45d1b3a323f1433bd6", 5000000]], diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index c4b18151a7..5ca243f42e 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -106,7 +106,7 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest) } template<typename name> -static void CheckSort(CTxMemPool &pool, std::vector<std::string> &sortedOrder) +static void CheckSort(CTxMemPool &pool, std::vector<std::string> &sortedOrder) EXCLUSIVE_LOCKS_REQUIRED(pool.cs) { BOOST_CHECK_EQUAL(pool.size(), sortedOrder.size()); typename CTxMemPool::indexed_transaction_set::index<name>::type::iterator it = pool.mapTx.get<name>().begin(); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index f561660fef..33b31565a6 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -97,6 +97,8 @@ static ScriptErrorDesc script_errors[]={ {SCRIPT_ERR_WITNESS_MALLEATED_P2SH, "WITNESS_MALLEATED_P2SH"}, {SCRIPT_ERR_WITNESS_UNEXPECTED, "WITNESS_UNEXPECTED"}, {SCRIPT_ERR_WITNESS_PUBKEYTYPE, "WITNESS_PUBKEYTYPE"}, + {SCRIPT_ERR_OP_CODESEPARATOR, "OP_CODESEPARATOR"}, + {SCRIPT_ERR_SIG_FINDANDDELETE, "SIG_FINDANDDELETE"}, }; static const char *FormatScriptError(ScriptError_t err) diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index c753e0a11d..cc72e96eb1 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -53,6 +53,7 @@ static std::map<std::string, unsigned int> mapFlagNames = { {std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS}, {std::string("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM}, {std::string("WITNESS_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_WITNESS_PUBKEYTYPE}, + {std::string("CONST_SCRIPTCODE"), (unsigned int)SCRIPT_VERIFY_CONST_SCRIPTCODE}, }; unsigned int ParseScriptFlags(std::string strFlags) diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 1c3acfb1a5..2af6d9e0f3 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -1065,6 +1065,7 @@ static void TestOtherProcess(fs::path dirname, std::string lockname, int fd) ReleaseDirectoryLocks(); ch = true; // Always succeeds rv = write(fd, &ch, 1); + assert(rv == 1); break; case ExitCommand: close(fd); diff --git a/src/timedata.cpp b/src/timedata.cpp index 27d08172f5..dfb8fe55af 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -17,7 +17,7 @@ static CCriticalSection cs_nTimeOffset; -static int64_t nTimeOffset = 0; +static int64_t nTimeOffset GUARDED_BY(cs_nTimeOffset) = 0; /** * "Never go to sea with two chronometers; take one or three." diff --git a/src/txmempool.cpp b/src/txmempool.cpp index d03429ca81..bb585fc075 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -618,6 +618,7 @@ static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& m void CTxMemPool::check(const CCoinsViewCache *pcoins) const { + LOCK(cs); if (nCheckFrequency == 0) return; @@ -632,7 +633,6 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins)); const int64_t spendheight = GetSpendHeight(mempoolDuplicate); - LOCK(cs); std::list<const CTxMemPoolEntry*> waitingOnDependants; for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { unsigned int i = 0; diff --git a/src/txmempool.h b/src/txmempool.h index 3f9fb4850c..ca7b1cd4be 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -440,7 +440,7 @@ public: class CTxMemPool { private: - uint32_t nCheckFrequency; //!< Value n means that n times in 2^32 we check. + uint32_t nCheckFrequency GUARDED_BY(cs); //!< Value n means that n times in 2^32 we check. unsigned int nTransactionsUpdated; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation CBlockPolicyEstimator* minerPolicyEstimator; @@ -484,7 +484,7 @@ public: > indexed_transaction_set; mutable CCriticalSection cs; - indexed_transaction_set mapTx; + indexed_transaction_set mapTx GUARDED_BY(cs); typedef indexed_transaction_set::nth_index<0>::type::iterator txiter; std::vector<std::pair<uint256, txiter> > vTxHashes; //!< All tx witness hashes/entries in mapTx, in random order @@ -496,8 +496,8 @@ public: }; typedef std::set<txiter, CompareIteratorByHash> setEntries; - const setEntries & GetMemPoolParents(txiter entry) const; - const setEntries & GetMemPoolChildren(txiter entry) const; + const setEntries & GetMemPoolParents(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs); + const setEntries & GetMemPoolChildren(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs); private: typedef std::map<txiter, setEntries, CompareIteratorByHash> cacheMap; @@ -515,7 +515,7 @@ private: std::vector<indexed_transaction_set::const_iterator> GetSortedDepthAndScore() const EXCLUSIVE_LOCKS_REQUIRED(cs); public: - indirectmap<COutPoint, const CTransaction*> mapNextTx; + indirectmap<COutPoint, const CTransaction*> mapNextTx GUARDED_BY(cs); std::map<uint256, CAmount> mapDeltas; /** Create a new CTxMemPool. @@ -529,7 +529,7 @@ public: * check does nothing. */ void check(const CCoinsViewCache *pcoins) const; - void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = static_cast<uint32_t>(dFrequency * 4294967295.0); } + void setSanityCheck(double dFrequency = 1.0) { LOCK(cs); 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 @@ -547,7 +547,7 @@ public: void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight); void clear(); - void _clear(); //lock free + void _clear() EXCLUSIVE_LOCKS_REQUIRED(cs); //lock free bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb); void queryHashes(std::vector<uint256>& vtxid); bool isSpent(const COutPoint& outpoint) const; @@ -600,7 +600,7 @@ public: /** Populate setDescendants with all in-mempool descendants of hash. * Assumes that setDescendants includes all in-mempool descendants of anything * already in it. */ - void CalculateDescendants(txiter it, setEntries& setDescendants) const; + void CalculateDescendants(txiter it, setEntries& setDescendants) const EXCLUSIVE_LOCKS_REQUIRED(cs); /** The minimum fee to get into the mempool, which may itself not be enough * for larger-sized transactions. @@ -665,17 +665,17 @@ private: */ void UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendants, - const std::set<uint256> &setExclude); + const std::set<uint256> &setExclude) EXCLUSIVE_LOCKS_REQUIRED(cs); /** Update ancestors of hash to add/remove it as a descendant transaction. */ - void UpdateAncestorsOf(bool add, txiter hash, setEntries &setAncestors); + void UpdateAncestorsOf(bool add, txiter hash, setEntries &setAncestors) EXCLUSIVE_LOCKS_REQUIRED(cs); /** Set ancestor state for an entry */ - void UpdateEntryForAncestors(txiter it, const setEntries &setAncestors); + void UpdateEntryForAncestors(txiter it, const setEntries &setAncestors) EXCLUSIVE_LOCKS_REQUIRED(cs); /** For each transaction being removed, update ancestors and any direct children. * If updateDescendants is true, then also update in-mempool descendants' * ancestor state. */ - void UpdateForRemoveFromMempool(const setEntries &entriesToRemove, bool updateDescendants); + void UpdateForRemoveFromMempool(const setEntries &entriesToRemove, bool updateDescendants) EXCLUSIVE_LOCKS_REQUIRED(cs); /** Sever link between specified transaction and direct children. */ - void UpdateChildrenForRemoval(txiter entry); + void UpdateChildrenForRemoval(txiter entry) EXCLUSIVE_LOCKS_REQUIRED(cs); /** Before calling removeUnchecked for a given transaction, * UpdateForRemoveFromMempool must be called on the entire (dependent) set @@ -685,7 +685,7 @@ private: * transactions in a chain before we've updated all the state for the * removal. */ - void removeUnchecked(txiter entry, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN); + void removeUnchecked(txiter entry, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN) EXCLUSIVE_LOCKS_REQUIRED(cs); }; /** diff --git a/src/util.cpp b/src/util.cpp index b4d0a61ab2..6a2b2c1ed4 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -445,6 +445,17 @@ void ArgsManager::ParseParameters(int argc, const char* const argv[]) m_override_args[key].push_back(val); } } + + // we do not allow -includeconf from command line, so we clear it here + auto it = m_override_args.find("-includeconf"); + if (it != m_override_args.end()) { + if (it->second.size() > 0) { + for (const auto& ic : it->second) { + fprintf(stderr, "warning: -includeconf cannot be used from commandline; ignoring -includeconf=%s\n", ic.c_str()); + } + m_override_args.erase(it); + } + } } std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const @@ -536,6 +547,55 @@ void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strV m_override_args[strArg] = {strValue}; } +void ArgsManager::AddArg(const std::string& name, const std::string& help, const bool debug_only, const OptionsCategory& cat) +{ + std::pair<OptionsCategory, std::string> key(cat, name); + assert(m_available_args.count(key) == 0); + m_available_args.emplace(key, std::pair<std::string, bool>(help, debug_only)); +} + +std::string ArgsManager::GetHelpMessage() +{ + const bool show_debug = gArgs.GetBoolArg("-help-debug", false); + + std::string usage = HelpMessageGroup(_("Options:")); + + OptionsCategory last_cat = OptionsCategory::OPTIONS; + for (auto& arg : m_available_args) { + if (arg.first.first != last_cat) { + last_cat = arg.first.first; + if (last_cat == OptionsCategory::CONNECTION) + usage += HelpMessageGroup(_("Connection options:")); + else if (last_cat == OptionsCategory::ZMQ) + usage += HelpMessageGroup(_("ZeroMQ notification options:")); + else if (last_cat == OptionsCategory::DEBUG_TEST) + usage += HelpMessageGroup(_("Debugging/Testing options:")); + else if (last_cat == OptionsCategory::NODE_RELAY) + usage += HelpMessageGroup(_("Node relay options:")); + else if (last_cat == OptionsCategory::BLOCK_CREATION) + usage += HelpMessageGroup(_("Block creation options:")); + else if (last_cat == OptionsCategory::RPC) + usage += HelpMessageGroup(_("RPC server options:")); + else if (last_cat == OptionsCategory::WALLET) + usage += HelpMessageGroup(_("Wallet options:")); + else if (last_cat == OptionsCategory::WALLET_DEBUG_TEST && show_debug) + usage += HelpMessageGroup(_("Wallet debugging/testing options:")); + else if (last_cat == OptionsCategory::CHAINPARAMS) + usage += HelpMessageGroup(_("Chain selection options:")); + else if (last_cat == OptionsCategory::GUI) + usage += HelpMessageGroup(_("UI Options:")); + else if (last_cat == OptionsCategory::COMMANDS) + usage += HelpMessageGroup(_("Commands:")); + else if (last_cat == OptionsCategory::REGISTER_COMMANDS) + usage += HelpMessageGroup(_("Register Commands:")); + } + if (show_debug || !arg.second.second) { + usage += HelpMessageOpt(arg.first.second, arg.second.first); + } + } + return usage; +} + bool HelpRequested(const ArgsManager& args) { return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help"); @@ -706,18 +766,58 @@ void ArgsManager::ReadConfigStream(std::istream& stream) } } -void ArgsManager::ReadConfigFile(const std::string& confPath) +void ArgsManager::ReadConfigFiles() { { LOCK(cs_args); m_config_args.clear(); } + const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME); fs::ifstream stream(GetConfigFile(confPath)); // ok to not have a config file if (stream.good()) { ReadConfigStream(stream); + // if there is an -includeconf in the override args, but it is empty, that means the user + // passed '-noincludeconf' on the command line, in which case we should not include anything + if (m_override_args.count("-includeconf") == 0) { + std::vector<std::string> includeconf(GetArgs("-includeconf")); + { + // We haven't set m_network yet (that happens in SelectParams()), so manually check + // for network.includeconf args. + std::vector<std::string> includeconf_net(GetArgs(std::string("-") + GetChainName() + ".includeconf")); + includeconf.insert(includeconf.end(), includeconf_net.begin(), includeconf_net.end()); + } + + // Remove -includeconf from configuration, so we can warn about recursion + // later + { + LOCK(cs_args); + m_config_args.erase("-includeconf"); + m_config_args.erase(std::string("-") + GetChainName() + ".includeconf"); + } + + for (const std::string& to_include : includeconf) { + fs::ifstream include_config(GetConfigFile(to_include)); + if (include_config.good()) { + ReadConfigStream(include_config); + LogPrintf("Included configuration file %s\n", to_include.c_str()); + } else { + fprintf(stderr, "Failed to include configuration file %s\n", to_include.c_str()); + } + } + + // Warn about recursive -includeconf + includeconf = GetArgs("-includeconf"); + { + std::vector<std::string> includeconf_net(GetArgs(std::string("-") + GetChainName() + ".includeconf")); + includeconf.insert(includeconf.end(), includeconf_net.begin(), includeconf_net.end()); + } + for (const std::string& to_include : includeconf) { + fprintf(stderr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", to_include.c_str()); + } + } } // If datadir is changed in .conf file: diff --git a/src/util.h b/src/util.h index 2da8023285..17dd5c0683 100644 --- a/src/util.h +++ b/src/util.h @@ -118,6 +118,23 @@ inline bool IsSwitchChar(char c) #endif } +enum class OptionsCategory +{ + OPTIONS, + CONNECTION, + WALLET, + WALLET_DEBUG_TEST, + ZMQ, + DEBUG_TEST, + CHAINPARAMS, + NODE_RELAY, + BLOCK_CREATION, + RPC, + GUI, + COMMANDS, + REGISTER_COMMANDS +}; + class ArgsManager { protected: @@ -128,6 +145,7 @@ protected: std::map<std::string, std::vector<std::string>> m_config_args; std::string m_network; std::set<std::string> m_network_only_args; + std::map<std::pair<OptionsCategory, std::string>, std::pair<std::string, bool>> m_available_args; void ReadConfigStream(std::istream& stream); @@ -140,7 +158,7 @@ public: void SelectConfigNetwork(const std::string& network); void ParseParameters(int argc, const char*const argv[]); - void ReadConfigFile(const std::string& confPath); + void ReadConfigFiles(); /** * Log warnings for options in m_section_only_args when @@ -229,6 +247,16 @@ public: * @return CBaseChainParams::MAIN by default; raises runtime error if an invalid combination is given. */ std::string GetChainName() const; + + /** + * Add argument + */ + void AddArg(const std::string& name, const std::string& help, const bool debug_only, const OptionsCategory& cat); + + /** + * Get the help string + */ + std::string GetHelpMessage(); }; extern ArgsManager gArgs; diff --git a/src/validation.cpp b/src/validation.cpp index fc1f6477d5..f22ae7f9be 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -582,6 +582,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool if (fRequireStandard && !IsStandardTx(tx, reason, witnessEnabled)) return state.DoS(0, false, REJECT_NONSTANDARD, reason); + // Do not work on transactions that are too small. + // A transaction with 1 segwit input and 1 P2WPHK output has non-witness size of 82 bytes. + // Transactions smaller than this are not relayed to reduce unnecessary malloc overhead. + if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) < MIN_STANDARD_TX_NONWITNESS_SIZE) + return state.DoS(0, false, REJECT_NONSTANDARD, "tx-size-small"); + // Only accept nLockTime-using transactions that can be mined in the next // block; we don't want our mempool filled up with transactions that can't // be mined yet. @@ -2551,8 +2557,9 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) { if (state.IsInvalid()) { // The block violates a consensus rule. - if (!state.CorruptionPossible()) - InvalidChainFound(vpindexToConnect.back()); + if (!state.CorruptionPossible()) { + InvalidChainFound(vpindexToConnect.front()); + } state = CValidationState(); fInvalidFound = true; fContinue = false; @@ -2834,6 +2841,7 @@ bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) { if (pindex->nStatus & BLOCK_FAILED_MASK) { pindex->nStatus &= ~BLOCK_FAILED_MASK; setDirtyBlockIndex.insert(pindex); + m_failed_blocks.erase(pindex); } pindex = pindex->pprev; } diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 10a06e4b9a..410dd5009f 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -53,7 +53,7 @@ void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filena } CCriticalSection cs_db; -std::map<std::string, BerkeleyEnvironment> g_dbenvs; //!< Map from directory name to open db environment. +std::map<std::string, BerkeleyEnvironment> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to open db environment. } // namespace BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename) @@ -102,7 +102,7 @@ void BerkeleyEnvironment::Close() int ret = dbenv->close(0); if (ret != 0) - LogPrintf("BerkeleyEnvironment::EnvShutdown: Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret)); + LogPrintf("BerkeleyEnvironment::Close: Error %d closing database environment: %s\n", ret, DbEnv::strerror(ret)); if (!fMockDb) DbEnv((u_int32_t)0).remove(strPath.c_str(), 0); } @@ -168,8 +168,12 @@ bool BerkeleyEnvironment::Open(bool retry) nEnvFlags, S_IRUSR | S_IWUSR); if (ret != 0) { - dbenv->close(0); LogPrintf("BerkeleyEnvironment::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret)); + int ret2 = dbenv->close(0); + if (ret2 != 0) { + LogPrintf("BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2)); + } + Reset(); if (retry) { // try moving the database env out of the way fs::path pathDatabaseBak = pathIn / strprintf("database.%d.bak", GetTime()); diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 4d70dde72a..7742d5cec4 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -18,7 +18,7 @@ //! Check whether transaction has descendant in wallet or mempool, or has been //! mined, or conflicts with a mined transaction. Return a feebumper::Result. -static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors) +static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet) { if (wallet->HasWalletSpend(wtx.GetHash())) { errors.push_back("Transaction has descendants in the wallet"); diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 06f9c730c0..6c5522e4bc 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -18,7 +18,7 @@ class WalletInit : public WalletInitInterface { public: //! Return the wallets help message. - std::string GetHelpString(bool showDebug) const override; + void AddWalletOptions() const override; //! Wallets parameter interaction bool ParameterInteraction() const override; @@ -49,46 +49,38 @@ public: const WalletInitInterface& g_wallet_init_interface = WalletInit(); -std::string WalletInit::GetHelpString(bool showDebug) const +void WalletInit::AddWalletOptions() const { - std::string strUsage = HelpMessageGroup(_("Wallet options:")); - strUsage += HelpMessageOpt("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE))); - strUsage += HelpMessageOpt("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)"); - strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); - 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). " + gArgs.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), false, OptionsCategory::WALLET); + gArgs.AddArg("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)", false, OptionsCategory::WALLET); + gArgs.AddArg("-disablewallet", _("Do not load the wallet and disable wallet RPC calls"), false, OptionsCategory::WALLET); + gArgs.AddArg("-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("-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("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE)); - 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(CFeeRate{DEFAULT_PAY_TX_FEE}.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("-upgradewallet", _("Upgrade wallet to latest format on startup")); - strUsage += HelpMessageOpt("-wallet=<path>", _("Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)")); - strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); - strUsage += HelpMessageOpt("-walletdir=<dir>", _("Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)")); - strUsage += HelpMessageOpt("-walletnotify=<cmd>", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); - strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)"), DEFAULT_WALLET_RBF)); - 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; + CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE)), false, OptionsCategory::WALLET); + gArgs.AddArg("-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)), false, OptionsCategory::WALLET); + gArgs.AddArg("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE), false, OptionsCategory::WALLET); + gArgs.AddArg("-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)), false, OptionsCategory::WALLET); + gArgs.AddArg("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), + CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())), false, OptionsCategory::WALLET); + gArgs.AddArg("-rescan", _("Rescan the block chain for missing wallet transactions on startup"), false, OptionsCategory::WALLET); + gArgs.AddArg("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup"), false, OptionsCategory::WALLET); + gArgs.AddArg("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE), false, OptionsCategory::WALLET); + gArgs.AddArg("-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), false, OptionsCategory::WALLET); + gArgs.AddArg("-upgradewallet", _("Upgrade wallet to latest format on startup"), false, OptionsCategory::WALLET); + gArgs.AddArg("-wallet=<path>", _("Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)"), false, OptionsCategory::WALLET); + gArgs.AddArg("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST), false, OptionsCategory::WALLET); + gArgs.AddArg("-walletdir=<dir>", _("Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)"), false, OptionsCategory::WALLET); + gArgs.AddArg("-walletnotify=<cmd>", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)"), false, OptionsCategory::WALLET); + gArgs.AddArg("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)"), DEFAULT_WALLET_RBF), false, OptionsCategory::WALLET); + gArgs.AddArg("-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)"), false, OptionsCategory::WALLET); + + gArgs.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), true, OptionsCategory::WALLET_DEBUG_TEST); + gArgs.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), true, OptionsCategory::WALLET_DEBUG_TEST); + gArgs.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB), true, OptionsCategory::WALLET_DEBUG_TEST); + gArgs.AddArg("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS), true, OptionsCategory::WALLET_DEBUG_TEST); } bool WalletInit::ParameterInteraction() const diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index e957c1b1ca..4fe630b660 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -209,7 +209,7 @@ UniValue abortrescan(const JSONRPCRequest& request) } static void ImportAddress(CWallet*, const CTxDestination& dest, const std::string& strLabel); -static void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript) +static void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { if (!isRedeemScript && ::IsMine(*pwallet, script) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); @@ -235,7 +235,7 @@ static void ImportScript(CWallet* const pwallet, const CScript& script, const st } } -static void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, const std::string& strLabel) +static void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, const std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { CScript script = GetScriptForDestination(dest); ImportScript(pwallet, script, strLabel, false); @@ -811,7 +811,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) } -static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) +static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { try { bool success = false; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 94e61aa20e..a7b2b05d9b 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -4079,6 +4079,76 @@ static UniValue listlabels(const JSONRPCRequest& request) return ret; } +UniValue sethdseed(const JSONRPCRequest& request) +{ + CWallet* const pwallet = GetWalletForJSONRPCRequest(request); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + return NullUniValue; + } + + if (request.fHelp || request.params.size() > 2) { + throw std::runtime_error( + "sethdseed ( \"newkeypool\" \"seed\" )\n" + "\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n" + "HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n" + "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed.\n" + + HelpRequiringPassphrase(pwallet) + + "\nArguments:\n" + "1. \"newkeypool\" (boolean, optional, default=true) Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n" + " If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n" + " If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n" + " keypool will be used until it has been depleted.\n" + "2. \"seed\" (string, optional) The WIF private key to use as the new HD seed; if not provided a random seed will be used.\n" + " The seed value can be retrieved using the dumpwallet command. It is the private key marked hdmaster=1\n" + "\nExamples:\n" + + HelpExampleCli("sethdseed", "") + + HelpExampleCli("sethdseed", "false") + + HelpExampleCli("sethdseed", "true \"wifkey\"") + + HelpExampleRpc("sethdseed", "true, \"wifkey\"") + ); + } + + if (IsInitialBlockDownload()) { + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download"); + } + + LOCK2(cs_main, pwallet->cs_wallet); + + // Do not do anything to non-HD wallets + if (!pwallet->IsHDEnabled()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed on a non-HD wallet. Start with -upgradewallet in order to upgrade a non-HD wallet to HD"); + } + + EnsureWalletIsUnlocked(pwallet); + + bool flush_key_pool = true; + if (!request.params[0].isNull()) { + flush_key_pool = request.params[0].get_bool(); + } + + CPubKey master_pub_key; + if (request.params[1].isNull()) { + master_pub_key = pwallet->GenerateNewHDMasterKey(); + } else { + CKey key = DecodeSecret(request.params[1].get_str()); + if (!key.IsValid()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); + } + + if (HaveKey(*pwallet, key)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or as a loose private key)"); + } + + master_pub_key = pwallet->DeriveNewMasterHDKey(key); + } + + pwallet->SetHDMasterKey(master_pub_key); + if (flush_key_pool) pwallet->NewKeyPool(); + + return NullUniValue; +} + extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue importprivkey(const JSONRPCRequest& request); @@ -4139,6 +4209,7 @@ static const CRPCCommand commands[] = { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} }, { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} }, { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} }, + { "wallet", "sethdseed", &sethdseed, {"newkeypool","seed"} }, /** Account functions (deprecated) */ { "wallet", "getaccountaddress", &getlabeladdress, {"account"} }, diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp index ac47d4448a..e90370cf06 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -536,19 +536,9 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test) std::exponential_distribution<double> distribution (100); FastRandomContext rand; - // Output stuff - CAmount out_value = 0; - CoinSet out_set; - CAmount target = 0; - bool bnb_used; - // Run this test 100 times for (int i = 0; i < 100; ++i) { - // Reset - out_value = 0; - target = 0; - out_set.clear(); empty_wallet(); // Make a wallet with 1000 exponentially distributed random inputs @@ -561,11 +551,14 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test) CFeeRate rate(rand.randrange(300) + 100); // Generate a random target value between 1000 and wallet balance - target = rand.randrange(balance - 1000) + 1000; + CAmount target = rand.randrange(balance - 1000) + 1000; // Perform selection CoinSelectionParams coin_selection_params_knapsack(false, 34, 148, CFeeRate(0), 0); CoinSelectionParams coin_selection_params_bnb(true, 34, 148, CFeeRate(0), 0); + CoinSet out_set; + CAmount out_value = 0; + bool bnb_used = false; BOOST_CHECK(testWallet.SelectCoinsMinConf(target, filter_standard, vCoins, out_set, out_value, coin_selection_params_bnb, bnb_used) || testWallet.SelectCoinsMinConf(target, filter_standard, vCoins, out_set, out_value, coin_selection_params_knapsack, bnb_used)); BOOST_CHECK_GE(out_value, target); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 96242c6b13..5be9f4a290 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1452,7 +1452,11 @@ CPubKey CWallet::GenerateNewHDMasterKey() { CKey key; key.MakeNewKey(true); + return DeriveNewMasterHDKey(key); +} +CPubKey CWallet::DeriveNewMasterHDKey(const CKey& key) +{ int64_t nCreationTime = GetTime(); CKeyMetadata metadata(nCreationTime); @@ -3326,6 +3330,11 @@ bool CWallet::NewKeyPool() } setExternalKeyPool.clear(); + for (int64_t nIndex : set_pre_split_keypool) { + batch.ErasePool(nIndex); + } + set_pre_split_keypool.clear(); + m_pool_key_to_index.clear(); if (!TopUpKeyPool()) { @@ -3339,13 +3348,15 @@ bool CWallet::NewKeyPool() size_t CWallet::KeypoolCountExternalKeys() { AssertLockHeld(cs_wallet); // setExternalKeyPool - return setExternalKeyPool.size(); + return setExternalKeyPool.size() + set_pre_split_keypool.size(); } void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) { AssertLockHeld(cs_wallet); - if (keypool.fInternal) { + if (keypool.m_pre_split) { + set_pre_split_keypool.insert(nIndex); + } else if (keypool.fInternal) { setInternalKeyPool.insert(nIndex); } else { setExternalKeyPool.insert(nIndex); @@ -3410,7 +3421,7 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) m_pool_key_to_index[pubkey.GetID()] = index; } if (missingInternal + missingExternal > 0) { - LogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size()); + LogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(), setInternalKeyPool.size()); } } return true; @@ -3427,7 +3438,7 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe TopUpKeyPool(); bool fReturningInternal = IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT) && fRequestedInternal; - std::set<int64_t>& setKeyPool = fReturningInternal ? setInternalKeyPool : setExternalKeyPool; + std::set<int64_t>& setKeyPool = set_pre_split_keypool.empty() ? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) : set_pre_split_keypool; // Get the oldest key if(setKeyPool.empty()) @@ -3444,7 +3455,8 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe if (!HaveKey(keypool.vchPubKey.GetID())) { throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); } - if (keypool.fInternal != fReturningInternal) { + // If the key was pre-split keypool, we don't care about what type it is + if (set_pre_split_keypool.size() == 0 && keypool.fInternal != fReturningInternal) { throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified"); } @@ -3469,6 +3481,8 @@ void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey) LOCK(cs_wallet); if (fInternal) { setInternalKeyPool.insert(nIndex); + } else if (!set_pre_split_keypool.empty()) { + set_pre_split_keypool.insert(nIndex); } else { setExternalKeyPool.insert(nIndex); } @@ -3521,6 +3535,9 @@ int64_t CWallet::GetOldestKeyPoolTime() int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch); if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) { oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch), oldestKey); + if (!set_pre_split_keypool.empty()) { + oldestKey = std::max(GetOldestKeyTimeInPool(set_pre_split_keypool, batch), oldestKey); + } } return oldestKey; @@ -3718,8 +3735,8 @@ void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) { AssertLockHeld(cs_wallet); bool internal = setInternalKeyPool.count(keypool_id); - if (!internal) assert(setExternalKeyPool.count(keypool_id)); - std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : &setExternalKeyPool; + if (!internal) assert(setExternalKeyPool.count(keypool_id) || set_pre_split_keypool.count(keypool_id)); + std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : (set_pre_split_keypool.empty() ? &setExternalKeyPool : &set_pre_split_keypool); auto it = setKeyPool->begin(); WalletBatch batch(*database); @@ -3955,6 +3972,24 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const return values; } +void CWallet::MarkPreSplitKeys() +{ + WalletBatch batch(*database); + for (auto it = setExternalKeyPool.begin(); it != setExternalKeyPool.end();) { + int64_t index = *it; + CKeyPool keypool; + if (!batch.ReadPool(index, keypool)) { + throw std::runtime_error(std::string(__func__) + ": read keypool entry failed"); + } + keypool.m_pre_split = true; + if (!batch.WritePool(index, keypool)) { + throw std::runtime_error(std::string(__func__) + ": writing modified keypool entry failed"); + } + set_pre_split_keypool.insert(index); + it = setExternalKeyPool.erase(it); + } +} + CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path) { const std::string& walletFile = name; @@ -4006,6 +4041,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& } } + int prev_version = walletInstance->nWalletVersion; if (gArgs.GetBoolArg("-upgradewallet", fFirstRun)) { int nMaxVersion = gArgs.GetArg("-upgradewallet", 0); @@ -4025,6 +4061,49 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& walletInstance->SetMaxVersion(nMaxVersion); } + // Upgrade to HD if explicit upgrade + if (gArgs.GetBoolArg("-upgradewallet", false)) { + LOCK(walletInstance->cs_wallet); + + // Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT + int max_version = walletInstance->nWalletVersion; + if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) && max_version >=FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) { + InitError(_("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.")); + return nullptr; + } + + bool hd_upgrade = false; + bool split_upgrade = false; + if (walletInstance->CanSupportFeature(FEATURE_HD) && !walletInstance->IsHDEnabled()) { + LogPrintf("Upgrading wallet to HD\n"); + walletInstance->SetMinVersion(FEATURE_HD); + + // generate a new master key + CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); + if (!walletInstance->SetHDMasterKey(masterPubKey)) { + throw std::runtime_error(std::string(__func__) + ": Storing master key failed"); + } + hd_upgrade = true; + } + // Upgrade to HD chain split if necessary + if (walletInstance->CanSupportFeature(FEATURE_HD_SPLIT)) { + LogPrintf("Upgrading wallet to use HD chain split\n"); + walletInstance->SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL); + split_upgrade = FEATURE_HD_SPLIT > prev_version; + } + // Mark all keys currently in the keypool as pre-split + if (split_upgrade) { + walletInstance->MarkPreSplitKeys(); + } + // Regenerate the keypool if upgraded to HD + if (hd_upgrade) { + if (!walletInstance->TopUpKeyPool()) { + InitError(_("Unable to generate keys") += "\n"); + return nullptr; + } + } + } + if (fFirstRun) { // ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key @@ -4032,7 +4111,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& InitError(strprintf(_("Error creating %s: You can't create non-HD wallets with this version."), walletFile)); return nullptr; } - walletInstance->SetMinVersion(FEATURE_NO_DEFAULT_KEY); + walletInstance->SetMinVersion(FEATURE_LATEST); // generate a new master key CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); @@ -4246,6 +4325,7 @@ CKeyPool::CKeyPool() { nTime = GetTime(); fInternal = false; + m_pre_split = false; } CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn) @@ -4253,6 +4333,7 @@ CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn) nTime = GetTime(); vchPubKey = vchPubKeyIn; fInternal = internalIn; + m_pre_split = false; } CWalletKey::CWalletKey(int64_t nExpires) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 3208a20f0f..1e23b44eae 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -89,7 +89,9 @@ enum WalletFeature FEATURE_NO_DEFAULT_KEY = 159900, // Wallet without a default key written - FEATURE_LATEST = FEATURE_COMPRPUBKEY // HD is optional, use FEATURE_COMPRPUBKEY as latest version + FEATURE_PRE_SPLIT_KEYPOOL = 169900, // Upgraded to HD SPLIT and can have a pre-split keypool + + FEATURE_LATEST = FEATURE_PRE_SPLIT_KEYPOOL }; enum class OutputType { @@ -119,6 +121,7 @@ public: int64_t nTime; CPubKey vchPubKey; bool fInternal; // for change outputs + bool m_pre_split; // For keys generated before keypool split upgrade CKeyPool(); CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn); @@ -141,9 +144,18 @@ public: (this will be the case for any wallet before the HD chain split version) */ fInternal = false; } + try { + READWRITE(m_pre_split); + } + catch (std::ios_base::failure&) { + /* flag as postsplit address if we can't read the m_pre_split boolean + (this will be the case for any wallet that upgrades to HD chain split)*/ + m_pre_split = false; + } } else { READWRITE(fInternal); + READWRITE(m_pre_split); } } }; @@ -698,16 +710,17 @@ private: /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected. * Should be called with pindexBlock and posInBlock if this is for a transaction that is included in a block. */ - void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = nullptr, int posInBlock = 0); + void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = nullptr, int posInBlock = 0) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /* the HD chain data model (external chain counters) */ CHDChain hdChain; /* HD derive new child key (on internal or external chain) */ - void DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, bool internal = false); + void DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); std::set<int64_t> setInternalKeyPool; std::set<int64_t> setExternalKeyPool; + std::set<int64_t> set_pre_split_keypool; int64_t m_max_keypool_index = 0; std::map<CKeyID, int64_t> m_pool_key_to_index; @@ -722,7 +735,7 @@ private: * of the other AddWatchOnly which accepts a timestamp and sets * nTimeFirstKey more intelligently for more efficient rescans. */ - bool AddWatchOnly(const CScript& dest) override; + bool AddWatchOnly(const CScript& dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** * Wallet filename from wallet=<path> command line or config option. @@ -773,7 +786,8 @@ public: */ const std::string& GetName() const { return m_name; } - void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool); + void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void MarkPreSplitKeys(); // Map from Key ID to key metadata. std::map<CKeyID, CKeyMetadata> mapKeyMetadata; @@ -814,12 +828,12 @@ public: const CWalletTx* GetWalletTx(const uint256& hash) const; //! check whether we are allowed to upgrade (or already support) to the named feature - bool CanSupportFeature(enum WalletFeature wf) const { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } + bool CanSupportFeature(enum WalletFeature wf) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } /** * populate vCoins with vector of available COutputs. */ - void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999) const; + void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** * Return list of available coins and locked coins grouped by non-change output address. @@ -842,11 +856,11 @@ public: bool IsSpent(const uint256& hash, unsigned int n) const; - bool IsLockedCoin(uint256 hash, unsigned int n) const; - void LockCoin(const COutPoint& output); - void UnlockCoin(const COutPoint& output); - void UnlockAllCoins(); - void ListLockedCoins(std::vector<COutPoint>& vOutpts) const; + bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void LockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void UnlockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void UnlockAllCoins() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void ListLockedCoins(std::vector<COutPoint>& vOutpts) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /* * Rescan abort properties @@ -859,18 +873,18 @@ public: * keystore implementation * Generate a new key */ - CPubKey GenerateNewKey(WalletBatch& batch, bool internal = false); + CPubKey GenerateNewKey(WalletBatch& batch, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Adds a key to the store, and saves it to disk. - bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; - bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey); + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Adds a key to the store, without saving it to disk (used by LoadWallet) bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); } //! Load metadata (used by LoadWallet) - bool LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata); - bool LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata); + bool LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - bool LoadMinVersion(int nVersion) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; } - void UpdateTimeFirstKey(int64_t nCreateTime); + bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; } + void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Adds an encrypted key to the store, and saves it to disk. bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) override; @@ -891,8 +905,8 @@ public: std::vector<std::string> GetDestValues(const std::string& prefix) const; //! Adds a watch-only address to the store, and saves it to disk. - bool AddWatchOnly(const CScript& dest, int64_t nCreateTime); - bool RemoveWatchOnly(const CScript &dest) override; + bool AddWatchOnly(const CScript& dest, int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool RemoveWatchOnly(const CScript &dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet) bool LoadWatchOnly(const CScript &dest); @@ -903,16 +917,16 @@ public: bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); bool EncryptWallet(const SecureString& strWalletPassphrase); - void GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const; + void GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); unsigned int ComputeTimeSmart(const CWalletTx& wtx) const; /** * Increment the next transaction order id * @return next transaction order id */ - int64_t IncOrderPosNext(WalletBatch *batch = nullptr); + int64_t IncOrderPosNext(WalletBatch *batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); DBErrors ReorderTransactions(); - bool AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment = ""); + bool AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment = "") EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool GetLabelDestination(CTxDestination &dest, const std::string& label, bool bForceNew = false); void MarkDirty(); @@ -921,7 +935,7 @@ public: void TransactionAddedToMempool(const CTransactionRef& tx) override; void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) override; void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override; - bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate); + bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update); CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver& reserver, bool fUpdate = false); void TransactionRemovedFromMempool(const CTransactionRef &ptx) override; @@ -945,7 +959,7 @@ public: * calling CreateTransaction(); */ bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl); - bool SignTransaction(CMutableTransaction& tx); + bool SignTransaction(CMutableTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** * Create a new transaction paying the recipients with a set of coins @@ -985,7 +999,7 @@ public: OutputType m_default_change_type{DEFAULT_CHANGE_TYPE}; bool NewKeyPool(); - size_t KeypoolCountExternalKeys(); + size_t KeypoolCountExternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool TopUpKeyPool(unsigned int kpSize = 0); void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal); void KeepKey(int64_t nIndex); @@ -995,10 +1009,10 @@ public: /** * Marks all keys in the keypool up to and including reserve_key as used. */ - void MarkReserveKeysAsUsed(int64_t keypool_id); + void MarkReserveKeysAsUsed(int64_t keypool_id) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); const std::map<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; } - std::set< std::set<CTxDestination> > GetAddressGroupings(); + std::set<std::set<CTxDestination>> GetAddressGroupings() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); std::map<CTxDestination, CAmount> GetAddressBalances(); std::set<CTxDestination> GetLabelAddresses(const std::string& label) const; @@ -1026,7 +1040,7 @@ public: DBErrors LoadWallet(bool& fFirstRunRet); DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx); - DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut); + DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose); @@ -1046,7 +1060,7 @@ public: void GetScriptForMining(std::shared_ptr<CReserveScript> &script); - unsigned int GetKeyPoolSize() + unsigned int GetKeyPoolSize() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); // set{Ex,In}ternalKeyPool return setInternalKeyPool.size() + setExternalKeyPool.size(); @@ -1065,7 +1079,7 @@ public: std::set<uint256> GetConflicts(const uint256& txid) const; //! Check if a given transaction has any of its outputs spent by another transaction in the wallet - bool HasWalletSpend(const uint256& txid) const; + bool HasWalletSpend(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Flush wallet (bitdb flush) void Flush(bool shutdown=false); @@ -1127,6 +1141,9 @@ public: /* Generates a new HD master key (will not be activated) */ CPubKey GenerateNewHDMasterKey(); + /* Derives a new HD master key (will not be activated) */ + CPubKey DeriveNewMasterHDKey(const CKey& key); + /* Set the current HD master key (will reset the chain child index counters) Sets the master key's version based on the current wallet version (so the caller must ensure the current wallet version is correct before calling @@ -1139,7 +1156,7 @@ public: * Obviously holding cs_main/cs_wallet when going into this call may cause * deadlock */ - void BlockUntilSyncedToCurrentChain(); + void BlockUntilSyncedToCurrentChain() LOCKS_EXCLUDED(cs_wallet); /** * Explicitly make the wallet learn the related scripts for outputs to the diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 4d1a6d48d0..66a50db15d 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -248,7 +248,7 @@ public: static bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, - CWalletScanState &wss, std::string& strType, std::string& strErr) + CWalletScanState &wss, std::string& strType, std::string& strErr) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { try { // Unserialize diff --git a/src/walletinitinterface.h b/src/walletinitinterface.h index 5bfde6faaf..020d8971cb 100644 --- a/src/walletinitinterface.h +++ b/src/walletinitinterface.h @@ -13,7 +13,7 @@ class CRPCTable; class WalletInitInterface { public: /** Get wallet help string */ - virtual std::string GetHelpString(bool showDebug) const = 0; + virtual void AddWalletOptions() const = 0; /** Check wallet parameter interaction */ virtual bool ParameterInteraction() const = 0; /** Register wallet RPC*/ diff --git a/src/warnings.cpp b/src/warnings.cpp index dc4e6e4842..534745f998 100644 --- a/src/warnings.cpp +++ b/src/warnings.cpp @@ -9,9 +9,9 @@ #include <warnings.h> CCriticalSection cs_warnings; -std::string strMiscWarning; -bool fLargeWorkForkFound = false; -bool fLargeWorkInvalidChainFound = false; +std::string strMiscWarning GUARDED_BY(cs_warnings); +bool fLargeWorkForkFound GUARDED_BY(cs_warnings) = false; +bool fLargeWorkInvalidChainFound GUARDED_BY(cs_warnings) = false; void SetMiscWarning(const std::string& strWarning) { diff --git a/test/functional/feature_bip68_sequence.py b/test/functional/feature_bip68_sequence.py index eee38ce648..d8521550b5 100755 --- a/test/functional/feature_bip68_sequence.py +++ b/test/functional/feature_bip68_sequence.py @@ -67,7 +67,7 @@ class BIP68Test(BitcoinTestFramework): # If sequence locks were used, this would require 1 block for the # input to mature. sequence_value = SEQUENCE_LOCKTIME_DISABLE_FLAG | 1 - tx1.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=sequence_value)] + tx1.vin = [CTxIn(COutPoint(int(utxo["txid"], 16), utxo["vout"]), nSequence=sequence_value)] tx1.vout = [CTxOut(value, CScript([b'a']))] tx1_signed = self.nodes[0].signrawtransactionwithwallet(ToHex(tx1))["hex"] @@ -80,7 +80,7 @@ class BIP68Test(BitcoinTestFramework): tx2.nVersion = 2 sequence_value = sequence_value & 0x7fffffff tx2.vin = [CTxIn(COutPoint(tx1_id, 0), nSequence=sequence_value)] - tx2.vout = [CTxOut(int(value-self.relayfee*COIN), CScript([b'a']))] + tx2.vout = [CTxOut(int(value - self.relayfee * COIN), CScript([b'a' * 35]))] tx2.rehash() assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx2)) @@ -222,7 +222,7 @@ class BIP68Test(BitcoinTestFramework): tx = CTransaction() tx.nVersion = 2 tx.vin = [CTxIn(COutPoint(orig_tx.sha256, 0), nSequence=sequence_value)] - tx.vout = [CTxOut(int(orig_tx.vout[0].nValue - relayfee*COIN), CScript([b'a']))] + tx.vout = [CTxOut(int(orig_tx.vout[0].nValue - relayfee * COIN), CScript([b'a' * 35]))] tx.rehash() if (orig_tx.hash in node.getrawmempool()): @@ -350,7 +350,7 @@ class BIP68Test(BitcoinTestFramework): tx3 = CTransaction() tx3.nVersion = 2 tx3.vin = [CTxIn(COutPoint(tx2.sha256, 0), nSequence=sequence_value)] - tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee*COIN), CScript([b'a']))] + tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee * COIN), CScript([b'a' * 35]))] tx3.rehash() assert_raises_rpc_error(-26, NOT_FINAL_ERROR, self.nodes[0].sendrawtransaction, ToHex(tx3)) diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index 17d3ddae4a..f943fdf176 100755 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -32,6 +32,7 @@ from test_framework.script import ( OP_ELSE, OP_ENDIF, OP_EQUAL, + OP_DROP, OP_FALSE, OP_HASH160, OP_IF, @@ -1215,7 +1216,7 @@ class FullBlockTest(BitcoinTestFramework): block.vtx.extend(tx_list) # this is a little handier to use than the version in blocktools.py - def create_tx(self, spend_tx, n, value, script=CScript([OP_TRUE])): + def create_tx(self, spend_tx, n, value, script=CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])): return create_transaction(spend_tx, n, b"", value, script) # sign a transaction, using the key we know about diff --git a/test/functional/feature_includeconf.py b/test/functional/feature_includeconf.py new file mode 100755 index 0000000000..9ccb89af43 --- /dev/null +++ b/test/functional/feature_includeconf.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# Copyright (c) 2018 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Tests the includeconf argument + +Verify that: + +1. adding includeconf to the configuration file causes the includeconf + file to be loaded in the correct order. +2. includeconf cannot be used as a command line argument. +3. includeconf cannot be used recursively (ie includeconf can only + be used from the base config file). +4. multiple includeconf arguments can be specified in the main config + file. +""" +import os + +from test_framework.test_framework import BitcoinTestFramework + +class IncludeConfTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = False + self.num_nodes = 1 + + def setup_chain(self): + super().setup_chain() + # Create additional config files + # - tmpdir/node0/relative.conf + with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "w", encoding="utf8") as f: + f.write("uacomment=relative\n") + # - tmpdir/node0/relative2.conf + with open(os.path.join(self.options.tmpdir, "node0", "relative2.conf"), "w", encoding="utf8") as f: + f.write("uacomment=relative2\n") + with open(os.path.join(self.options.tmpdir, "node0", "bitcoin.conf"), "a", encoding='utf8') as f: + f.write("uacomment=main\nincludeconf=relative.conf\n") + + def run_test(self): + self.log.info("-includeconf works from config file. subversion should end with 'main; relative)/'") + + subversion = self.nodes[0].getnetworkinfo()["subversion"] + assert subversion.endswith("main; relative)/") + + self.log.info("-includeconf cannot be used as command-line arg. subversion should still end with 'main; relative)/'") + self.stop_node(0) + + self.start_node(0, extra_args=["-includeconf=relative2.conf"]) + + subversion = self.nodes[0].getnetworkinfo()["subversion"] + assert subversion.endswith("main; relative)/") + self.stop_node(0, expected_stderr="warning: -includeconf cannot be used from commandline; ignoring -includeconf=relative2.conf") + + self.log.info("-includeconf cannot be used recursively. subversion should end with 'main; relative)/'") + with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "a", encoding="utf8") as f: + f.write("includeconf=relative2.conf\n") + self.start_node(0) + + subversion = self.nodes[0].getnetworkinfo()["subversion"] + assert subversion.endswith("main; relative)/") + self.stop_node(0, expected_stderr="warning: -includeconf cannot be used from included files; ignoring -includeconf=relative2.conf") + + self.log.info("multiple -includeconf args can be used from the base config file. subversion should end with 'main; relative; relative2)/'") + with open(os.path.join(self.options.tmpdir, "node0", "relative.conf"), "w", encoding="utf8") as f: + f.write("uacomment=relative\n") + + with open(os.path.join(self.options.tmpdir, "node0", "bitcoin.conf"), "a", encoding='utf8') as f: + f.write("includeconf=relative2.conf\n") + + self.start_node(0) + + subversion = self.nodes[0].getnetworkinfo()["subversion"] + assert subversion.endswith("main; relative; relative2)/") + +if __name__ == '__main__': + IncludeConfTest().main() diff --git a/test/functional/feature_maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py index 072ba6c7c7..0946f27b90 100755 --- a/test/functional/feature_maxuploadtarget.py +++ b/test/functional/feature_maxuploadtarget.py @@ -100,7 +100,7 @@ class MaxUploadTest(BitcoinTestFramework): assert_equal(p2p_conns[0].block_receive_map[big_old_block], i+1) assert_equal(len(self.nodes[0].getpeerinfo()), 3) - # At most a couple more tries should succeed (depending on how long + # At most a couple more tries should succeed (depending on how long # the test has been running so far). for i in range(3): p2p_conns[0].send_message(getdata_request) diff --git a/test/functional/feature_proxy.py b/test/functional/feature_proxy.py index 60859de7a5..2d10c547e2 100755 --- a/test/functional/feature_proxy.py +++ b/test/functional/feature_proxy.py @@ -79,9 +79,9 @@ class ProxyTest(BitcoinTestFramework): # Note: proxies are not used to connect to local nodes # this is because the proxy to use is based on CService.GetNetwork(), which return NET_UNROUTABLE for localhost args = [ - ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-proxyrandomize=1'], - ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),'-proxyrandomize=0'], - ['-listen', '-proxy=%s:%i' % (self.conf2.addr),'-proxyrandomize=1'], + ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-proxyrandomize=1'], + ['-listen', '-proxy=%s:%i' % (self.conf1.addr),'-onion=%s:%i' % (self.conf2.addr),'-proxyrandomize=0'], + ['-listen', '-proxy=%s:%i' % (self.conf2.addr),'-proxyrandomize=1'], [] ] if self.have_ipv6: diff --git a/test/functional/feature_rbf.py b/test/functional/feature_rbf.py index d6ab5ecc37..c087960782 100755 --- a/test/functional/feature_rbf.py +++ b/test/functional/feature_rbf.py @@ -123,7 +123,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) @@ -132,7 +132,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Should fail because we haven't changed the fee tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1b.vout = [CTxOut(1*COIN, CScript([b'b']))] + tx1b.vout = [CTxOut(1 * COIN, CScript([b'b' * 35]))] tx1b_hex = txToHex(tx1b) # This will raise an exception due to insufficient fee @@ -143,7 +143,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # 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.vout = [CTxOut(int(0.9 * COIN), CScript([b'b' * 35]))] 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) @@ -175,7 +175,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): remaining_value -= 1*COIN tx = CTransaction() tx.vin = [CTxIn(prevout, nSequence=0)] - tx.vout = [CTxOut(remaining_value, CScript([1]))] + tx.vout = [CTxOut(remaining_value, CScript([1, OP_DROP] * 15 + [1]))] tx_hex = txToHex(tx) txid = self.nodes[0].sendrawtransaction(tx_hex, True) chain_txids.append(txid) @@ -185,7 +185,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # child fees - 40 BTC - so this attempt is rejected. dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(initial_nValue - 30*COIN, CScript([1]))] + dbl_tx.vout = [CTxOut(initial_nValue - 30 * COIN, CScript([1] * 35))] dbl_tx_hex = txToHex(dbl_tx) # This will raise an exception due to insufficient fee @@ -194,7 +194,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Accepted with sufficient fee dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(1*COIN, CScript([1]))] + dbl_tx.vout = [CTxOut(1 * COIN, CScript([1] * 35))] dbl_tx_hex = txToHex(dbl_tx) self.nodes[0].sendrawtransaction(dbl_tx_hex, True) @@ -247,7 +247,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Attempt double-spend, will fail because too little fee paid dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(initial_nValue - fee*n, CScript([1]))] + dbl_tx.vout = [CTxOut(initial_nValue - fee * n, CScript([1] * 35))] dbl_tx_hex = txToHex(dbl_tx) # This will raise an exception due to insufficient fee assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) @@ -255,7 +255,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # 1 BTC fee is enough dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(initial_nValue - fee*n - 1*COIN, CScript([1]))] + dbl_tx.vout = [CTxOut(initial_nValue - fee * n - 1 * COIN, CScript([1] * 35))] dbl_tx_hex = txToHex(dbl_tx) self.nodes[0].sendrawtransaction(dbl_tx_hex, True) @@ -275,7 +275,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): dbl_tx = CTransaction() dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)] - dbl_tx.vout = [CTxOut(initial_nValue - 2*fee*n, CScript([1]))] + dbl_tx.vout = [CTxOut(initial_nValue - 2 * fee * n, CScript([1] * 35))] dbl_tx_hex = txToHex(dbl_tx) # This will raise an exception assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, dbl_tx_hex, True) @@ -290,7 +290,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx1a_hex = txToHex(tx1a) self.nodes[0].sendrawtransaction(tx1a_hex, True) @@ -311,7 +311,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1a = CTransaction() tx1a.vin = [CTxIn(utxo1, nSequence=0)] - tx1a.vout = [CTxOut(int(1.1*COIN), CScript([b'a']))] + tx1a.vout = [CTxOut(int(1.1 * COIN), CScript([b'a' * 35]))] tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) @@ -330,7 +330,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Spend tx1a's output to test the indirect case. tx1b = CTransaction() tx1b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)] - tx1b.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1b.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx1b_hex = txToHex(tx1b) tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) tx1b_txid = int(tx1b_txid, 16) @@ -351,7 +351,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1 = CTransaction() tx1.vin = [CTxIn(confirmed_utxo)] - tx1.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx1_hex = txToHex(tx1) self.nodes[0].sendrawtransaction(tx1_hex, True) @@ -390,7 +390,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): for i in range(MAX_REPLACEMENT_LIMIT+1): tx_i = CTransaction() tx_i.vin = [CTxIn(COutPoint(txid, i), nSequence=0)] - tx_i.vout = [CTxOut(split_value-fee, CScript([b'a']))] + tx_i.vout = [CTxOut(split_value - fee, CScript([b'a' * 35]))] tx_i_hex = txToHex(tx_i) self.nodes[0].sendrawtransaction(tx_i_hex, True) @@ -423,14 +423,14 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Create a non-opting in transaction tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0xffffffff)] - tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) # Shouldn't be able to double-spend tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] + tx1b.vout = [CTxOut(int(0.9 * COIN), CScript([b'b' * 35]))] tx1b_hex = txToHex(tx1b) # This will raise an exception @@ -441,14 +441,14 @@ class ReplaceByFeeTest(BitcoinTestFramework): # Create a different non-opting in transaction tx2a = CTransaction() tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0xfffffffe)] - tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx2a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx2a_hex = txToHex(tx2a) tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True) # Still shouldn't be able to double-spend tx2b = CTransaction() tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)] - tx2b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] + tx2b.vout = [CTxOut(int(0.9 * COIN), CScript([b'b' * 35]))] tx2b_hex = txToHex(tx2b) # This will raise an exception @@ -471,12 +471,12 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx3b = CTransaction() tx3b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)] - tx3b.vout = [CTxOut(int(0.5*COIN), CScript([b'e']))] + tx3b.vout = [CTxOut(int(0.5 * COIN), CScript([b'e' * 35]))] tx3b_hex = txToHex(tx3b) tx3c = CTransaction() tx3c.vin = [CTxIn(COutPoint(tx2a_txid, 0), nSequence=0)] - tx3c.vout = [CTxOut(int(0.5*COIN), CScript([b'f']))] + tx3c.vout = [CTxOut(int(0.5 * COIN), CScript([b'f' * 35]))] tx3c_hex = txToHex(tx3c) self.nodes[0].sendrawtransaction(tx3b_hex, True) @@ -493,7 +493,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1a = CTransaction() tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)] - tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) @@ -519,14 +519,14 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx2a = CTransaction() tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0)] - tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))] + tx2a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))] tx2a_hex = txToHex(tx2a) self.nodes[0].sendrawtransaction(tx2a_hex, True) # Lower fee, but we'll prioritise it tx2b = CTransaction() tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)] - tx2b.vout = [CTxOut(int(1.01*COIN), CScript([b'a']))] + tx2b.vout = [CTxOut(int(1.01 * COIN), CScript([b'a' * 35]))] tx2b.rehash() tx2b_hex = txToHex(tx2b) diff --git a/test/functional/feature_segwit.py b/test/functional/feature_segwit.py index a47c42829a..3622e1834a 100755 --- a/test/functional/feature_segwit.py +++ b/test/functional/feature_segwit.py @@ -16,7 +16,7 @@ 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.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 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, OP_DROP from io import BytesIO NODE_0 = 0 @@ -212,7 +212,7 @@ class SegWitTest(BitcoinTestFramework): # Now create tx2, which will spend from txid1. tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int(txid1, 16), 0), b'')) - tx.vout.append(CTxOut(int(49.99*COIN), CScript([OP_TRUE]))) + tx.vout.append(CTxOut(int(49.99 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx2_hex = self.nodes[0].signrawtransactionwithwallet(ToHex(tx))['hex'] txid2 = self.nodes[0].sendrawtransaction(tx2_hex) tx = FromHex(CTransaction(), tx2_hex) @@ -221,7 +221,7 @@ class SegWitTest(BitcoinTestFramework): # Now create tx3, which will spend from txid2 tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int(txid2, 16), 0), b"")) - tx.vout.append(CTxOut(int(49.95*COIN), CScript([OP_TRUE]))) # Huge fee + tx.vout.append(CTxOut(int(49.95 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) # Huge fee tx.calc_sha256() txid3 = self.nodes[0].sendrawtransaction(ToHex(tx)) assert(tx.wit.is_null()) diff --git a/test/functional/p2p_compactblocks.py b/test/functional/p2p_compactblocks.py index 1657d97281..cb4c9867a3 100755 --- a/test/functional/p2p_compactblocks.py +++ b/test/functional/p2p_compactblocks.py @@ -12,7 +12,8 @@ from test_framework.mininode import * from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * from test_framework.blocktools import create_block, create_coinbase, add_witness_commitment -from test_framework.script import CScript, OP_TRUE +from test_framework.script import CScript, OP_TRUE, OP_DROP + # TestP2PConn: A peer we use to send messages to bitcoind, and store responses. class TestP2PConn(P2PInterface): @@ -423,7 +424,7 @@ class CompactBlocksTest(BitcoinTestFramework): for i in range(num_transactions): tx = CTransaction() tx.vin.append(CTxIn(COutPoint(utxo[0], utxo[1]), b'')) - tx.vout.append(CTxOut(utxo[2] - 1000, CScript([OP_TRUE]))) + tx.vout.append(CTxOut(utxo[2] - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx.rehash() utxo = [tx.sha256, 0, tx.vout[0].nValue] block.vtx.append(tx) diff --git a/test/functional/p2p_feefilter.py b/test/functional/p2p_feefilter.py index 7c954cdca2..c304bbba85 100755 --- a/test/functional/p2p_feefilter.py +++ b/test/functional/p2p_feefilter.py @@ -69,7 +69,7 @@ class FeeFilterTest(BitcoinTestFramework): # Change tx fee rate to 10 sat/byte and test they are no longer received node1.settxfee(Decimal("0.00010000")) [node1.sendtoaddress(node1.getnewaddress(), 1) for x in range(3)] - sync_mempools(self.nodes) # must be sure node 0 has received all txs + sync_mempools(self.nodes) # must be sure node 0 has received all txs # Send one transaction from node0 that should be received, so that we # we can sync the test on receipt (if node1's txs were relayed, they'd diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py index 8a0961be1f..a12c1d6023 100755 --- a/test/functional/p2p_invalid_tx.py +++ b/test/functional/p2p_invalid_tx.py @@ -70,7 +70,7 @@ class InvalidTxRequestTest(BitcoinTestFramework): # Transaction will be rejected with code 16 (REJECT_INVALID) # and we get disconnected immediately self.log.info('Test a transaction that is rejected') - tx1 = create_transaction(block1.vtx[0], 0, b'\x64', 50 * COIN - 12000) + tx1 = create_transaction(block1.vtx[0], 0, b'\x64' * 35, 50 * COIN - 12000) node.p2p.send_txs_and_test([tx1], node, success=False, expect_disconnect=True) # Make two p2p connections to provide the node with orphans @@ -81,32 +81,33 @@ class InvalidTxRequestTest(BitcoinTestFramework): self.log.info('Test orphan transaction handling ... ') # Create a root transaction that we withold until all dependend transactions # are sent out and in the orphan cache + SCRIPT_PUB_KEY_OP_TRUE = b'\x51\x75' * 15 + b'\x51' tx_withhold = CTransaction() tx_withhold.vin.append(CTxIn(outpoint=COutPoint(block1.vtx[0].sha256, 0))) - tx_withhold.vout.append(CTxOut(nValue=50 * COIN - 12000, scriptPubKey=b'\x51')) + tx_withhold.vout.append(CTxOut(nValue=50 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_withhold.calc_sha256() # Our first orphan tx with some outputs to create further orphan txs tx_orphan_1 = CTransaction() tx_orphan_1.vin.append(CTxIn(outpoint=COutPoint(tx_withhold.sha256, 0))) - tx_orphan_1.vout = [CTxOut(nValue=10 * COIN, scriptPubKey=b'\x51')] * 3 + tx_orphan_1.vout = [CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 3 tx_orphan_1.calc_sha256() # A valid transaction with low fee tx_orphan_2_no_fee = CTransaction() tx_orphan_2_no_fee.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 0))) - tx_orphan_2_no_fee.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=b'\x51')) + tx_orphan_2_no_fee.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) # A valid transaction with sufficient fee tx_orphan_2_valid = CTransaction() tx_orphan_2_valid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 1))) - tx_orphan_2_valid.vout.append(CTxOut(nValue=10 * COIN - 12000, scriptPubKey=b'\x51')) + tx_orphan_2_valid.vout.append(CTxOut(nValue=10 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) tx_orphan_2_valid.calc_sha256() # An invalid transaction with negative fee tx_orphan_2_invalid = CTransaction() tx_orphan_2_invalid.vin.append(CTxIn(outpoint=COutPoint(tx_orphan_1.sha256, 2))) - tx_orphan_2_invalid.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=b'\x51')) + tx_orphan_2_invalid.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)) self.log.info('Send the orphans ... ') # Send valid orphan txs from p2ps[0] diff --git a/test/functional/p2p_segwit.py b/test/functional/p2p_segwit.py index 4fecd4ffee..e80a764477 100755 --- a/test/functional/p2p_segwit.py +++ b/test/functional/p2p_segwit.py @@ -73,11 +73,15 @@ class TestP2PConn(P2PInterface): for inv in message.inv: self.getdataset.add(inv.hash) - def announce_tx_and_wait_for_getdata(self, tx, timeout=60): + def announce_tx_and_wait_for_getdata(self, tx, timeout=60, success=True): with mininode_lock: self.last_message.pop("getdata", None) self.send_message(msg_inv(inv=[CInv(1, tx.sha256)])) - self.wait_for_getdata(timeout) + if success: + self.wait_for_getdata(timeout) + else: + time.sleep(timeout) + assert not self.last_message.get("getdata") def announce_block_and_wait_for_getdata(self, block, use_header, timeout=60): with mininode_lock: @@ -173,7 +177,7 @@ class SegWitTest(BitcoinTestFramework): # Create a transaction that spends the coinbase tx = CTransaction() tx.vin.append(CTxIn(COutPoint(txid, 0), b"")) - tx.vout.append(CTxOut(49*100000000, CScript([OP_TRUE]))) + tx.vout.append(CTxOut(49 * 100000000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx.calc_sha256() # Check that serializing it with or without witness is the same @@ -264,7 +268,7 @@ class SegWitTest(BitcoinTestFramework): # Now create a new anyone-can-spend utxo for the next test. tx4 = CTransaction() tx4.vin.append(CTxIn(COutPoint(tx3.sha256, 0), CScript([p2sh_program]))) - tx4.vout.append(CTxOut(tx3.vout[0].nValue-1000, CScript([OP_TRUE]))) + tx4.vout.append(CTxOut(tx3.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx4.rehash() test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, False, True) test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx4, False, True) @@ -893,7 +897,7 @@ class SegWitTest(BitcoinTestFramework): assert(len(self.utxo)) tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) - tx.vout.append(CTxOut(self.utxo[0].nValue-1000, CScript([OP_TRUE]))) + tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx.wit.vtxinwit.append(CTxInWitness()) tx.wit.vtxinwit[0].scriptWitness.stack = [ b'a' ] tx.rehash() @@ -908,12 +912,7 @@ class SegWitTest(BitcoinTestFramework): # Since we haven't delivered the tx yet, inv'ing the same tx from # a witness transaction ought not result in a getdata. - try: - self.test_node.announce_tx_and_wait_for_getdata(tx, timeout=2) - self.log.error("Error: duplicate tx getdata!") - assert(False) - except AssertionError: - pass + self.test_node.announce_tx_and_wait_for_getdata(tx, timeout=2, success=False) # Delivering this transaction with witness should fail (no matter who # its from) @@ -945,7 +944,7 @@ class SegWitTest(BitcoinTestFramework): assert(len(self.utxo)) tx = CTransaction() tx.vin.append(CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")) - tx.vout.append(CTxOut(self.utxo[0].nValue-1000, CScript([OP_TRUE]))) + tx.vout.append(CTxOut(self.utxo[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) tx.wit.vtxinwit.append(CTxInWitness()) tx.wit.vtxinwit[0].scriptWitness.stack = [ b'a' ] tx.rehash() @@ -989,7 +988,7 @@ class SegWitTest(BitcoinTestFramework): test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, True, False, b'tx-size') # Remove witness stuffing, instead add extra witness push on stack - tx3.vout[0] = CTxOut(tx2.vout[0].nValue-1000, CScript([OP_TRUE])) + tx3.vout[0] = CTxOut(tx2.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])) tx3.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)]), witness_program ] tx3.rehash() @@ -1177,7 +1176,7 @@ class SegWitTest(BitcoinTestFramework): # P2PKH output; just send tx's first output back to an anyone-can-spend. sync_mempools([self.nodes[0], self.nodes[1]]) tx3.vin = [CTxIn(COutPoint(tx.sha256, 0), b"")] - tx3.vout = [CTxOut(tx.vout[0].nValue-1000, CScript([OP_TRUE]))] + tx3.vout = [CTxOut(tx.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))] tx3.wit.vtxinwit.append(CTxInWitness()) tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program] tx3.rehash() @@ -1185,7 +1184,7 @@ class SegWitTest(BitcoinTestFramework): else: # tx and tx2 didn't go anywhere; just clean up the p2sh_tx output. tx3.vin = [CTxIn(COutPoint(p2sh_tx.sha256, 0), CScript([witness_program]))] - tx3.vout = [CTxOut(p2sh_tx.vout[0].nValue-1000, witness_program)] + tx3.vout = [CTxOut(p2sh_tx.vout[0].nValue - 1000, CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))] tx3.rehash() test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True) @@ -1659,7 +1658,7 @@ class SegWitTest(BitcoinTestFramework): tx2.wit.vtxinwit.append(CTxInWitness()) tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program ] total_value += tx.vout[i].nValue - tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program_toomany ] + tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program_toomany ] tx2.vout.append(CTxOut(total_value, CScript([OP_TRUE]))) tx2.rehash() diff --git a/test/functional/p2p_sendheaders.py b/test/functional/p2p_sendheaders.py index 4c7d5e65c5..095cc4b734 100755 --- a/test/functional/p2p_sendheaders.py +++ b/test/functional/p2p_sendheaders.py @@ -116,6 +116,7 @@ class BaseNode(P2PInterface): self.block_announced = False self.last_blockhash_announced = None + self.recent_headers_announced = [] def send_get_data(self, block_hashes): """Request data for a list of block hashes.""" @@ -163,40 +164,45 @@ class BaseNode(P2PInterface): def on_headers(self, message): if len(message.headers): self.block_announced = True - message.headers[-1].calc_sha256() + for x in message.headers: + x.calc_sha256() + # append because headers may be announced over multiple messages. + self.recent_headers_announced.append(x.sha256) self.last_blockhash_announced = message.headers[-1].sha256 - def clear_last_announcement(self): + def clear_block_announcements(self): with mininode_lock: self.block_announced = False self.last_message.pop("inv", None) self.last_message.pop("headers", None) + self.recent_headers_announced = [] - def check_last_announcement(self, headers=None, inv=None): - """Test whether the last announcement received had the right header or the right inv. - inv and headers should be lists of block hashes.""" + def check_last_headers_announcement(self, headers): + """Test whether the last headers announcements received are right. + Headers may be announced across more than one message.""" + test_function = lambda: (len(self.recent_headers_announced) >= len(headers)) + wait_until(test_function, timeout=60, lock=mininode_lock) + with mininode_lock: + assert_equal(self.recent_headers_announced, headers) + self.block_announced = False + self.last_message.pop("headers", None) + self.recent_headers_announced = [] + + def check_last_inv_announcement(self, inv): + """Test whether the last announcement received had the right inv. + inv should be a list of block hashes.""" test_function = lambda: self.block_announced wait_until(test_function, timeout=60, lock=mininode_lock) with mininode_lock: - self.block_announced = False - compare_inv = [] if "inv" in self.last_message: compare_inv = [x.hash for x in self.last_message["inv"].inv] - if inv is not None: - assert_equal(compare_inv, inv) - - compare_headers = [] - if "headers" in self.last_message: - compare_headers = [x.sha256 for x in self.last_message["headers"].headers] - if headers is not None: - assert_equal(compare_headers, headers) - + assert_equal(compare_inv, inv) + self.block_announced = False self.last_message.pop("inv", None) - self.last_message.pop("headers", None) class SendHeadersTest(BitcoinTestFramework): def set_test_params(self): @@ -206,8 +212,8 @@ class SendHeadersTest(BitcoinTestFramework): def mine_blocks(self, count): """Mine count blocks and return the new tip.""" - # Clear out last block announcement from each p2p listener - [x.clear_last_announcement() for x in self.nodes[0].p2ps] + # Clear out block announcements from each p2p listener + [x.clear_block_announcements() for x in self.nodes[0].p2ps] self.nodes[0].generate(count) return int(self.nodes[0].getbestblockhash(), 16) @@ -222,7 +228,7 @@ class SendHeadersTest(BitcoinTestFramework): sync_blocks(self.nodes, wait=0.1) for x in self.nodes[0].p2ps: x.wait_for_block_announcement(int(self.nodes[0].getbestblockhash(), 16)) - x.clear_last_announcement() + x.clear_block_announcements() tip_height = self.nodes[1].getblockcount() hash_to_invalidate = self.nodes[1].getblockhash(tip_height - (length - 1)) @@ -255,25 +261,25 @@ class SendHeadersTest(BitcoinTestFramework): tip = self.nodes[0].getblockheader(self.nodes[0].generate(1)[0]) tip_hash = int(tip["hash"], 16) - inv_node.check_last_announcement(inv=[tip_hash], headers=[]) - test_node.check_last_announcement(inv=[tip_hash], headers=[]) + inv_node.check_last_inv_announcement(inv=[tip_hash]) + test_node.check_last_inv_announcement(inv=[tip_hash]) self.log.info("Verify getheaders with null locator and valid hashstop returns headers.") - test_node.clear_last_announcement() + test_node.clear_block_announcements() test_node.send_get_headers(locator=[], hashstop=tip_hash) - test_node.check_last_announcement(headers=[tip_hash]) + test_node.check_last_headers_announcement(headers=[tip_hash]) self.log.info("Verify getheaders with null locator and invalid hashstop does not return headers.") block = create_block(int(tip["hash"], 16), create_coinbase(tip["height"] + 1), tip["mediantime"] + 1) block.solve() test_node.send_header_for_blocks([block]) - test_node.clear_last_announcement() + test_node.clear_block_announcements() test_node.send_get_headers(locator=[], hashstop=int(block.hash, 16)) test_node.sync_with_ping() assert_equal(test_node.block_announced, False) - inv_node.clear_last_announcement() + inv_node.clear_block_announcements() test_node.send_message(msg_block(block)) - inv_node.check_last_announcement(inv=[int(block.hash, 16)], headers=[]) + inv_node.check_last_inv_announcement(inv=[int(block.hash, 16)]) def test_nonnull_locators(self, test_node, inv_node): tip = int(self.nodes[0].getbestblockhash(), 16) @@ -284,8 +290,8 @@ class SendHeadersTest(BitcoinTestFramework): for i in range(4): old_tip = tip tip = self.mine_blocks(1) - inv_node.check_last_announcement(inv=[tip], headers=[]) - test_node.check_last_announcement(inv=[tip], headers=[]) + inv_node.check_last_inv_announcement(inv=[tip]) + test_node.check_last_inv_announcement(inv=[tip]) # Try a few different responses; none should affect next announcement if i == 0: # first request the block @@ -296,7 +302,7 @@ class SendHeadersTest(BitcoinTestFramework): test_node.send_get_headers(locator=[old_tip], hashstop=tip) test_node.send_get_data([tip]) test_node.wait_for_block(tip) - test_node.clear_last_announcement() # since we requested headers... + test_node.clear_block_announcements() # since we requested headers... elif i == 2: # this time announce own block via headers height = self.nodes[0].getblockcount() @@ -308,8 +314,8 @@ class SendHeadersTest(BitcoinTestFramework): test_node.wait_for_getdata([new_block.sha256]) test_node.send_message(msg_block(new_block)) test_node.sync_with_ping() # make sure this block is processed - inv_node.clear_last_announcement() - test_node.clear_last_announcement() + inv_node.clear_block_announcements() + test_node.clear_block_announcements() self.log.info("Part 1: success!") self.log.info("Part 2: announce blocks with headers after sendheaders message...") @@ -323,8 +329,8 @@ class SendHeadersTest(BitcoinTestFramework): # Now that we've synced headers, headers announcements should work tip = self.mine_blocks(1) - inv_node.check_last_announcement(inv=[tip], headers=[]) - test_node.check_last_announcement(headers=[tip]) + inv_node.check_last_inv_announcement(inv=[tip]) + test_node.check_last_headers_announcement(headers=[tip]) height = self.nodes[0].getblockcount() + 1 block_time += 10 # Advance far enough ahead @@ -368,8 +374,8 @@ class SendHeadersTest(BitcoinTestFramework): assert "inv" not in inv_node.last_message assert "headers" not in inv_node.last_message tip = self.mine_blocks(1) - inv_node.check_last_announcement(inv=[tip], headers=[]) - test_node.check_last_announcement(headers=[tip]) + inv_node.check_last_inv_announcement(inv=[tip]) + test_node.check_last_headers_announcement(headers=[tip]) height += 1 block_time += 1 @@ -383,16 +389,16 @@ class SendHeadersTest(BitcoinTestFramework): # First try mining a reorg that can propagate with header announcement new_block_hashes = self.mine_reorg(length=7) tip = new_block_hashes[-1] - inv_node.check_last_announcement(inv=[tip], headers=[]) - test_node.check_last_announcement(headers=new_block_hashes) + inv_node.check_last_inv_announcement(inv=[tip]) + test_node.check_last_headers_announcement(headers=new_block_hashes) block_time += 8 # Mine a too-large reorg, which should be announced with a single inv new_block_hashes = self.mine_reorg(length=8) tip = new_block_hashes[-1] - inv_node.check_last_announcement(inv=[tip], headers=[]) - test_node.check_last_announcement(inv=[tip], headers=[]) + inv_node.check_last_inv_announcement(inv=[tip]) + test_node.check_last_inv_announcement(inv=[tip]) block_time += 9 @@ -401,15 +407,15 @@ class SendHeadersTest(BitcoinTestFramework): # Use getblocks/getdata test_node.send_getblocks(locator=[fork_point]) - test_node.check_last_announcement(inv=new_block_hashes, headers=[]) + test_node.check_last_inv_announcement(inv=new_block_hashes) test_node.send_get_data(new_block_hashes) test_node.wait_for_block(new_block_hashes[-1]) for i in range(3): # Mine another block, still should get only an inv tip = self.mine_blocks(1) - inv_node.check_last_announcement(inv=[tip], headers=[]) - test_node.check_last_announcement(inv=[tip], headers=[]) + inv_node.check_last_inv_announcement(inv=[tip]) + test_node.check_last_inv_announcement(inv=[tip]) if i == 0: self.log.debug("Just get the data -- shouldn't cause headers announcements to resume") test_node.send_get_data([tip]) @@ -431,8 +437,8 @@ class SendHeadersTest(BitcoinTestFramework): test_node.sync_with_ping() # New blocks should now be announced with header tip = self.mine_blocks(1) - inv_node.check_last_announcement(inv=[tip], headers=[]) - test_node.check_last_announcement(headers=[tip]) + inv_node.check_last_inv_announcement(inv=[tip]) + test_node.check_last_headers_announcement(headers=[tip]) self.log.info("Part 3: success!") diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py index 0f09c3c552..1e91cf598c 100755 --- a/test/functional/rpc_fundrawtransaction.py +++ b/test/functional/rpc_fundrawtransaction.py @@ -4,8 +4,18 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the fundrawtransaction RPC.""" +from decimal import Decimal from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * +from test_framework.util import ( + assert_equal, + assert_fee_amount, + assert_greater_than, + assert_greater_than_or_equal, + assert_raises_rpc_error, + connect_nodes_bi, + count_bytes, + find_vout_for_address, +) def get_unspent(listunspent, amount): @@ -57,6 +67,11 @@ class RawTransactionsTest(BitcoinTestFramework): watchonly_amount = Decimal(200) self.nodes[3].importpubkey(watchonly_pubkey, "", True) watchonly_txid = self.nodes[0].sendtoaddress(watchonly_address, watchonly_amount) + + # Lock UTXO so nodes[0] doesn't accidentally spend it + watchonly_vout = find_vout_for_address(self.nodes[0], watchonly_txid, watchonly_address) + self.nodes[0].lockunspent(False, [{"txid": watchonly_txid, "vout": watchonly_vout}]) + self.nodes[0].sendtoaddress(self.nodes[3].getnewaddress(), watchonly_amount / 10) self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.5) @@ -475,6 +490,9 @@ class RawTransactionsTest(BitcoinTestFramework): connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) connect_nodes_bi(self.nodes,0,3) + # Again lock the watchonly UTXO or nodes[0] may spend it, because + # lockunspent is memory-only and thus lost on restart + self.nodes[0].lockunspent(False, [{"txid": watchonly_txid, "vout": watchonly_vout}]) self.sync_all() # drain the keypool diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 472664a314..b842e6ef4e 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -98,8 +98,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): help="Leave bitcoinds and test.* datadir on exit or error") parser.add_option("--noshutdown", dest="noshutdown", default=False, action="store_true", help="Don't stop bitcoinds after the test execution") - parser.add_option("--srcdir", dest="srcdir", default=os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/../../../src"), - help="Source directory containing bitcoind/bitcoin-cli (default: %default)") parser.add_option("--cachedir", dest="cachedir", default=os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/../../cache"), help="Directory for caching pregenerated datadirs (default: %default)") parser.add_option("--tmpdir", dest="tmpdir", help="Root directory for datadirs") @@ -123,10 +121,6 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): PortSeed.n = self.options.port_seed - os.environ['PATH'] = self.options.srcdir + os.pathsep + \ - self.options.srcdir + os.path.sep + "qt" + os.pathsep + \ - os.environ['PATH'] - check_json_precision() self.options.cachedir = os.path.abspath(self.options.cachedir) @@ -136,6 +130,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): self.options.bitcoind = os.getenv("BITCOIND", default=config["environment"]["BUILDDIR"] + '/src/bitcoind' + config["environment"]["EXEEXT"]) self.options.bitcoincli = os.getenv("BITCOINCLI", default=config["environment"]["BUILDDIR"] + '/src/bitcoin-cli' + config["environment"]["EXEEXT"]) + os.environ['PATH'] = config['environment']['BUILDDIR'] + os.pathsep + \ + config['environment']['BUILDDIR'] + os.path.sep + "qt" + os.pathsep + \ + os.environ['PATH'] + # Set up temp directory and start logging if self.options.tmpdir: self.options.tmpdir = os.path.abspath(self.options.tmpdir) @@ -258,7 +256,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): assert_equal(len(extra_args), num_nodes) assert_equal(len(binary), num_nodes) for i in range(num_nodes): - self.nodes.append(TestNode(i, get_datadir_path(self.options.tmpdir, i), rpchost=rpchost, timewait=timewait, bitcoind=binary[i], bitcoin_cli=self.options.bitcoincli, stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, extra_conf=extra_confs[i], extra_args=extra_args[i], use_cli=self.options.usecli)) + self.nodes.append(TestNode(i, get_datadir_path(self.options.tmpdir, i), rpchost=rpchost, timewait=timewait, bitcoind=binary[i], bitcoin_cli=self.options.bitcoincli, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, extra_conf=extra_confs[i], extra_args=extra_args[i], use_cli=self.options.usecli)) def start_node(self, i, *args, **kwargs): """Start a bitcoind""" @@ -291,9 +289,9 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): for node in self.nodes: coverage.write_all_rpc_commands(self.options.coveragedir, node.rpc) - def stop_node(self, i): + def stop_node(self, i, expected_stderr=''): """Stop a bitcoind test node""" - self.nodes[i].stop_node() + self.nodes[i].stop_node(expected_stderr) self.nodes[i].wait_until_stopped() def stop_nodes(self): @@ -409,7 +407,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): args = [self.options.bitcoind, "-datadir=" + datadir] if i > 0: args.append("-connect=127.0.0.1:" + str(p2p_port(0))) - self.nodes.append(TestNode(i, get_datadir_path(self.options.cachedir, i), extra_conf=["bind=127.0.0.1"], extra_args=[], rpchost=None, timewait=None, bitcoind=self.options.bitcoind, bitcoin_cli=self.options.bitcoincli, stderr=None, mocktime=self.mocktime, coverage_dir=None)) + self.nodes.append(TestNode(i, get_datadir_path(self.options.cachedir, i), extra_conf=["bind=127.0.0.1"], extra_args=[], rpchost=None, timewait=None, bitcoind=self.options.bitcoind, bitcoin_cli=self.options.bitcoincli, mocktime=self.mocktime, coverage_dir=None)) self.nodes[i].args = args self.start_node(i) diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 5a6a659392..0353fc0712 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -10,6 +10,7 @@ from enum import Enum import http.client import json import logging +import os import re import subprocess import tempfile @@ -55,9 +56,11 @@ class TestNode(): To make things easier for the test writer, any unrecognised messages will be dispatched to the RPC connection.""" - def __init__(self, i, datadir, rpchost, timewait, bitcoind, bitcoin_cli, stderr, mocktime, coverage_dir, extra_conf=None, extra_args=None, use_cli=False): + def __init__(self, i, datadir, rpchost, timewait, bitcoind, bitcoin_cli, mocktime, coverage_dir, extra_conf=None, extra_args=None, use_cli=False): self.index = i self.datadir = datadir + self.stdout_dir = os.path.join(self.datadir, "stdout") + self.stderr_dir = os.path.join(self.datadir, "stderr") self.rpchost = rpchost if timewait: self.rpc_timeout = timewait @@ -65,7 +68,6 @@ class TestNode(): # Wait for up to 60 seconds for the RPC server to respond self.rpc_timeout = 60 self.binary = bitcoind - self.stderr = stderr self.coverage_dir = coverage_dir if extra_conf != None: append_config(datadir, extra_conf) @@ -81,8 +83,7 @@ class TestNode(): "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), - "-uacomment=testnode%d" % i, - "-noprinttoconsole" + "-uacomment=testnode%d" % i ] self.cli = TestNodeCLI(bitcoin_cli, self.datadir) @@ -124,17 +125,29 @@ class TestNode(): assert self.rpc_connected and self.rpc is not None, self._node_msg("Error: no RPC connection") return getattr(self.rpc, name) - def start(self, extra_args=None, stderr=None, *args, **kwargs): + def start(self, extra_args=None, stdout=None, stderr=None, *args, **kwargs): """Start the node.""" if extra_args is None: extra_args = self.extra_args + + # Add a new stdout and stderr file each time bitcoind is started if stderr is None: - stderr = self.stderr + stderr = tempfile.NamedTemporaryFile(dir=self.stderr_dir, delete=False) + if stdout is None: + stdout = tempfile.NamedTemporaryFile(dir=self.stdout_dir, delete=False) + self.stderr = stderr + self.stdout = stdout + # Delete any existing cookie file -- if such a file exists (eg due to # unclean shutdown), it will get overwritten anyway by bitcoind, and # potentially interfere with our attempt to authenticate delete_cookie_file(self.datadir) - self.process = subprocess.Popen(self.args + extra_args, stderr=stderr, *args, **kwargs) + + # add environment variable LIBC_FATAL_STDERR_=1 so that libc errors are written to stderr and not the terminal + subp_env = dict(os.environ, LIBC_FATAL_STDERR_="1") + + self.process = subprocess.Popen(self.args + extra_args, env=subp_env, stdout=stdout, stderr=stderr, *args, **kwargs) + self.running = True self.log.debug("bitcoind started, waiting for RPC to come up") @@ -174,7 +187,7 @@ class TestNode(): wallet_path = "wallet/%s" % wallet_name return self.rpc / wallet_path - def stop_node(self): + def stop_node(self, expected_stderr=''): """Stop the node.""" if not self.running: return @@ -183,6 +196,13 @@ class TestNode(): self.stop() except http.client.CannotSendRequest: self.log.exception("Unable to stop node.") + + # Check that stderr is as expected + self.stderr.seek(0) + stderr = self.stderr.read().decode('utf-8').strip() + if stderr != expected_stderr: + raise AssertionError("Unexpected stderr {} != {}".format(stderr, expected_stderr)) + del self.p2ps[:] def is_node_stopped(self): @@ -217,9 +237,10 @@ class TestNode(): Will throw if bitcoind starts without an error. Will throw if an expected_msg is provided and it does not match bitcoind's stdout.""" - with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr: + with tempfile.NamedTemporaryFile(dir=self.stderr_dir, delete=False) as log_stderr, \ + tempfile.NamedTemporaryFile(dir=self.stdout_dir, delete=False) as log_stdout: try: - self.start(extra_args, stderr=log_stderr, *args, **kwargs) + self.start(extra_args, stdout=log_stdout, stderr=log_stderr, *args, **kwargs) self.wait_for_rpc_connection() self.stop_node() self.wait_until_stopped() diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 4ec3175cd6..e016148f70 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -301,6 +301,9 @@ def initialize_datadir(dirname, n): f.write("keypool=1\n") f.write("discover=0\n") f.write("listenonion=0\n") + f.write("printtoconsole=0\n") + os.makedirs(os.path.join(datadir, 'stderr'), exist_ok=True) + os.makedirs(os.path.join(datadir, 'stdout'), exist_ok=True) return datadir def get_datadir_path(dirname, n): @@ -349,7 +352,14 @@ def set_node_times(nodes, t): def disconnect_nodes(from_connection, node_num): for peer_id in [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']]: - from_connection.disconnectnode(nodeid=peer_id) + try: + from_connection.disconnectnode(nodeid=peer_id) + except JSONRPCException as e: + # If this node is disconnected between calculating the peer id + # and issuing the disconnect, don't worry about it. + # This avoids a race condition if we're mass-disconnecting peers. + if e.error['code'] != -29: # RPC_CLIENT_NODE_NOT_CONNECTED + raise # wait to disconnect wait_until(lambda: [peer['id'] for peer in from_connection.getpeerinfo() if "testnode%d" % node_num in peer['subver']] == [], timeout=5) @@ -556,3 +566,14 @@ def mine_large_block(node, utxos=None): fee = 100 * node.getnetworkinfo()["relayfee"] create_lots_of_big_transactions(node, txouts, utxos, num, fee=fee) node.generate(1) + +def find_vout_for_address(node, txid, addr): + """ + Locate the vout index of the given transaction sending to the + given address. Raises runtime error exception if not found. + """ + tx = node.getrawtransaction(txid, True) + for i in range(len(tx["vout"])): + if any([addr == a for a in tx["vout"][i]["scriptPubKey"]["addresses"]]): + return i + raise RuntimeError("Vout not found for address: txid=%s, addr=%s" % (txid, addr)) diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index ff4b480165..dfa8c33728 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -138,6 +138,7 @@ BASE_SCRIPTS = [ 'p2p_fingerprint.py', 'feature_uacomment.py', 'p2p_unrequested_blocks.py', + 'feature_includeconf.py', 'feature_logging.py', 'p2p_node_network_limited.py', 'feature_blocksdir.py', @@ -313,8 +314,7 @@ def run_tests(test_list, src_dir, build_dir, tmpdir, jobs=1, enable_coverage=Fal tests_dir = src_dir + '/test/functional/' - flags = ["--srcdir={}/src".format(build_dir)] + args - flags.append("--cachedir=%s" % cache_dir) + flags = ['--cachedir={}'.format(cache_dir)] + args if enable_coverage: coverage = RPCCoverage() diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py index 0e095a6132..9c58b84819 100755 --- a/test/functional/wallet_basic.py +++ b/test/functional/wallet_basic.py @@ -22,10 +22,9 @@ class WalletTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 4 self.setup_clean_chain = True - self.extra_args = [['-deprecatedrpc=accounts']] * 4 def setup_network(self): - self.add_nodes(4, self.extra_args) + self.add_nodes(4) self.start_node(0) self.start_node(1) self.start_node(2) @@ -151,7 +150,7 @@ class WalletTest(BitcoinTestFramework): inputs = [] outputs = {} inputs.append({"txid": utxo["txid"], "vout": utxo["vout"]}) - outputs[self.nodes[2].getnewaddress("from1")] = utxo["amount"] - 3 + outputs[self.nodes[2].getnewaddress()] = utxo["amount"] - 3 raw_tx = self.nodes[0].createrawtransaction(inputs, outputs) txns_to_send.append(self.nodes[0].signrawtransactionwithwallet(raw_tx)) @@ -165,7 +164,6 @@ class WalletTest(BitcoinTestFramework): assert_equal(self.nodes[0].getbalance(), 0) assert_equal(self.nodes[2].getbalance(), 94) - assert_equal(self.nodes[2].getbalance("from1"), 94 - 21) # Verify that a spent output cannot be locked anymore spent_0 = {"txid": node0utxos[0]["txid"], "vout": node0utxos[0]["vout"]} @@ -190,7 +188,7 @@ class WalletTest(BitcoinTestFramework): node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid))) # Sendmany 10 BTC - txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", []) + txid = self.nodes[2].sendmany('', {address: 10}, 0, "", []) self.nodes[2].generate(1) self.sync_all([self.nodes[0:3]]) node_0_bal += Decimal('10') @@ -198,7 +196,7 @@ class WalletTest(BitcoinTestFramework): assert_equal(self.nodes[0].getbalance(), node_0_bal) # Sendmany 10 BTC with subtract fee from amount - txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", [address]) + txid = self.nodes[2].sendmany('', {address: 10}, 0, "", [address]) self.nodes[2].generate(1) self.sync_all([self.nodes[0:3]]) node_2_bal -= Decimal('10') @@ -365,14 +363,14 @@ class WalletTest(BitcoinTestFramework): # - True: unicode escaped as \u.... # - False: unicode directly as UTF-8 for mode in [True, False]: - self.nodes[0].ensure_ascii = mode + self.nodes[0].rpc.ensure_ascii = mode # unicode check: Basic Multilingual Plane, Supplementary Plane respectively - for s in [u'рыба', u'𝅘𝅥𝅯']: - addr = self.nodes[0].getaccountaddress(s) - label = self.nodes[0].getaccount(addr) - assert_equal(label, s) - assert(s in self.nodes[0].listaccounts().keys()) - self.nodes[0].ensure_ascii = True # restore to default + for label in [u'рыба', u'𝅘𝅥𝅯']: + addr = self.nodes[0].getnewaddress() + self.nodes[0].setlabel(addr, label) + assert_equal(self.nodes[0].getaddressinfo(addr)['label'], label) + assert(label in self.nodes[0].listlabels()) + self.nodes[0].rpc.ensure_ascii = True # restore to default # maintenance tests maintenance = [ @@ -388,9 +386,9 @@ class WalletTest(BitcoinTestFramework): self.log.info("check " + m) self.stop_nodes() # set lower ancestor limit for later - self.start_node(0, [m, "-deprecatedrpc=accounts", "-limitancestorcount=" + str(chainlimit)]) - self.start_node(1, [m, "-deprecatedrpc=accounts", "-limitancestorcount=" + str(chainlimit)]) - self.start_node(2, [m, "-deprecatedrpc=accounts", "-limitancestorcount=" + str(chainlimit)]) + self.start_node(0, [m, "-limitancestorcount=" + str(chainlimit)]) + self.start_node(1, [m, "-limitancestorcount=" + str(chainlimit)]) + self.start_node(2, [m, "-limitancestorcount=" + str(chainlimit)]) if m == '-reindex': # reindex will leave rpc warm up "early"; Wait for it to finish wait_until(lambda: [block_count] * 3 == [self.nodes[i].getblockcount() for i in range(3)]) @@ -438,7 +436,7 @@ class WalletTest(BitcoinTestFramework): # Try with walletrejectlongchains # Double chain limit but require combining inputs, so we pass SelectCoinsMinConf self.stop_node(0) - self.start_node(0, extra_args=["-deprecatedrpc=accounts", "-walletrejectlongchains", "-limitancestorcount=" + str(2 * chainlimit)]) + self.start_node(0, extra_args=["-walletrejectlongchains", "-limitancestorcount=" + str(2 * chainlimit)]) # wait for loadmempool timeout = 10 diff --git a/test/functional/wallet_dump.py b/test/functional/wallet_dump.py index 997f67ec7e..01d27dabe7 100755 --- a/test/functional/wallet_dump.py +++ b/test/functional/wallet_dump.py @@ -136,7 +136,7 @@ class WalletDumpTest(BitcoinTestFramework): assert_equal(found_addr, test_addr_count) assert_equal(found_script_addr, 2) assert_equal(found_addr_chg, 90*2 + 50) # old reserve keys are marked as change now - assert_equal(found_addr_rsv, 90*2) + assert_equal(found_addr_rsv, 90*2) assert_equal(witness_addr_ret, witness_addr) # Overwriting should fail diff --git a/test/functional/wallet_hd.py b/test/functional/wallet_hd.py index 8c754807e6..97a945721f 100755 --- a/test/functional/wallet_hd.py +++ b/test/functional/wallet_hd.py @@ -11,6 +11,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, connect_nodes_bi, + assert_raises_rpc_error ) @@ -120,5 +121,39 @@ class WalletHDTest(BitcoinTestFramework): assert_equal(keypath[0:7], "m/0'/1'") + # Generate a new HD seed on node 1 and make sure it is set + orig_masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid'] + self.nodes[1].sethdseed() + new_masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid'] + assert orig_masterkeyid != new_masterkeyid + addr = self.nodes[1].getnewaddress() + assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is the first from the keypool + self.nodes[1].keypoolrefill(1) # Fill keypool with 1 key + + # Set a new HD seed on node 1 without flushing the keypool + new_seed = self.nodes[0].dumpprivkey(self.nodes[0].getnewaddress()) + orig_masterkeyid = new_masterkeyid + self.nodes[1].sethdseed(False, new_seed) + new_masterkeyid = self.nodes[1].getwalletinfo()['hdmasterkeyid'] + assert orig_masterkeyid != new_masterkeyid + addr = self.nodes[1].getnewaddress() + assert_equal(orig_masterkeyid, self.nodes[1].getaddressinfo(addr)['hdmasterkeyid']) + assert_equal(self.nodes[1].getaddressinfo(addr)['hdkeypath'], 'm/0\'/0\'/1\'') # Make sure the new address continues previous keypool + + # Check that the next address is from the new seed + self.nodes[1].keypoolrefill(1) + next_addr = self.nodes[1].getnewaddress() + assert_equal(new_masterkeyid, self.nodes[1].getaddressinfo(next_addr)['hdmasterkeyid']) + assert_equal(self.nodes[1].getaddressinfo(next_addr)['hdkeypath'], 'm/0\'/0\'/0\'') # Make sure the new address is not from previous keypool + assert next_addr != addr + + # Sethdseed parameter validity + assert_raises_rpc_error(-1, 'sethdseed', self.nodes[0].sethdseed, False, new_seed, 0) + assert_raises_rpc_error(-5, "Invalid private key", self.nodes[1].sethdseed, False, "not_wif") + assert_raises_rpc_error(-1, "JSON value is not a boolean as expected", self.nodes[1].sethdseed, "Not_bool") + assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[1].sethdseed, False, True) + assert_raises_rpc_error(-5, "Already have this key", self.nodes[1].sethdseed, False, new_seed) + assert_raises_rpc_error(-5, "Already have this key", self.nodes[1].sethdseed, False, self.nodes[1].dumpprivkey(self.nodes[1].getnewaddress())) + if __name__ == '__main__': WalletHDTest().main () diff --git a/test/functional/wallet_import_rescan.py b/test/functional/wallet_import_rescan.py index baf933f079..4c0ffb938b 100755 --- a/test/functional/wallet_import_rescan.py +++ b/test/functional/wallet_import_rescan.py @@ -42,16 +42,15 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")): def do_import(self, timestamp): """Call one key import RPC.""" + rescan = self.rescan == Rescan.yes if self.call == Call.single: if self.data == Data.address: - response = self.try_rpc(self.node.importaddress, self.address["address"], self.label, - self.rescan == Rescan.yes) + response = self.try_rpc(self.node.importaddress, address=self.address["address"], rescan=rescan) elif self.data == Data.pub: - response = self.try_rpc(self.node.importpubkey, self.address["pubkey"], self.label, - self.rescan == Rescan.yes) + response = self.try_rpc(self.node.importpubkey, pubkey=self.address["pubkey"], rescan=rescan) elif self.data == Data.priv: - response = self.try_rpc(self.node.importprivkey, self.key, self.label, self.rescan == Rescan.yes) + response = self.try_rpc(self.node.importprivkey, privkey=self.key, rescan=rescan) assert_equal(response, None) elif self.call == Call.multi: @@ -62,30 +61,22 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")): "timestamp": timestamp + TIMESTAMP_WINDOW + (1 if self.rescan == Rescan.late_timestamp else 0), "pubkeys": [self.address["pubkey"]] if self.data == Data.pub else [], "keys": [self.key] if self.data == Data.priv else [], - "label": self.label, "watchonly": self.data != Data.priv }], {"rescan": self.rescan in (Rescan.yes, Rescan.late_timestamp)}) assert_equal(response, [{"success": True}]) def check(self, txid=None, amount=None, confirmations=None): - """Verify that getbalance/listtransactions return expected values.""" + """Verify that listreceivedbyaddress returns expected values.""" - balance = self.node.getbalance(self.label, 0, True) - assert_equal(balance, self.expected_balance) - - txs = self.node.listtransactions(self.label, 10000, 0, True) - assert_equal(len(txs), self.expected_txs) + addresses = self.node.listreceivedbyaddress(minconf=0, include_watchonly=True, address_filter=self.address['address']) + if self.expected_txs: + assert_equal(len(addresses[0]["txids"]), self.expected_txs) if txid is not None: - tx, = [tx for tx in txs if tx["txid"] == txid] - assert_equal(tx["label"], self.label) - assert_equal(tx["address"], self.address["address"]) - assert_equal(tx["amount"], amount) - assert_equal(tx["category"], "receive") - assert_equal(tx["label"], self.label) - assert_equal(tx["txid"], txid) - assert_equal(tx["confirmations"], confirmations) - assert_equal("trusted" not in tx, True) + address, = [ad for ad in addresses if txid in ad["txids"]] + assert_equal(address["address"], self.address["address"]) + assert_equal(address["amount"], self.expected_balance) + assert_equal(address["confirmations"], confirmations) # Verify the transaction is correctly marked watchonly depending on # whether the transaction pays to an imported public key or # imported private key. The test setup ensures that transaction @@ -93,9 +84,9 @@ class Variant(collections.namedtuple("Variant", "call data rescan prune")): # involvesWatchonly will be true if either the transaction output # or inputs are watchonly). if self.data != Data.priv: - assert_equal(tx["involvesWatchonly"], True) + assert_equal(address["involvesWatchonly"], True) else: - assert_equal("involvesWatchonly" not in tx, True) + assert_equal("involvesWatchonly" not in address, True) # List of Variants for each way a key or address could be imported. @@ -119,7 +110,7 @@ class ImportRescanTest(BitcoinTestFramework): self.num_nodes = 2 + len(IMPORT_NODES) def setup_network(self): - extra_args = [["-addresstype=legacy", '-deprecatedrpc=accounts'] for _ in range(self.num_nodes)] + extra_args = [["-addresstype=legacy"] for _ in range(self.num_nodes)] for i, import_node in enumerate(IMPORT_NODES, 2): if import_node.prune: extra_args[i] += ["-prune=1"] @@ -130,11 +121,10 @@ class ImportRescanTest(BitcoinTestFramework): connect_nodes(self.nodes[i], 0) def run_test(self): - # Create one transaction on node 0 with a unique amount and label for + # Create one transaction on node 0 with a unique amount for # each possible type of wallet import RPC. for i, variant in enumerate(IMPORT_VARIANTS): - variant.label = "label {} {}".format(i, variant) - variant.address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress(variant.label)) + variant.address = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress()) variant.key = self.nodes[1].dumpprivkey(variant.address["address"]) variant.initial_amount = 10 - (i + 1) / 4.0 variant.initial_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.initial_amount) diff --git a/test/functional/wallet_keypool_topup.py b/test/functional/wallet_keypool_topup.py index ab1493dd04..d657dc30c4 100755 --- a/test/functional/wallet_keypool_topup.py +++ b/test/functional/wallet_keypool_topup.py @@ -25,7 +25,7 @@ class KeypoolRestoreTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 - self.extra_args = [['-deprecatedrpc=accounts'], ['-deprecatedrpc=accounts', '-keypool=100', '-keypoolmin=20']] + self.extra_args = [[], ['-keypool=100']] def run_test(self): wallet_path = os.path.join(self.nodes[1].datadir, "regtest", "wallets", "wallet.dat") diff --git a/test/functional/wallet_listreceivedby.py b/test/functional/wallet_listreceivedby.py index e0e20cc9a3..7447211296 100755 --- a/test/functional/wallet_listreceivedby.py +++ b/test/functional/wallet_listreceivedby.py @@ -14,7 +14,6 @@ from test_framework.util import (assert_array_result, class ReceivedByTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 - self.extra_args = [['-deprecatedrpc=accounts']] * 2 def run_test(self): # Generate block to get out of IBD @@ -112,8 +111,9 @@ class ReceivedByTest(BitcoinTestFramework): self.log.info("listreceivedbylabel + getreceivedbylabel Test") # set pre-state + label = '' address = self.nodes[1].getnewaddress() - label = self.nodes[1].getaccount(address) + assert_equal(self.nodes[1].getaddressinfo(address)['label'], label) received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel() if r["label"] == label][0] balance_by_label = self.nodes[1].getreceivedbylabel(label) @@ -141,7 +141,8 @@ class ReceivedByTest(BitcoinTestFramework): assert_equal(balance, balance_by_label + Decimal("0.1")) # Create a new label named "mynewlabel" that has a 0 balance - self.nodes[1].getlabeladdress(label="mynewlabel", force=True) + address = self.nodes[1].getnewaddress() + self.nodes[1].setlabel(address, "mynewlabel") received_by_label_json = [r for r in self.nodes[1].listreceivedbylabel(0, True) if r["label"] == "mynewlabel"][0] # Test includeempty of listreceivedbylabel diff --git a/test/functional/wallet_listsinceblock.py b/test/functional/wallet_listsinceblock.py index 50a3313e2f..63c179411c 100755 --- a/test/functional/wallet_listsinceblock.py +++ b/test/functional/wallet_listsinceblock.py @@ -11,7 +11,6 @@ class ListSinceBlockTest (BitcoinTestFramework): def set_test_params(self): self.num_nodes = 4 self.setup_clean_chain = True - self.extra_args = [['-deprecatedrpc=accounts']] * 4 def run_test(self): self.nodes[2].generate(101) diff --git a/test/functional/wallet_listtransactions.py b/test/functional/wallet_listtransactions.py index 883942cc19..7cf2e456cf 100755 --- a/test/functional/wallet_listtransactions.py +++ b/test/functional/wallet_listtransactions.py @@ -25,7 +25,6 @@ def tx_from_hex(hexstring): class ListTransactionsTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 - self.extra_args = [['-deprecatedrpc=accounts']] * 2 self.enable_mocktime() def run_test(self): @@ -34,19 +33,19 @@ class ListTransactionsTest(BitcoinTestFramework): self.sync_all() assert_array_result(self.nodes[0].listtransactions(), {"txid": txid}, - {"category": "send", "account": "", "amount": Decimal("-0.1"), "confirmations": 0}) + {"category": "send", "amount": Decimal("-0.1"), "confirmations": 0}) assert_array_result(self.nodes[1].listtransactions(), {"txid": txid}, - {"category": "receive", "account": "", "amount": Decimal("0.1"), "confirmations": 0}) + {"category": "receive", "amount": Decimal("0.1"), "confirmations": 0}) # mine a block, confirmations should change: self.nodes[0].generate(1) self.sync_all() assert_array_result(self.nodes[0].listtransactions(), {"txid": txid}, - {"category": "send", "account": "", "amount": Decimal("-0.1"), "confirmations": 1}) + {"category": "send", "amount": Decimal("-0.1"), "confirmations": 1}) assert_array_result(self.nodes[1].listtransactions(), {"txid": txid}, - {"category": "receive", "account": "", "amount": Decimal("0.1"), "confirmations": 1}) + {"category": "receive", "amount": Decimal("0.1"), "confirmations": 1}) # send-to-self: txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 0.2) @@ -60,8 +59,8 @@ class ListTransactionsTest(BitcoinTestFramework): # sendmany from node1: twice to self, twice to node2: send_to = {self.nodes[0].getnewaddress(): 0.11, self.nodes[1].getnewaddress(): 0.22, - self.nodes[0].getaccountaddress("from1"): 0.33, - self.nodes[1].getaccountaddress("toself"): 0.44} + self.nodes[0].getnewaddress(): 0.33, + self.nodes[1].getnewaddress(): 0.44} txid = self.nodes[1].sendmany("", send_to) self.sync_all() assert_array_result(self.nodes[1].listtransactions(), @@ -81,13 +80,13 @@ class ListTransactionsTest(BitcoinTestFramework): {"txid": txid}) assert_array_result(self.nodes[0].listtransactions(), {"category": "receive", "amount": Decimal("0.33")}, - {"txid": txid, "account": "from1"}) + {"txid": txid}) assert_array_result(self.nodes[1].listtransactions(), {"category": "send", "amount": Decimal("-0.44")}, - {"txid": txid, "account": ""}) + {"txid": txid}) assert_array_result(self.nodes[1].listtransactions(), {"category": "receive", "amount": Decimal("0.44")}, - {"txid": txid, "account": "toself"}) + {"txid": txid}) pubkey = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress())['pubkey'] multisig = self.nodes[1].createmultisig(1, [pubkey]) @@ -95,10 +94,9 @@ class ListTransactionsTest(BitcoinTestFramework): txid = self.nodes[1].sendtoaddress(multisig["address"], 0.1) self.nodes[1].generate(1) self.sync_all() - assert(len(self.nodes[0].listtransactions("watchonly", 100, 0, False)) == 0) - assert_array_result(self.nodes[0].listtransactions("watchonly", 100, 0, True), - {"category": "receive", "amount": Decimal("0.1")}, - {"txid": txid, "account": "watchonly"}) + assert not [tx for tx in self.nodes[0].listtransactions(dummy="*", count=100, skip=0, include_watchonly=False) if "label" in tx and tx["label"] == "watchonly"] + txs = [tx for tx in self.nodes[0].listtransactions(dummy="*", count=100, skip=0, include_watchonly=True) if "label" in tx and tx['label'] == 'watchonly'] + assert_array_result(txs, {"category": "receive", "amount": Decimal("0.1")}, {"txid": txid}) self.run_rbf_opt_in_test() diff --git a/test/functional/wallet_txn_clone.py b/test/functional/wallet_txn_clone.py index b4e4cb1686..72ae2d5db7 100755 --- a/test/functional/wallet_txn_clone.py +++ b/test/functional/wallet_txn_clone.py @@ -15,7 +15,6 @@ from test_framework.util import ( class TxnMallTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 4 - self.extra_args = [['-deprecatedrpc=accounts']] * 4 def add_options(self, parser): parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true", @@ -39,28 +38,27 @@ class TxnMallTest(BitcoinTestFramework): starting_balance = 1250 for i in range(4): assert_equal(self.nodes[i].getbalance(), starting_balance) - self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress! + self.nodes[i].getnewaddress() # bug workaround, coins generated assigned to first getnewaddress! - # Assign coins to foo and bar accounts: self.nodes[0].settxfee(.001) - node0_address_foo = self.nodes[0].getnewaddress("foo", output_type) - fund_foo_txid = self.nodes[0].sendfrom("", node0_address_foo, 1219) - fund_foo_tx = self.nodes[0].gettransaction(fund_foo_txid) + node0_address1 = self.nodes[0].getnewaddress(address_type=output_type) + node0_txid1 = self.nodes[0].sendtoaddress(node0_address1, 1219) + node0_tx1 = self.nodes[0].gettransaction(node0_txid1) - node0_address_bar = self.nodes[0].getnewaddress("bar", output_type) - fund_bar_txid = self.nodes[0].sendfrom("", node0_address_bar, 29) - fund_bar_tx = self.nodes[0].gettransaction(fund_bar_txid) + node0_address2 = self.nodes[0].getnewaddress(address_type=output_type) + node0_txid2 = self.nodes[0].sendtoaddress(node0_address2, 29) + node0_tx2 = self.nodes[0].gettransaction(node0_txid2) - assert_equal(self.nodes[0].getbalance(""), - starting_balance - 1219 - 29 + fund_foo_tx["fee"] + fund_bar_tx["fee"]) + assert_equal(self.nodes[0].getbalance(), + starting_balance + node0_tx1["fee"] + node0_tx2["fee"]) # Coins are sent to node1_address - node1_address = self.nodes[1].getnewaddress("from0") + node1_address = self.nodes[1].getnewaddress() # Send tx1, and another transaction tx2 that won't be cloned - txid1 = self.nodes[0].sendfrom("foo", node1_address, 40, 0) - txid2 = self.nodes[0].sendfrom("bar", node1_address, 20, 0) + txid1 = self.nodes[0].sendtoaddress(node1_address, 40) + txid2 = self.nodes[0].sendtoaddress(node1_address, 20) # Construct a clone of tx1, to be malleated rawtx1 = self.nodes[0].getrawtransaction(txid1, 1) @@ -96,28 +94,22 @@ class TxnMallTest(BitcoinTestFramework): # Node0's balance should be starting balance, plus 50BTC for another # matured block, minus tx1 and tx2 amounts, and minus transaction fees: - expected = starting_balance + fund_foo_tx["fee"] + fund_bar_tx["fee"] + expected = starting_balance + node0_tx1["fee"] + node0_tx2["fee"] if self.options.mine_block: expected += 50 expected += tx1["amount"] + tx1["fee"] expected += tx2["amount"] + tx2["fee"] assert_equal(self.nodes[0].getbalance(), expected) - # foo and bar accounts should be debited: - assert_equal(self.nodes[0].getbalance("foo", 0), 1219 + tx1["amount"] + tx1["fee"]) - assert_equal(self.nodes[0].getbalance("bar", 0), 29 + tx2["amount"] + tx2["fee"]) - if self.options.mine_block: assert_equal(tx1["confirmations"], 1) assert_equal(tx2["confirmations"], 1) - # Node1's "from0" balance should be both transaction amounts: - assert_equal(self.nodes[1].getbalance("from0"), -(tx1["amount"] + tx2["amount"])) else: assert_equal(tx1["confirmations"], 0) assert_equal(tx2["confirmations"], 0) # Send clone and its parent to miner - self.nodes[2].sendrawtransaction(fund_foo_tx["hex"]) + self.nodes[2].sendrawtransaction(node0_tx1["hex"]) txid1_clone = self.nodes[2].sendrawtransaction(tx1_clone["hex"]) if self.options.segwit: assert_equal(txid1, txid1_clone) @@ -128,7 +120,7 @@ class TxnMallTest(BitcoinTestFramework): # Reconnect the split network, and sync chain: connect_nodes(self.nodes[1], 2) - self.nodes[2].sendrawtransaction(fund_bar_tx["hex"]) + self.nodes[2].sendrawtransaction(node0_tx2["hex"]) self.nodes[2].sendrawtransaction(tx2["hex"]) self.nodes[2].generate(1) # Mine another block to make sure we sync sync_blocks(self.nodes) @@ -149,19 +141,6 @@ class TxnMallTest(BitcoinTestFramework): if (self.options.mine_block): expected -= 50 assert_equal(self.nodes[0].getbalance(), expected) - assert_equal(self.nodes[0].getbalance("*", 0), expected) - - # Check node0's individual account balances. - # "foo" should have been debited by the equivalent clone of tx1 - assert_equal(self.nodes[0].getbalance("foo"), 1219 + tx1["amount"] + tx1["fee"]) - # "bar" should have been debited by (possibly unconfirmed) tx2 - assert_equal(self.nodes[0].getbalance("bar", 0), 29 + tx2["amount"] + tx2["fee"]) - # "" should have starting balance, less funding txes, plus subsidies - assert_equal(self.nodes[0].getbalance("", 0), - starting_balance - 1219 + fund_foo_tx["fee"] - 29 + fund_bar_tx["fee"] + 100) - - # Node1's "from0" account balance - assert_equal(self.nodes[1].getbalance("from0", 0), -(tx1["amount"] + tx2["amount"])) if __name__ == '__main__': TxnMallTest().main() diff --git a/test/functional/wallet_txn_doublespend.py b/test/functional/wallet_txn_doublespend.py index d8d91132d1..7ee60d5611 100755 --- a/test/functional/wallet_txn_doublespend.py +++ b/test/functional/wallet_txn_doublespend.py @@ -17,7 +17,6 @@ from test_framework.util import ( class TxnMallTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 4 - self.extra_args = [['-deprecatedrpc=accounts']] * 4 def add_options(self, parser): parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true", @@ -36,20 +35,20 @@ class TxnMallTest(BitcoinTestFramework): assert_equal(self.nodes[i].getbalance(), starting_balance) self.nodes[i].getnewaddress("") # bug workaround, coins generated assigned to first getnewaddress! - # Assign coins to foo and bar accounts: - node0_address_foo = self.nodes[0].getnewaddress("foo") - fund_foo_txid = self.nodes[0].sendfrom("", node0_address_foo, 1219) + # Assign coins to foo and bar addresses: + node0_address_foo = self.nodes[0].getnewaddress() + fund_foo_txid = self.nodes[0].sendtoaddress(node0_address_foo, 1219) fund_foo_tx = self.nodes[0].gettransaction(fund_foo_txid) - node0_address_bar = self.nodes[0].getnewaddress("bar") - fund_bar_txid = self.nodes[0].sendfrom("", node0_address_bar, 29) + node0_address_bar = self.nodes[0].getnewaddress() + fund_bar_txid = self.nodes[0].sendtoaddress(node0_address_bar, 29) fund_bar_tx = self.nodes[0].gettransaction(fund_bar_txid) - assert_equal(self.nodes[0].getbalance(""), - starting_balance - 1219 - 29 + fund_foo_tx["fee"] + fund_bar_tx["fee"]) + assert_equal(self.nodes[0].getbalance(), + starting_balance + fund_foo_tx["fee"] + fund_bar_tx["fee"]) # Coins are sent to node1_address - node1_address = self.nodes[1].getnewaddress("from0") + node1_address = self.nodes[1].getnewaddress() # First: use raw transaction API to send 1240 BTC to node1_address, # but don't broadcast: @@ -70,8 +69,8 @@ class TxnMallTest(BitcoinTestFramework): assert_equal(doublespend["complete"], True) # Create two spends using 1 50 BTC coin each - txid1 = self.nodes[0].sendfrom("foo", node1_address, 40, 0) - txid2 = self.nodes[0].sendfrom("bar", node1_address, 20, 0) + txid1 = self.nodes[0].sendtoaddress(node1_address, 40) + txid2 = self.nodes[0].sendtoaddress(node1_address, 20) # Have node0 mine a block: if (self.options.mine_block): @@ -90,15 +89,11 @@ class TxnMallTest(BitcoinTestFramework): expected += tx2["amount"] + tx2["fee"] assert_equal(self.nodes[0].getbalance(), expected) - # foo and bar accounts should be debited: - assert_equal(self.nodes[0].getbalance("foo", 0), 1219 + tx1["amount"] + tx1["fee"]) - assert_equal(self.nodes[0].getbalance("bar", 0), 29 + tx2["amount"] + tx2["fee"]) - if self.options.mine_block: assert_equal(tx1["confirmations"], 1) assert_equal(tx2["confirmations"], 1) - # Node1's "from0" balance should be both transaction amounts: - assert_equal(self.nodes[1].getbalance("from0"), -(tx1["amount"] + tx2["amount"])) + # Node1's balance should be both transaction amounts: + assert_equal(self.nodes[1].getbalance(), starting_balance - tx1["amount"] - tx2["amount"]) else: assert_equal(tx1["confirmations"], 0) assert_equal(tx2["confirmations"], 0) @@ -129,17 +124,9 @@ class TxnMallTest(BitcoinTestFramework): # negative): expected = starting_balance + 100 - 1240 + fund_foo_tx["fee"] + fund_bar_tx["fee"] + doublespend_fee assert_equal(self.nodes[0].getbalance(), expected) - assert_equal(self.nodes[0].getbalance("*"), expected) - - # Final "" balance is starting_balance - amount moved to accounts - doublespend + subsidies + - # fees (which are negative) - assert_equal(self.nodes[0].getbalance("foo"), 1219) - assert_equal(self.nodes[0].getbalance("bar"), 29) - assert_equal(self.nodes[0].getbalance(""), - starting_balance - 1219 - 29 - 1240 + 100 + fund_foo_tx["fee"] + fund_bar_tx["fee"] + doublespend_fee) - # Node1's "from0" account balance should be just the doublespend: - assert_equal(self.nodes[1].getbalance("from0"), 1240) + # Node1's balance should be its initial balance (1250 for 25 block rewards) plus the doublespend: + assert_equal(self.nodes[1].getbalance(), 1250 + 1240) if __name__ == '__main__': TxnMallTest().main() |