diff options
Diffstat (limited to 'qa')
94 files changed, 2309 insertions, 787 deletions
diff --git a/qa/README.md b/qa/README.md index 225207cc1c..f4dce7af5c 100644 --- a/qa/README.md +++ b/qa/README.md @@ -39,12 +39,12 @@ Run the regression test suite with Run all possible tests with - qa/pull-tester/rpc-tests.py -extended + qa/pull-tester/rpc-tests.py --extended By default, tests will be run in parallel. To specify how many jobs to run, -append `-parallel=n` (default n=4). +append `--jobs=n` (default n=4). -If you want to create a basic coverage report for the rpc test suite, append `--coverage`. +If you want to create a basic coverage report for the RPC test suite, append `--coverage`. Possible options, which apply to each individual test run: @@ -83,5 +83,5 @@ killall bitcoind Writing tests ============= You are encouraged to write tests for new or existing features. -Further information about the test framework and individual rpc +Further information about the test framework and individual RPC tests is found in [qa/rpc-tests](/qa/rpc-tests). diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py index 58bd00fdfc..68e11b4c2b 100755 --- a/qa/pull-tester/rpc-tests.py +++ b/qa/pull-tester/rpc-tests.py @@ -2,25 +2,21 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - """ -Run Regression Test Suite +rpc-tests.py - run regression test suite This module calls down into individual test cases via subprocess. It will -forward all unrecognized arguments onto the individual test scripts, other -than: +forward all unrecognized arguments onto the individual test scripts. - - `-extended`: run the "extended" test suite in addition to the basic one. - - `-win`: signal that this is running in a Windows environment, and we - should run the tests. - - `--coverage`: this generates a basic coverage report for the RPC - interface. +RPC tests are disabled on Windows by default. Use --force to run them anyway. For a description of arguments recognized by test scripts, see `qa/pull-tester/test_framework/test_framework.py:BitcoinTestFramework.main`. """ +import argparse +import configparser import os import time import shutil @@ -29,189 +25,228 @@ import subprocess import tempfile import re -sys.path.append("qa/pull-tester/") -from tests_config import * - -BOLD = ("","") -if os.name == 'posix': - # primitive formatting on supported - # terminal via ANSI escape sequences: - BOLD = ('\033[0m', '\033[1m') - -RPC_TESTS_DIR = SRCDIR + '/qa/rpc-tests/' - -#If imported values are not defined then set to zero (or disabled) -if 'ENABLE_WALLET' not in vars(): - ENABLE_WALLET=0 -if 'ENABLE_BITCOIND' not in vars(): - ENABLE_BITCOIND=0 -if 'ENABLE_UTILS' not in vars(): - ENABLE_UTILS=0 -if 'ENABLE_ZMQ' not in vars(): - ENABLE_ZMQ=0 - -ENABLE_COVERAGE=0 - -#Create a set to store arguments and create the passon string -opts = set() -passon_args = [] -PASSON_REGEX = re.compile("^--") -PARALLEL_REGEX = re.compile('^-parallel=') - -print_help = False -run_parallel = 4 - -for arg in sys.argv[1:]: - if arg == "--help" or arg == "-h" or arg == "-?": - print_help = True - break - if arg == '--coverage': - ENABLE_COVERAGE = 1 - elif PASSON_REGEX.match(arg): - passon_args.append(arg) - elif PARALLEL_REGEX.match(arg): - run_parallel = int(arg.split(sep='=', maxsplit=1)[1]) - else: - opts.add(arg) - -#Set env vars -if "BITCOIND" not in os.environ: - os.environ["BITCOIND"] = BUILDDIR + '/src/bitcoind' + EXEEXT - -if EXEEXT == ".exe" and "-win" not in opts: - # https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9 - # https://github.com/bitcoin/bitcoin/pull/5677#issuecomment-136646964 - print("Win tests currently disabled by default. Use -win option to enable") - sys.exit(0) - -if not (ENABLE_WALLET == 1 and ENABLE_UTILS == 1 and ENABLE_BITCOIND == 1): - print("No rpc tests to run. Wallet, utils, and bitcoind must all be enabled") - sys.exit(0) - -# python3-zmq may not be installed. Handle this gracefully and with some helpful info -if ENABLE_ZMQ: - try: - import zmq - except ImportError: - print("ERROR: \"import zmq\" failed. Set ENABLE_ZMQ=0 or " - "to run zmq tests, see dependency info in /qa/README.md.") - # ENABLE_ZMQ=0 - raise - -testScripts = [ - # longest test should go first, to favor running tests in parallel - 'p2p-fullblocktest.py', +BASE_SCRIPTS= [ + # Scripts that are run by the travis build process. + # Longest test should go first, to favor running tests in parallel + 'wallet-hd.py', 'walletbackup.py', - 'bip68-112-113-p2p.py', + # vv Tests less than 5m vv + 'p2p-fullblocktest.py', + 'fundrawtransaction.py', + 'p2p-compactblocks.py', + 'segwit.py', + # vv Tests less than 2m vv 'wallet.py', 'wallet-accounts.py', - 'wallet-hd.py', + 'p2p-segwit.py', 'wallet-dump.py', 'listtransactions.py', + # vv Tests less than 60s vv + 'sendheaders.py', + 'zapwallettxes.py', + 'importmulti.py', + 'mempool_limit.py', + 'merkle_blocks.py', 'receivedby.py', + 'abandonconflict.py', + 'bip68-112-113-p2p.py', + 'rawtransactions.py', + 'reindex.py', + # vv Tests less than 30s vv 'mempool_resurrect_test.py', 'txn_doublespend.py --mineblock', - 'p2p-segwit.py', - 'segwit.py', 'txn_clone.py', 'getchaintips.py', - 'rawtransactions.py', 'rest.py', 'mempool_spendcoinbase.py', 'mempool_reorg.py', - 'mempool_limit.py', 'httpbasics.py', 'multi_rpc.py', - 'zapwallettxes.py', 'proxy_test.py', - 'merkle_blocks.py', - 'fundrawtransaction.py', 'signrawtransactions.py', 'nodehandling.py', - 'reindex.py', 'decodescript.py', 'blockchain.py', 'disablewallet.py', - 'sendheaders.py', 'keypool.py', 'p2p-mempool.py', 'prioritise_transaction.py', 'invalidblockrequest.py', 'invalidtxrequest.py', - 'abandonconflict.py', 'p2p-versionbits-warning.py', 'preciousblock.py', 'importprunedfunds.py', 'signmessages.py', - 'p2p-compactblocks.py', 'nulldummy.py', - 'importmulti.py', + 'import-rescan.py', + 'bumpfee.py', + 'rpcnamedargs.py', + 'listsinceblock.py', + 'p2p-leaktests.py', ] -if ENABLE_ZMQ: - testScripts.append('zmq_test.py') -testScriptsExt = [ +ZMQ_SCRIPTS = [ + # ZMQ test can only be run if bitcoin was built with zmq-enabled. + # call rpc_tests.py with -nozmq to explicitly exclude these tests. + "zmq_test.py"] + +EXTENDED_SCRIPTS = [ + # These tests are not run by the travis build process. + # Longest test should go first, to favor running tests in parallel + 'pruning.py', + # vv Tests less than 20m vv + 'smartfees.py', + # vv Tests less than 5m vv + 'maxuploadtarget.py', + 'mempool_packages.py', + # vv Tests less than 2m vv + 'bip68-sequence.py', + 'getblocktemplate_longpoll.py', + 'p2p-timeouts.py', + # vv Tests less than 60s vv 'bip9-softforks.py', + 'p2p-feefilter.py', + 'rpcbind_test.py', + # vv Tests less than 30s vv 'bip65-cltv.py', 'bip65-cltv-p2p.py', - 'bip68-sequence.py', 'bipdersig-p2p.py', 'bipdersig.py', - 'getblocktemplate_longpoll.py', 'getblocktemplate_proposals.py', 'txn_doublespend.py', 'txn_clone.py --mineblock', 'forknotify.py', 'invalidateblock.py', - 'rpcbind_test.py', - 'smartfees.py', 'maxblocksinflight.py', 'p2p-acceptblock.py', - 'mempool_packages.py', - 'maxuploadtarget.py', 'replace-by-fee.py', - 'p2p-feefilter.py', - 'pruning.py', # leave pruning last as it takes a REALLY long time ] +ALL_SCRIPTS = BASE_SCRIPTS + ZMQ_SCRIPTS + EXTENDED_SCRIPTS + +def main(): + # Parse arguments and pass through unrecognised args + parser = argparse.ArgumentParser(add_help=False, + usage='%(prog)s [rpc-test.py options] [script options] [scripts]', + description=__doc__, + epilog=''' + Help text and arguments for individual test script:''', + formatter_class=argparse.RawTextHelpFormatter) + parser.add_argument('--coverage', action='store_true', help='generate a basic coverage report for the RPC interface') + parser.add_argument('--exclude', '-x', help='specify a comma-seperated-list of scripts to exclude. Do not include the .py extension in the name.') + parser.add_argument('--extended', action='store_true', help='run the extended test suite in addition to the basic tests') + parser.add_argument('--force', '-f', action='store_true', help='run tests even on platforms where they are disabled by default (e.g. windows).') + parser.add_argument('--help', '-h', '-?', action='store_true', help='print help text and exit') + parser.add_argument('--jobs', '-j', type=int, default=4, help='how many test scripts to run in parallel. Default=4.') + parser.add_argument('--nozmq', action='store_true', help='do not run the zmq tests') + args, unknown_args = parser.parse_known_args() + + # Create a set to store arguments and create the passon string + tests = set(arg for arg in unknown_args if arg[:2] != "--") + passon_args = [arg for arg in unknown_args if arg[:2] == "--"] + + # Read config generated by configure. + config = configparser.ConfigParser() + config.read_file(open(os.path.dirname(__file__) + "/tests_config.ini")) + + enable_wallet = config["components"].getboolean("ENABLE_WALLET") + enable_utils = config["components"].getboolean("ENABLE_UTILS") + enable_bitcoind = config["components"].getboolean("ENABLE_BITCOIND") + enable_zmq = config["components"].getboolean("ENABLE_ZMQ") and not args.nozmq + + if config["environment"]["EXEEXT"] == ".exe" and not args.force: + # https://github.com/bitcoin/bitcoin/commit/d52802551752140cf41f0d9a225a43e84404d3e9 + # https://github.com/bitcoin/bitcoin/pull/5677#issuecomment-136646964 + print("Tests currently disabled on Windows by default. Use --force option to enable") + sys.exit(0) -def runtests(): - test_list = [] - if '-extended' in opts: - test_list = testScripts + testScriptsExt - elif len(opts) == 0 or (len(opts) == 1 and "-win" in opts): - test_list = testScripts + if not (enable_wallet and enable_utils and enable_bitcoind): + print("No rpc tests to run. Wallet, utils, and bitcoind must all be enabled") + print("Rerun `configure` with -enable-wallet, -with-utils and -with-daemon and rerun make") + sys.exit(0) + + # python3-zmq may not be installed. Handle this gracefully and with some helpful info + if enable_zmq: + try: + import zmq + except ImportError: + print("ERROR: \"import zmq\" failed. Use -nozmq to run without the ZMQ tests." + "To run zmq tests, see dependency info in /qa/README.md.") + raise + + # Build list of tests + if tests: + # Individual tests have been specified. Run specified tests that exist + # in the ALL_SCRIPTS list. Accept the name with or without .py extension. + test_list = [t for t in ALL_SCRIPTS if + (t in tests or re.sub(".py$", "", t) in tests)] else: - for t in testScripts + testScriptsExt: - if t in opts or re.sub(".py$", "", t) in opts: - test_list.append(t) + # No individual tests have been specified. Run base tests, and + # optionally ZMQ tests and extended tests. + test_list = BASE_SCRIPTS + if enable_zmq: + test_list += ZMQ_SCRIPTS + if args.extended: + test_list += EXTENDED_SCRIPTS + # TODO: BASE_SCRIPTS and EXTENDED_SCRIPTS are sorted by runtime + # (for parallel running efficiency). This combined list will is no + # longer sorted. + + # Remove the test cases that the user has explicitly asked to exclude. + if args.exclude: + for exclude_test in args.exclude.split(','): + if exclude_test + ".py" in test_list: + test_list.remove(exclude_test + ".py") + + if not test_list: + print("No valid test scripts specified. Check that your test is in one " + "of the test lists in rpc-tests.py, or run rpc-tests.py with no arguments to run all tests") + sys.exit(0) - if print_help: - # Only print help of the first script and exit - subprocess.check_call((RPC_TESTS_DIR + test_list[0]).split() + ['-h']) + if args.help: + # Print help for rpc-tests.py, then print help of the first script and exit. + parser.print_help() + subprocess.check_call((config["environment"]["SRCDIR"] + '/qa/rpc-tests/' + test_list[0]).split() + ['-h']) sys.exit(0) - coverage = None + run_tests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], config["environment"]["EXEEXT"], args.jobs, args.coverage, passon_args) + +def run_tests(test_list, src_dir, build_dir, exeext, jobs=1, enable_coverage=False, args=[]): + BOLD = ("","") + if os.name == 'posix': + # primitive formatting on supported + # terminal via ANSI escape sequences: + BOLD = ('\033[0m', '\033[1m') + + #Set env vars + if "BITCOIND" not in os.environ: + os.environ["BITCOIND"] = build_dir + '/src/bitcoind' + exeext - if ENABLE_COVERAGE: + tests_dir = src_dir + '/qa/rpc-tests/' + + flags = ["--srcdir={}/src".format(build_dir)] + args + flags.append("--cachedir=%s/qa/cache" % build_dir) + + if enable_coverage: coverage = RPCCoverage() - print("Initializing coverage directory at %s\n" % coverage.dir) - flags = ["--srcdir=%s/src" % BUILDDIR] + passon_args - flags.append("--cachedir=%s/qa/cache" % BUILDDIR) - if coverage: flags.append(coverage.flag) + print("Initializing coverage directory at %s\n" % coverage.dir) + else: + coverage = None - if len(test_list) > 1 and run_parallel > 1: + if len(test_list) > 1 and jobs > 1: # Populate cache - subprocess.check_output([RPC_TESTS_DIR + 'create_cache.py'] + flags) + subprocess.check_output([tests_dir + 'create_cache.py'] + flags) #Run Tests - max_len_name = len(max(test_list, key=len)) + all_passed = True time_sum = 0 time0 = time.time() - job_queue = RPCTestHandler(run_parallel, test_list, flags) + + job_queue = RPCTestHandler(jobs, tests_dir, test_list, flags) + + max_len_name = len(max(test_list, key=len)) results = BOLD[1] + "%s | %s | %s\n\n" % ("TEST".ljust(max_len_name), "PASSED", "DURATION") + BOLD[0] - all_passed = True for _ in range(len(test_list)): (name, stdout, stderr, passed, duration) = job_queue.get_next() all_passed = all_passed and passed @@ -220,8 +255,10 @@ def runtests(): print('\n' + BOLD[1] + name + BOLD[0] + ":") print('' if passed else stdout + '\n', end='') print('' if stderr == '' else 'stderr:\n' + stderr + '\n', end='') - results += "%s | %s | %s s\n" % (name.ljust(max_len_name), str(passed).ljust(6), duration) print("Pass: %s%s%s, Duration: %s s\n" % (BOLD[1], passed, BOLD[0], duration)) + + results += "%s | %s | %s s\n" % (name.ljust(max_len_name), str(passed).ljust(6), duration) + results += BOLD[1] + "\n%s | %s | %s s (accumulated)" % ("ALL".ljust(max_len_name), str(all_passed).ljust(6), time_sum) + BOLD[0] print(results) print("\nRuntime: %s s" % (int(time.time() - time0))) @@ -234,15 +271,15 @@ def runtests(): sys.exit(not all_passed) - class RPCTestHandler: """ Trigger the testscrips passed in via the list. """ - def __init__(self, num_tests_parallel, test_list=None, flags=None): + def __init__(self, num_tests_parallel, tests_dir, test_list=None, flags=None): assert(num_tests_parallel >= 1) self.num_jobs = num_tests_parallel + self.tests_dir = tests_dir self.test_list = test_list self.flags = flags self.num_running = 0 @@ -262,7 +299,7 @@ class RPCTestHandler: log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16) self.jobs.append((t, time.time(), - subprocess.Popen((RPC_TESTS_DIR + t).split() + self.flags + port_seed, + subprocess.Popen((self.tests_dir + t).split() + self.flags + port_seed, universal_newlines=True, stdout=log_stdout, stderr=log_stderr), @@ -327,10 +364,10 @@ class RPCCoverage(object): """ # This is shared from `qa/rpc-tests/test-framework/coverage.py` - REFERENCE_FILENAME = 'rpc_interface.txt' - COVERAGE_FILE_PREFIX = 'coverage.' + reference_filename = 'rpc_interface.txt' + coverage_file_prefix = 'coverage.' - coverage_ref_filename = os.path.join(self.dir, REFERENCE_FILENAME) + coverage_ref_filename = os.path.join(self.dir, reference_filename) coverage_filenames = set() all_cmds = set() covered_cmds = set() @@ -343,7 +380,7 @@ class RPCCoverage(object): for root, dirs, files in os.walk(self.dir): for filename in files: - if filename.startswith(COVERAGE_FILE_PREFIX): + if filename.startswith(coverage_file_prefix): coverage_filenames.add(os.path.join(root, filename)) for filename in coverage_filenames: @@ -354,4 +391,4 @@ class RPCCoverage(object): if __name__ == '__main__': - runtests() + main() diff --git a/qa/pull-tester/run-bitcoind-for-test.sh.in b/qa/pull-tester/run-bitcoind-for-test.sh.in deleted file mode 100644 index 14ae08e4e5..0000000000 --- a/qa/pull-tester/run-bitcoind-for-test.sh.in +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -# Copyright (c) 2013-2014 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -# -DATADIR="@abs_top_builddir@/.bitcoin" -rm -rf "$DATADIR" -mkdir -p "$DATADIR"/regtest -touch "$DATADIR/regtest/debug.log" -tail -q -n 1 -F "$DATADIR/regtest/debug.log" | grep -m 1 -q "Done loading" & -WAITER=$! -PORT=`expr 10000 + $$ % 55536` -"@abs_top_builddir@/src/bitcoind@EXEEXT@" -connect=0.0.0.0 -datadir="$DATADIR" -rpcuser=user -rpcpassword=pass -listen -keypool=3 -debug -debug=net -logtimestamps -checkmempool=0 -relaypriority=0 -port=$PORT -whitelist=127.0.0.1 -regtest -rpcport=`expr $PORT + 1` & -BITCOIND=$! - -#Install a watchdog. -(sleep 10 && kill -0 $WAITER 2>/dev/null && kill -9 $BITCOIND $$)& -wait $WAITER - -if [ -n "$TIMEOUT" ]; then - timeout "$TIMEOUT"s "$@" $PORT - RETURN=$? -else - "$@" $PORT - RETURN=$? -fi - -(sleep 15 && kill -0 $BITCOIND 2>/dev/null && kill -9 $BITCOIND $$)& -kill $BITCOIND && wait $BITCOIND - -# timeout returns 124 on timeout, otherwise the return value of the child - -# If $RETURN is not 0, the test failed. Dump the tail of the debug log. -if [ $RETURN -ne 0 ]; then tail -n 200 $DATADIR/regtest/debug.log; fi - -exit $RETURN diff --git a/qa/pull-tester/tests_config.ini.in b/qa/pull-tester/tests_config.ini.in new file mode 100644 index 0000000000..e3e457d0b1 --- /dev/null +++ b/qa/pull-tester/tests_config.ini.in @@ -0,0 +1,18 @@ +# Copyright (c) 2013-2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +# These environment variables are set by the build process and read by +# rpc-tests.py + +[environment] +SRCDIR=@abs_top_srcdir@ +BUILDDIR=@abs_top_builddir@ +EXEEXT=@EXEEXT@ + +[components] +# Which components are enabled. These are commented out by `configure` if they were disabled when running config. +@ENABLE_WALLET_TRUE@ENABLE_WALLET=true +@BUILD_BITCOIN_UTILS_TRUE@ENABLE_UTILS=true +@BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=true +@ENABLE_ZMQ_TRUE@ENABLE_ZMQ=true diff --git a/qa/pull-tester/tests_config.py.in b/qa/pull-tester/tests_config.py.in deleted file mode 100644 index a0d0a3d98a..0000000000 --- a/qa/pull-tester/tests_config.py.in +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2013-2016 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -SRCDIR="@abs_top_srcdir@" -BUILDDIR="@abs_top_builddir@" -EXEEXT="@EXEEXT@" - -# These will turn into comments if they were disabled when configuring. -@ENABLE_WALLET_TRUE@ENABLE_WALLET=1 -@BUILD_BITCOIN_UTILS_TRUE@ENABLE_UTILS=1 -@BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=1 -@ENABLE_ZMQ_TRUE@ENABLE_ZMQ=1 diff --git a/qa/rpc-tests/abandonconflict.py b/qa/rpc-tests/abandonconflict.py index 874df48777..b32d4e2ce0 100755 --- a/qa/rpc-tests/abandonconflict.py +++ b/qa/rpc-tests/abandonconflict.py @@ -2,7 +2,14 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - +"""Test the abandontransaction RPC. + + The abandontransaction RPC marks a transaction and all its in-wallet + descendants as abandoned which allows their inputs to be respent. It can be + used to replace "stuck" or evicted transactions. It only works on transactions + which are not included in a block and are not currently in the mempool. It has + no effect on transactions which are already conflicted or abandoned. +""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * diff --git a/qa/rpc-tests/assumevalid.py b/qa/rpc-tests/assumevalid.py new file mode 100755 index 0000000000..b8dafff161 --- /dev/null +++ b/qa/rpc-tests/assumevalid.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python3 +# Copyright (c) 2014-2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test logic for skipping signature validation on old blocks. + +Test logic for skipping signature validation on blocks which we've assumed +valid (https://github.com/bitcoin/bitcoin/pull/9484) + +We build a chain that includes and invalid signature for one of the +transactions: + + 0: genesis block + 1: block 1 with coinbase transaction output. + 2-101: bury that block with 100 blocks so the coinbase transaction + output can be spent + 102: a block containing a transaction spending the coinbase + transaction output. The transaction has an invalid signature. + 103-2202: bury the bad block with just over two weeks' worth of blocks + (2100 blocks) + +Start three nodes: + + - node0 has no -assumevalid parameter. Try to sync to block 2202. It will + reject block 102 and only sync as far as block 101 + - node1 has -assumevalid set to the hash of block 102. Try to sync to + block 2202. node1 will sync all the way to block 2202. + - node2 has -assumevalid set to the hash of block 102. Try to sync to + block 200. node2 will reject block 102 since it's assumed valid, but it + isn't buried by at least two weeks' work. +""" + +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 +from test_framework.key import CECKey +from test_framework.script import * + +class BaseNode(SingleNodeConnCB): + def __init__(self): + SingleNodeConnCB.__init__(self) + self.last_inv = None + self.last_headers = None + self.last_block = None + self.last_getdata = None + self.block_announced = False + self.last_getheaders = None + self.disconnected = False + self.last_blockhash_announced = None + + def on_close(self, conn): + self.disconnected = True + + def wait_for_disconnect(self, timeout=60): + test_function = lambda: self.disconnected + assert(wait_until(test_function, timeout=timeout)) + return + + def send_header_for_blocks(self, new_blocks): + headers_message = msg_headers() + headers_message.headers = [ CBlockHeader(b) for b in new_blocks ] + self.send_message(headers_message) + +class SendHeadersTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 3 + + def setup_network(self): + # Start node0. We don't start the other nodes yet since + # we need to pre-mine a block with an invalid transaction + # signature so we can pass in the block hash as assumevalid. + self.nodes = [] + self.nodes.append(start_node(0, self.options.tmpdir, ["-debug"])) + + def run_test(self): + + # Connect to node0 + node0 = BaseNode() + connections = [] + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], node0)) + node0.add_connection(connections[0]) + + NetworkThread().start() # Start up network handling in another thread + node0.wait_for_verack() + + # Build the blockchain + self.tip = int(self.nodes[0].getbestblockhash(), 16) + self.block_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time'] + 1 + + self.blocks = [] + + # Get a pubkey for the coinbase TXO + coinbase_key = CECKey() + coinbase_key.set_secretbytes(b"horsebattery") + coinbase_pubkey = coinbase_key.get_pubkey() + + # Create the first block with a coinbase output to our key + height = 1 + block = create_block(self.tip, create_coinbase(height, coinbase_pubkey), self.block_time) + self.blocks.append(block) + self.block_time += 1 + block.solve() + # Save the coinbase for later + self.block1 = block + self.tip = block.sha256 + height += 1 + + # Bury the block 100 deep so the coinbase output is spendable + for i in range(100): + block = create_block(self.tip, create_coinbase(height), self.block_time) + block.solve() + self.blocks.append(block) + self.tip = block.sha256 + self.block_time += 1 + height += 1 + + # Create a transaction spending the coinbase output with an invalid (null) signature + tx = CTransaction() + tx.vin.append(CTxIn(COutPoint(self.block1.vtx[0].sha256, 0), scriptSig=b"")) + tx.vout.append(CTxOut(49*100000000, CScript([OP_TRUE]))) + tx.calc_sha256() + + block102 = create_block(self.tip, create_coinbase(height), self.block_time) + self.block_time += 1 + block102.vtx.extend([tx]) + block102.hashMerkleRoot = block102.calc_merkle_root() + block102.rehash() + block102.solve() + self.blocks.append(block102) + self.tip = block102.sha256 + self.block_time += 1 + height += 1 + + # Bury the assumed valid block 2100 deep + for i in range(2100): + block = create_block(self.tip, create_coinbase(height), self.block_time) + block.nVersion = 4 + block.solve() + self.blocks.append(block) + self.tip = block.sha256 + self.block_time += 1 + height += 1 + + # Start node1 and node2 with assumevalid so they accept a block with a bad signature. + self.nodes.append(start_node(1, self.options.tmpdir, + ["-debug", "-assumevalid=" + hex(block102.sha256)])) + node1 = BaseNode() # connects to node1 + connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], node1)) + node1.add_connection(connections[1]) + node1.wait_for_verack() + + self.nodes.append(start_node(2, self.options.tmpdir, + ["-debug", "-assumevalid=" + hex(block102.sha256)])) + node2 = BaseNode() # connects to node2 + connections.append(NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], node2)) + node2.add_connection(connections[2]) + node2.wait_for_verack() + + # send header lists to all three nodes + node0.send_header_for_blocks(self.blocks[0:2000]) + node0.send_header_for_blocks(self.blocks[2000:]) + node1.send_header_for_blocks(self.blocks[0:2000]) + node1.send_header_for_blocks(self.blocks[2000:]) + node2.send_header_for_blocks(self.blocks[0:200]) + + # Send 102 blocks to node0. Block 102 will be rejected. + for i in range(101): + node0.send_message(msg_block(self.blocks[i])) + node0.sync_with_ping() # make sure the most recent block is synced + node0.send_message(msg_block(self.blocks[101])) + assert_equal(self.nodes[0].getblock(self.nodes[0].getbestblockhash())['height'], 101) + + # Send 3102 blocks to node1. All blocks will be accepted. + for i in range(2202): + node1.send_message(msg_block(self.blocks[i])) + node1.sync_with_ping() # make sure the most recent block is synced + assert_equal(self.nodes[1].getblock(self.nodes[1].getbestblockhash())['height'], 2202) + + # Send 102 blocks to node2. Block 102 will be rejected. + for i in range(101): + node2.send_message(msg_block(self.blocks[i])) + node2.sync_with_ping() # make sure the most recent block is synced + node2.send_message(msg_block(self.blocks[101])) + assert_equal(self.nodes[2].getblock(self.nodes[2].getbestblockhash())['height'], 101) + +if __name__ == '__main__': + SendHeadersTest().main() diff --git a/qa/rpc-tests/bip65-cltv-p2p.py b/qa/rpc-tests/bip65-cltv-p2p.py index e903b2fbf0..ea9c3d73ab 100755 --- a/qa/rpc-tests/bip65-cltv-p2p.py +++ b/qa/rpc-tests/bip65-cltv-p2p.py @@ -2,6 +2,19 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test BIP65 (CHECKLOCKTIMEVERIFY). + +Connect to a single node. +Mine 2 (version 3) blocks (save the coinbases for later). +Generate 98 more version 3 blocks, verify the node accepts. +Mine 749 version 4 blocks, verify the node accepts. +Check that the new CLTV rules are not enforced on the 750th version 4 block. +Check that the new CLTV rules are enforced on the 751st version 4 block. +Mine 199 new version blocks. +Mine 1 old-version block. +Mine 1 new version block. +Mine 1 old version block, see that the node rejects. +""" from test_framework.test_framework import ComparisonTestFramework from test_framework.util import * @@ -20,19 +33,6 @@ def cltv_invalidate(tx): tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_CHECKLOCKTIMEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig))) -''' -This test is meant to exercise BIP65 (CHECKLOCKTIMEVERIFY) -Connect to a single node. -Mine 2 (version 3) blocks (save the coinbases for later). -Generate 98 more version 3 blocks, verify the node accepts. -Mine 749 version 4 blocks, verify the node accepts. -Check that the new CLTV rules are not enforced on the 750th version 4 block. -Check that the new CLTV rules are enforced on the 751st version 4 block. -Mine 199 new version blocks. -Mine 1 old-version block. -Mine 1 new version block. -Mine 1 old version block, see that the node rejects. -''' class BIP65Test(ComparisonTestFramework): diff --git a/qa/rpc-tests/bip65-cltv.py b/qa/rpc-tests/bip65-cltv.py index baa77b92a0..c9d02a98f3 100755 --- a/qa/rpc-tests/bip65-cltv.py +++ b/qa/rpc-tests/bip65-cltv.py @@ -2,10 +2,7 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test the CHECKLOCKTIMEVERIFY (BIP65) soft-fork logic -# +"""Test the CHECKLOCKTIMEVERIFY (BIP65) soft-fork logic.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * diff --git a/qa/rpc-tests/bip68-112-113-p2p.py b/qa/rpc-tests/bip68-112-113-p2p.py index 55b3e2a04a..836267ea7f 100755 --- a/qa/rpc-tests/bip68-112-113-p2p.py +++ b/qa/rpc-tests/bip68-112-113-p2p.py @@ -1,19 +1,9 @@ #!/usr/bin/env python3 -# Copyright (c) 2015 The Bitcoin Core developers +# Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test activation of the first version bits soft fork. -from test_framework.test_framework import ComparisonTestFramework -from test_framework.util import * -from test_framework.mininode import ToHex, CTransaction, NetworkThread -from test_framework.blocktools import create_coinbase, create_block -from test_framework.comptool import TestInstance, TestManager -from test_framework.script import * -from io import BytesIO -import time - -''' -This test is meant to exercise activation of the first version bits soft fork This soft fork will activate the following BIPS: BIP 68 - nSequence relative lock times BIP 112 - CHECKSEQUENCEVERIFY @@ -51,7 +41,16 @@ bip112txs_vary_nSequence_9 - 16 txs with nSequence relative_locktimes of 9 evalu bip112txs_vary_OP_CSV - 16 txs with nSequence = 10 evaluated against varying {relative_locktimes of 10} OP_CSV OP_DROP bip112txs_vary_OP_CSV_9 - 16 txs with nSequence = 9 evaluated against varying {relative_locktimes of 10} OP_CSV OP_DROP bip112tx_special - test negative argument to OP_CSV -''' +""" + +from test_framework.test_framework import ComparisonTestFramework +from test_framework.util import * +from test_framework.mininode import ToHex, CTransaction, NetworkThread +from test_framework.blocktools import create_coinbase, create_block +from test_framework.comptool import TestInstance, TestManager +from test_framework.script import * +from io import BytesIO +import time base_relative_locktime = 10 seq_disable_flag = 1<<31 @@ -289,6 +288,7 @@ class BIP68_112_113Test(ComparisonTestFramework): # BIP113 test transaction will be modified before each use to put in appropriate block time bip113tx_v1 = self.create_transaction(self.nodes[0], bip113input, self.nodeaddress, Decimal("49.98")) bip113tx_v1.vin[0].nSequence = 0xFFFFFFFE + bip113tx_v1.nVersion = 1 bip113tx_v2 = self.create_transaction(self.nodes[0], bip113input, self.nodeaddress, Decimal("49.98")) bip113tx_v2.vin[0].nSequence = 0xFFFFFFFE bip113tx_v2.nVersion = 2 diff --git a/qa/rpc-tests/bip68-sequence.py b/qa/rpc-tests/bip68-sequence.py index a12bf10ebd..74ac393fe9 100755 --- a/qa/rpc-tests/bip68-sequence.py +++ b/qa/rpc-tests/bip68-sequence.py @@ -2,15 +2,10 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test BIP68 implementation -# +"""Test BIP68 implementation.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * -from test_framework.script import * -from test_framework.mininode import * from test_framework.blocktools import * SEQUENCE_LOCKTIME_DISABLE_FLAG = (1<<31) diff --git a/qa/rpc-tests/bip9-softforks.py b/qa/rpc-tests/bip9-softforks.py index c42ed44c25..70d4be3f69 100755 --- a/qa/rpc-tests/bip9-softforks.py +++ b/qa/rpc-tests/bip9-softforks.py @@ -1,21 +1,9 @@ #!/usr/bin/env python3 -# Copyright (c) 2015 The Bitcoin Core developers +# Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test BIP 9 soft forks. -from test_framework.blockstore import BlockStore -from test_framework.test_framework import ComparisonTestFramework -from test_framework.util import * -from test_framework.mininode import CTransaction, NetworkThread -from test_framework.blocktools import create_coinbase, create_block -from test_framework.comptool import TestInstance, TestManager -from test_framework.script import CScript, OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP -from io import BytesIO -import time -import itertools - -''' -This test is meant to exercise BIP forks Connect to a single node. regtest lock-in with 108/144 block signalling activation after a further 144 blocks @@ -26,8 +14,18 @@ mine 108 blocks signalling readiness and 36 blocks not signalling readiness (STA mine a further 143 blocks (LOCKED_IN) test that enforcement has not triggered (which triggers ACTIVE) test that enforcement has triggered -''' +""" +from test_framework.blockstore import BlockStore +from test_framework.test_framework import ComparisonTestFramework +from test_framework.util import * +from test_framework.mininode import CTransaction, NetworkThread +from test_framework.blocktools import create_coinbase, create_block +from test_framework.comptool import TestInstance, TestManager +from test_framework.script import CScript, OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP +from io import BytesIO +import time +import itertools class BIP9SoftForksTest(ComparisonTestFramework): @@ -225,21 +223,21 @@ class BIP9SoftForksTest(ComparisonTestFramework): return def csv_invalidate(self, tx): - '''Modify the signature in vin 0 of the tx to fail CSV + """Modify the signature in vin 0 of the tx to fail CSV Prepends -1 CSV DROP in the scriptSig itself. - ''' + """ tx.vin[0].scriptSig = CScript([OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP] + list(CScript(tx.vin[0].scriptSig))) def sequence_lock_invalidate(self, tx): - '''Modify the nSequence to make it fails once sequence lock rule is activated (high timespan) - ''' + """Modify the nSequence to make it fails once sequence lock rule is + activated (high timespan). + """ tx.vin[0].nSequence = 0x00FFFFFF tx.nLockTime = 0 def mtp_invalidate(self, tx): - '''Modify the nLockTime to make it fails once MTP rule is activated - ''' + """Modify the nLockTime to make it fails once MTP rule is activated.""" # Disable Sequence lock, Activate nLockTime tx.vin[0].nSequence = 0x90FFFFFF tx.nLockTime = self.last_block_time diff --git a/qa/rpc-tests/bipdersig-p2p.py b/qa/rpc-tests/bipdersig-p2p.py index 3bad5af5e6..b82ef89395 100755 --- a/qa/rpc-tests/bipdersig-p2p.py +++ b/qa/rpc-tests/bipdersig-p2p.py @@ -2,6 +2,19 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test BIP66 (DER SIG). + +Connect to a single node. +Mine 2 (version 2) blocks (save the coinbases for later). +Generate 98 more version 2 blocks, verify the node accepts. +Mine 749 version 3 blocks, verify the node accepts. +Check that the new DERSIG rules are not enforced on the 750th version 3 block. +Check that the new DERSIG rules are enforced on the 751st version 3 block. +Mine 199 new version blocks. +Mine 1 old-version block. +Mine 1 new version block. +Mine 1 old version block, see that the node rejects. +""" from test_framework.test_framework import ComparisonTestFramework from test_framework.util import * @@ -15,10 +28,10 @@ import time # A canonical signature consists of: # <30> <total len> <02> <len R> <R> <02> <len S> <S> <hashtype> def unDERify(tx): - ''' + """ Make the signature in vin 0 of a tx non-DER-compliant, by adding padding after the S-value. - ''' + """ scriptSig = CScript(tx.vin[0].scriptSig) newscript = [] for i in scriptSig: @@ -27,20 +40,6 @@ def unDERify(tx): else: newscript.append(i) tx.vin[0].scriptSig = CScript(newscript) - -''' -This test is meant to exercise BIP66 (DER SIG). -Connect to a single node. -Mine 2 (version 2) blocks (save the coinbases for later). -Generate 98 more version 2 blocks, verify the node accepts. -Mine 749 version 3 blocks, verify the node accepts. -Check that the new DERSIG rules are not enforced on the 750th version 3 block. -Check that the new DERSIG rules are enforced on the 751st version 3 block. -Mine 199 new version blocks. -Mine 1 old-version block. -Mine 1 new version block. -Mine 1 old version block, see that the node rejects. -''' class BIP66Test(ComparisonTestFramework): diff --git a/qa/rpc-tests/bipdersig.py b/qa/rpc-tests/bipdersig.py index 17c2ced79a..fa54bc2749 100755 --- a/qa/rpc-tests/bipdersig.py +++ b/qa/rpc-tests/bipdersig.py @@ -2,10 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test the BIP66 changeover logic -# +"""Test the BIP66 changeover logic.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * diff --git a/qa/rpc-tests/blockchain.py b/qa/rpc-tests/blockchain.py index 410b85d15e..67b5826840 100755 --- a/qa/rpc-tests/blockchain.py +++ b/qa/rpc-tests/blockchain.py @@ -2,11 +2,14 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test RPCs related to blockchainstate. -# -# Test RPC calls related to blockchain state. Tests correspond to code in -# rpc/blockchain.cpp. -# +Test the following RPCs: + - gettxoutsetinfo + - verifychain + +Tests correspond to code in rpc/blockchain.cpp. +""" from decimal import Decimal @@ -23,13 +26,6 @@ from test_framework.util import ( class BlockchainTest(BitcoinTestFramework): - """ - Test blockchain-related RPC calls: - - - gettxoutsetinfo - - verifychain - - """ def __init__(self): super().__init__() diff --git a/qa/rpc-tests/bumpfee.py b/qa/rpc-tests/bumpfee.py new file mode 100755 index 0000000000..cc897a32c7 --- /dev/null +++ b/qa/rpc-tests/bumpfee.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python3 +# Copyright (c) 2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the bumpfee RPC.""" + +from segwit import send_to_witness +from test_framework.test_framework import BitcoinTestFramework +from test_framework import blocktools +from test_framework.mininode import CTransaction +from test_framework.util import * + +import io + +# Sequence number that is BIP 125 opt-in and BIP 68-compliant +BIP125_SEQUENCE_NUMBER = 0xfffffffd + +WALLET_PASSPHRASE = "test" +WALLET_PASSPHRASE_TIMEOUT = 3600 + + +class BumpFeeTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 2 + self.setup_clean_chain = True + + def setup_network(self, split=False): + extra_args = [["-debug", "-prematurewitness", "-walletprematurewitness", "-walletrbf={}".format(i)] + for i in range(self.num_nodes)] + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args) + + # Encrypt wallet for test_locked_wallet_fails test + self.nodes[1].encryptwallet(WALLET_PASSPHRASE) + bitcoind_processes[1].wait() + self.nodes[1] = start_node(1, self.options.tmpdir, extra_args[1]) + self.nodes[1].walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) + + connect_nodes_bi(self.nodes, 0, 1) + self.is_network_split = False + self.sync_all() + + def run_test(self): + peer_node, rbf_node = self.nodes + rbf_node_address = rbf_node.getnewaddress() + + # fund rbf node with 10 coins of 0.001 btc (100,000 satoshis) + print("Mining blocks...") + peer_node.generate(110) + self.sync_all() + for i in range(25): + peer_node.sendtoaddress(rbf_node_address, 0.001) + self.sync_all() + peer_node.generate(1) + self.sync_all() + assert_equal(rbf_node.getbalance(), Decimal("0.025")) + + print("Running tests") + dest_address = peer_node.getnewaddress() + test_small_output_fails(rbf_node, dest_address) + test_dust_to_fee(rbf_node, dest_address) + test_simple_bumpfee_succeeds(rbf_node, peer_node, dest_address) + test_segwit_bumpfee_succeeds(rbf_node, dest_address) + test_nonrbf_bumpfee_fails(peer_node, dest_address) + test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address) + test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address) + test_settxfee(rbf_node, dest_address) + test_rebumping(rbf_node, dest_address) + test_rebumping_not_replaceable(rbf_node, dest_address) + test_unconfirmed_not_spendable(rbf_node, rbf_node_address) + test_bumpfee_metadata(rbf_node, dest_address) + test_locked_wallet_fails(rbf_node, dest_address) + print("Success") + + +def test_simple_bumpfee_succeeds(rbf_node, peer_node, dest_address): + rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.00090000}) + rbftx = rbf_node.gettransaction(rbfid) + sync_mempools((rbf_node, peer_node)) + assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool() + bumped_tx = rbf_node.bumpfee(rbfid) + assert bumped_tx["fee"] - abs(rbftx["fee"]) > 0 + # check that bumped_tx propogates, original tx was evicted and has a wallet conflict + sync_mempools((rbf_node, peer_node)) + assert bumped_tx["txid"] in rbf_node.getrawmempool() + assert bumped_tx["txid"] in peer_node.getrawmempool() + assert rbfid not in rbf_node.getrawmempool() + assert rbfid not in peer_node.getrawmempool() + oldwtx = rbf_node.gettransaction(rbfid) + assert len(oldwtx["walletconflicts"]) > 0 + # check wallet transaction replaces and replaced_by values + bumpedwtx = rbf_node.gettransaction(bumped_tx["txid"]) + assert_equal(oldwtx["replaced_by_txid"], bumped_tx["txid"]) + assert_equal(bumpedwtx["replaces_txid"], rbfid) + + +def test_segwit_bumpfee_succeeds(rbf_node, dest_address): + # Create a transaction with segwit output, then create an RBF transaction + # which spends it, and make sure bumpfee can be called on it. + + segwit_in = next(u for u in rbf_node.listunspent() if u["amount"] == Decimal("0.001")) + segwit_out = rbf_node.validateaddress(rbf_node.getnewaddress()) + rbf_node.addwitnessaddress(segwit_out["address"]) + segwitid = send_to_witness( + version=0, + node=rbf_node, + utxo=segwit_in, + pubkey=segwit_out["pubkey"], + encode_p2sh=False, + amount=Decimal("0.0009"), + sign=True) + + rbfraw = rbf_node.createrawtransaction([{ + 'txid': segwitid, + 'vout': 0, + "sequence": BIP125_SEQUENCE_NUMBER + }], {dest_address: Decimal("0.0005"), + get_change_address(rbf_node): Decimal("0.0003")}) + rbfsigned = rbf_node.signrawtransaction(rbfraw) + rbfid = rbf_node.sendrawtransaction(rbfsigned["hex"]) + assert rbfid in rbf_node.getrawmempool() + + bumped_tx = rbf_node.bumpfee(rbfid) + assert bumped_tx["txid"] in rbf_node.getrawmempool() + assert rbfid not in rbf_node.getrawmempool() + + +def test_nonrbf_bumpfee_fails(peer_node, dest_address): + # cannot replace a non RBF transaction (from node which did not enable RBF) + not_rbfid = create_fund_sign_send(peer_node, {dest_address: 0.00090000}) + assert_raises_message(JSONRPCException, "not BIP 125 replaceable", peer_node.bumpfee, not_rbfid) + + +def test_notmine_bumpfee_fails(rbf_node, peer_node, dest_address): + # cannot bump fee unless the tx has only inputs that we own. + # here, the rbftx has a peer_node coin and then adds a rbf_node input + # Note that this test depends upon the RPC code checking input ownership prior to change outputs + # (since it can't use fundrawtransaction, it lacks a proper change output) + utxos = [node.listunspent()[-1] for node in (rbf_node, peer_node)] + inputs = [{ + "txid": utxo["txid"], + "vout": utxo["vout"], + "address": utxo["address"], + "sequence": BIP125_SEQUENCE_NUMBER + } for utxo in utxos] + output_val = sum(utxo["amount"] for utxo in utxos) - Decimal("0.001") + rawtx = rbf_node.createrawtransaction(inputs, {dest_address: output_val}) + signedtx = rbf_node.signrawtransaction(rawtx) + signedtx = peer_node.signrawtransaction(signedtx["hex"]) + rbfid = rbf_node.sendrawtransaction(signedtx["hex"]) + assert_raises_message(JSONRPCException, "Transaction contains inputs that don't belong to this wallet", + rbf_node.bumpfee, rbfid) + + +def test_bumpfee_with_descendant_fails(rbf_node, rbf_node_address, dest_address): + # cannot bump fee if the transaction has a descendant + # parent is send-to-self, so we don't have to check which output is change when creating the child tx + parent_id = create_fund_sign_send(rbf_node, {rbf_node_address: 0.00050000}) + tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000}) + tx = rbf_node.signrawtransaction(tx) + txid = rbf_node.sendrawtransaction(tx["hex"]) + assert_raises_message(JSONRPCException, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id) + + +def test_small_output_fails(rbf_node, dest_address): + # cannot bump fee with a too-small output + rbfid = spend_one_input(rbf_node, + Decimal("0.00100000"), + {dest_address: 0.00080000, + get_change_address(rbf_node): Decimal("0.00010000")}) + rbf_node.bumpfee(rbfid, {"totalFee": 20000}) + + rbfid = spend_one_input(rbf_node, + Decimal("0.00100000"), + {dest_address: 0.00080000, + get_change_address(rbf_node): Decimal("0.00010000")}) + assert_raises_message(JSONRPCException, "Change output is too small", rbf_node.bumpfee, rbfid, {"totalFee": 20001}) + + +def test_dust_to_fee(rbf_node, dest_address): + # check that if output is reduced to dust, it will be converted to fee + # the bumped tx sets fee=9900, but it converts to 10,000 + rbfid = spend_one_input(rbf_node, + Decimal("0.00100000"), + {dest_address: 0.00080000, + get_change_address(rbf_node): Decimal("0.00010000")}) + fulltx = rbf_node.getrawtransaction(rbfid, 1) + bumped_tx = rbf_node.bumpfee(rbfid, {"totalFee": 19900}) + full_bumped_tx = rbf_node.getrawtransaction(bumped_tx["txid"], 1) + assert_equal(bumped_tx["fee"], Decimal("0.00020000")) + assert_equal(len(fulltx["vout"]), 2) + assert_equal(len(full_bumped_tx["vout"]), 1) #change output is eliminated + + +def test_settxfee(rbf_node, dest_address): + # check that bumpfee reacts correctly to the use of settxfee (paytxfee) + # increase feerate by 2.5x, test that fee increased at least 2x + rbf_node.settxfee(Decimal("0.00001000")) + rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.00090000}) + rbftx = rbf_node.gettransaction(rbfid) + rbf_node.settxfee(Decimal("0.00002500")) + bumped_tx = rbf_node.bumpfee(rbfid) + assert bumped_tx["fee"] > 2 * abs(rbftx["fee"]) + rbf_node.settxfee(Decimal("0.00000000")) # unset paytxfee + + +def test_rebumping(rbf_node, dest_address): + # check that re-bumping the original tx fails, but bumping the bumper succeeds + rbf_node.settxfee(Decimal("0.00001000")) + rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.00090000}) + bumped = rbf_node.bumpfee(rbfid, {"totalFee": 1000}) + assert_raises_message(JSONRPCException, "already bumped", rbf_node.bumpfee, rbfid, {"totalFee": 2000}) + rbf_node.bumpfee(bumped["txid"], {"totalFee": 2000}) + + +def test_rebumping_not_replaceable(rbf_node, dest_address): + # check that re-bumping a non-replaceable bump tx fails + rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.00090000}) + bumped = rbf_node.bumpfee(rbfid, {"totalFee": 10000, "replaceable": False}) + assert_raises_message(JSONRPCException, "Transaction is not BIP 125 replaceable", rbf_node.bumpfee, bumped["txid"], + {"totalFee": 20000}) + + +def test_unconfirmed_not_spendable(rbf_node, rbf_node_address): + # check that unconfirmed outputs from bumped transactions are not spendable + rbfid = create_fund_sign_send(rbf_node, {rbf_node_address: 0.00090000}) + rbftx = rbf_node.gettransaction(rbfid)["hex"] + assert rbfid in rbf_node.getrawmempool() + bumpid = rbf_node.bumpfee(rbfid)["txid"] + assert bumpid in rbf_node.getrawmempool() + assert rbfid not in rbf_node.getrawmempool() + + # check that outputs from the bump transaction are not spendable + # due to the replaces_txid check in CWallet::AvailableCoins + assert_equal([t for t in rbf_node.listunspent(minconf=0, include_unsafe=False) if t["txid"] == bumpid], []) + + # submit a block with the rbf tx to clear the bump tx out of the mempool, + # then call abandon to make sure the wallet doesn't attempt to resubmit the + # bump tx, then invalidate the block so the rbf tx will be put back in the + # mempool. this makes it possible to check whether the rbf tx outputs are + # spendable before the rbf tx is confirmed. + block = submit_block_with_tx(rbf_node, rbftx) + rbf_node.abandontransaction(bumpid) + rbf_node.invalidateblock(block.hash) + assert bumpid not in rbf_node.getrawmempool() + assert rbfid in rbf_node.getrawmempool() + + # check that outputs from the rbf tx are not spendable before the + # transaction is confirmed, due to the replaced_by_txid check in + # CWallet::AvailableCoins + assert_equal([t for t in rbf_node.listunspent(minconf=0, include_unsafe=False) if t["txid"] == rbfid], []) + + # check that the main output from the rbf tx is spendable after confirmed + rbf_node.generate(1) + assert_equal( + sum(1 for t in rbf_node.listunspent(minconf=0, include_unsafe=False) + if t["txid"] == rbfid and t["address"] == rbf_node_address and t["spendable"]), 1) + + +def test_bumpfee_metadata(rbf_node, dest_address): + rbfid = rbf_node.sendtoaddress(dest_address, 0.00090000, "comment value", "to value") + bumped_tx = rbf_node.bumpfee(rbfid) + bumped_wtx = rbf_node.gettransaction(bumped_tx["txid"]) + assert_equal(bumped_wtx["comment"], "comment value") + assert_equal(bumped_wtx["to"], "to value") + + +def test_locked_wallet_fails(rbf_node, dest_address): + rbfid = create_fund_sign_send(rbf_node, {dest_address: 0.00090000}) + rbf_node.walletlock() + assert_raises_message(JSONRPCException, "Please enter the wallet passphrase with walletpassphrase first.", + rbf_node.bumpfee, rbfid) + + +def create_fund_sign_send(node, outputs): + rawtx = node.createrawtransaction([], outputs) + fundtx = node.fundrawtransaction(rawtx) + signedtx = node.signrawtransaction(fundtx["hex"]) + txid = node.sendrawtransaction(signedtx["hex"]) + return txid + + +def spend_one_input(node, input_amount, outputs): + input = dict(sequence=BIP125_SEQUENCE_NUMBER, **next(u for u in node.listunspent() if u["amount"] == input_amount)) + rawtx = node.createrawtransaction([input], outputs) + signedtx = node.signrawtransaction(rawtx) + txid = node.sendrawtransaction(signedtx["hex"]) + return txid + + +def get_change_address(node): + """Get a wallet change address. + + There is no wallet RPC to access unused change addresses, so this creates a + dummy transaction, calls fundrawtransaction to give add an input and change + output, then returns the change address.""" + dest_address = node.getnewaddress() + dest_amount = Decimal("0.00012345") + rawtx = node.createrawtransaction([], {dest_address: dest_amount}) + fundtx = node.fundrawtransaction(rawtx) + info = node.decoderawtransaction(fundtx["hex"]) + return next(address for out in info["vout"] + if out["value"] != dest_amount for address in out["scriptPubKey"]["addresses"]) + + +def submit_block_with_tx(node, tx): + ctx = CTransaction() + ctx.deserialize(io.BytesIO(hex_str_to_bytes(tx))) + + tip = node.getbestblockhash() + height = node.getblockcount() + 1 + block_time = node.getblockheader(tip)["mediantime"] + 1 + block = blocktools.create_block(int(tip, 16), blocktools.create_coinbase(height), block_time) + block.vtx.append(ctx) + block.rehash() + block.hashMerkleRoot = block.calc_merkle_root() + block.solve() + error = node.submitblock(bytes_to_hex_str(block.serialize(True))) + if error is not None: + raise Exception(error) + return block + + +if __name__ == "__main__": + BumpFeeTest().main() diff --git a/qa/rpc-tests/create_cache.py b/qa/rpc-tests/create_cache.py index 1ace6310d0..13eac92bbc 100755 --- a/qa/rpc-tests/create_cache.py +++ b/qa/rpc-tests/create_cache.py @@ -2,11 +2,12 @@ # Copyright (c) 2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Create a blockchain cache. -# -# Helper script to create the cache -# (see BitcoinTestFramework.setup_chain) -# +Creating a cache of the blockchain speeds up test execution when running +multiple qa tests. This helper script is executed by rpc-tests when multiple +tests are being run in parallel. +""" from test_framework.test_framework import BitcoinTestFramework diff --git a/qa/rpc-tests/decodescript.py b/qa/rpc-tests/decodescript.py index 24768c2655..5555e96c44 100755 --- a/qa/rpc-tests/decodescript.py +++ b/qa/rpc-tests/decodescript.py @@ -2,6 +2,7 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test decoding scripts via decodescript RPC command.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -9,7 +10,6 @@ from test_framework.mininode import * from io import BytesIO class DecodeScriptTest(BitcoinTestFramework): - """Tests decoding scripts via RPC command "decodescript".""" def __init__(self): super().__init__() @@ -111,7 +111,7 @@ class DecodeScriptTest(BitcoinTestFramework): assert_equal('OP_IF ' + public_key + ' OP_CHECKSIGVERIFY OP_ELSE 500000 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_ENDIF ' + public_key + ' OP_CHECKSIG', rpc_result['asm']) def decoderawtransaction_asm_sighashtype(self): - """Tests decoding scripts via RPC command "decoderawtransaction". + """Test decoding scripts via RPC command "decoderawtransaction". This test is in with the "decodescript" tests because they are testing the same "asm" script decodes. """ diff --git a/qa/rpc-tests/disablewallet.py b/qa/rpc-tests/disablewallet.py index 36c147edad..ff7121659b 100755 --- a/qa/rpc-tests/disablewallet.py +++ b/qa/rpc-tests/disablewallet.py @@ -2,10 +2,11 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test a node with the -disablewallet option. -# -# Exercise API with -disablewallet. -# +- Test that validateaddress RPC works when running with -disablewallet +- Test that it is not possible to mine to an invalid address. +""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -24,7 +25,6 @@ class DisableWalletTest (BitcoinTestFramework): self.sync_all() def run_test (self): - # Check regression: https://github.com/bitcoin/bitcoin/issues/6963#issuecomment-154548880 x = self.nodes[0].validateaddress('3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy') assert(x['isvalid'] == False) x = self.nodes[0].validateaddress('mneYUmWYsuk7kySiURxCi3AGxrAqZxLgPZ') diff --git a/qa/rpc-tests/forknotify.py b/qa/rpc-tests/forknotify.py index a1901aedab..c2724ba5df 100755 --- a/qa/rpc-tests/forknotify.py +++ b/qa/rpc-tests/forknotify.py @@ -2,10 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test -alertnotify -# +"""Test the -alertnotify option.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -22,7 +19,7 @@ class ForkNotifyTest(BitcoinTestFramework): def setup_network(self): self.nodes = [] self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt") - with open(self.alert_filename, 'w', encoding='utf8') as f: + with open(self.alert_filename, 'w', encoding='utf8'): pass # Just open then close to create zero-length file self.nodes.append(start_node(0, self.options.tmpdir, ["-blockversion=2", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""])) diff --git a/qa/rpc-tests/fundrawtransaction.py b/qa/rpc-tests/fundrawtransaction.py index 8c45578fcf..7892e85e22 100755 --- a/qa/rpc-tests/fundrawtransaction.py +++ b/qa/rpc-tests/fundrawtransaction.py @@ -2,6 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the fundrawtransaction RPC.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -226,7 +227,7 @@ class RawTransactionsTest(BitcoinTestFramework): assert(False) rawtxfund = self.nodes[2].fundrawtransaction(rawtx, {'changeAddress': change, 'changePosition': 0}) dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex']) - out = dec_tx['vout'][0]; + out = dec_tx['vout'][0] assert_equal(change, out['scriptPubKey']['addresses'][0]) @@ -469,7 +470,9 @@ class RawTransactionsTest(BitcoinTestFramework): # locked wallet test self.nodes[1].encryptwallet("test") self.nodes.pop(1) - stop_nodes(self.nodes) + stop_node(self.nodes[0], 0) + stop_node(self.nodes[1], 2) + stop_node(self.nodes[2], 3) self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) # This test is not meant to test fee estimation and we'd like @@ -484,6 +487,23 @@ class RawTransactionsTest(BitcoinTestFramework): self.is_network_split=False self.sync_all() + # drain the keypool + self.nodes[1].getnewaddress() + inputs = [] + outputs = {self.nodes[0].getnewaddress():1.1} + rawTx = self.nodes[1].createrawtransaction(inputs, outputs) + # fund a transaction that requires a new key for the change output + # creating the key must be impossible because the wallet is locked + try: + fundedTx = self.nodes[1].fundrawtransaction(rawTx) + raise AssertionError("Wallet unlocked without passphrase") + except JSONRPCException as e: + assert('Keypool ran out' in e.error['message']) + + #refill the keypool + self.nodes[1].walletpassphrase("test", 100) + self.nodes[1].walletlock() + try: self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.2) raise AssertionError("Wallet unlocked without passphrase") @@ -498,7 +518,7 @@ class RawTransactionsTest(BitcoinTestFramework): fundedTx = self.nodes[1].fundrawtransaction(rawTx) #now we need to unlock - self.nodes[1].walletpassphrase("test", 100) + self.nodes[1].walletpassphrase("test", 600) signedTx = self.nodes[1].signrawtransaction(fundedTx['hex']) txId = self.nodes[1].sendrawtransaction(signedTx['hex']) self.nodes[1].generate(1) @@ -634,7 +654,7 @@ class RawTransactionsTest(BitcoinTestFramework): assert_equal(len(self.nodes[3].listunspent(1)), 1) inputs = [] - outputs = {self.nodes[2].getnewaddress() : 1} + outputs = {self.nodes[3].getnewaddress() : 1} rawtx = self.nodes[3].createrawtransaction(inputs, outputs) result = self.nodes[3].fundrawtransaction(rawtx) # uses min_relay_tx_fee (set by settxfee) result2 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee}) @@ -643,5 +663,101 @@ class RawTransactionsTest(BitcoinTestFramework): assert_fee_amount(result2['fee'], count_bytes(result2['hex']), 2 * result_fee_rate) assert_fee_amount(result3['fee'], count_bytes(result3['hex']), 10 * result_fee_rate) + ############################# + # Test address reuse option # + ############################# + + result3 = self.nodes[3].fundrawtransaction(rawtx, {"reserveChangeKey": False}) + res_dec = self.nodes[0].decoderawtransaction(result3["hex"]) + changeaddress = "" + for out in res_dec['vout']: + if out['value'] > 1.0: + changeaddress += out['scriptPubKey']['addresses'][0] + assert(changeaddress != "") + nextaddr = self.nodes[3].getnewaddress() + # frt should not have removed the key from the keypool + assert(changeaddress == nextaddr) + + result3 = self.nodes[3].fundrawtransaction(rawtx) + res_dec = self.nodes[0].decoderawtransaction(result3["hex"]) + changeaddress = "" + for out in res_dec['vout']: + if out['value'] > 1.0: + changeaddress += out['scriptPubKey']['addresses'][0] + assert(changeaddress != "") + nextaddr = self.nodes[3].getnewaddress() + # Now the change address key should be removed from the keypool + assert(changeaddress != nextaddr) + + ###################################### + # Test subtractFeeFromOutputs option # + ###################################### + + # Make sure there is exactly one input so coin selection can't skew the result + assert_equal(len(self.nodes[3].listunspent(1)), 1) + + inputs = [] + outputs = {self.nodes[2].getnewaddress(): 1} + rawtx = self.nodes[3].createrawtransaction(inputs, outputs) + + result = [self.nodes[3].fundrawtransaction(rawtx), # uses min_relay_tx_fee (set by settxfee) + self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": []}), # empty subtraction list + self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": [0]}), # uses min_relay_tx_fee (set by settxfee) + self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee}), + self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 2*min_relay_tx_fee, "subtractFeeFromOutputs": [0]})] + + dec_tx = [self.nodes[3].decoderawtransaction(tx['hex']) for tx in result] + output = [d['vout'][1 - r['changepos']]['value'] for d, r in zip(dec_tx, result)] + change = [d['vout'][r['changepos']]['value'] for d, r in zip(dec_tx, result)] + + assert_equal(result[0]['fee'], result[1]['fee'], result[2]['fee']) + assert_equal(result[3]['fee'], result[4]['fee']) + assert_equal(change[0], change[1]) + assert_equal(output[0], output[1]) + assert_equal(output[0], output[2] + result[2]['fee']) + assert_equal(change[0] + result[0]['fee'], change[2]) + assert_equal(output[3], output[4] + result[4]['fee']) + assert_equal(change[3] + result[3]['fee'], change[4]) + + inputs = [] + outputs = {self.nodes[2].getnewaddress(): value for value in (1.0, 1.1, 1.2, 1.3)} + keys = list(outputs.keys()) + rawtx = self.nodes[3].createrawtransaction(inputs, outputs) + + result = [self.nodes[3].fundrawtransaction(rawtx), + # split the fee between outputs 0, 2, and 3, but not output 1 + self.nodes[3].fundrawtransaction(rawtx, {"subtractFeeFromOutputs": [0, 2, 3]})] + + dec_tx = [self.nodes[3].decoderawtransaction(result[0]['hex']), + self.nodes[3].decoderawtransaction(result[1]['hex'])] + + # Nested list of non-change output amounts for each transaction + output = [[out['value'] for i, out in enumerate(d['vout']) if i != r['changepos']] + for d, r in zip(dec_tx, result)] + + # List of differences in output amounts between normal and subtractFee transactions + share = [o0 - o1 for o0, o1 in zip(output[0], output[1])] + + # output 1 is the same in both transactions + assert_equal(share[1], 0) + + # the other 3 outputs are smaller as a result of subtractFeeFromOutputs + assert_greater_than(share[0], 0) + assert_greater_than(share[2], 0) + assert_greater_than(share[3], 0) + + # outputs 2 and 3 take the same share of the fee + assert_equal(share[2], share[3]) + + # output 0 takes at least as much share of the fee, and no more than 2 satoshis more, than outputs 2 and 3 + assert_greater_than_or_equal(share[0], share[2]) + assert_greater_than_or_equal(share[2] + Decimal(2e-8), share[0]) + + # the fee is the same in both transactions + assert_equal(result[0]['fee'], result[1]['fee']) + + # the total subtracted from the outputs is equal to the fee + assert_equal(share[0] + share[2] + share[3], result[0]['fee']) + if __name__ == '__main__': RawTransactionsTest().main() diff --git a/qa/rpc-tests/getblocktemplate_longpoll.py b/qa/rpc-tests/getblocktemplate_longpoll.py index 3cddf4046a..b73fea0695 100755 --- a/qa/rpc-tests/getblocktemplate_longpoll.py +++ b/qa/rpc-tests/getblocktemplate_longpoll.py @@ -2,6 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test longpolling with getblocktemplate.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -22,10 +23,6 @@ class LongpollThread(threading.Thread): self.node.getblocktemplate({'longpollid':self.longpollid}) class GetBlockTemplateLPTest(BitcoinTestFramework): - ''' - Test longpolling with getblocktemplate. - ''' - def __init__(self): super().__init__() self.num_nodes = 4 diff --git a/qa/rpc-tests/getblocktemplate_proposals.py b/qa/rpc-tests/getblocktemplate_proposals.py index 7a4f8f8fdc..3b17dfcfb5 100755 --- a/qa/rpc-tests/getblocktemplate_proposals.py +++ b/qa/rpc-tests/getblocktemplate_proposals.py @@ -2,6 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test block proposals with getblocktemplate.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -66,9 +67,6 @@ def assert_template(node, tmpl, txlist, expect): raise AssertionError('unexpected: %s' % (rsp,)) class GetBlockTemplateProposalTest(BitcoinTestFramework): - ''' - Test block proposals with getblocktemplate. - ''' def __init__(self): super().__init__() diff --git a/qa/rpc-tests/getchaintips.py b/qa/rpc-tests/getchaintips.py index 1c66b8c289..14222334a6 100755 --- a/qa/rpc-tests/getchaintips.py +++ b/qa/rpc-tests/getchaintips.py @@ -2,10 +2,13 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the getchaintips RPC. -# Exercise the getchaintips API. We introduce a network split, work -# on chains of different lengths, and join the network together again. -# This gives us two tips, verify that it works. +- introduce a network split +- work on chains of different lengths +- join the network together again +- verify that getchaintips now returns two chain tips. +""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal diff --git a/qa/rpc-tests/httpbasics.py b/qa/rpc-tests/httpbasics.py index 10bc927e1a..8f35f0ab87 100755 --- a/qa/rpc-tests/httpbasics.py +++ b/qa/rpc-tests/httpbasics.py @@ -2,10 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test rpc http basics -# +"""Test the RPC HTTP basics.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * diff --git a/qa/rpc-tests/import-rescan.py b/qa/rpc-tests/import-rescan.py new file mode 100755 index 0000000000..7ca61824c9 --- /dev/null +++ b/qa/rpc-tests/import-rescan.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python3 +# Copyright (c) 2014-2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test wallet import RPCs. + +Test rescan behavior of importaddress, importpubkey, importprivkey, and +importmulti RPCs with different types of keys and rescan options. + +In the first part of the test, node 1 creates an address for each type of +import RPC call and node 0 sends BTC to it. Then other nodes import the +addresses, and the test makes listtransactions and getbalance calls to confirm +that the importing node either did or did not execute rescans picking up the +send transactions. + +In the second part of the test, node 0 sends more BTC to each address, and the +test makes more listtransactions and getbalance calls to confirm that the +importing nodes pick up the new transactions regardless of whether rescans +happened previously. +""" + +from test_framework.authproxy import JSONRPCException +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import (start_nodes, connect_nodes, sync_blocks, assert_equal, set_node_times) +from decimal import Decimal + +import collections +import enum +import itertools + +Call = enum.Enum("Call", "single multi") +Data = enum.Enum("Data", "address pub priv") +Rescan = enum.Enum("Rescan", "no yes late_timestamp") + + +class Variant(collections.namedtuple("Variant", "call data rescan prune")): + """Helper for importing one key and verifying scanned transactions.""" + + def do_import(self, timestamp): + """Call one key import RPC.""" + + if self.call == Call.single: + if self.data == Data.address: + response, error = try_rpc(self.node.importaddress, self.address["address"], self.label, + self.rescan == Rescan.yes) + elif self.data == Data.pub: + response, error = try_rpc(self.node.importpubkey, self.address["pubkey"], self.label, + self.rescan == Rescan.yes) + elif self.data == Data.priv: + response, error = try_rpc(self.node.importprivkey, self.key, self.label, self.rescan == Rescan.yes) + assert_equal(response, None) + assert_equal(error, {'message': 'Rescan is disabled in pruned mode', + 'code': -4} if self.expect_disabled else None) + elif self.call == Call.multi: + response = self.node.importmulti([{ + "scriptPubKey": { + "address": self.address["address"] + }, + "timestamp": timestamp + RESCAN_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.""" + + 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) + + if txid is not None: + tx, = [tx for tx in txs if tx["txid"] == txid] + assert_equal(tx["account"], 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) + # 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 + # inputs will not be from watchonly keys (important because + # involvesWatchonly will be true if either the transaction output + # or inputs are watchonly). + if self.data != Data.priv: + assert_equal(tx["involvesWatchonly"], True) + else: + assert_equal("involvesWatchonly" not in tx, True) + + +# List of Variants for each way a key or address could be imported. +IMPORT_VARIANTS = [Variant(*variants) for variants in itertools.product(Call, Data, Rescan, (False, True))] + +# List of nodes to import keys to. Half the nodes will have pruning disabled, +# half will have it enabled. Different nodes will be used for imports that are +# expected to cause rescans, and imports that are not expected to cause +# rescans, in order to prevent rescans during later imports picking up +# transactions associated with earlier imports. This makes it easier to keep +# track of expected balances and transactions. +ImportNode = collections.namedtuple("ImportNode", "prune rescan") +IMPORT_NODES = [ImportNode(*fields) for fields in itertools.product((False, True), repeat=2)] + +# Rescans start at the earliest block up to 2 hours before the key timestamp. +RESCAN_WINDOW = 2 * 60 * 60 + + +class ImportRescanTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 2 + len(IMPORT_NODES) + + def setup_network(self): + extra_args = [["-debug=1"] for _ in range(self.num_nodes)] + for i, import_node in enumerate(IMPORT_NODES, 2): + if import_node.prune: + extra_args[i] += ["-prune=1"] + + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args) + for i in range(1, self.num_nodes): + connect_nodes(self.nodes[i], 0) + + def run_test(self): + # Create one transaction on node 0 with a unique amount and label 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].validateaddress(self.nodes[1].getnewaddress(variant.label)) + 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) + + # Generate a block containing the initial transactions, then another + # block further in the future (past the rescan window). + self.nodes[0].generate(1) + assert_equal(self.nodes[0].getrawmempool(), []) + timestamp = self.nodes[0].getblockheader(self.nodes[0].getbestblockhash())["time"] + set_node_times(self.nodes, timestamp + RESCAN_WINDOW + 1) + self.nodes[0].generate(1) + sync_blocks(self.nodes) + + # For each variation of wallet key import, invoke the import RPC and + # check the results from getbalance and listtransactions. + for variant in IMPORT_VARIANTS: + variant.expect_disabled = variant.rescan == Rescan.yes and variant.prune and variant.call == Call.single + expect_rescan = variant.rescan == Rescan.yes and not variant.expect_disabled + variant.node = self.nodes[2 + IMPORT_NODES.index(ImportNode(variant.prune, expect_rescan))] + variant.do_import(timestamp) + if expect_rescan: + variant.expected_balance = variant.initial_amount + variant.expected_txs = 1 + variant.check(variant.initial_txid, variant.initial_amount, 2) + else: + variant.expected_balance = 0 + variant.expected_txs = 0 + variant.check() + + # Create new transactions sending to each address. + fee = self.nodes[0].getnetworkinfo()["relayfee"] + for i, variant in enumerate(IMPORT_VARIANTS): + variant.sent_amount = 10 - (2 * i + 1) / 8.0 + variant.sent_txid = self.nodes[0].sendtoaddress(variant.address["address"], variant.sent_amount) + + # Generate a block containing the new transactions. + self.nodes[0].generate(1) + assert_equal(self.nodes[0].getrawmempool(), []) + sync_blocks(self.nodes) + + # Check the latest results from getbalance and listtransactions. + for variant in IMPORT_VARIANTS: + if not variant.expect_disabled: + variant.expected_balance += variant.sent_amount + variant.expected_txs += 1 + variant.check(variant.sent_txid, variant.sent_amount, 1) + else: + variant.check() + + +def try_rpc(func, *args, **kwargs): + try: + return func(*args, **kwargs), None + except JSONRPCException as e: + return None, e.error + + +if __name__ == "__main__": + ImportRescanTest().main() diff --git a/qa/rpc-tests/importmulti.py b/qa/rpc-tests/importmulti.py index 5c536f2f49..015336effe 100755 --- a/qa/rpc-tests/importmulti.py +++ b/qa/rpc-tests/importmulti.py @@ -2,7 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - +"""Test the importmulti RPC.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -20,6 +20,7 @@ class ImportMultiTest (BitcoinTestFramework): print ("Mining blocks...") self.nodes[0].generate(1) self.nodes[1].generate(1) + timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime'] # keyword definition PRIV_KEY = 'privkey' @@ -52,31 +53,48 @@ class ImportMultiTest (BitcoinTestFramework): result = self.nodes[1].importmulti([{ "scriptPubKey": { "address": address['address'] - } + }, + "timestamp": "now", }]) assert_equal(result[0]['success'], True) address_assert = self.nodes[1].validateaddress(address['address']) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) + assert_equal(address_assert['timestamp'], timestamp) + watchonly_address = address['address'] + watchonly_timestamp = timestamp + print("Should not import an invalid address") + result = self.nodes[1].importmulti([{ + "scriptPubKey": { + "address": "not valid address", + }, + "timestamp": "now", + }]) + assert_equal(result[0]['success'], False) + assert_equal(result[0]['error']['code'], -5) + assert_equal(result[0]['error']['message'], 'Invalid address') # ScriptPubKey + internal print("Should import a scriptPubKey with internal flag") address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ "scriptPubKey": address['scriptPubKey'], + "timestamp": "now", "internal": True }]) assert_equal(result[0]['success'], True) address_assert = self.nodes[1].validateaddress(address['address']) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) + assert_equal(address_assert['timestamp'], timestamp) # ScriptPubKey + !internal print("Should not import a scriptPubKey without internal flag") address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ - "scriptPubKey": address['scriptPubKey'] + "scriptPubKey": address['scriptPubKey'], + "timestamp": "now", }]) assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -8) @@ -84,6 +102,7 @@ class ImportMultiTest (BitcoinTestFramework): address_assert = self.nodes[1].validateaddress(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) + assert_equal('timestamp' in address_assert, False) # Address + Public key + !Internal @@ -93,12 +112,14 @@ class ImportMultiTest (BitcoinTestFramework): "scriptPubKey": { "address": address['address'] }, + "timestamp": "now", "pubkeys": [ address['pubkey'] ] }]) assert_equal(result[0]['success'], True) address_assert = self.nodes[1].validateaddress(address['address']) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) + assert_equal(address_assert['timestamp'], timestamp) # ScriptPubKey + Public key + internal @@ -106,22 +127,25 @@ class ImportMultiTest (BitcoinTestFramework): address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) request = [{ "scriptPubKey": address['scriptPubKey'], + "timestamp": "now", "pubkeys": [ address['pubkey'] ], "internal": True - }]; + }] result = self.nodes[1].importmulti(request) assert_equal(result[0]['success'], True) address_assert = self.nodes[1].validateaddress(address['address']) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) + assert_equal(address_assert['timestamp'], timestamp) # ScriptPubKey + Public key + !internal print("Should not import a scriptPubKey without internal and with public key") address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) request = [{ "scriptPubKey": address['scriptPubKey'], + "timestamp": "now", "pubkeys": [ address['pubkey'] ] - }]; + }] result = self.nodes[1].importmulti(request) assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -8) @@ -129,6 +153,7 @@ class ImportMultiTest (BitcoinTestFramework): address_assert = self.nodes[1].validateaddress(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) + assert_equal('timestamp' in address_assert, False) # Address + Private key + !watchonly print("Should import an address with private key") @@ -137,12 +162,14 @@ class ImportMultiTest (BitcoinTestFramework): "scriptPubKey": { "address": address['address'] }, + "timestamp": "now", "keys": [ self.nodes[0].dumpprivkey(address['address']) ] }]) assert_equal(result[0]['success'], True) address_assert = self.nodes[1].validateaddress(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], True) + assert_equal(address_assert['timestamp'], timestamp) # Address + Private key + watchonly print("Should not import an address with private key and with watchonly") @@ -151,6 +178,7 @@ class ImportMultiTest (BitcoinTestFramework): "scriptPubKey": { "address": address['address'] }, + "timestamp": "now", "keys": [ self.nodes[0].dumpprivkey(address['address']) ], "watchonly": True }]) @@ -160,12 +188,14 @@ class ImportMultiTest (BitcoinTestFramework): address_assert = self.nodes[1].validateaddress(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) + assert_equal('timestamp' in address_assert, False) # ScriptPubKey + Private key + internal print("Should import a scriptPubKey with internal and with private key") address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ "scriptPubKey": address['scriptPubKey'], + "timestamp": "now", "keys": [ self.nodes[0].dumpprivkey(address['address']) ], "internal": True }]) @@ -173,12 +203,14 @@ class ImportMultiTest (BitcoinTestFramework): address_assert = self.nodes[1].validateaddress(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], True) + assert_equal(address_assert['timestamp'], timestamp) # ScriptPubKey + Private key + !internal print("Should not import a scriptPubKey without internal and with private key") address = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ "scriptPubKey": address['scriptPubKey'], + "timestamp": "now", "keys": [ self.nodes[0].dumpprivkey(address['address']) ] }]) assert_equal(result[0]['success'], False) @@ -187,6 +219,7 @@ class ImportMultiTest (BitcoinTestFramework): address_assert = self.nodes[1].validateaddress(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) + assert_equal('timestamp' in address_assert, False) # P2SH address @@ -197,18 +230,21 @@ class ImportMultiTest (BitcoinTestFramework): self.nodes[1].generate(100) transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) self.nodes[1].generate(1) - transaction = self.nodes[1].gettransaction(transactionid); + timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime'] + transaction = self.nodes[1].gettransaction(transactionid) print("Should import a p2sh") result = self.nodes[1].importmulti([{ "scriptPubKey": { "address": multi_sig_script['address'] - } + }, + "timestamp": "now", }]) assert_equal(result[0]['success'], True) address_assert = self.nodes[1].validateaddress(multi_sig_script['address']) assert_equal(address_assert['isscript'], True) assert_equal(address_assert['iswatchonly'], True) + assert_equal(address_assert['timestamp'], timestamp) p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0] assert_equal(p2shunspent['spendable'], False) assert_equal(p2shunspent['solvable'], False) @@ -222,16 +258,20 @@ class ImportMultiTest (BitcoinTestFramework): self.nodes[1].generate(100) transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) self.nodes[1].generate(1) - transaction = self.nodes[1].gettransaction(transactionid); + timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime'] + transaction = self.nodes[1].gettransaction(transactionid) print("Should import a p2sh with respective redeem script") result = self.nodes[1].importmulti([{ "scriptPubKey": { "address": multi_sig_script['address'] }, + "timestamp": "now", "redeemscript": multi_sig_script['redeemScript'] }]) assert_equal(result[0]['success'], True) + address_assert = self.nodes[1].validateaddress(multi_sig_script['address']) + assert_equal(address_assert['timestamp'], timestamp) p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0] assert_equal(p2shunspent['spendable'], False) @@ -246,17 +286,21 @@ class ImportMultiTest (BitcoinTestFramework): self.nodes[1].generate(100) transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) self.nodes[1].generate(1) - transaction = self.nodes[1].gettransaction(transactionid); + timestamp = self.nodes[1].getblock(self.nodes[1].getbestblockhash())['mediantime'] + transaction = self.nodes[1].gettransaction(transactionid) print("Should import a p2sh with respective redeem script and private keys") result = self.nodes[1].importmulti([{ "scriptPubKey": { "address": multi_sig_script['address'] }, + "timestamp": "now", "redeemscript": multi_sig_script['redeemScript'], "keys": [ self.nodes[0].dumpprivkey(sig_address_1['address']), self.nodes[0].dumpprivkey(sig_address_2['address'])] }]) assert_equal(result[0]['success'], True) + address_assert = self.nodes[1].validateaddress(multi_sig_script['address']) + assert_equal(address_assert['timestamp'], timestamp) p2shunspent = self.nodes[1].listunspent(0,999999, [multi_sig_script['address']])[0] assert_equal(p2shunspent['spendable'], False) @@ -270,13 +314,14 @@ class ImportMultiTest (BitcoinTestFramework): self.nodes[1].generate(100) transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) self.nodes[1].generate(1) - transaction = self.nodes[1].gettransaction(transactionid); + transaction = self.nodes[1].gettransaction(transactionid) print("Should import a p2sh with respective redeem script and private keys") result = self.nodes[1].importmulti([{ "scriptPubKey": { "address": multi_sig_script['address'] }, + "timestamp": "now", "redeemscript": multi_sig_script['redeemScript'], "keys": [ self.nodes[0].dumpprivkey(sig_address_1['address']), self.nodes[0].dumpprivkey(sig_address_2['address'])], "watchonly": True @@ -294,6 +339,7 @@ class ImportMultiTest (BitcoinTestFramework): "scriptPubKey": { "address": address['address'] }, + "timestamp": "now", "pubkeys": [ address2['pubkey'] ] }]) assert_equal(result[0]['success'], False) @@ -302,6 +348,7 @@ class ImportMultiTest (BitcoinTestFramework): address_assert = self.nodes[1].validateaddress(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) + assert_equal('timestamp' in address_assert, False) # ScriptPubKey + Public key + internal + Wrong pubkey @@ -310,9 +357,10 @@ class ImportMultiTest (BitcoinTestFramework): address2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) request = [{ "scriptPubKey": address['scriptPubKey'], + "timestamp": "now", "pubkeys": [ address2['pubkey'] ], "internal": True - }]; + }] result = self.nodes[1].importmulti(request) assert_equal(result[0]['success'], False) assert_equal(result[0]['error']['code'], -5) @@ -320,6 +368,7 @@ class ImportMultiTest (BitcoinTestFramework): address_assert = self.nodes[1].validateaddress(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) + assert_equal('timestamp' in address_assert, False) # Address + Private key + !watchonly + Wrong private key @@ -330,6 +379,7 @@ class ImportMultiTest (BitcoinTestFramework): "scriptPubKey": { "address": address['address'] }, + "timestamp": "now", "keys": [ self.nodes[0].dumpprivkey(address2['address']) ] }]) assert_equal(result[0]['success'], False) @@ -338,6 +388,7 @@ class ImportMultiTest (BitcoinTestFramework): address_assert = self.nodes[1].validateaddress(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) + assert_equal('timestamp' in address_assert, False) # ScriptPubKey + Private key + internal + Wrong private key @@ -346,6 +397,7 @@ class ImportMultiTest (BitcoinTestFramework): address2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) result = self.nodes[1].importmulti([{ "scriptPubKey": address['scriptPubKey'], + "timestamp": "now", "keys": [ self.nodes[0].dumpprivkey(address2['address']) ], "internal": True }]) @@ -355,6 +407,28 @@ class ImportMultiTest (BitcoinTestFramework): address_assert = self.nodes[1].validateaddress(address['address']) assert_equal(address_assert['iswatchonly'], False) assert_equal(address_assert['ismine'], False) + assert_equal('timestamp' in address_assert, False) + + # restart nodes to check for proper serialization/deserialization of watch only address + stop_nodes(self.nodes) + self.nodes = start_nodes(2, self.options.tmpdir) + address_assert = self.nodes[1].validateaddress(watchonly_address) + assert_equal(address_assert['iswatchonly'], True) + assert_equal(address_assert['ismine'], False) + assert_equal(address_assert['timestamp'], watchonly_timestamp); + + # Bad or missing timestamps + print("Should throw on invalid or missing timestamp values") + assert_raises_message(JSONRPCException, 'Missing required timestamp field for key', + self.nodes[1].importmulti, [{ + "scriptPubKey": address['scriptPubKey'], + }]) + assert_raises_message(JSONRPCException, 'Expected number or "now" timestamp value for key. got type string', + self.nodes[1].importmulti, [{ + "scriptPubKey": address['scriptPubKey'], + "timestamp": "", + }]) + if __name__ == '__main__': ImportMultiTest ().main () diff --git a/qa/rpc-tests/importprunedfunds.py b/qa/rpc-tests/importprunedfunds.py index 0dee8ad4ec..a941124656 100755 --- a/qa/rpc-tests/importprunedfunds.py +++ b/qa/rpc-tests/importprunedfunds.py @@ -2,7 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - +"""Test the importprunedfunds and removeprunedfunds RPCs.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * diff --git a/qa/rpc-tests/invalidateblock.py b/qa/rpc-tests/invalidateblock.py index 0faadd33ab..92e65927d5 100755 --- a/qa/rpc-tests/invalidateblock.py +++ b/qa/rpc-tests/invalidateblock.py @@ -2,10 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test InvalidateBlock code -# +"""Test the invalidateblock RPC.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * diff --git a/qa/rpc-tests/invalidblockrequest.py b/qa/rpc-tests/invalidblockrequest.py index 3d8107a76c..eabc0db8df 100755 --- a/qa/rpc-tests/invalidblockrequest.py +++ b/qa/rpc-tests/invalidblockrequest.py @@ -2,6 +2,14 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test node responses to invalid blocks. + +In this test we connect to one node over p2p, and test block requests: +1) Valid blocks should be requested and become chain tip. +2) Invalid block with duplicated transaction should be re-requested. +3) Invalid block with bad coinbase value should be rejected and not +re-requested. +""" from test_framework.test_framework import ComparisonTestFramework from test_framework.util import * @@ -10,15 +18,6 @@ from test_framework.blocktools import * import copy import time - -''' -In this test we connect to one node over p2p, and test block requests: -1) Valid blocks should be requested and become chain tip. -2) Invalid block with duplicated transaction should be re-requested. -3) Invalid block with bad coinbase value should be rejected and not -re-requested. -''' - # Use the ComparisonTestFramework with 1 node: only use --testbinary. class InvalidBlockRequestTest(ComparisonTestFramework): diff --git a/qa/rpc-tests/invalidtxrequest.py b/qa/rpc-tests/invalidtxrequest.py index 93205d79de..a9ac231f09 100755 --- a/qa/rpc-tests/invalidtxrequest.py +++ b/qa/rpc-tests/invalidtxrequest.py @@ -2,6 +2,10 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test node responses to invalid transactions. + +In this test we connect to one node over p2p, and test tx requests. +""" from test_framework.test_framework import ComparisonTestFramework from test_framework.comptool import TestManager, TestInstance, RejectResult @@ -9,9 +13,6 @@ from test_framework.blocktools import * import time -''' -In this test we connect to one node over p2p, and test tx requests. -''' # Use the ComparisonTestFramework with 1 node: only use --testbinary. class InvalidTxRequestTest(ComparisonTestFramework): diff --git a/qa/rpc-tests/keypool.py b/qa/rpc-tests/keypool.py index fa39476568..4b9936a1fd 100755 --- a/qa/rpc-tests/keypool.py +++ b/qa/rpc-tests/keypool.py @@ -2,8 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# Exercise the wallet keypool, and interaction with wallet encryption/locking +"""Test the wallet keypool and interaction with wallet encryption/locking.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * diff --git a/qa/rpc-tests/listsinceblock.py b/qa/rpc-tests/listsinceblock.py new file mode 100755 index 0000000000..88304af5b0 --- /dev/null +++ b/qa/rpc-tests/listsinceblock.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the listsincelast RPC.""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal + +class ListSinceBlockTest (BitcoinTestFramework): + + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 4 + + def run_test (self): + ''' + `listsinceblock` did not behave correctly when handed a block that was + no longer in the main chain: + + ab0 + / \ + aa1 [tx0] bb1 + | | + aa2 bb2 + | | + aa3 bb3 + | + bb4 + + Consider a client that has only seen block `aa3` above. It asks the node + to `listsinceblock aa3`. But at some point prior the main chain switched + to the bb chain. + + Previously: listsinceblock would find height=4 for block aa3 and compare + this to height=5 for the tip of the chain (bb4). It would then return + results restricted to bb3-bb4. + + Now: listsinceblock finds the fork at ab0 and returns results in the + range bb1-bb4. + + This test only checks that [tx0] is present. + ''' + + assert_equal(self.is_network_split, False) + self.nodes[2].generate(101) + self.sync_all() + + assert_equal(self.nodes[0].getbalance(), 0) + assert_equal(self.nodes[1].getbalance(), 0) + assert_equal(self.nodes[2].getbalance(), 50) + assert_equal(self.nodes[3].getbalance(), 0) + + # Split network into two + self.split_network() + assert_equal(self.is_network_split, True) + + # send to nodes[0] from nodes[2] + senttx = self.nodes[2].sendtoaddress(self.nodes[0].getnewaddress(), 1) + + # generate on both sides + lastblockhash = self.nodes[1].generate(6)[5] + self.nodes[2].generate(7) + print('lastblockhash=%s' % (lastblockhash)) + + self.sync_all() + + self.join_network() + + # listsinceblock(lastblockhash) should now include tx, as seen from nodes[0] + lsbres = self.nodes[0].listsinceblock(lastblockhash) + found = False + for tx in lsbres['transactions']: + if tx['txid'] == senttx: + found = True + break + assert_equal(found, True) + +if __name__ == '__main__': + ListSinceBlockTest().main() diff --git a/qa/rpc-tests/listtransactions.py b/qa/rpc-tests/listtransactions.py index 5ec6ce17e0..92fb96c809 100755 --- a/qa/rpc-tests/listtransactions.py +++ b/qa/rpc-tests/listtransactions.py @@ -2,8 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# Exercise the listtransactions API +"""Test the listtransactions API.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * diff --git a/qa/rpc-tests/maxblocksinflight.py b/qa/rpc-tests/maxblocksinflight.py index 1df1c484be..8ea405b4d5 100755 --- a/qa/rpc-tests/maxblocksinflight.py +++ b/qa/rpc-tests/maxblocksinflight.py @@ -2,18 +2,19 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test nodes responses to having many blocks in flight. + +In this test we connect to one node over p2p, send it numerous inv's, and +compare the resulting number of getdata requests to a max allowed value. We +test for exceeding 128 blocks in flight, which was the limit an 0.9 client will +reach. [0.10 clients shouldn't request more than 16 from a single peer.] +""" from test_framework.mininode import * from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * import logging -''' -In this test we connect to one node over p2p, send it numerous inv's, and -compare the resulting number of getdata requests to a max allowed value. We -test for exceeding 128 blocks in flight, which was the limit an 0.9 client will -reach. [0.10 clients shouldn't request more than 16 from a single peer.] -''' MAX_REQUESTS = 128 class TestManager(NodeConnCB): diff --git a/qa/rpc-tests/maxuploadtarget.py b/qa/rpc-tests/maxuploadtarget.py index 83168a7ce7..757aa60afd 100755 --- a/qa/rpc-tests/maxuploadtarget.py +++ b/qa/rpc-tests/maxuploadtarget.py @@ -2,21 +2,19 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -from test_framework.mininode import * -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -import time - -''' -Test behavior of -maxuploadtarget. +"""Test behavior of -maxuploadtarget. * Verify that getdata requests for old blocks (>1week) are dropped if uploadtarget has been reached. * Verify that getdata requests for recent blocks are respecteved even if uploadtarget has been reached. * Verify that the upload counters are reset after 24 hours. -''' +""" + +from test_framework.mininode import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +import time # TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending # p2p messages to a node, generating the messages in the main testing logic. @@ -86,6 +84,9 @@ class MaxUploadTest(BitcoinTestFramework): self.setup_clean_chain = True self.num_nodes = 1 + # Cache for utxos, as the listunspent may take a long time later in the test + self.utxo_cache = [] + def setup_network(self): # Start a node with maxuploadtarget of 200 MB (/24h) self.nodes = [] @@ -118,7 +119,7 @@ class MaxUploadTest(BitcoinTestFramework): # Test logic begins here # Now mine a big block - mine_large_block(self.nodes[0]) + mine_large_block(self.nodes[0], self.utxo_cache) # Store the hash; we'll request this later big_old_block = self.nodes[0].getbestblockhash() @@ -129,7 +130,7 @@ class MaxUploadTest(BitcoinTestFramework): self.nodes[0].setmocktime(int(time.time()) - 2*60*60*24) # Mine one more block, so that the prior block looks old - mine_large_block(self.nodes[0]) + mine_large_block(self.nodes[0], self.utxo_cache) # We'll be requesting this new block too big_new_block = self.nodes[0].getbestblockhash() diff --git a/qa/rpc-tests/mempool_limit.py b/qa/rpc-tests/mempool_limit.py index 4438c152df..8fae92ad2b 100755 --- a/qa/rpc-tests/mempool_limit.py +++ b/qa/rpc-tests/mempool_limit.py @@ -2,8 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# Test mempool limiting together/eviction with the wallet +"""Test mempool limiting together/eviction with the wallet.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -26,7 +25,7 @@ class MempoolLimitTest(BitcoinTestFramework): def run_test(self): txids = [] - utxos = create_confirmed_utxos(self.relayfee, self.nodes[0], 90) + utxos = create_confirmed_utxos(self.relayfee, self.nodes[0], 91) #create a mempool tx that will be evicted us0 = utxos.pop() @@ -41,9 +40,9 @@ class MempoolLimitTest(BitcoinTestFramework): relayfee = self.nodes[0].getnetworkinfo()['relayfee'] base_fee = relayfee*100 - for i in range (4): + for i in range (3): txids.append([]) - txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[30*i:30*i+30], (i+1)*base_fee) + txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[30*i:30*i+30], 30, (i+1)*base_fee) # by now, the tx should be evicted, check confirmation state assert(txid not in self.nodes[0].getrawmempool()) diff --git a/qa/rpc-tests/mempool_packages.py b/qa/rpc-tests/mempool_packages.py index 45dc0e65c4..388889d07c 100755 --- a/qa/rpc-tests/mempool_packages.py +++ b/qa/rpc-tests/mempool_packages.py @@ -2,8 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# Test descendant package tracking code +"""Test descendant package tracking code.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -20,8 +19,8 @@ class MempoolPackagesTest(BitcoinTestFramework): def setup_network(self): self.nodes = [] - self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", "-relaypriority=0", "-debug"])) - self.nodes.append(start_node(1, self.options.tmpdir, ["-maxorphantx=1000", "-relaypriority=0", "-limitancestorcount=5", "-debug"])) + self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", "-debug"])) + self.nodes.append(start_node(1, self.options.tmpdir, ["-maxorphantx=1000", "-limitancestorcount=5", "-debug"])) connect_nodes(self.nodes[0], 1) self.is_network_split = False self.sync_all() diff --git a/qa/rpc-tests/mempool_reorg.py b/qa/rpc-tests/mempool_reorg.py index 301b094eb0..2cd5573277 100755 --- a/qa/rpc-tests/mempool_reorg.py +++ b/qa/rpc-tests/mempool_reorg.py @@ -2,11 +2,11 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test mempool re-org scenarios. -# -# Test re-org scenarios with a mempool that contains transactions -# that spend (directly or indirectly) coinbase transactions. -# +Test re-org scenarios with a mempool that contains transactions +that spend (directly or indirectly) coinbase transactions. +""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -55,7 +55,7 @@ class MempoolCoinbaseTest(BitcoinTestFramework): # Create a block-height-locked transaction which will be invalid after reorg timelock_tx = self.nodes[0].createrawtransaction([{"txid": coinbase_txids[0], "vout": 0}], {node0_address: 49.99}) # Set the time lock - timelock_tx = timelock_tx.replace("ffffffff", "11111111", 1) + timelock_tx = timelock_tx.replace("ffffffff", "11111191", 1) timelock_tx = timelock_tx[:-8] + hex(self.nodes[0].getblockcount() + 2)[2:] + "000000" timelock_tx = self.nodes[0].signrawtransaction(timelock_tx)["hex"] assert_raises(JSONRPCException, self.nodes[0].sendrawtransaction, timelock_tx) diff --git a/qa/rpc-tests/mempool_resurrect_test.py b/qa/rpc-tests/mempool_resurrect_test.py index 3db12cbf76..c7f37aaa44 100755 --- a/qa/rpc-tests/mempool_resurrect_test.py +++ b/qa/rpc-tests/mempool_resurrect_test.py @@ -2,11 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test resurrection of mined transactions when -# the blockchain is re-organized. -# +"""Test resurrection of mined transactions when the blockchain is re-organized.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * diff --git a/qa/rpc-tests/mempool_spendcoinbase.py b/qa/rpc-tests/mempool_spendcoinbase.py index d5e4bf52d2..da8485e2a2 100755 --- a/qa/rpc-tests/mempool_spendcoinbase.py +++ b/qa/rpc-tests/mempool_spendcoinbase.py @@ -2,16 +2,15 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test spending coinbase transactions. -# The coinbase transaction in block N can appear in block -# N+100... so is valid in the mempool when the best block -# height is N+99. -# This test makes sure coinbase spends that will be mature -# in the next block are accepted into the memory pool, -# but less mature coinbase spends are NOT. -# +"""Test spending coinbase transactions. + +The coinbase transaction in block N can appear in block +N+100... so is valid in the mempool when the best block +height is N+99. +This test makes sure coinbase spends that will be mature +in the next block are accepted into the memory pool, +but less mature coinbase spends are NOT. +""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * diff --git a/qa/rpc-tests/merkle_blocks.py b/qa/rpc-tests/merkle_blocks.py index b2155d7fc3..c4b45425d7 100755 --- a/qa/rpc-tests/merkle_blocks.py +++ b/qa/rpc-tests/merkle_blocks.py @@ -2,10 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test merkleblock fetch/validation -# +"""Test gettxoutproof and verifytxoutproof RPCs.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * diff --git a/qa/rpc-tests/multi_rpc.py b/qa/rpc-tests/multi_rpc.py index 95d9090ce2..3b74bf1c46 100755 --- a/qa/rpc-tests/multi_rpc.py +++ b/qa/rpc-tests/multi_rpc.py @@ -2,10 +2,7 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test multiple rpc user config option rpcauth -# +"""Test multiple RPC users.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import str_to_b64str, assert_equal diff --git a/qa/rpc-tests/nodehandling.py b/qa/rpc-tests/nodehandling.py index e9682c4908..89059d2760 100755 --- a/qa/rpc-tests/nodehandling.py +++ b/qa/rpc-tests/nodehandling.py @@ -2,15 +2,11 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test node handling -# +"""Test node handling.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * -import http.client import urllib.parse class NodeHandlingTest (BitcoinTestFramework): diff --git a/qa/rpc-tests/nulldummy.py b/qa/rpc-tests/nulldummy.py index 54b7eac376..66c4e90f21 100755 --- a/qa/rpc-tests/nulldummy.py +++ b/qa/rpc-tests/nulldummy.py @@ -2,6 +2,16 @@ # Copyright (c) 2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test NULLDUMMY softfork. + +Connect to a single node. +Generate 2 blocks (save the coinbases for later). +Generate 427 more blocks. +[Policy/Consensus] Check that NULLDUMMY compliant transactions are accepted in the 430th block. +[Policy] Check that non-NULLDUMMY transactions are rejected before activation. +[Consensus] Check that the new NULLDUMMY rules are not enforced on the 431st block. +[Policy/Consensus] Check that the new NULLDUMMY rules are enforced on the 432nd block. +""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -25,17 +35,6 @@ def trueDummy(tx): tx.vin[0].scriptSig = CScript(newscript) tx.rehash() -''' -This test is meant to exercise NULLDUMMY softfork. -Connect to a single node. -Generate 2 blocks (save the coinbases for later). -Generate 427 more blocks. -[Policy/Consensus] Check that NULLDUMMY compliant transactions are accepted in the 430th block. -[Policy] Check that non-NULLDUMMY transactions are rejected before activation. -[Consensus] Check that the new NULLDUMMY rules are not enforced on the 431st block. -[Policy/Consensus] Check that the new NULLDUMMY rules are enforced on the 432nd block. -''' - class NULLDUMMYTest(BitcoinTestFramework): def __init__(self): diff --git a/qa/rpc-tests/p2p-acceptblock.py b/qa/rpc-tests/p2p-acceptblock.py index 015ec34eff..2f21fa149a 100755 --- a/qa/rpc-tests/p2p-acceptblock.py +++ b/qa/rpc-tests/p2p-acceptblock.py @@ -2,15 +2,7 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -from test_framework.mininode import * -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -import time -from test_framework.blocktools import create_block, create_coinbase - -''' -AcceptBlockTest -- test processing of unrequested blocks. +"""Test processing of unrequested blocks. Since behavior differs when receiving unrequested blocks from whitelisted peers versus non-whitelisted peers, this tests the behavior of both (effectively two @@ -54,7 +46,13 @@ The test: 7. Send Node0 the missing block again. Node0 should process and the tip should advance. -''' +""" + +from test_framework.mininode import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * +import time +from test_framework.blocktools import create_block, create_coinbase # TestNode: bare-bones "peer". Used mostly as a conduit for a test to sending # p2p messages to a node, generating the messages in the main testing logic. diff --git a/qa/rpc-tests/p2p-compactblocks.py b/qa/rpc-tests/p2p-compactblocks.py index ab4b809ded..79561f35f7 100755 --- a/qa/rpc-tests/p2p-compactblocks.py +++ b/qa/rpc-tests/p2p-compactblocks.py @@ -2,21 +2,18 @@ # Copyright (c) 2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test compact blocks (BIP 152). + +Version 1 compact blocks are pre-segwit (txids) +Version 2 compact blocks are post-segwit (wtxids) +""" 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.siphash import siphash256 from test_framework.script import CScript, OP_TRUE -''' -CompactBlocksTest -- test compact blocks (BIP 152) - -Version 1 compact blocks are pre-segwit (txids) -Version 2 compact blocks are post-segwit (wtxids) -''' - # TestNode: A peer we use to send messages to bitcoind, and store responses. class TestNode(SingleNodeConnCB): def __init__(self): @@ -310,6 +307,9 @@ class CompactBlocksTest(BitcoinTestFramework): tip = int(node.getbestblockhash(), 16) assert(test_node.wait_for_block_announcement(tip)) + # Make sure we will receive a fast-announce compact block + self.request_cb_announcements(test_node, node, version) + # Now mine a block, and look at the resulting compact block. test_node.clear_block_announcement() block_hash = int(node.generate(1)[0], 16) @@ -319,27 +319,36 @@ class CompactBlocksTest(BitcoinTestFramework): [tx.calc_sha256() for tx in block.vtx] block.rehash() - # Don't care which type of announcement came back for this test; just - # request the compact block if we didn't get one yet. + # Wait until the block was announced (via compact blocks) wait_until(test_node.received_block_announcement, timeout=30) assert(test_node.received_block_announcement()) + # Now fetch and check the compact block + header_and_shortids = None with mininode_lock: - if test_node.last_cmpctblock is None: - test_node.clear_block_announcement() - inv = CInv(4, block_hash) # 4 == "CompactBlock" - test_node.send_message(msg_getdata([inv])) + assert(test_node.last_cmpctblock is not None) + # Convert the on-the-wire representation to absolute indexes + header_and_shortids = HeaderAndShortIDs(test_node.last_cmpctblock.header_and_shortids) + self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block) + + # Now fetch the compact block using a normal non-announce getdata + with mininode_lock: + test_node.clear_block_announcement() + inv = CInv(4, block_hash) # 4 == "CompactBlock" + test_node.send_message(msg_getdata([inv])) wait_until(test_node.received_block_announcement, timeout=30) assert(test_node.received_block_announcement()) - # Now we should have the compactblock + # Now fetch and check the compact block header_and_shortids = None with mininode_lock: assert(test_node.last_cmpctblock is not None) # Convert the on-the-wire representation to absolute indexes header_and_shortids = HeaderAndShortIDs(test_node.last_cmpctblock.header_and_shortids) + self.check_compactblock_construction_from_block(version, header_and_shortids, block_hash, block) + def check_compactblock_construction_from_block(self, version, header_and_shortids, block_hash, block): # Check that we got the right block! header_and_shortids.header.calc_sha256() assert_equal(header_and_shortids.header.sha256, block_hash) @@ -764,6 +773,54 @@ class CompactBlocksTest(BitcoinTestFramework): msg.announce = True peer.send_and_ping(msg) + def test_compactblock_reconstruction_multiple_peers(self, node, stalling_peer, delivery_peer): + assert(len(self.utxos)) + + def announce_cmpct_block(node, peer): + utxo = self.utxos.pop(0) + block = self.build_block_with_transactions(node, utxo, 5) + + cmpct_block = HeaderAndShortIDs() + cmpct_block.initialize_from_block(block) + msg = msg_cmpctblock(cmpct_block.to_p2p()) + peer.send_and_ping(msg) + with mininode_lock: + assert(peer.last_getblocktxn is not None) + return block, cmpct_block + + block, cmpct_block = announce_cmpct_block(node, stalling_peer) + + for tx in block.vtx[1:]: + delivery_peer.send_message(msg_tx(tx)) + delivery_peer.sync_with_ping() + mempool = node.getrawmempool() + for tx in block.vtx[1:]: + assert(tx.hash in mempool) + + delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p())) + assert_equal(int(node.getbestblockhash(), 16), block.sha256) + + self.utxos.append([block.vtx[-1].sha256, 0, block.vtx[-1].vout[0].nValue]) + + # Now test that delivering an invalid compact block won't break relay + + block, cmpct_block = announce_cmpct_block(node, stalling_peer) + for tx in block.vtx[1:]: + delivery_peer.send_message(msg_tx(tx)) + delivery_peer.sync_with_ping() + + cmpct_block.prefilled_txn[0].tx.wit.vtxinwit = [ CTxInWitness() ] + cmpct_block.prefilled_txn[0].tx.wit.vtxinwit[0].scriptWitness.stack = [ser_uint256(0)] + + cmpct_block.use_witness = True + delivery_peer.send_and_ping(msg_cmpctblock(cmpct_block.to_p2p())) + assert(int(node.getbestblockhash(), 16) != block.sha256) + + msg = msg_blocktxn() + msg.block_transactions.blockhash = block.sha256 + msg.block_transactions.transactions = block.vtx[1:] + stalling_peer.send_and_ping(msg) + assert_equal(int(node.getbestblockhash(), 16), block.sha256) def run_test(self): # Setup the p2p connections and start up the network thread. @@ -848,6 +905,10 @@ class CompactBlocksTest(BitcoinTestFramework): self.test_invalid_tx_in_compactblock(self.nodes[1], self.segwit_node, False) self.test_invalid_tx_in_compactblock(self.nodes[1], self.old_node, False) + print("\tTesting reconstructing compact blocks from all peers...") + self.test_compactblock_reconstruction_multiple_peers(self.nodes[1], self.segwit_node, self.old_node) + sync_blocks(self.nodes) + # Advance to segwit activation print ("\nAdvancing to segwit activation\n") self.activate_segwit(self.nodes[1]) diff --git a/qa/rpc-tests/p2p-feefilter.py b/qa/rpc-tests/p2p-feefilter.py index 96d99d38a7..1c62961db0 100755 --- a/qa/rpc-tests/p2p-feefilter.py +++ b/qa/rpc-tests/p2p-feefilter.py @@ -2,16 +2,13 @@ # Copyright (c) 2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -# +"""Test processing of feefilter messages.""" from test_framework.mininode import * from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * import time -''' -FeeFilterTest -- test processing of feefilter messages -''' def hashToHex(hash): return format(hash, '064x') @@ -21,9 +18,9 @@ def allInvsMatch(invsExpected, testnode): for x in range(60): with mininode_lock: if (sorted(invsExpected) == sorted(testnode.txinvs)): - return True; + return True time.sleep(1) - return False; + return False # TestNode: bare-bones "peer". Used to track which invs are received from a node # and to send the node feefilter messages. diff --git a/qa/rpc-tests/p2p-fullblocktest.py b/qa/rpc-tests/p2p-fullblocktest.py index e4b889d761..274dbb8a92 100755 --- a/qa/rpc-tests/p2p-fullblocktest.py +++ b/qa/rpc-tests/p2p-fullblocktest.py @@ -2,6 +2,14 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test block processing. + +This reimplements tests from the bitcoinj/FullBlockTestGenerator used +by the pull-tester. + +We use the testing framework in which we expect a particular answer from +each test. +""" from test_framework.test_framework import ComparisonTestFramework from test_framework.util import * @@ -17,14 +25,6 @@ class PreviousSpendableOutput(object): self.tx = tx self.n = n # the output we're spending -''' -This reimplements tests from the bitcoinj/FullBlockTestGenerator used -by the pull-tester. - -We use the testing framework in which we expect a particular answer from -each test. -''' - # Use this class for tests that require behavior other than normal "mininode" behavior. # For now, it is used to serialize a bloated varint (b64). class CBrokenBlock(CBlock): @@ -398,7 +398,7 @@ class FullBlockTest(ComparisonTestFramework): # Extend the b26 chain to make sure bitcoind isn't accepting b26 b27 = block(27, spend=out[7]) - yield rejected(RejectResult(16, b'bad-prevblk')) + yield rejected(RejectResult(0, b'bad-prevblk')) # Now try a too-large-coinbase script tip(15) @@ -410,7 +410,7 @@ class FullBlockTest(ComparisonTestFramework): # Extend the b28 chain to make sure bitcoind isn't accepting b28 b29 = block(29, spend=out[7]) - yield rejected(RejectResult(16, b'bad-prevblk')) + yield rejected(RejectResult(0, b'bad-prevblk')) # b30 has a max-sized coinbase scriptSig. tip(23) diff --git a/qa/rpc-tests/p2p-leaktests.py b/qa/rpc-tests/p2p-leaktests.py new file mode 100755 index 0000000000..bf08ea32f4 --- /dev/null +++ b/qa/rpc-tests/p2p-leaktests.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test message sending before handshake completion. + +A node should never send anything other than VERSION/VERACK/REJECT until it's +received a VERACK. + +This test connects to a node and sends it a few messages, trying to intice it +into sending us something it shouldn't. +""" + +from test_framework.mininode import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +banscore = 10 + +class CLazyNode(NodeConnCB): + def __init__(self): + self.connection = None + self.unexpected_msg = False + self.connected = False + super().__init__() + + def add_connection(self, conn): + self.connection = conn + + def send_message(self, message): + self.connection.send_message(message) + + def bad_message(self, message): + self.unexpected_msg = True + print("should not have received message: %s" % message.command) + + def on_open(self, conn): + self.connected = True + + def on_version(self, conn, message): self.bad_message(message) + def on_verack(self, conn, message): self.bad_message(message) + def on_reject(self, conn, message): self.bad_message(message) + def on_inv(self, conn, message): self.bad_message(message) + def on_addr(self, conn, message): self.bad_message(message) + def on_alert(self, conn, message): self.bad_message(message) + def on_getdata(self, conn, message): self.bad_message(message) + def on_getblocks(self, conn, message): self.bad_message(message) + def on_tx(self, conn, message): self.bad_message(message) + def on_block(self, conn, message): self.bad_message(message) + def on_getaddr(self, conn, message): self.bad_message(message) + def on_headers(self, conn, message): self.bad_message(message) + def on_getheaders(self, conn, message): self.bad_message(message) + def on_ping(self, conn, message): self.bad_message(message) + def on_mempool(self, conn): self.bad_message(message) + def on_pong(self, conn, message): self.bad_message(message) + def on_feefilter(self, conn, message): self.bad_message(message) + def on_sendheaders(self, conn, message): self.bad_message(message) + def on_sendcmpct(self, conn, message): self.bad_message(message) + def on_cmpctblock(self, conn, message): self.bad_message(message) + def on_getblocktxn(self, conn, message): self.bad_message(message) + def on_blocktxn(self, conn, message): self.bad_message(message) + +# Node that never sends a version. We'll use this to send a bunch of messages +# anyway, and eventually get disconnected. +class CNodeNoVersionBan(CLazyNode): + def __init__(self): + super().__init__() + + # send a bunch of veracks without sending a message. This should get us disconnected. + # NOTE: implementation-specific check here. Remove if bitcoind ban behavior changes + def on_open(self, conn): + super().on_open(conn) + for i in range(banscore): + self.send_message(msg_verack()) + + def on_reject(self, conn, message): pass + +# Node that never sends a version. This one just sits idle and hopes to receive +# any message (it shouldn't!) +class CNodeNoVersionIdle(CLazyNode): + def __init__(self): + super().__init__() + +# Node that sends a version but not a verack. +class CNodeNoVerackIdle(CLazyNode): + def __init__(self): + self.version_received = False + super().__init__() + + def on_reject(self, conn, message): pass + def on_verack(self, conn, message): pass + # When version is received, don't reply with a verack. Instead, see if the + # node will give us a message that it shouldn't. This is not an exhaustive + # list! + def on_version(self, conn, message): + self.version_received = True + conn.send_message(msg_ping()) + conn.send_message(msg_getaddr()) + +class P2PLeakTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.num_nodes = 1 + def setup_network(self): + extra_args = [['-debug', '-banscore='+str(banscore)] + for i in range(self.num_nodes)] + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args) + + def run_test(self): + no_version_bannode = CNodeNoVersionBan() + no_version_idlenode = CNodeNoVersionIdle() + no_verack_idlenode = CNodeNoVerackIdle() + + connections = [] + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_version_bannode, send_version=False)) + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_version_idlenode, send_version=False)) + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_verack_idlenode)) + no_version_bannode.add_connection(connections[0]) + no_version_idlenode.add_connection(connections[1]) + no_verack_idlenode.add_connection(connections[2]) + + NetworkThread().start() # Start up network handling in another thread + + assert(wait_until(lambda: no_version_bannode.connected and no_version_idlenode.connected and no_verack_idlenode.version_received, timeout=10)) + + # Mine a block and make sure that it's not sent to the connected nodes + self.nodes[0].generate(1) + + #Give the node enough time to possibly leak out a message + time.sleep(5) + + #This node should have been banned + assert(no_version_bannode.connection.state == "closed") + + [conn.disconnect_node() for conn in connections] + + # Make sure no unexpected messages came in + assert(no_version_bannode.unexpected_msg == False) + assert(no_version_idlenode.unexpected_msg == False) + assert(no_verack_idlenode.unexpected_msg == False) + +if __name__ == '__main__': + P2PLeakTest().main() diff --git a/qa/rpc-tests/p2p-mempool.py b/qa/rpc-tests/p2p-mempool.py index 382d7f1e82..c6b1490211 100755 --- a/qa/rpc-tests/p2p-mempool.py +++ b/qa/rpc-tests/p2p-mempool.py @@ -2,11 +2,15 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test p2p mempool message. + +Test that nodes are disconnected if they send mempool messages when bloom +filters are not enabled. +""" from test_framework.mininode import * from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * -import time class TestNode(NodeConnCB): def __init__(self): diff --git a/qa/rpc-tests/p2p-segwit.py b/qa/rpc-tests/p2p-segwit.py index 51adec5cae..470a5398b6 100755 --- a/qa/rpc-tests/p2p-segwit.py +++ b/qa/rpc-tests/p2p-segwit.py @@ -2,6 +2,7 @@ # Copyright (c) 2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test segwit transactions and blocks on P2P network.""" from test_framework.mininode import * from test_framework.test_framework import BitcoinTestFramework @@ -21,9 +22,6 @@ VB_TOP_BITS = 0x20000000 MAX_SIGOP_COST = 80000 -''' -SegWit p2p test. -''' # Calculate the virtual size of a witness block: # (base + witness/4) @@ -951,7 +949,6 @@ class SegWitTest(BitcoinTestFramework): tx.rehash() tx_hash = tx.sha256 - tx_value = tx.vout[0].nValue # Verify that unnecessary witnesses are rejected. self.test_node.announce_tx_and_wait_for_getdata(tx) @@ -1662,7 +1659,7 @@ class SegWitTest(BitcoinTestFramework): # too many sigops (contributing to legacy sigop count). checksig_count = (extra_sigops_available // 4) + 1 scriptPubKey_checksigs = CScript([OP_CHECKSIG]*checksig_count) - tx2.vout.append(CTxOut(0, scriptPubKey_checksigs)); + tx2.vout.append(CTxOut(0, scriptPubKey_checksigs)) tx2.vin.pop() tx2.wit.vtxinwit.pop() tx2.vout[0].nValue -= tx.vout[-2].nValue diff --git a/qa/rpc-tests/p2p-timeouts.py b/qa/rpc-tests/p2p-timeouts.py new file mode 100755 index 0000000000..7f596b6e4b --- /dev/null +++ b/qa/rpc-tests/p2p-timeouts.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +# Copyright (c) 2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test various net timeouts. + +- Create three bitcoind nodes: + + no_verack_node - we never send a verack in response to their version + no_version_node - we never send a version (only a ping) + no_send_node - we never send any P2P message. + +- Start all three nodes +- Wait 1 second +- Assert that we're connected +- Send a ping to no_verack_node and no_version_node +- Wait 30 seconds +- Assert that we're still connected +- Send a ping to no_verack_node and no_version_node +- Wait 31 seconds +- Assert that we're no longer connected (timeout to receive version/verack is 60 seconds) +""" + +from time import sleep + +from test_framework.mininode import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import * + +class TestNode(SingleNodeConnCB): + def __init__(self): + SingleNodeConnCB.__init__(self) + self.connected = False + self.received_version = False + + def on_open(self, conn): + self.connected = True + + def on_close(self, conn): + self.connected = False + + def on_version(self, conn, message): + # Don't send a verack in response + self.received_version = True + +class TimeoutsTest(BitcoinTestFramework): + def __init__(self): + super().__init__() + self.setup_clean_chain = True + self.num_nodes = 1 + + def setup_network(self): + self.nodes = [] + + # Start up node0 to be a version 1, pre-segwit node. + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, + [["-debug", "-logtimemicros=1"]]) + + def run_test(self): + # Setup the p2p connections and start up the network thread. + self.no_verack_node = TestNode() # never send verack + self.no_version_node = TestNode() # never send version (just ping) + self.no_send_node = TestNode() # never send anything + + connections = [] + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.no_verack_node)) + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.no_version_node, send_version=False)) + connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], self.no_send_node, send_version=False)) + self.no_verack_node.add_connection(connections[0]) + self.no_version_node.add_connection(connections[1]) + self.no_send_node.add_connection(connections[2]) + + NetworkThread().start() # Start up network handling in another thread + + sleep(1) + + assert(self.no_verack_node.connected) + assert(self.no_version_node.connected) + assert(self.no_send_node.connected) + + ping_msg = msg_ping() + connections[0].send_message(ping_msg) + connections[1].send_message(ping_msg) + + sleep(30) + + assert(self.no_verack_node.received_version) + + assert(self.no_verack_node.connected) + assert(self.no_version_node.connected) + assert(self.no_send_node.connected) + + connections[0].send_message(ping_msg) + connections[1].send_message(ping_msg) + + sleep(31) + + assert(not self.no_verack_node.connected) + assert(not self.no_version_node.connected) + assert(not self.no_send_node.connected) + +if __name__ == '__main__': + TimeoutsTest().main() diff --git a/qa/rpc-tests/p2p-versionbits-warning.py b/qa/rpc-tests/p2p-versionbits-warning.py index fc3eddddee..8e3e361fc1 100755 --- a/qa/rpc-tests/p2p-versionbits-warning.py +++ b/qa/rpc-tests/p2p-versionbits-warning.py @@ -2,6 +2,11 @@ # Copyright (c) 2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test version bits warning system. + +Generate chains with block versions that appear to be signalling unknown +soft-forks, and test that warning alerts are generated. +""" from test_framework.mininode import * from test_framework.test_framework import BitcoinTestFramework @@ -10,13 +15,6 @@ import re import time from test_framework.blocktools import create_block, create_coinbase -''' -Test version bits' warning system. - -Generate chains with block versions that appear to be signalling unknown -soft-forks, and test that warning alerts are generated. -''' - VB_PERIOD = 144 # versionbits period length for regtest VB_THRESHOLD = 108 # versionbits activation threshold for regtest VB_TOP_BITS = 0x20000000 diff --git a/qa/rpc-tests/preciousblock.py b/qa/rpc-tests/preciousblock.py index 3cefa51c0a..dde164f329 100755 --- a/qa/rpc-tests/preciousblock.py +++ b/qa/rpc-tests/preciousblock.py @@ -1,11 +1,8 @@ #!/usr/bin/env python3 -# Copyright (c) 2015 The Bitcoin Core developers +# Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test PreciousBlock code -# +"""Test the preciousblock RPC.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( @@ -102,7 +99,7 @@ class PreciousTest(BitcoinTestFramework): assert_equal(self.nodes[2].getblockcount(), 6) hashL = self.nodes[2].getbestblockhash() print("Connect nodes and check no reorg occurs") - node_sync_via_rpc(self.nodes[0:3]) + node_sync_via_rpc(self.nodes[1:3]) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) assert_equal(self.nodes[0].getbestblockhash(), hashH) diff --git a/qa/rpc-tests/prioritise_transaction.py b/qa/rpc-tests/prioritise_transaction.py index 85afeab2e3..9a63d0838f 100755 --- a/qa/rpc-tests/prioritise_transaction.py +++ b/qa/rpc-tests/prioritise_transaction.py @@ -2,10 +2,7 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test PrioritiseTransaction code -# +"""Test the prioritisetransaction mining RPC.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -39,7 +36,7 @@ class PrioritiseTransactionTest(BitcoinTestFramework): txids.append([]) start_range = i * range_size end_range = start_range + range_size - txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[start_range:end_range], (i+1)*base_fee) + txids[i] = create_lots_of_big_transactions(self.nodes[0], self.txouts, utxos[start_range:end_range], end_range - start_range, (i+1)*base_fee) # Make sure that the size of each group of transactions exceeds # MAX_BLOCK_BASE_SIZE -- otherwise the test needs to be revised to create diff --git a/qa/rpc-tests/proxy_test.py b/qa/rpc-tests/proxy_test.py index 9ccc0ffbb0..e4e231f312 100755 --- a/qa/rpc-tests/proxy_test.py +++ b/qa/rpc-tests/proxy_test.py @@ -2,20 +2,8 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test bitcoind with different proxy configuration. -import socket -import os - -from test_framework.socks5 import Socks5Configuration, Socks5Command, Socks5Server, AddressType -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import ( - PORT_MIN, - PORT_RANGE, - start_nodes, - assert_equal, -) -from test_framework.netutil import test_ipv6_local -''' Test plan: - Start bitcoind's with different proxy configurations - Use addnode to initiate connections @@ -37,7 +25,20 @@ addnode connect to IPv4 addnode connect to IPv6 addnode connect to onion addnode connect to generic DNS name -''' +""" + +import socket +import os + +from test_framework.socks5 import Socks5Configuration, Socks5Command, Socks5Server, AddressType +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + PORT_MIN, + PORT_RANGE, + start_nodes, + assert_equal, +) +from test_framework.netutil import test_ipv6_local RANGE_BEGIN = PORT_MIN + 2 * PORT_RANGE # Start after p2p and rpc ports diff --git a/qa/rpc-tests/pruning.py b/qa/rpc-tests/pruning.py index 6635b0dff2..ace8ced422 100755 --- a/qa/rpc-tests/pruning.py +++ b/qa/rpc-tests/pruning.py @@ -2,17 +2,25 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the pruning code. -# -# Test pruning code -# ******** -# WARNING: -# This test uses 4GB of disk space. -# This test takes 30 mins or more (up to 2 hours) -# ******** +WARNING: +This test uses 4GB of disk space. +This test takes 30 mins or more (up to 2 hours) +""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * +import time +import os + +MIN_BLOCKS_TO_KEEP = 288 + +# Rescans start at the earliest block up to 2 hours before a key timestamp, so +# the manual prune RPC avoids pruning blocks in the same window to be +# compatible with pruning based on key creation time. +RESCAN_WINDOW = 2 * 60 * 60 + def calc_usage(blockdir): return sum(os.path.getsize(blockdir+f) for f in os.listdir(blockdir) if os.path.isfile(blockdir+f)) / (1024. * 1024.) @@ -22,7 +30,11 @@ class PruneTest(BitcoinTestFramework): def __init__(self): super().__init__() self.setup_clean_chain = True - self.num_nodes = 3 + self.num_nodes = 6 + + # Cache for utxos, as the listunspent may take a long time later in the test + self.utxo_cache_0 = [] + self.utxo_cache_1 = [] def setup_network(self): self.nodes = [] @@ -36,10 +48,22 @@ class PruneTest(BitcoinTestFramework): self.nodes.append(start_node(2, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-prune=550"], timewait=900)) self.prunedir = self.options.tmpdir+"/node2/regtest/blocks/" + # Create nodes 3 and 4 to test manual pruning (they will be re-started with manual pruning later) + self.nodes.append(start_node(3, self.options.tmpdir, ["-debug=0","-maxreceivebuffer=20000","-blockmaxsize=999000"], timewait=900)) + self.nodes.append(start_node(4, self.options.tmpdir, ["-debug=0","-maxreceivebuffer=20000","-blockmaxsize=999000"], timewait=900)) + + # Create nodes 5 to test wallet in prune mode, but do not connect + self.nodes.append(start_node(5, self.options.tmpdir, ["-debug=0", "-prune=550"])) + + # Determine default relay fee + self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"] + connect_nodes(self.nodes[0], 1) connect_nodes(self.nodes[1], 2) connect_nodes(self.nodes[2], 0) - sync_blocks(self.nodes[0:3]) + connect_nodes(self.nodes[0], 3) + connect_nodes(self.nodes[0], 4) + sync_blocks(self.nodes[0:5]) def create_big_chain(self): # Start by creating some coinbases we can spend later @@ -48,9 +72,9 @@ class PruneTest(BitcoinTestFramework): self.nodes[0].generate(150) # Then mine enough full blocks to create more than 550MiB of data for i in range(645): - mine_large_block(self.nodes[0]) + mine_large_block(self.nodes[0], self.utxo_cache_0) - sync_blocks(self.nodes[0:3]) + sync_blocks(self.nodes[0:5]) def test_height_min(self): if not os.path.isfile(self.prunedir+"blk00000.dat"): @@ -60,7 +84,7 @@ class PruneTest(BitcoinTestFramework): print("Mining 25 more blocks should cause the first block file to be pruned") # Pruning doesn't run until we're allocating another chunk, 20 full blocks past the height cutoff will ensure this for i in range(25): - mine_large_block(self.nodes[0]) + mine_large_block(self.nodes[0], self.utxo_cache_0) waitstart = time.time() while os.path.isfile(self.prunedir+"blk00000.dat"): @@ -82,18 +106,18 @@ class PruneTest(BitcoinTestFramework): # Disconnect node 0 so it can mine a longer reorg chain without knowing about node 1's soon-to-be-stale chain # Node 2 stays connected, so it hears about the stale blocks and then reorg's when node0 reconnects # Stopping node 0 also clears its mempool, so it doesn't have node1's transactions to accidentally mine - stop_node(self.nodes[0],0) + self.stop_node(0) self.nodes[0]=start_node(0, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-blockmaxsize=999000", "-checkblocks=5"], timewait=900) # Mine 24 blocks in node 1 for i in range(24): if j == 0: - mine_large_block(self.nodes[1]) + mine_large_block(self.nodes[1], self.utxo_cache_1) else: self.nodes[1].generate(1) #tx's already in mempool from previous disconnects # Reorg back with 25 block chain from node 0 for i in range(25): - mine_large_block(self.nodes[0]) + mine_large_block(self.nodes[0], self.utxo_cache_0) # Create connections in the order so both nodes can see the reorg at the same time connect_nodes(self.nodes[1], 0) @@ -107,7 +131,7 @@ class PruneTest(BitcoinTestFramework): # This will cause Node 2 to do a reorg requiring 288 blocks of undo data to the reorg_test chain # Reboot node 1 to clear its mempool (hopefully make the invalidate faster) # Lower the block max size so we don't keep mining all our big mempool transactions (from disconnected blocks) - stop_node(self.nodes[1],1) + self.stop_node(1) self.nodes[1]=start_node(1, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"], timewait=900) height = self.nodes[1].getblockcount() @@ -130,7 +154,7 @@ class PruneTest(BitcoinTestFramework): print("New best height", self.nodes[1].getblockcount()) # Reboot node1 to clear those giant tx's from mempool - stop_node(self.nodes[1],1) + self.stop_node(1) self.nodes[1]=start_node(1, self.options.tmpdir, ["-debug","-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"], timewait=900) print("Generating new longer chain of 300 more blocks") @@ -205,6 +229,114 @@ class PruneTest(BitcoinTestFramework): # Verify we can now have the data for a block previously pruned assert(self.nodes[2].getblock(self.forkhash)["height"] == self.forkheight) + def manual_test(self, node_number, use_timestamp): + # at this point, node has 995 blocks and has not yet run in prune mode + node = self.nodes[node_number] = start_node(node_number, self.options.tmpdir, ["-debug=0"], timewait=900) + assert_equal(node.getblockcount(), 995) + assert_raises_message(JSONRPCException, "not in prune mode", node.pruneblockchain, 500) + self.stop_node(node_number) + + # now re-start in manual pruning mode + node = self.nodes[node_number] = start_node(node_number, self.options.tmpdir, ["-debug=0","-prune=1"], timewait=900) + assert_equal(node.getblockcount(), 995) + + def height(index): + if use_timestamp: + return node.getblockheader(node.getblockhash(index))["time"] + RESCAN_WINDOW + else: + return index + + def prune(index, expected_ret=None): + ret = node.pruneblockchain(height(index)) + # Check the return value. When use_timestamp is True, just check + # that the return value is less than or equal to the expected + # value, because when more than one block is generated per second, + # a timestamp will not be granular enough to uniquely identify an + # individual block. + if expected_ret is None: + expected_ret = index + if use_timestamp: + assert_greater_than(ret, 0) + assert_greater_than(expected_ret + 1, ret) + else: + assert_equal(ret, expected_ret) + + def has_block(index): + return os.path.isfile(self.options.tmpdir + "/node{}/regtest/blocks/blk{:05}.dat".format(node_number, index)) + + # should not prune because chain tip of node 3 (995) < PruneAfterHeight (1000) + assert_raises_message(JSONRPCException, "Blockchain is too short for pruning", node.pruneblockchain, height(500)) + + # mine 6 blocks so we are at height 1001 (i.e., above PruneAfterHeight) + node.generate(6) + assert_equal(node.getblockchaininfo()["blocks"], 1001) + + # negative heights should raise an exception + assert_raises_message(JSONRPCException, "Negative", node.pruneblockchain, -10) + + # height=100 too low to prune first block file so this is a no-op + prune(100) + if not has_block(0): + raise AssertionError("blk00000.dat is missing when should still be there") + + # Does nothing + node.pruneblockchain(height(0)) + if not has_block(0): + raise AssertionError("blk00000.dat is missing when should still be there") + + # height=500 should prune first file + prune(500) + if has_block(0): + raise AssertionError("blk00000.dat is still there, should be pruned by now") + if not has_block(1): + raise AssertionError("blk00001.dat is missing when should still be there") + + # height=650 should prune second file + prune(650) + if has_block(1): + raise AssertionError("blk00001.dat is still there, should be pruned by now") + + # height=1000 should not prune anything more, because tip-288 is in blk00002.dat. + prune(1000, 1001 - MIN_BLOCKS_TO_KEEP) + if not has_block(2): + raise AssertionError("blk00002.dat is still there, should be pruned by now") + + # advance the tip so blk00002.dat and blk00003.dat can be pruned (the last 288 blocks should now be in blk00004.dat) + node.generate(288) + prune(1000) + if has_block(2): + raise AssertionError("blk00002.dat is still there, should be pruned by now") + if has_block(3): + raise AssertionError("blk00003.dat is still there, should be pruned by now") + + # stop node, start back up with auto-prune at 550MB, make sure still runs + self.stop_node(node_number) + self.nodes[node_number] = start_node(node_number, self.options.tmpdir, ["-debug=0","-prune=550"], timewait=900) + + print("Success") + + def wallet_test(self): + # check that the pruning node's wallet is still in good shape + print("Stop and start pruning node to trigger wallet rescan") + try: + self.stop_node(2) + start_node(2, self.options.tmpdir, ["-debug=1","-prune=550"]) + print("Success") + except Exception as detail: + raise AssertionError("Wallet test: unable to re-start the pruning node") + + # check that wallet loads loads successfully when restarting a pruned node after IBD. + # this was reported to fail in #7494. + print ("Syncing node 5 to test wallet") + connect_nodes(self.nodes[0], 5) + nds = [self.nodes[0], self.nodes[5]] + sync_blocks(nds, wait=5, timeout=300) + try: + self.stop_node(5) #stop and start to trigger rescan + start_node(5, self.options.tmpdir, ["-debug=1","-prune=550"]) + print ("Success") + except Exception as detail: + raise AssertionError("Wallet test: unable to re-start node5") def run_test(self): print("Warning! This test requires 4GB of disk space and takes over 30 mins (up to 2 hours)") @@ -219,6 +351,10 @@ class PruneTest(BitcoinTestFramework): # Start by mining a simple chain that all nodes have # N0=N1=N2 **...*(995) + # stop manual-pruning node with 995 blocks + self.stop_node(3) + self.stop_node(4) + print("Check that we haven't started pruning yet because we're below PruneAfterHeight") self.test_height_min() # Extend this chain past the PruneAfterHeight @@ -301,6 +437,15 @@ class PruneTest(BitcoinTestFramework): # # N1 doesn't change because 1033 on main chain (*) is invalid + print("Test manual pruning with block indices") + self.manual_test(3, use_timestamp=False) + + print("Test manual pruning with timestamps") + self.manual_test(4, use_timestamp=True) + + print("Test wallet re-scan") + self.wallet_test() + print("Done") if __name__ == '__main__': diff --git a/qa/rpc-tests/rawtransactions.py b/qa/rpc-tests/rawtransactions.py index 33a6f2b0cc..5adef31207 100755 --- a/qa/rpc-tests/rawtransactions.py +++ b/qa/rpc-tests/rawtransactions.py @@ -2,14 +2,14 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""rawtranscation RPCs QA test. - -# Tests the following RPCs: -# - createrawtransaction -# - signrawtransaction -# - sendrawtransaction -# - decoderawtransaction -# - getrawtransaction +"""Test the rawtranscation RPCs. + +Test the following RPCs: + - createrawtransaction + - signrawtransaction + - sendrawtransaction + - decoderawtransaction + - getrawtransaction """ from test_framework.test_framework import BitcoinTestFramework diff --git a/qa/rpc-tests/receivedby.py b/qa/rpc-tests/receivedby.py index 4f17b661cb..248bcdbd68 100755 --- a/qa/rpc-tests/receivedby.py +++ b/qa/rpc-tests/receivedby.py @@ -2,19 +2,16 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# Exercise the listreceivedbyaddress API +"""Test the listreceivedbyaddress RPC.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * - def get_sub_array_from_array(object_array, to_match): ''' Finds and returns a sub array from an array of arrays. to_match should be a unique idetifier of a sub array ''' - num_matched = 0 for item in object_array: all_match = True for key,value in to_match.items(): @@ -104,7 +101,7 @@ class ReceivedByTest(BitcoinTestFramework): received_by_account_json = get_sub_array_from_array(self.nodes[1].listreceivedbyaccount(),{"account":account}) if len(received_by_account_json) == 0: raise AssertionError("No accounts found in node") - balance_by_account = rec_by_accountArr = self.nodes[1].getreceivedbyaccount(account) + balance_by_account = self.nodes[1].getreceivedbyaccount(account) txid = self.nodes[0].sendtoaddress(addr, 0.1) self.sync_all() diff --git a/qa/rpc-tests/reindex.py b/qa/rpc-tests/reindex.py index 25cf4c1679..1b547a920f 100755 --- a/qa/rpc-tests/reindex.py +++ b/qa/rpc-tests/reindex.py @@ -2,10 +2,13 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test running bitcoind with -reindex and -reindex-chainstate options. + +- Start a single node and generate 3 blocks. +- Stop the node and restart it with -reindex. Verify that the node has reindexed up to block 3. +- Stop the node and restart it with -reindex-chainstate. Verify that the node has reindexed up to block 3. +""" -# -# Test -reindex and -reindex-chainstate with CheckBlockIndex -# from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( start_nodes, diff --git a/qa/rpc-tests/replace-by-fee.py b/qa/rpc-tests/replace-by-fee.py index 34c0f9d795..a3c1deddf6 100755 --- a/qa/rpc-tests/replace-by-fee.py +++ b/qa/rpc-tests/replace-by-fee.py @@ -2,10 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test replace by fee code -# +"""Test the RBF code.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -76,7 +73,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): def setup_network(self): self.nodes = [] self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", "-debug", - "-relaypriority=0", "-whitelist=127.0.0.1", + "-whitelist=127.0.0.1", "-limitancestorcount=50", "-limitancestorsize=101", "-limitdescendantcount=200", @@ -393,7 +390,6 @@ class ReplaceByFeeTest(BitcoinTestFramework): utxo = make_utxo(self.nodes[0], initial_nValue) fee = int(0.0001*COIN) split_value = int((initial_nValue-fee)/(MAX_REPLACEMENT_LIMIT+1)) - actual_fee = initial_nValue - split_value*(MAX_REPLACEMENT_LIMIT+1) outputs = [] for i in range(MAX_REPLACEMENT_LIMIT+1): @@ -443,7 +439,7 @@ class ReplaceByFeeTest(BitcoinTestFramework): self.nodes[0].sendrawtransaction(double_tx_hex, True) def test_opt_in(self): - """ Replacing should only work if orig tx opted in """ + """Replacing should only work if orig tx opted in""" tx0_outpoint = make_utxo(self.nodes[0], int(1.1*COIN)) # Create a non-opting in transaction diff --git a/qa/rpc-tests/rest.py b/qa/rpc-tests/rest.py index b769cd71f2..9ca1cc187a 100755 --- a/qa/rpc-tests/rest.py +++ b/qa/rpc-tests/rest.py @@ -2,11 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test REST interface -# - +"""Test the REST API.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * diff --git a/qa/rpc-tests/rpcbind_test.py b/qa/rpc-tests/rpcbind_test.py index d78d0b884e..499fe33679 100755 --- a/qa/rpc-tests/rpcbind_test.py +++ b/qa/rpc-tests/rpcbind_test.py @@ -2,8 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# Test for -rpcbind, as well as -rpcallowip and -rpcconnect +"""Test running bitcoind with the -rpcbind and -rpcallowip options.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -35,11 +34,9 @@ class RPCBindTest(BitcoinTestFramework): base_args += ['-rpcallowip=' + x for x in allow_ips] binds = ['-rpcbind='+addr for addr in addresses] self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [base_args + binds], connect_to) - try: - pid = bitcoind_processes[0].pid - assert_equal(set(get_bind_addrs(pid)), set(expected)) - finally: - stop_nodes(self.nodes) + pid = bitcoind_processes[0].pid + assert_equal(set(get_bind_addrs(pid)), set(expected)) + stop_nodes(self.nodes) def run_allowip_test(self, allow_ips, rpchost, rpcport): ''' @@ -48,13 +45,10 @@ class RPCBindTest(BitcoinTestFramework): ''' base_args = ['-disablewallet', '-nolisten'] + ['-rpcallowip='+x for x in allow_ips] self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, [base_args]) - try: - # connect to node through non-loopback interface - node = get_rpc_proxy(rpc_url(0, "%s:%d" % (rpchost, rpcport)), 0) - node.getnetworkinfo() - finally: - node = None # make sure connection will be garbage collected and closed - stop_nodes(self.nodes) + # connect to node through non-loopback interface + node = get_rpc_proxy(rpc_url(0, "%s:%d" % (rpchost, rpcport)), 0) + node.getnetworkinfo() + stop_nodes(self.nodes) def run_test(self): # due to OS-specific network stats queries, this test works only on Linux diff --git a/qa/rpc-tests/rpcnamedargs.py b/qa/rpc-tests/rpcnamedargs.py new file mode 100755 index 0000000000..f6175c8ca7 --- /dev/null +++ b/qa/rpc-tests/rpcnamedargs.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# Copyright (c) 2016 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test using named arguments for RPCs.""" + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + assert_raises_jsonrpc, + start_nodes, +) + + +class NamedArgumentTest(BitcoinTestFramework): + """ + Test named arguments on RPC calls. + """ + + def __init__(self): + super().__init__() + self.setup_clean_chain = False + self.num_nodes = 1 + + def setup_network(self, split=False): + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir) + self.is_network_split = False + self.sync_all() + + def run_test(self): + node = self.nodes[0] + h = node.help(command='getinfo') + assert(h.startswith('getinfo\n')) + + assert_raises_jsonrpc(-8, 'Unknown named parameter', node.help, random='getinfo') + + h = node.getblockhash(height=0) + node.getblock(blockhash=h) + + assert_equal(node.echo(), []) + assert_equal(node.echo(arg0=0,arg9=9), [0] + [None]*8 + [9]) + assert_equal(node.echo(arg1=1), [None, 1]) + assert_equal(node.echo(arg9=None), [None]*10) + assert_equal(node.echo(arg0=0,arg3=3,arg9=9), [0] + [None]*2 + [3] + [None]*5 + [9]) + +if __name__ == '__main__': + NamedArgumentTest().main() diff --git a/qa/rpc-tests/segwit.py b/qa/rpc-tests/segwit.py index 41a1b3b20f..f475427842 100755 --- a/qa/rpc-tests/segwit.py +++ b/qa/rpc-tests/segwit.py @@ -2,10 +2,7 @@ # Copyright (c) 2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test the SegWit changeover logic -# +"""Test the SegWit changeover logic.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -13,6 +10,7 @@ from test_framework.mininode import sha256, ripemd160, CTransaction, CTxIn, COut 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 from io import BytesIO +from test_framework.mininode import FromHex NODE_0 = 0 NODE_1 = 1 @@ -38,7 +36,7 @@ def addlength(script): return scriptlen + script def create_witnessprogram(version, node, utxo, pubkey, encode_p2sh, amount): - pkscript = witness_script(version, pubkey); + pkscript = witness_script(version, pubkey) if (encode_p2sh): p2sh_hash = bytes_to_hex_str(ripemd160(sha256(hex_str_to_bytes(pkscript)))) pkscript = "a914"+p2sh_hash+"87" @@ -84,8 +82,8 @@ class SegWitTest(BitcoinTestFramework): def setup_network(self): self.nodes = [] - self.nodes.append(start_node(0, self.options.tmpdir, ["-logtimemicros", "-debug", "-walletprematurewitness"])) - self.nodes.append(start_node(1, self.options.tmpdir, ["-logtimemicros", "-debug", "-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness"])) + self.nodes.append(start_node(0, self.options.tmpdir, ["-logtimemicros", "-debug", "-walletprematurewitness", "-rpcserialversion=0"])) + self.nodes.append(start_node(1, self.options.tmpdir, ["-logtimemicros", "-debug", "-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-rpcserialversion=1"])) self.nodes.append(start_node(2, self.options.tmpdir, ["-logtimemicros", "-debug", "-blockversion=536870915", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness"])) connect_nodes(self.nodes[1], 0) connect_nodes(self.nodes[2], 1) @@ -129,10 +127,14 @@ class SegWitTest(BitcoinTestFramework): print("Verify sigops are counted in GBT with pre-BIP141 rules before the fork") txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) tmpl = self.nodes[0].getblocktemplate({}) + assert(tmpl['sizelimit'] == 1000000) + assert('weightlimit' not in tmpl) assert(tmpl['sigoplimit'] == 20000) assert(tmpl['transactions'][0]['hash'] == txid) assert(tmpl['transactions'][0]['sigops'] == 2) tmpl = self.nodes[0].getblocktemplate({'rules':['segwit']}) + assert(tmpl['sizelimit'] == 1000000) + assert('weightlimit' not in tmpl) assert(tmpl['sigoplimit'] == 20000) assert(tmpl['transactions'][0]['hash'] == txid) assert(tmpl['transactions'][0]['sigops'] == 2) @@ -211,7 +213,19 @@ class SegWitTest(BitcoinTestFramework): block = self.nodes[2].generate(1) #block 432 (first block with new rules; 432 = 144 * 3) sync_blocks(self.nodes) assert_equal(len(self.nodes[2].getrawmempool()), 0) - assert_equal(len(self.nodes[2].getblock(block[0])["tx"]), 5) + segwit_tx_list = self.nodes[2].getblock(block[0])["tx"] + assert_equal(len(segwit_tx_list), 5) + + print("Verify block and transaction serialization rpcs return differing serializations depending on rpc serialization flag") + assert(self.nodes[2].getblock(block[0], False) != self.nodes[0].getblock(block[0], False)) + assert(self.nodes[1].getblock(block[0], False) == self.nodes[2].getblock(block[0], False)) + for i in range(len(segwit_tx_list)): + tx = FromHex(CTransaction(), self.nodes[2].gettransaction(segwit_tx_list[i])["hex"]) + assert(self.nodes[2].getrawtransaction(segwit_tx_list[i]) != self.nodes[0].getrawtransaction(segwit_tx_list[i])) + assert(self.nodes[1].getrawtransaction(segwit_tx_list[i], 0) == self.nodes[2].getrawtransaction(segwit_tx_list[i])) + assert(self.nodes[0].getrawtransaction(segwit_tx_list[i]) != self.nodes[2].gettransaction(segwit_tx_list[i])["hex"]) + assert(self.nodes[1].getrawtransaction(segwit_tx_list[i]) == self.nodes[2].gettransaction(segwit_tx_list[i])["hex"]) + assert(self.nodes[0].getrawtransaction(segwit_tx_list[i]) == bytes_to_hex_str(tx.serialize_without_witness())) print("Verify witness txs without witness data are invalid after the fork") self.fail_mine(self.nodes[2], wit_ids[NODE_2][WIT_V0][2], False) @@ -228,6 +242,8 @@ class SegWitTest(BitcoinTestFramework): print("Verify sigops are counted in GBT with BIP141 rules after the fork") txid = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), 1) tmpl = self.nodes[0].getblocktemplate({'rules':['segwit']}) + assert(tmpl['sizelimit'] >= 3999577) # actual maximum size is lower due to minimum mandatory non-witness data + assert(tmpl['weightlimit'] == 4000000) assert(tmpl['sigoplimit'] == 80000) assert(tmpl['transactions'][0]['txid'] == txid) assert(tmpl['transactions'][0]['sigops'] == 8) @@ -237,6 +253,8 @@ class SegWitTest(BitcoinTestFramework): try: tmpl = self.nodes[0].getblocktemplate({}) assert(len(tmpl['transactions']) == 1) # Doesn't include witness tx + assert(tmpl['sizelimit'] == 1000000) + assert('weightlimit' not in tmpl) assert(tmpl['sigoplimit'] == 20000) assert(tmpl['transactions'][0]['hash'] == txid) assert(tmpl['transactions'][0]['sigops'] == 2) diff --git a/qa/rpc-tests/sendheaders.py b/qa/rpc-tests/sendheaders.py index 37b98c576e..16ca4a4913 100755 --- a/qa/rpc-tests/sendheaders.py +++ b/qa/rpc-tests/sendheaders.py @@ -2,14 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -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 - -''' -SendHeadersTest -- test behavior of headers messages to announce blocks. +"""Test behavior of headers messages to announce blocks. Setup: @@ -78,7 +71,13 @@ d. Announce 49 headers that don't connect. Expect: getheaders message each time. e. Announce one more that doesn't connect. Expect: disconnect. -''' +""" + +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 + direct_fetch_response_time = 0.05 diff --git a/qa/rpc-tests/signmessages.py b/qa/rpc-tests/signmessages.py index 31b6f14a26..91f5abef5d 100755 --- a/qa/rpc-tests/signmessages.py +++ b/qa/rpc-tests/signmessages.py @@ -2,13 +2,12 @@ # Copyright (c) 2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test RPC commands for signing and verifying messages.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * - class SignMessagesTest(BitcoinTestFramework): - """Tests RPC commands for signing and verifying messages.""" def __init__(self): super().__init__() diff --git a/qa/rpc-tests/signrawtransactions.py b/qa/rpc-tests/signrawtransactions.py index c61a280616..b24162ab97 100755 --- a/qa/rpc-tests/signrawtransactions.py +++ b/qa/rpc-tests/signrawtransactions.py @@ -2,14 +2,13 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test transaction signing using the signrawtransaction RPC.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class SignRawTransactionsTest(BitcoinTestFramework): - """Tests transaction signing via RPC command "signrawtransaction".""" - def __init__(self): super().__init__() self.setup_clean_chain = True @@ -20,18 +19,20 @@ class SignRawTransactionsTest(BitcoinTestFramework): self.is_network_split = False def successful_signing_test(self): - """Creates and signs a valid raw transaction with one input. + """Create and sign a valid raw transaction with one input. Expected results: 1) The transaction has a complete set of signatures 2) No script verification error occurred""" - privKeys = ['cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N'] + privKeys = ['cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N', 'cVKpPfVKSJxKqVpE9awvXNWuLHCa5j5tiE7K6zbUSptFpTEtiFrA'] inputs = [ - # Valid pay-to-pubkey script + # Valid pay-to-pubkey scripts {'txid': '9b907ef1e3c26fc71fe4a4b3580bc75264112f95050014157059c736f0202e71', 'vout': 0, - 'scriptPubKey': '76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac'} + 'scriptPubKey': '76a91460baa0f494b38ce3c940dea67f3804dc52d1fb9488ac'}, + {'txid': '83a4f6a6b73660e13ee6cb3c6063fa3759c50c9b7521d0536022961898f4fb02', 'vout': 0, + 'scriptPubKey': '76a914669b857c03a5ed269d5d85a1ffac9ed5d663072788ac'}, ] outputs = {'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB': 0.1} @@ -46,8 +47,24 @@ class SignRawTransactionsTest(BitcoinTestFramework): # 2) No script verification error occurred assert 'errors' not in rawTxSigned + # Check that signrawtransaction doesn't blow up on garbage merge attempts + dummyTxInconsistent = self.nodes[0].createrawtransaction([inputs[0]], outputs) + rawTxUnsigned = self.nodes[0].signrawtransaction(rawTx + dummyTxInconsistent, inputs) + + assert 'complete' in rawTxUnsigned + assert_equal(rawTxUnsigned['complete'], False) + + # Check that signrawtransaction properly merges unsigned and signed txn, even with garbage in the middle + rawTxSigned2 = self.nodes[0].signrawtransaction(rawTxUnsigned["hex"] + dummyTxInconsistent + rawTxSigned["hex"], inputs) + + assert 'complete' in rawTxSigned2 + assert_equal(rawTxSigned2['complete'], True) + + assert 'errors' not in rawTxSigned2 + + def script_verification_error_test(self): - """Creates and signs a raw transaction with valid (vin 0), invalid (vin 1) and one missing (vin 2) input script. + """Create and sign a raw transaction with valid (vin 0), invalid (vin 1) and one missing (vin 2) input script. Expected results: @@ -78,6 +95,16 @@ class SignRawTransactionsTest(BitcoinTestFramework): outputs = {'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB': 0.1} rawTx = self.nodes[0].createrawtransaction(inputs, outputs) + + # Make sure decoderawtransaction is at least marginally sane + decodedRawTx = self.nodes[0].decoderawtransaction(rawTx) + for i, inp in enumerate(inputs): + assert_equal(decodedRawTx["vin"][i]["txid"], inp["txid"]) + assert_equal(decodedRawTx["vin"][i]["vout"], inp["vout"]) + + # Make sure decoderawtransaction throws if there is extra data + assert_raises(JSONRPCException, self.nodes[0].decoderawtransaction, rawTx + "00") + rawTxSigned = self.nodes[0].signrawtransaction(rawTx, scripts, privKeys) # 3) The transaction has no complete set of signatures diff --git a/qa/rpc-tests/smartfees.py b/qa/rpc-tests/smartfees.py index 74a74f679a..bde454968f 100755 --- a/qa/rpc-tests/smartfees.py +++ b/qa/rpc-tests/smartfees.py @@ -2,10 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test fee estimation code -# +"""Test fee estimation code.""" from collections import OrderedDict from test_framework.test_framework import BitcoinTestFramework @@ -21,7 +18,7 @@ P2SH_2 = "2NBdpwq8Aoo1EEKEXPNrKvr5xQr3M9UfcZA" # P2SH of "OP_2 OP_DROP" SCRIPT_SIG = ["0451025175", "0451025275"] def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee_increment): - ''' + """ Create and send a transaction with a random fee. The transaction pays to a trivial P2SH script, and assumes that its inputs are of the same form. @@ -29,7 +26,7 @@ def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee and attempts to use the confirmed list first for its inputs. It adds the newly created outputs to the unconfirmed list. Returns (raw transaction, fee) - ''' + """ # It's best to exponentially distribute our random fees # because the buckets are exponentially spaced. # Exponentially distributed from 1-128 * fee_increment @@ -71,12 +68,12 @@ def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee return (completetx, fee) def split_inputs(from_node, txins, txouts, initial_split = False): - ''' + """ We need to generate a lot of very small inputs so we can generate a ton of transactions and they will have low priority. This function takes an input from txins, and creates and sends a transaction which splits the value into 2 outputs which are appended to txouts. - ''' + """ prevtxout = txins.pop() inputs = [] inputs.append({ "txid" : prevtxout["txid"], "vout" : prevtxout["vout"] }) @@ -95,10 +92,10 @@ def split_inputs(from_node, txins, txouts, initial_split = False): txouts.append({ "txid" : txid, "vout" : 1 , "amount" : rem_change}) def check_estimates(node, fees_seen, max_invalid, print_estimates = True): - ''' + """ This function calls estimatefee and verifies that the estimates meet certain invariants. - ''' + """ all_estimates = [ node.estimatefee(i) for i in range(1,26) ] if print_estimates: print([str(all_estimates[e-1]) for e in [1,2,3,6,15,25]]) @@ -151,15 +148,15 @@ class EstimateFeeTest(BitcoinTestFramework): self.setup_clean_chain = False def setup_network(self): - ''' + """ We'll setup the network to have 3 nodes that all mine with different parameters. But first we need to use one node to create a lot of small low priority outputs which we will use to generate our transactions. - ''' + """ self.nodes = [] # Use node0 to mine blocks for input splitting self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", - "-relaypriority=0", "-whitelist=127.0.0.1"])) + "-whitelist=127.0.0.1"])) print("This test is time consuming, please be patient") print("Splitting inputs to small size so we can generate low priority tx's") @@ -197,12 +194,12 @@ class EstimateFeeTest(BitcoinTestFramework): # (17k is room enough for 110 or so transactions) self.nodes.append(start_node(1, self.options.tmpdir, ["-blockprioritysize=1500", "-blockmaxsize=17000", - "-maxorphantx=1000", "-relaypriority=0", "-debug=estimatefee"])) + "-maxorphantx=1000", "-debug=estimatefee"])) connect_nodes(self.nodes[1], 0) # Node2 is a stingy miner, that # produces too small blocks (room for only 55 or so transactions) - node2args = ["-blockprioritysize=0", "-blockmaxsize=8000", "-maxorphantx=1000", "-relaypriority=0"] + node2args = ["-blockprioritysize=0", "-blockmaxsize=8000", "-maxorphantx=1000"] self.nodes.append(start_node(2, self.options.tmpdir, node2args)) connect_nodes(self.nodes[0], 2) diff --git a/qa/rpc-tests/test_framework/address.py b/qa/rpc-tests/test_framework/address.py index 50b999be61..96bebe1ea1 100644 --- a/qa/rpc-tests/test_framework/address.py +++ b/qa/rpc-tests/test_framework/address.py @@ -2,12 +2,7 @@ # Copyright (c) 2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# address.py -# -# This file encodes and decodes BASE58 P2PKH and P2SH addresses -# +"""Encode and decode BASE58, P2PKH and P2SH addresses.""" from .script import hash256, hash160, sha256, CScript, OP_0 from .util import bytes_to_hex_str, hex_str_to_bytes diff --git a/qa/rpc-tests/test_framework/authproxy.py b/qa/rpc-tests/test_framework/authproxy.py index 9bee1962e2..9ab3094b06 100644 --- a/qa/rpc-tests/test_framework/authproxy.py +++ b/qa/rpc-tests/test_framework/authproxy.py @@ -1,37 +1,36 @@ - -""" - Copyright (c) 2011 Jeff Garzik - - AuthServiceProxy has the following improvements over python-jsonrpc's - ServiceProxy class: - - - HTTP connections persist for the life of the AuthServiceProxy object - (if server supports HTTP/1.1) - - sends protocol 'version', per JSON-RPC 1.1 - - sends proper, incrementing 'id' - - sends Basic HTTP authentication headers - - parses all JSON numbers that look like floats as Decimal - - uses standard Python json lib - - Previous copyright, from python-jsonrpc/jsonrpc/proxy.py: - - Copyright (c) 2007 Jan-Klaas Kollhof - - This file is part of jsonrpc. - - jsonrpc is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - This software is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this software; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# Copyright (c) 2011 Jeff Garzik +# +# Previous copyright, from python-jsonrpc/jsonrpc/proxy.py: +# +# Copyright (c) 2007 Jan-Klaas Kollhof +# +# This file is part of jsonrpc. +# +# jsonrpc is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# This software is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this software; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +"""HTTP proxy for opening RPC connection to bitcoind. + +AuthServiceProxy has the following improvements over python-jsonrpc's +ServiceProxy class: + +- HTTP connections persist for the life of the AuthServiceProxy object + (if server supports HTTP/1.1) +- sends protocol 'version', per JSON-RPC 1.1 +- sends proper, incrementing 'id' +- sends Basic HTTP authentication headers +- parses all JSON numbers that look like floats as Decimal +- uses standard Python json lib """ try: @@ -138,14 +137,16 @@ class AuthServiceProxy(object): self.__conn.request(method, path, postdata, headers) return self._get_response() - def __call__(self, *args): + def __call__(self, *args, **argsn): AuthServiceProxy.__id_count += 1 log.debug("-%s-> %s %s"%(AuthServiceProxy.__id_count, self._service_name, json.dumps(args, default=EncodeDecimal, ensure_ascii=self.ensure_ascii))) + if args and argsn: + raise ValueError('Cannot handle both named and positional arguments') postdata = json.dumps({'version': '1.1', 'method': self._service_name, - 'params': args, + 'params': args or argsn, 'id': AuthServiceProxy.__id_count}, default=EncodeDecimal, ensure_ascii=self.ensure_ascii) response = self._request('POST', self.__url.path, postdata.encode('utf-8')) if response['error'] is not None: diff --git a/qa/rpc-tests/test_framework/bignum.py b/qa/rpc-tests/test_framework/bignum.py index ef800e4d57..024611da6e 100644 --- a/qa/rpc-tests/test_framework/bignum.py +++ b/qa/rpc-tests/test_framework/bignum.py @@ -1,15 +1,11 @@ #!/usr/bin/env python3 # -# bignum.py -# -# This file is copied from python-bitcoinlib. -# # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -# - -"""Bignum routines""" +"""Big number routines. +This file is copied from python-bitcoinlib. +""" import struct diff --git a/qa/rpc-tests/test_framework/blockstore.py b/qa/rpc-tests/test_framework/blockstore.py index 28a6b92b81..5280d18cdc 100644 --- a/qa/rpc-tests/test_framework/blockstore.py +++ b/qa/rpc-tests/test_framework/blockstore.py @@ -2,16 +2,20 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -# BlockStore: a helper class that keeps a map of blocks and implements -# helper functions for responding to getheaders and getdata, -# and for constructing a getheaders message -# +"""BlockStore and TxStore helper classes.""" from .mininode import * from io import BytesIO import dbm.dumb as dbmd class BlockStore(object): + """BlockStore helper class. + + BlockStore keeps a map of blocks and implements helper functions for + responding to getheaders and getdata, and for constructing a getheaders + message. + """ + def __init__(self, datadir): self.blockDB = dbmd.open(datadir + "/blocks", 'c') self.currentBlock = 0 diff --git a/qa/rpc-tests/test_framework/blocktools.py b/qa/rpc-tests/test_framework/blocktools.py index f69958823c..2c9a0857df 100644 --- a/qa/rpc-tests/test_framework/blocktools.py +++ b/qa/rpc-tests/test_framework/blocktools.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 -# blocktools.py - utilities for manipulating blocks and transactions # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Utilities for manipulating blocks and transactions.""" from .mininode import * from .script import CScript, OP_TRUE, OP_CHECKSIG, OP_RETURN diff --git a/qa/rpc-tests/test_framework/comptool.py b/qa/rpc-tests/test_framework/comptool.py index 17679fc7e1..b617895cac 100755 --- a/qa/rpc-tests/test_framework/comptool.py +++ b/qa/rpc-tests/test_framework/comptool.py @@ -2,34 +2,29 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -from .mininode import * -from .blockstore import BlockStore, TxStore -from .util import p2p_port - -''' -This is a tool for comparing two or more bitcoinds to each other -using a script provided. +"""Compare two or more bitcoinds to each other. To use, create a class that implements get_tests(), and pass it in as the test generator to TestManager. get_tests() should be a python generator that returns TestInstance objects. See below for definition. -''' -# TestNode behaves as follows: -# Configure with a BlockStore and TxStore -# on_inv: log the message but don't request -# on_headers: log the chain tip -# on_pong: update ping response map (for synchronization) -# on_getheaders: provide headers via BlockStore -# on_getdata: provide blocks via BlockStore +TestNode behaves as follows: + Configure with a BlockStore and TxStore + on_inv: log the message but don't request + on_headers: log the chain tip + on_pong: update ping response map (for synchronization) + on_getheaders: provide headers via BlockStore + on_getdata: provide blocks via BlockStore +""" + +from .mininode import * +from .blockstore import BlockStore, TxStore +from .util import p2p_port global mininode_lock class RejectResult(object): - ''' - Outcome that expects rejection of a transaction or block. - ''' + """Outcome that expects rejection of a transaction or block.""" def __init__(self, code, reason=b''): self.code = code self.reason = reason diff --git a/qa/rpc-tests/test_framework/coverage.py b/qa/rpc-tests/test_framework/coverage.py index 13b33869f5..3f87ef91f6 100644 --- a/qa/rpc-tests/test_framework/coverage.py +++ b/qa/rpc-tests/test_framework/coverage.py @@ -2,15 +2,12 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Utilities for doing coverage analysis on the RPC interface. -""" -This module contains utilities for doing coverage analysis on the RPC -interface. - -It provides a way to track which RPC commands are exercised during +Provides a way to track which RPC commands are exercised during testing. - """ + import os diff --git a/qa/rpc-tests/test_framework/key.py b/qa/rpc-tests/test_framework/key.py index c63a15c1e0..85a6158a2f 100644 --- a/qa/rpc-tests/test_framework/key.py +++ b/qa/rpc-tests/test_framework/key.py @@ -1,14 +1,10 @@ # Copyright (c) 2011 Sam Rushing -# -# key.py - OpenSSL wrapper -# -# This file is modified from python-bitcoinlib. -# - -"""ECC secp256k1 crypto routines +"""ECC secp256k1 OpenSSL wrapper. WARNING: This module does not mlock() secrets; your private keys may end up on disk in swap! Use with caution! + +This file is modified from python-bitcoinlib. """ import ctypes diff --git a/qa/rpc-tests/test_framework/mininode.py b/qa/rpc-tests/test_framework/mininode.py index 91daa4ab1f..b2292f6477 100755 --- a/qa/rpc-tests/test_framework/mininode.py +++ b/qa/rpc-tests/test_framework/mininode.py @@ -4,23 +4,21 @@ # Copyright (c) 2010-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# mininode.py - Bitcoin P2P network half-a-node -# -# This python code was modified from ArtForz' public domain half-a-node, as -# found in the mini-node branch of http://github.com/jgarzik/pynode. -# -# NodeConn: an object which manages p2p connectivity to a bitcoin node -# NodeConnCB: a base class that describes the interface for receiving -# callbacks with network messages from a NodeConn -# CBlock, CTransaction, CBlockHeader, CTxIn, CTxOut, etc....: -# data structures that should map to corresponding structures in -# bitcoin/primitives -# msg_block, msg_tx, msg_headers, etc.: -# data structures that represent network messages -# ser_*, deser_*: functions that handle serialization/deserialization - +"""Bitcoin P2P network half-a-node. + +This python code was modified from ArtForz' public domain half-a-node, as +found in the mini-node branch of http://github.com/jgarzik/pynode. + +NodeConn: an object which manages p2p connectivity to a bitcoin node +NodeConnCB: a base class that describes the interface for receiving + callbacks with network messages from a NodeConn +CBlock, CTransaction, CBlockHeader, CTxIn, CTxOut, etc....: + data structures that should map to corresponding structures in + bitcoin/primitives +msg_block, msg_tx, msg_headers, etc.: + data structures that represent network messages +ser_*, deser_*: functions that handle serialization/deserialization +""" import struct import socket @@ -1540,6 +1538,7 @@ class NodeConnCB(object): if conn.ver_send > BIP0031_VERSION: conn.send_message(msg_pong(message.nonce)) def on_reject(self, conn, message): pass + def on_open(self, conn): pass def on_close(self, conn): pass def on_mempool(self, conn): pass def on_pong(self, conn, message): pass @@ -1614,7 +1613,7 @@ class NodeConn(asyncore.dispatcher): "regtest": b"\xfa\xbf\xb5\xda", # regtest } - def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=NODE_NETWORK): + def __init__(self, dstaddr, dstport, rpc, callback, net="regtest", services=NODE_NETWORK, send_version=True): asyncore.dispatcher.__init__(self, map=mininode_socket_map) self.log = logging.getLogger("NodeConn(%s:%d)" % (dstaddr, dstport)) self.dstaddr = dstaddr @@ -1631,14 +1630,16 @@ class NodeConn(asyncore.dispatcher): self.disconnect = False self.nServices = 0 - # stuff version msg into sendbuf - vt = msg_version() - vt.nServices = services - vt.addrTo.ip = self.dstaddr - vt.addrTo.port = self.dstport - vt.addrFrom.ip = "0.0.0.0" - vt.addrFrom.port = 0 - self.send_message(vt, True) + if send_version: + # stuff version msg into sendbuf + vt = msg_version() + vt.nServices = services + vt.addrTo.ip = self.dstaddr + vt.addrTo.port = self.dstport + vt.addrFrom.ip = "0.0.0.0" + vt.addrFrom.port = 0 + self.send_message(vt, True) + print('MiniNode: Connecting to Bitcoin Node IP # ' + dstaddr + ':' \ + str(dstport)) @@ -1652,8 +1653,10 @@ class NodeConn(asyncore.dispatcher): self.log.debug(msg) def handle_connect(self): - self.show_debug_msg("MiniNode: Connected & Listening: \n") - self.state = "connected" + if self.state != "connected": + self.show_debug_msg("MiniNode: Connected & Listening: \n") + self.state = "connected" + self.cb.on_open(self) def handle_close(self): self.show_debug_msg("MiniNode: Closing Connection to %s:%d... " @@ -1681,11 +1684,20 @@ class NodeConn(asyncore.dispatcher): def writable(self): with mininode_lock: + pre_connection = self.state == "connecting" length = len(self.sendbuf) - return (length > 0) + return (length > 0 or pre_connection) def handle_write(self): with mininode_lock: + # asyncore does not expose socket connection, only the first read/write + # event, thus we must check connection manually here to know when we + # actually connect + if self.state == "connecting": + self.handle_connect() + if not self.writable(): + return + try: sent = self.send(self.sendbuf) except: diff --git a/qa/rpc-tests/test_framework/netutil.py b/qa/rpc-tests/test_framework/netutil.py index b92a9f6e1c..45d8e22d22 100644 --- a/qa/rpc-tests/test_framework/netutil.py +++ b/qa/rpc-tests/test_framework/netutil.py @@ -2,8 +2,10 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Linux network utilities. -# Linux network utilities +Roughly based on http://voorloopnul.com/blog/a-python-netstat-in-less-than-100-lines-of-code/ by Ricardo Pascal +""" import sys import socket @@ -13,7 +15,6 @@ import array import os from binascii import unhexlify, hexlify -# Roughly based on http://voorloopnul.com/blog/a-python-netstat-in-less-than-100-lines-of-code/ by Ricardo Pascal STATE_ESTABLISHED = '01' STATE_SYN_SENT = '02' STATE_SYN_RECV = '03' diff --git a/qa/rpc-tests/test_framework/script.py b/qa/rpc-tests/test_framework/script.py index 83bbf20479..3d9572788e 100644 --- a/qa/rpc-tests/test_framework/script.py +++ b/qa/rpc-tests/test_framework/script.py @@ -2,19 +2,11 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Functionality to build scripts, as well as SignatureHash(). -# -# script.py -# -# This file is modified from python-bitcoinlib. -# - -"""Scripts - -Functionality to build scripts, as well as SignatureHash(). +This file is modified from python-bitcoinlib. """ - from .mininode import CTransaction, CTxOut, sha256, hash256, uint256_from_str, ser_uint256, ser_string from binascii import hexlify import hashlib diff --git a/qa/rpc-tests/test_framework/siphash.py b/qa/rpc-tests/test_framework/siphash.py index 9c0574bd93..f68ecad36b 100644 --- a/qa/rpc-tests/test_framework/siphash.py +++ b/qa/rpc-tests/test_framework/siphash.py @@ -2,11 +2,10 @@ # Copyright (c) 2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Specialized SipHash-2-4 implementations. -# -# siphash.py - Specialized SipHash-2-4 implementations -# -# This implements SipHash-2-4 for 256-bit integers. +This implements SipHash-2-4 for 256-bit integers. +""" def rotl64(n, b): return n >> (64 - b) | (n & ((1 << (64 - b)) - 1)) << b diff --git a/qa/rpc-tests/test_framework/socks5.py b/qa/rpc-tests/test_framework/socks5.py index 372f5ed605..450bf3775e 100644 --- a/qa/rpc-tests/test_framework/socks5.py +++ b/qa/rpc-tests/test_framework/socks5.py @@ -2,9 +2,7 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -''' -Dummy Socks5 server for testing. -''' +"""Dummy Socks5 server for testing.""" import socket, threading, queue import traceback, sys @@ -20,7 +18,7 @@ class AddressType: ### Utility functions def recvall(s, n): - '''Receive n bytes from a socket, or fail''' + """Receive n bytes from a socket, or fail.""" rv = bytearray() while n > 0: d = s.recv(n) @@ -32,7 +30,7 @@ def recvall(s, n): ### Implementation classes class Socks5Configuration(object): - '''Proxy configuration''' + """Proxy configuration.""" def __init__(self): self.addr = None # Bind address (must be set) self.af = socket.AF_INET # Bind address family @@ -40,7 +38,7 @@ class Socks5Configuration(object): self.auth = False # Support authentication class Socks5Command(object): - '''Information about an incoming socks5 command''' + """Information about an incoming socks5 command.""" def __init__(self, cmd, atyp, addr, port, username, password): self.cmd = cmd # Command (one of Command.*) self.atyp = atyp # Address type (one of AddressType.*) @@ -58,9 +56,7 @@ class Socks5Connection(object): self.peer = peer def handle(self): - ''' - Handle socks5 request according to RFC1928 - ''' + """Handle socks5 request according to RFC192.""" try: # Verify socks version ver = recvall(self.conn, 1)[0] diff --git a/qa/rpc-tests/test_framework/test_framework.py b/qa/rpc-tests/test_framework/test_framework.py index 98c4f6070b..c5e9c6a658 100755 --- a/qa/rpc-tests/test_framework/test_framework.py +++ b/qa/rpc-tests/test_framework/test_framework.py @@ -2,8 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# Base class for RPC testing +"""Base class for RPC testing.""" import logging import optparse diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index 1d6c9aa230..e838a40582 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -2,11 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - - -# -# Helpful routines for regression testing -# +"""Helpful routines for regression testing.""" import os import sys @@ -375,28 +371,19 @@ def stop_node(node, i): node.stop() except http.client.CannotSendRequest as e: print("WARN: Unable to stop node: " + repr(e)) - bitcoind_processes[i].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) + return_code = bitcoind_processes[i].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) + assert_equal(return_code, 0) del bitcoind_processes[i] def stop_nodes(nodes): - for node in nodes: - try: - node.stop() - except http.client.CannotSendRequest as e: - print("WARN: Unable to stop node: " + repr(e)) - del nodes[:] # Emptying array closes connections as a side effect - wait_bitcoinds() + for i, node in enumerate(nodes): + stop_node(node, i) + assert not bitcoind_processes.values() # All connections must be gone now def set_node_times(nodes, t): for node in nodes: node.setmocktime(t) -def wait_bitcoinds(): - # Wait for all bitcoinds to cleanly exit - for bitcoind in bitcoind_processes.values(): - bitcoind.wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) - bitcoind_processes.clear() - def connect_nodes(from_connection, node_num): ip_port = "127.0.0.1:"+str(p2p_port(node_num)) from_connection.addnode(ip_port, "onetry") @@ -524,14 +511,18 @@ def assert_fee_amount(fee, tx_size, fee_per_kB): if fee > (tx_size + 2) * fee_per_kB / 1000: raise AssertionError("Fee of %s BTC too high! (Should be %s BTC)"%(str(fee), str(target_fee))) -def assert_equal(thing1, thing2): - if thing1 != thing2: - raise AssertionError("%s != %s"%(str(thing1),str(thing2))) +def assert_equal(thing1, thing2, *args): + if thing1 != thing2 or any(thing1 != arg for arg in args): + raise AssertionError("not(%s)" % " == ".join(str(arg) for arg in (thing1, thing2) + args)) def assert_greater_than(thing1, thing2): if thing1 <= thing2: raise AssertionError("%s <= %s"%(str(thing1),str(thing2))) +def assert_greater_than_or_equal(thing1, thing2): + if thing1 < thing2: + raise AssertionError("%s < %s"%(str(thing1),str(thing2))) + def assert_raises(exc, fun, *args, **kwds): assert_raises_message(exc, None, fun, *args, **kwds) @@ -546,6 +537,35 @@ def assert_raises_message(exc, message, fun, *args, **kwds): else: raise AssertionError("No exception raised") +def assert_raises_jsonrpc(code, message, fun, *args, **kwds): + """Run an RPC and verify that a specific JSONRPC exception code and message is raised. + + Calls function `fun` with arguments `args` and `kwds`. Catches a JSONRPCException + and verifies that the error code and message are as expected. Throws AssertionError if + no JSONRPCException was returned or if the error code/message are not as expected. + + Args: + code (int), optional: the error code returned by the RPC call (defined + in src/rpc/protocol.h). Set to None if checking the error code is not required. + message (string), optional: [a substring of] the error string returned by the + RPC call. Set to None if checking the error string is not required + fun (function): the function to call. This should be the name of an RPC. + args*: positional arguments for the function. + kwds**: named arguments for the function. + """ + try: + fun(*args, **kwds) + except JSONRPCException as e: + # JSONRPCException was thrown as expected. Check the code and message values are correct. + if (code is not None) and (code != e.error["code"]): + raise AssertionError("Unexpected JSONRPC error code %i" % e.error["code"]) + if (message is not None) and (message not in e.error['message']): + raise AssertionError("Expected substring not found:"+e.error['message']) + except Exception as e: + raise AssertionError("Unexpected exception raised: "+type(e).__name__) + else: + raise AssertionError("No exception raised") + def assert_is_hex_string(string): try: int(string, 16) @@ -654,10 +674,10 @@ def create_tx(node, coinbase, to_address, amount): # Create a spend of each passed-in utxo, splicing in "txouts" to each raw # transaction to make it large. See gen_return_txouts() above. -def create_lots_of_big_transactions(node, txouts, utxos, fee): +def create_lots_of_big_transactions(node, txouts, utxos, num, fee): addr = node.getnewaddress() txids = [] - for _ in range(len(utxos)): + for _ in range(num): t = utxos.pop() inputs=[{ "txid" : t["txid"], "vout" : t["vout"]}] outputs = {} @@ -672,13 +692,17 @@ def create_lots_of_big_transactions(node, txouts, utxos, fee): txids.append(txid) return txids -def mine_large_block(node): +def mine_large_block(node, utxos=None): # generate a 66k transaction, # and 14 of them is close to the 1MB block limit + num = 14 txouts = gen_return_txouts() - utxos = node.listunspent()[:14] + utxos = utxos if utxos is not None else [] + if len(utxos) < num: + utxos.clear() + utxos.extend(node.listunspent()) fee = 100 * node.getnetworkinfo()["relayfee"] - create_lots_of_big_transactions(node, txouts, utxos, fee=fee) + create_lots_of_big_transactions(node, txouts, utxos, num, fee=fee) node.generate(1) def get_bip9_status(node, key): diff --git a/qa/rpc-tests/txn_clone.py b/qa/rpc-tests/txn_clone.py index 22f850ece6..7a3b8d3474 100755 --- a/qa/rpc-tests/txn_clone.py +++ b/qa/rpc-tests/txn_clone.py @@ -2,10 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test proper accounting with an equivalent malleability clone -# +"""Test the wallet accounts properly when there are cloned transactions with malleated scriptsigs.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * diff --git a/qa/rpc-tests/txn_doublespend.py b/qa/rpc-tests/txn_doublespend.py index 84944c3c19..5b12cf4c29 100755 --- a/qa/rpc-tests/txn_doublespend.py +++ b/qa/rpc-tests/txn_doublespend.py @@ -2,10 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test proper accounting with a double-spend conflict -# +"""Test the wallet accounts properly when there is a double-spend conflict.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * diff --git a/qa/rpc-tests/wallet-accounts.py b/qa/rpc-tests/wallet-accounts.py index c51181e4f8..ea12d4ec22 100755 --- a/qa/rpc-tests/wallet-accounts.py +++ b/qa/rpc-tests/wallet-accounts.py @@ -2,16 +2,22 @@ # Copyright (c) 2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test account RPCs. + +RPCs tested are: + - getaccountaddress + - getaddressesbyaccount + - setaccount + - sendfrom (with account arguments) + - move (with account arguments) +""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( start_nodes, - start_node, assert_equal, - connect_nodes_bi, ) - class WalletAccountsTest(BitcoinTestFramework): def __init__(self): diff --git a/qa/rpc-tests/wallet-dump.py b/qa/rpc-tests/wallet-dump.py index c6dc2e3d10..b819b72b75 100755 --- a/qa/rpc-tests/wallet-dump.py +++ b/qa/rpc-tests/wallet-dump.py @@ -2,6 +2,7 @@ # Copyright (c) 2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the dumpwallet RPC.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import (start_nodes, start_node, assert_equal, bitcoind_processes) diff --git a/qa/rpc-tests/wallet-hd.py b/qa/rpc-tests/wallet-hd.py index a49d91f6f4..bf07590098 100755 --- a/qa/rpc-tests/wallet-hd.py +++ b/qa/rpc-tests/wallet-hd.py @@ -2,6 +2,7 @@ # Copyright (c) 2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test Hierarchical Deterministic wallet function.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( diff --git a/qa/rpc-tests/wallet.py b/qa/rpc-tests/wallet.py index 3c0dc0f4ea..ddeac657e5 100755 --- a/qa/rpc-tests/wallet.py +++ b/qa/rpc-tests/wallet.py @@ -2,7 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - +"""Test the wallet.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -330,10 +330,12 @@ class WalletTest (BitcoinTestFramework): # disabled until issue is fixed: https://github.com/bitcoin/bitcoin/issues/7463 # '-salvagewallet', ] + chainlimit = 6 for m in maintenance: print("check " + m) stop_nodes(self.nodes) - self.nodes = start_nodes(3, self.options.tmpdir, [[m]] * 3) + # set lower ancestor limit for later + self.nodes = start_nodes(3, self.options.tmpdir, [[m, "-limitancestorcount="+str(chainlimit)]] * 3) while m == '-reindex' and [block_count] * 3 != [self.nodes[i].getblockcount() for i in range(3)]: # reindex will leave rpc warm up "early"; Wait for it to finish time.sleep(0.1) @@ -346,5 +348,56 @@ class WalletTest (BitcoinTestFramework): assert_equal(coinbase_tx_1["transactions"][0]["blockhash"], blocks[1]) assert_equal(len(self.nodes[0].listsinceblock(blocks[1])["transactions"]), 0) + # ==Check that wallet prefers to use coins that don't exceed mempool limits ===== + + # Get all non-zero utxos together + chain_addrs = [self.nodes[0].getnewaddress(), self.nodes[0].getnewaddress()] + singletxid = self.nodes[0].sendtoaddress(chain_addrs[0], self.nodes[0].getbalance(), "", "", True) + self.nodes[0].generate(1) + node0_balance = self.nodes[0].getbalance() + # Split into two chains + rawtx = self.nodes[0].createrawtransaction([{"txid":singletxid, "vout":0}], {chain_addrs[0]:node0_balance/2-Decimal('0.01'), chain_addrs[1]:node0_balance/2-Decimal('0.01')}) + signedtx = self.nodes[0].signrawtransaction(rawtx) + singletxid = self.nodes[0].sendrawtransaction(signedtx["hex"]) + self.nodes[0].generate(1) + + # Make a long chain of unconfirmed payments without hitting mempool limit + # Each tx we make leaves only one output of change on a chain 1 longer + # Since the amount to send is always much less than the outputs, we only ever need one output + # So we should be able to generate exactly chainlimit txs for each original output + sending_addr = self.nodes[1].getnewaddress() + txid_list = [] + for i in range(chainlimit*2): + txid_list.append(self.nodes[0].sendtoaddress(sending_addr, Decimal('0.0001'))) + assert_equal(self.nodes[0].getmempoolinfo()['size'], chainlimit*2) + assert_equal(len(txid_list), chainlimit*2) + + # Without walletrejectlongchains, we will still generate a txid + # The tx will be stored in the wallet but not accepted to the mempool + extra_txid = self.nodes[0].sendtoaddress(sending_addr, Decimal('0.0001')) + assert(extra_txid not in self.nodes[0].getrawmempool()) + assert(extra_txid in [tx["txid"] for tx in self.nodes[0].listtransactions()]) + self.nodes[0].abandontransaction(extra_txid) + total_txs = len(self.nodes[0].listtransactions("*",99999)) + + # Try with walletrejectlongchains + # Double chain limit but require combining inputs, so we pass SelectCoinsMinConf + stop_node(self.nodes[0],0) + self.nodes[0] = start_node(0, self.options.tmpdir, ["-walletrejectlongchains", "-limitancestorcount="+str(2*chainlimit)]) + + # wait for loadmempool + timeout = 10 + while (timeout > 0 and len(self.nodes[0].getrawmempool()) < chainlimit*2): + time.sleep(0.5) + timeout -= 0.5 + assert_equal(len(self.nodes[0].getrawmempool()), chainlimit*2) + + node0_balance = self.nodes[0].getbalance() + # With walletrejectlongchains we will not create the tx and store it in our wallet. + assert_raises_message(JSONRPCException, "mempool chain", self.nodes[0].sendtoaddress, sending_addr, node0_balance - Decimal('0.01')) + + # Verify nothing new in wallet + assert_equal(total_txs, len(self.nodes[0].listtransactions("*",99999))) + if __name__ == '__main__': WalletTest().main() diff --git a/qa/rpc-tests/walletbackup.py b/qa/rpc-tests/walletbackup.py index e12cb10a50..ea249096cf 100755 --- a/qa/rpc-tests/walletbackup.py +++ b/qa/rpc-tests/walletbackup.py @@ -2,9 +2,7 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -""" -Exercise the wallet backup code. Ported from walletbackup.sh. +"""Test the wallet backup features. Test case is: 4 nodes. 1 2 and 3 send transactions between each other, diff --git a/qa/rpc-tests/zapwallettxes.py b/qa/rpc-tests/zapwallettxes.py index 17ba53a844..9597d05f3a 100755 --- a/qa/rpc-tests/zapwallettxes.py +++ b/qa/rpc-tests/zapwallettxes.py @@ -2,7 +2,16 @@ # Copyright (c) 2014-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the zapwallettxes functionality. +- start three bitcoind nodes +- create four transactions on node 0 - two are confirmed and two are + unconfirmed. +- restart node 1 and verify that both the confirmed and the unconfirmed + transactions are still available. +- restart node 0 and verify that the confirmed transactions are still + available, but that the unconfirmed transaction has been zapped. +""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * diff --git a/qa/rpc-tests/zmq_test.py b/qa/rpc-tests/zmq_test.py index 3a116317fe..1e2f06bd54 100755 --- a/qa/rpc-tests/zmq_test.py +++ b/qa/rpc-tests/zmq_test.py @@ -2,19 +2,13 @@ # Copyright (c) 2015-2016 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - -# -# Test ZMQ interface -# +"""Test the ZMQ API.""" from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * import zmq import struct -import http.client -import urllib.parse - class ZMQTest (BitcoinTestFramework): def __init__(self): |