diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/README.md | 6 | ||||
-rw-r--r-- | test/functional/README.md | 30 | ||||
-rwxr-xr-x | test/functional/create_cache.py | 3 | ||||
-rwxr-xr-x | test/functional/deprecated_rpc.py | 23 | ||||
-rwxr-xr-x | test/functional/example_test.py | 38 | ||||
-rwxr-xr-x | test/functional/feature_assumevalid.py (renamed from test/functional/assumevalid.py) | 28 | ||||
-rwxr-xr-x | test/functional/feature_bip68_sequence.py (renamed from test/functional/bip68-sequence.py) | 5 | ||||
-rwxr-xr-x | test/functional/feature_bip9_softforks.py (renamed from test/functional/bip9-softforks.py) | 10 | ||||
-rwxr-xr-x | test/functional/feature_block.py (renamed from test/functional/p2p-fullblocktest.py) | 12 | ||||
-rwxr-xr-x | test/functional/feature_cltv.py (renamed from test/functional/bip65-cltv-p2p.py) | 6 | ||||
-rwxr-xr-x | test/functional/feature_config_args.py | 49 | ||||
-rwxr-xr-x | test/functional/feature_csv_activation.py (renamed from test/functional/bip68-112-113-p2p.py) | 8 | ||||
-rwxr-xr-x | test/functional/feature_dbcrash.py (renamed from test/functional/dbcrash.py) | 0 | ||||
-rwxr-xr-x | test/functional/feature_dersig.py (renamed from test/functional/bipdersig-p2p.py) | 6 | ||||
-rwxr-xr-x | test/functional/feature_fee_estimation.py (renamed from test/functional/smartfees.py) | 163 | ||||
-rwxr-xr-x | test/functional/feature_logging.py | 59 | ||||
-rwxr-xr-x | test/functional/feature_maxuploadtarget.py (renamed from test/functional/maxuploadtarget.py) | 12 | ||||
-rwxr-xr-x | test/functional/feature_minchainwork.py (renamed from test/functional/minchainwork.py) | 2 | ||||
-rwxr-xr-x | test/functional/feature_notifications.py (renamed from test/functional/notifications.py) | 2 | ||||
-rwxr-xr-x | test/functional/feature_nulldummy.py (renamed from test/functional/nulldummy.py) | 12 | ||||
-rwxr-xr-x | test/functional/feature_proxy.py (renamed from test/functional/proxy_test.py) | 2 | ||||
-rwxr-xr-x | test/functional/feature_pruning.py (renamed from test/functional/pruning.py) | 2 | ||||
-rwxr-xr-x | test/functional/feature_rbf.py (renamed from test/functional/replace-by-fee.py) | 2 | ||||
-rwxr-xr-x | test/functional/feature_reindex.py (renamed from test/functional/reindex.py) | 2 | ||||
-rwxr-xr-x | test/functional/feature_segwit.py (renamed from test/functional/segwit.py) | 147 | ||||
-rwxr-xr-x | test/functional/feature_uacomment.py (renamed from test/functional/uacomment.py) | 2 | ||||
-rwxr-xr-x | test/functional/feature_versionbits_warning.py | 112 | ||||
-rwxr-xr-x | test/functional/interface_bitcoin_cli.py (renamed from test/functional/bitcoin_cli.py) | 5 | ||||
-rwxr-xr-x | test/functional/interface_http.py (renamed from test/functional/httpbasics.py) | 2 | ||||
-rwxr-xr-x | test/functional/interface_rest.py (renamed from test/functional/rest.py) | 2 | ||||
-rwxr-xr-x | test/functional/interface_zmq.py (renamed from test/functional/zmq_test.py) | 2 | ||||
-rwxr-xr-x | test/functional/invalidtxrequest.py | 73 | ||||
-rwxr-xr-x | test/functional/mempool_limit.py | 14 | ||||
-rwxr-xr-x | test/functional/mempool_packages.py | 2 | ||||
-rwxr-xr-x | test/functional/mempool_persist.py | 15 | ||||
-rwxr-xr-x | test/functional/mempool_reorg.py | 2 | ||||
-rwxr-xr-x | test/functional/mempool_resurrect.py (renamed from test/functional/mempool_resurrect_test.py) | 2 | ||||
-rwxr-xr-x | test/functional/mempool_spend_coinbase.py (renamed from test/functional/mempool_spendcoinbase.py) | 2 | ||||
-rwxr-xr-x | test/functional/mining_basic.py (renamed from test/functional/mining.py) | 2 | ||||
-rwxr-xr-x | test/functional/mining_getblocktemplate_longpoll.py (renamed from test/functional/getblocktemplate_longpoll.py) | 2 | ||||
-rwxr-xr-x | test/functional/mining_prioritisetransaction.py (renamed from test/functional/prioritise_transaction.py) | 21 | ||||
-rwxr-xr-x | test/functional/multiwallet.py | 90 | ||||
-rwxr-xr-x | test/functional/p2p-versionbits-warning.py | 121 | ||||
-rwxr-xr-x | test/functional/p2p_compactblocks.py (renamed from test/functional/p2p-compactblocks.py) | 20 | ||||
-rwxr-xr-x | test/functional/p2p_disconnect_ban.py (renamed from test/functional/disconnect_ban.py) | 2 | ||||
-rwxr-xr-x | test/functional/p2p_feefilter.py (renamed from test/functional/p2p-feefilter.py) | 8 | ||||
-rwxr-xr-x | test/functional/p2p_fingerprint.py (renamed from test/functional/p2p-fingerprint.py) | 11 | ||||
-rwxr-xr-x | test/functional/p2p_invalid_block.py (renamed from test/functional/invalidblockrequest.py) | 5 | ||||
-rwxr-xr-x | test/functional/p2p_invalid_tx.py | 55 | ||||
-rwxr-xr-x | test/functional/p2p_leak.py (renamed from test/functional/p2p-leaktests.py) | 81 | ||||
-rwxr-xr-x | test/functional/p2p_mempool.py (renamed from test/functional/p2p-mempool.py) | 6 | ||||
-rwxr-xr-x | test/functional/p2p_node_network_limited.py | 57 | ||||
-rwxr-xr-x | test/functional/p2p_segwit.py (renamed from test/functional/p2p-segwit.py) | 302 | ||||
-rwxr-xr-x | test/functional/p2p_sendheaders.py (renamed from test/functional/sendheaders.py) | 275 | ||||
-rwxr-xr-x | test/functional/p2p_timeouts.py (renamed from test/functional/p2p-timeouts.py) | 8 | ||||
-rwxr-xr-x | test/functional/p2p_unrequested_blocks.py (renamed from test/functional/p2p-acceptblock.py) | 22 | ||||
-rwxr-xr-x | test/functional/rpc_bind.py (renamed from test/functional/rpcbind_test.py) | 2 | ||||
-rwxr-xr-x | test/functional/rpc_blockchain.py (renamed from test/functional/blockchain.py) | 17 | ||||
-rwxr-xr-x | test/functional/rpc_decodescript.py (renamed from test/functional/decodescript.py) | 2 | ||||
-rwxr-xr-x | test/functional/rpc_deprecated.py | 27 | ||||
-rwxr-xr-x | test/functional/rpc_fundrawtransaction.py (renamed from test/functional/fundrawtransaction.py) | 26 | ||||
-rwxr-xr-x | test/functional/rpc_getchaintips.py (renamed from test/functional/getchaintips.py) | 2 | ||||
-rwxr-xr-x | test/functional/rpc_invalidateblock.py (renamed from test/functional/invalidateblock.py) | 2 | ||||
-rwxr-xr-x | test/functional/rpc_listtransactions.py (renamed from test/functional/listtransactions.py) | 5 | ||||
-rwxr-xr-x | test/functional/rpc_named_arguments.py (renamed from test/functional/rpcnamedargs.py) | 2 | ||||
-rwxr-xr-x | test/functional/rpc_net.py (renamed from test/functional/net.py) | 0 | ||||
-rwxr-xr-x | test/functional/rpc_preciousblock.py (renamed from test/functional/preciousblock.py) | 2 | ||||
-rwxr-xr-x | test/functional/rpc_rawtransaction.py (renamed from test/functional/rawtransactions.py) | 127 | ||||
-rwxr-xr-x | test/functional/rpc_signmessage.py (renamed from test/functional/signmessages.py) | 3 | ||||
-rwxr-xr-x | test/functional/rpc_signrawtransaction.py (renamed from test/functional/signrawtransactions.py) | 2 | ||||
-rwxr-xr-x | test/functional/rpc_txoutproof.py (renamed from test/functional/merkle_blocks.py) | 2 | ||||
-rwxr-xr-x | test/functional/rpc_uptime.py (renamed from test/functional/uptime.py) | 0 | ||||
-rwxr-xr-x | test/functional/rpc_users.py (renamed from test/functional/multi_rpc.py) | 2 | ||||
-rw-r--r-- | test/functional/test_framework/address.py | 2 | ||||
-rw-r--r-- | test/functional/test_framework/blockstore.py | 2 | ||||
-rw-r--r-- | test/functional/test_framework/blocktools.py | 66 | ||||
-rwxr-xr-x | test/functional/test_framework/comptool.py | 129 | ||||
-rw-r--r-- | test/functional/test_framework/coverage.py | 2 | ||||
-rw-r--r-- | test/functional/test_framework/messages.py | 52 | ||||
-rwxr-xr-x | test/functional/test_framework/mininode.py | 555 | ||||
-rw-r--r-- | test/functional/test_framework/netutil.py | 2 | ||||
-rw-r--r-- | test/functional/test_framework/script.py | 4 | ||||
-rw-r--r-- | test/functional/test_framework/siphash.py | 2 | ||||
-rw-r--r-- | test/functional/test_framework/socks5.py | 2 | ||||
-rwxr-xr-x | test/functional/test_framework/test_framework.py | 61 | ||||
-rwxr-xr-x | test/functional/test_framework/test_node.py | 101 | ||||
-rw-r--r-- | test/functional/test_framework/util.py | 7 | ||||
-rwxr-xr-x | test/functional/test_runner.py | 219 | ||||
-rwxr-xr-x | test/functional/wallet_abandonconflict.py (renamed from test/functional/abandonconflict.py) | 10 | ||||
-rwxr-xr-x | test/functional/wallet_accounts.py (renamed from test/functional/wallet-accounts.py) | 4 | ||||
-rwxr-xr-x | test/functional/wallet_address_types.py | 294 | ||||
-rwxr-xr-x | test/functional/wallet_backup.py (renamed from test/functional/walletbackup.py) | 22 | ||||
-rwxr-xr-x | test/functional/wallet_basic.py (renamed from test/functional/wallet.py) | 13 | ||||
-rwxr-xr-x | test/functional/wallet_bumpfee.py (renamed from test/functional/bumpfee.py) | 20 | ||||
-rwxr-xr-x | test/functional/wallet_disable.py (renamed from test/functional/disablewallet.py) | 2 | ||||
-rwxr-xr-x | test/functional/wallet_dump.py (renamed from test/functional/wallet-dump.py) | 62 | ||||
-rwxr-xr-x | test/functional/wallet_encryption.py (renamed from test/functional/wallet-encryption.py) | 21 | ||||
-rwxr-xr-x | test/functional/wallet_hd.py (renamed from test/functional/wallet-hd.py) | 4 | ||||
-rwxr-xr-x | test/functional/wallet_import_rescan.py (renamed from test/functional/import-rescan.py) | 4 | ||||
-rwxr-xr-x | test/functional/wallet_importmulti.py (renamed from test/functional/importmulti.py) | 11 | ||||
-rwxr-xr-x | test/functional/wallet_importprunedfunds.py (renamed from test/functional/importprunedfunds.py) | 2 | ||||
-rwxr-xr-x | test/functional/wallet_keypool.py (renamed from test/functional/keypool.py) | 2 | ||||
-rwxr-xr-x | test/functional/wallet_keypool_topup.py (renamed from test/functional/keypool-topup.py) | 4 | ||||
-rwxr-xr-x | test/functional/wallet_listreceivedby.py (renamed from test/functional/receivedby.py) | 2 | ||||
-rwxr-xr-x | test/functional/wallet_listsinceblock.py (renamed from test/functional/listsinceblock.py) | 0 | ||||
-rwxr-xr-x | test/functional/wallet_multiwallet.py | 133 | ||||
-rwxr-xr-x | test/functional/wallet_resendwallettransactions.py (renamed from test/functional/resendwallettransactions.py) | 0 | ||||
-rwxr-xr-x | test/functional/wallet_txn_clone.py (renamed from test/functional/txn_clone.py) | 17 | ||||
-rwxr-xr-x | test/functional/wallet_txn_doublespend.py (renamed from test/functional/txn_doublespend.py) | 2 | ||||
-rwxr-xr-x | test/functional/wallet_zapwallettxes.py (renamed from test/functional/zapwallettxes.py) | 5 | ||||
-rwxr-xr-x | test/util/bitcoin-util-test.py | 14 |
111 files changed, 2547 insertions, 1482 deletions
diff --git a/test/README.md b/test/README.md index 868eb667ae..b522c4469a 100644 --- a/test/README.md +++ b/test/README.md @@ -8,7 +8,7 @@ There are currently two sets of tests in this directory: - [functional](/test/functional) which test the functionality of bitcoind and bitcoin-qt by interacting with them through the RPC and P2P interfaces. -- [util](test/util) which tests the bitcoin utilities, currently only +- [util](/test/util) which tests the bitcoin utilities, currently only bitcoin-tx. The util tests are run as part of `make check` target. The functional @@ -33,13 +33,13 @@ The ZMQ functional test requires a python ZMQ library. To install it: Individual tests can be run by directly calling the test script, eg: ``` -test/functional/replace-by-fee.py +test/functional/feature_rbf.py ``` or can be run through the test_runner harness, eg: ``` -test/functional/test_runner.py replace-by-fee.py +test/functional/test_runner.py feature_rbf.py ``` You can run any combination (incl. duplicates) of tests by calling: diff --git a/test/functional/README.md b/test/functional/README.md index 2558bd017d..662b4b44d5 100644 --- a/test/functional/README.md +++ b/test/functional/README.md @@ -17,7 +17,7 @@ don't have test cases for. #### Style guidelines -- Where possible, try to adhere to [PEP-8 guidelines]([https://www.python.org/dev/peps/pep-0008/) +- Where possible, try to adhere to [PEP-8 guidelines](https://www.python.org/dev/peps/pep-0008/) - Use a python linter like flake8 before submitting PRs to catch common style nits (eg trailing whitespace, unused imports, etc) - Avoid wildcard imports where possible @@ -27,6 +27,20 @@ don't have test cases for. `set_test_params()`, `add_options()` and `setup_xxxx()` methods at the top of the subclass, then locally-defined helper methods, then the `run_test()` method. +#### Naming guidelines + +- Name the test `<area>_test.py`, where area can be one of the following: + - `feature` for tests for full features that aren't wallet/mining/mempool, eg `feature_rbf.py` + - `interface` for tests for other interfaces (REST, ZMQ, etc), eg `interface_rest.py` + - `mempool` for tests for mempool behaviour, eg `mempool_reorg.py` + - `mining` for tests for mining features, eg `mining_prioritisetransaction.py` + - `p2p` for tests that explicitly test the p2p interface, eg `p2p_disconnect_ban.py` + - `rpc` for tests for individual RPC methods or features, eg `rpc_listtransactions.py` + - `wallet` for tests for wallet features, eg `wallet_keypool.py` +- use an underscore to separate words + - exception: for tests for specific RPCs or command line options which don't include underscores, name the test after the exact RPC or argument name, eg `rpc_decodescript.py`, not `rpc_decode_script.py` +- Don't use the redundant word `test` in the name, eg `interface_zmq.py`, not `interface_zmq_test.py` + #### General test-writing advice - Set `self.num_nodes` to the minimum number of nodes necessary for the test. @@ -63,17 +77,17 @@ wrappers for them, `msg_block`, `msg_tx`, etc). with the bitcoind(s) being tested (using python's asyncore package); the other implements the test logic. -- `NodeConn` is the class used to connect to a bitcoind. If you implement -a callback class that derives from `NodeConnCB` and pass that to the -`NodeConn` object, your code will receive the appropriate callbacks when -events of interest arrive. +- `P2PConnection` is the class used to connect to a bitcoind. `P2PInterface` +contains the higher level logic for processing P2P payloads and connecting to +the Bitcoin Core node application logic. For custom behaviour, subclass the +P2PInterface object and override the callback methods. -- Call `NetworkThread.start()` after all `NodeConn` objects are created to +- Call `network_thread_start()` after all `P2PInterface` objects are created to start the networking thread. (Continue with the test logic in your existing thread.) - Can be used to write tests where specific P2P protocol behavior is tested. -Examples tests are `p2p-accept-block.py`, `p2p-compactblocks.py`. +Examples tests are `p2p_unrequested_blocks.py`, `p2p_compactblocks.py`. #### Comptool @@ -119,7 +133,7 @@ Each `TestInstance` consists of: acceptance is tested against the given outcome. - For examples of tests written in this framework, see - `invalidblockrequest.py` and `p2p-fullblocktest.py`. + `p2p_invalid_block.py` and `feature_block.py`. ### test-framework modules diff --git a/test/functional/create_cache.py b/test/functional/create_cache.py index 7d4d1a529b..9665c50a92 100755 --- a/test/functional/create_cache.py +++ b/test/functional/create_cache.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016 The Bitcoin Core developers +# Copyright (c) 2016-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. """Create a blockchain cache. @@ -16,6 +16,7 @@ class CreateCache(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 0 + self.supports_cli = True def setup_network(self): pass diff --git a/test/functional/deprecated_rpc.py b/test/functional/deprecated_rpc.py deleted file mode 100755 index 19fd24edb9..0000000000 --- a/test/functional/deprecated_rpc.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (c) 2017 The Bitcoin Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. -"""Test deprecation of RPC calls.""" -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_raises_rpc_error - -class DeprecatedRpcTest(BitcoinTestFramework): - def set_test_params(self): - self.num_nodes = 2 - self.setup_clean_chain = True - self.extra_args = [[], ["-deprecatedrpc=estimatefee"]] - - def run_test(self): - self.log.info("estimatefee: Shows deprecated message") - assert_raises_rpc_error(-32, 'estimatefee is deprecated', self.nodes[0].estimatefee, 1) - - self.log.info("Using -deprecatedrpc=estimatefee bypasses the error") - self.nodes[1].estimatefee(1) - -if __name__ == '__main__': - DeprecatedRpcTest().main() diff --git a/test/functional/example_test.py b/test/functional/example_test.py index ba40f33016..12be685ecf 100755 --- a/test/functional/example_test.py +++ b/test/functional/example_test.py @@ -17,11 +17,12 @@ from collections import defaultdict from test_framework.blocktools import (create_block, create_coinbase) from test_framework.mininode import ( CInv, - NetworkThread, - NodeConnCB, + P2PInterface, mininode_lock, msg_block, msg_getdata, + network_thread_join, + network_thread_start, ) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( @@ -30,15 +31,15 @@ from test_framework.util import ( wait_until, ) -# NodeConnCB is a class containing callbacks to be executed when a P2P -# message is received from the node-under-test. Subclass NodeConnCB and +# P2PInterface is a class containing callbacks to be executed when a P2P +# message is received from the node-under-test. Subclass P2PInterface and # override the on_*() methods if you need custom behaviour. -class BaseNode(NodeConnCB): +class BaseNode(P2PInterface): def __init__(self): - """Initialize the NodeConnCB + """Initialize the P2PInterface Used to inialize custom properties for the Node that aren't - included by default in the base class. Be aware that the NodeConnCB + included by default in the base class. Be aware that the P2PInterface base class already stores a counter for each P2P message type and the last received message of each type, which should be sufficient for the needs of most tests. @@ -49,14 +50,14 @@ class BaseNode(NodeConnCB): # Stores a dictionary of all blocks received self.block_receive_map = defaultdict(int) - def on_block(self, conn, message): + def on_block(self, message): """Override the standard on_block callback Store the hash of a received block in the dictionary.""" message.block.calc_sha256() self.block_receive_map[message.block.sha256] += 1 - def on_inv(self, conn, message): + def on_inv(self, message): """Override the standard on_inv callback""" pass @@ -131,12 +132,12 @@ class ExampleTest(BitcoinTestFramework): def run_test(self): """Main test logic""" - # Create a P2P connection to one of the nodes + # Create P2P connections to two of the nodes self.nodes[0].add_p2p_connection(BaseNode()) # Start up network handling in another thread. This needs to be called # after the P2P connections have been created. - NetworkThread().start() + network_thread_start() # wait_for_verack ensures that the P2P connection is fully up. self.nodes[0].p2p.wait_for_verack() @@ -174,7 +175,7 @@ class ExampleTest(BitcoinTestFramework): block = create_block(self.tip, create_coinbase(height), self.block_time) block.solve() block_message = msg_block(block) - # Send message is used to send a P2P message to the node over our NodeConn connection + # Send message is used to send a P2P message to the node over our P2PInterface self.nodes[0].p2p.send_message(block_message) self.tip = block.sha256 blocks.append(self.tip) @@ -188,7 +189,14 @@ class ExampleTest(BitcoinTestFramework): connect_nodes(self.nodes[1], 2) self.log.info("Add P2P connection to node2") + # We can't add additional P2P connections once the network thread has started. Disconnect the connection + # to node0, wait for the network thread to terminate, then connect to node2. This is specific to + # the current implementation of the network thread and may be improved in future. + self.nodes[0].disconnect_p2ps() + network_thread_join() + self.nodes[2].add_p2p_connection(BaseNode()) + network_thread_start() self.nodes[2].p2p.wait_for_verack() self.log.info("Wait for node2 reach current tip. Test that it has propagated all the blocks to us") @@ -199,12 +207,12 @@ class ExampleTest(BitcoinTestFramework): self.nodes[2].p2p.send_message(getdata_request) # wait_until() will loop until a predicate condition is met. Use it to test properties of the - # NodeConnCB objects. + # P2PInterface objects. wait_until(lambda: sorted(blocks) == sorted(list(self.nodes[2].p2p.block_receive_map.keys())), timeout=5, lock=mininode_lock) self.log.info("Check that each block was received only once") - # The network thread uses a global lock on data access to the NodeConn objects when sending and receiving - # messages. The test thread should acquire the global lock before accessing any NodeConn data to avoid locking + # The network thread uses a global lock on data access to the P2PConnection objects when sending and receiving + # messages. The test thread should acquire the global lock before accessing any P2PConnection data to avoid locking # and synchronization issues. Note wait_until() acquires this global lock when testing the predicate. with mininode_lock: for block in self.nodes[2].p2p.block_receive_map.values(): diff --git a/test/functional/assumevalid.py b/test/functional/feature_assumevalid.py index 36761d359e..5a09142412 100755 --- a/test/functional/assumevalid.py +++ b/test/functional/feature_assumevalid.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-2017 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test logic for skipping signature validation on old blocks. @@ -38,15 +38,16 @@ from test_framework.mininode import (CBlockHeader, CTransaction, CTxIn, CTxOut, - NetworkThread, - NodeConnCB, + network_thread_join, + network_thread_start, + P2PInterface, msg_block, msg_headers) from test_framework.script import (CScript, OP_TRUE) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal -class BaseNode(NodeConnCB): +class BaseNode(P2PInterface): def send_header_for_blocks(self, new_blocks): headers_message = msg_headers() headers_message.headers = [CBlockHeader(b) for b in new_blocks] @@ -67,7 +68,7 @@ class AssumeValidTest(BitcoinTestFramework): def send_blocks_until_disconnected(self, p2p_conn): """Keep sending blocks to the node until we're disconnected.""" for i in range(len(self.blocks)): - if not p2p_conn.connection: + if p2p_conn.state != "connected": break try: p2p_conn.send_message(msg_block(self.blocks[i])) @@ -98,7 +99,7 @@ class AssumeValidTest(BitcoinTestFramework): # Connect to node0 p2p0 = self.nodes[0].add_p2p_connection(BaseNode()) - NetworkThread().start() # Start up network handling in another thread + network_thread_start() self.nodes[0].p2p.wait_for_verack() # Build the blockchain @@ -159,13 +160,22 @@ class AssumeValidTest(BitcoinTestFramework): self.block_time += 1 height += 1 + # We're adding new connections so terminate the network thread + self.nodes[0].disconnect_p2ps() + network_thread_join() + # Start node1 and node2 with assumevalid so they accept a block with a bad signature. self.start_node(1, extra_args=["-assumevalid=" + hex(block102.sha256)]) - p2p1 = self.nodes[1].add_p2p_connection(BaseNode()) - p2p1.wait_for_verack() - self.start_node(2, extra_args=["-assumevalid=" + hex(block102.sha256)]) + + p2p0 = self.nodes[0].add_p2p_connection(BaseNode()) + p2p1 = self.nodes[1].add_p2p_connection(BaseNode()) p2p2 = self.nodes[2].add_p2p_connection(BaseNode()) + + network_thread_start() + + p2p0.wait_for_verack() + p2p1.wait_for_verack() p2p2.wait_for_verack() # send header lists to all three nodes diff --git a/test/functional/bip68-sequence.py b/test/functional/feature_bip68_sequence.py index 5f8f21701f..94b13653b9 100755 --- a/test/functional/bip68-sequence.py +++ b/test/functional/feature_bip68_sequence.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 BIP68 implementation.""" @@ -362,9 +362,10 @@ class BIP68Test(BitcoinTestFramework): block.vtx.extend([tx1, tx2, tx3]) block.hashMerkleRoot = block.calc_merkle_root() block.rehash() + add_witness_commitment(block) block.solve() - self.nodes[0].submitblock(ToHex(block)) + self.nodes[0].submitblock(bytes_to_hex_str(block.serialize(True))) assert_equal(self.nodes[0].getbestblockhash(), block.hash) def activateCSV(self): diff --git a/test/functional/bip9-softforks.py b/test/functional/feature_bip9_softforks.py index 904789301a..ae92e9f07c 100755 --- a/test/functional/bip9-softforks.py +++ b/test/functional/feature_bip9_softforks.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 BIP 9 soft forks. @@ -22,7 +22,7 @@ import itertools from test_framework.test_framework import ComparisonTestFramework from test_framework.util import * -from test_framework.mininode import CTransaction, NetworkThread +from test_framework.mininode import CTransaction, network_thread_start 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 @@ -36,7 +36,7 @@ class BIP9SoftForksTest(ComparisonTestFramework): def run_test(self): self.test = TestManager(self, self.options.tmpdir) self.test.add_all_connections(self.nodes) - NetworkThread().start() # Start up network handling in another thread + network_thread_start() self.test.run() def create_transaction(self, node, coinbase, to_address, amount): @@ -245,8 +245,8 @@ class BIP9SoftForksTest(ComparisonTestFramework): self.setup_chain() self.setup_network() self.test.add_all_connections(self.nodes) - NetworkThread().start() - self.test.test_nodes[0].wait_for_verack() + network_thread_start() + self.test.p2p_connections[0].wait_for_verack() def get_tests(self): for test in itertools.chain( diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/feature_block.py index f19b845a32..fe9bbda14b 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/feature_block.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 block processing. @@ -18,6 +18,7 @@ from test_framework.blocktools import * import time from test_framework.key import CECKey from test_framework.script import * +from test_framework.mininode import network_thread_start import struct class PreviousSpendableOutput(): @@ -35,12 +36,15 @@ class CBrokenBlock(CBlock): self.vtx = copy.deepcopy(base_block.vtx) self.hashMerkleRoot = self.calc_merkle_root() - def serialize(self): + def serialize(self, with_witness=False): r = b"" r += super(CBlock, self).serialize() r += struct.pack("<BQ", 255, len(self.vtx)) for tx in self.vtx: - r += tx.serialize() + if with_witness: + r += tx.serialize_with_witness() + else: + r += tx.serialize_without_witness() return r def normal_serialize(self): @@ -68,7 +72,7 @@ class FullBlockTest(ComparisonTestFramework): def run_test(self): self.test = TestManager(self, self.options.tmpdir) self.test.add_all_connections(self.nodes) - NetworkThread().start() # Start up network handling in another thread + network_thread_start() self.test.run() def add_transactions_to_block(self, block, tx_list): diff --git a/test/functional/bip65-cltv-p2p.py b/test/functional/feature_cltv.py index 3073324798..f62ae31654 100755 --- a/test/functional/bip65-cltv-p2p.py +++ b/test/functional/feature_cltv.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 BIP65 (CHECKLOCKTIMEVERIFY). @@ -66,9 +66,9 @@ class BIP65Test(BitcoinTestFramework): self.setup_clean_chain = True def run_test(self): - self.nodes[0].add_p2p_connection(NodeConnCB()) + self.nodes[0].add_p2p_connection(P2PInterface()) - NetworkThread().start() # Start up network handling in another thread + network_thread_start() # wait_for_verack ensures that the P2P connection is fully up. self.nodes[0].p2p.wait_for_verack() diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py new file mode 100755 index 0000000000..61abba8082 --- /dev/null +++ b/test/functional/feature_config_args.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test various command line arguments and configuration file parameters.""" + +import os + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import get_datadir_path + +class ConfArgsTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 1 + + def run_test(self): + self.stop_node(0) + # Remove the -datadir argument so it doesn't override the config file + self.nodes[0].args = [arg for arg in self.nodes[0].args if not arg.startswith("-datadir")] + + default_data_dir = get_datadir_path(self.options.tmpdir, 0) + new_data_dir = os.path.join(default_data_dir, 'newdatadir') + new_data_dir_2 = os.path.join(default_data_dir, 'newdatadir2') + + # Check that using -datadir argument on non-existent directory fails + self.nodes[0].datadir = new_data_dir + self.assert_start_raises_init_error(0, ['-datadir='+new_data_dir], 'Error: Specified data directory "' + new_data_dir + '" does not exist.') + + # Check that using non-existent datadir in conf file fails + conf_file = os.path.join(default_data_dir, "bitcoin.conf") + with open(conf_file, 'a', encoding='utf8') as f: + f.write("datadir=" + new_data_dir + "\n") + self.assert_start_raises_init_error(0, ['-conf='+conf_file], 'Error reading configuration file: specified data directory "' + new_data_dir + '" does not exist.') + + # Create the directory and ensure the config file now works + os.mkdir(new_data_dir) + self.start_node(0, ['-conf='+conf_file, '-wallet=w1']) + self.stop_node(0) + assert os.path.isfile(os.path.join(new_data_dir, 'regtest', 'wallets', 'w1')) + + # Ensure command line argument overrides datadir in conf + os.mkdir(new_data_dir_2) + self.nodes[0].datadir = new_data_dir_2 + self.start_node(0, ['-datadir='+new_data_dir_2, '-conf='+conf_file, '-wallet=w2']) + assert os.path.isfile(os.path.join(new_data_dir_2, 'regtest', 'wallets', 'w2')) + +if __name__ == '__main__': + ConfArgsTest().main() diff --git a/test/functional/bip68-112-113-p2p.py b/test/functional/feature_csv_activation.py index 7e6a4f4408..82aa0ff891 100755 --- a/test/functional/bip68-112-113-p2p.py +++ b/test/functional/feature_csv_activation.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 activation of the first version bits soft fork. @@ -45,7 +45,7 @@ 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.mininode import ToHex, CTransaction, network_thread_start from test_framework.blocktools import create_coinbase, create_block from test_framework.comptool import TestInstance, TestManager from test_framework.script import * @@ -95,12 +95,12 @@ class BIP68_112_113Test(ComparisonTestFramework): def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True - self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=4']] + self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=4', '-addresstype=legacy']] def run_test(self): test = TestManager(self, self.options.tmpdir) test.add_all_connections(self.nodes) - NetworkThread().start() # Start up network handling in another thread + network_thread_start() test.run() def send_generic_input_tx(self, node, coinbases): diff --git a/test/functional/dbcrash.py b/test/functional/feature_dbcrash.py index 24b9765b4e..24b9765b4e 100755 --- a/test/functional/dbcrash.py +++ b/test/functional/feature_dbcrash.py diff --git a/test/functional/bipdersig-p2p.py b/test/functional/feature_dersig.py index e5febde42d..3414571678 100755 --- a/test/functional/bipdersig-p2p.py +++ b/test/functional/feature_dersig.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 BIP66 (DER SIG). @@ -54,9 +54,9 @@ class BIP66Test(BitcoinTestFramework): self.setup_clean_chain = True def run_test(self): - self.nodes[0].add_p2p_connection(NodeConnCB()) + self.nodes[0].add_p2p_connection(P2PInterface()) - NetworkThread().start() # Start up network handling in another thread + network_thread_start() # wait_for_verack ensures that the P2P connection is fully up. self.nodes[0].p2p.wait_for_verack() diff --git a/test/functional/smartfees.py b/test/functional/feature_fee_estimation.py index 986f4546a8..e1263414bd 100755 --- a/test/functional/smartfees.py +++ b/test/functional/feature_fee_estimation.py @@ -1,41 +1,49 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 fee estimation code.""" +from decimal import Decimal +import random -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * -from test_framework.script import CScript, OP_1, OP_DROP, OP_2, OP_HASH160, OP_EQUAL, hash160, OP_TRUE from test_framework.mininode import CTransaction, CTxIn, CTxOut, COutPoint, ToHex, COIN +from test_framework.script import CScript, OP_1, OP_DROP, OP_2, OP_HASH160, OP_EQUAL, hash160, OP_TRUE +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + assert_greater_than, + assert_greater_than_or_equal, + connect_nodes, + satoshi_round, + sync_blocks, + sync_mempools, +) # Construct 2 trivial P2SH's and the ScriptSigs that spend them # So we can create many transactions without needing to spend # time signing. -redeem_script_1 = CScript([OP_1, OP_DROP]) -redeem_script_2 = CScript([OP_2, OP_DROP]) -P2SH_1 = CScript([OP_HASH160, hash160(redeem_script_1), OP_EQUAL]) -P2SH_2 = CScript([OP_HASH160, hash160(redeem_script_2), OP_EQUAL]) +REDEEM_SCRIPT_1 = CScript([OP_1, OP_DROP]) +REDEEM_SCRIPT_2 = CScript([OP_2, OP_DROP]) +P2SH_1 = CScript([OP_HASH160, hash160(REDEEM_SCRIPT_1), OP_EQUAL]) +P2SH_2 = CScript([OP_HASH160, hash160(REDEEM_SCRIPT_2), OP_EQUAL]) # Associated ScriptSig's to spend satisfy P2SH_1 and P2SH_2 -SCRIPT_SIG = [CScript([OP_TRUE, redeem_script_1]), CScript([OP_TRUE, redeem_script_2])] - -global log +SCRIPT_SIG = [CScript([OP_TRUE, REDEEM_SCRIPT_1]), CScript([OP_TRUE, REDEEM_SCRIPT_2])] def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee_increment): - """ - Create and send a transaction with a random fee. + """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. The function takes a list of confirmed outputs and unconfirmed outputs 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) - """ + 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 - rand_fee = float(fee_increment)*(1.1892**random.randint(0,28)) + rand_fee = float(fee_increment) * (1.1892 ** random.randint(0, 28)) # Total fee ranges from min_fee to min_fee + 127*fee_increment fee = min_fee - fee_increment + satoshi_round(rand_fee) tx = CTransaction() @@ -50,95 +58,69 @@ def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee total_in += t["amount"] tx.vin.append(CTxIn(COutPoint(int(t["txid"], 16), t["vout"]), b"")) if total_in <= amount + fee: - raise RuntimeError("Insufficient funds: need %d, have %d"%(amount+fee, total_in)) - tx.vout.append(CTxOut(int((total_in - amount - fee)*COIN), P2SH_1)) - tx.vout.append(CTxOut(int(amount*COIN), P2SH_2)) + raise RuntimeError("Insufficient funds: need %d, have %d" % (amount + fee, total_in)) + tx.vout.append(CTxOut(int((total_in - amount - fee) * COIN), P2SH_1)) + tx.vout.append(CTxOut(int(amount * COIN), P2SH_2)) # These transactions don't need to be signed, but we still have to insert # the ScriptSig that will satisfy the ScriptPubKey. for inp in tx.vin: inp.scriptSig = SCRIPT_SIG[inp.prevout.n] txid = from_node.sendrawtransaction(ToHex(tx), True) - unconflist.append({ "txid" : txid, "vout" : 0 , "amount" : total_in - amount - fee}) - unconflist.append({ "txid" : txid, "vout" : 1 , "amount" : amount}) + unconflist.append({"txid": txid, "vout": 0, "amount": total_in - amount - fee}) + unconflist.append({"txid": txid, "vout": 1, "amount": amount}) return (ToHex(tx), fee) -def split_inputs(from_node, txins, txouts, initial_split = False): - """ - We need to generate a lot of inputs so we can generate a ton of transactions. +def split_inputs(from_node, txins, txouts, initial_split=False): + """Generate a lot of inputs so we can generate a ton of transactions. + 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. Previously this was designed to be small inputs so they wouldn't have - a high coin age when the notion of priority still existed. - """ + a high coin age when the notion of priority still existed.""" + prevtxout = txins.pop() tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int(prevtxout["txid"], 16), prevtxout["vout"]), b"")) - half_change = satoshi_round(prevtxout["amount"]/2) - rem_change = prevtxout["amount"] - half_change - Decimal("0.00001000") - tx.vout.append(CTxOut(int(half_change*COIN), P2SH_1)) - tx.vout.append(CTxOut(int(rem_change*COIN), P2SH_2)) + half_change = satoshi_round(prevtxout["amount"] / 2) + rem_change = prevtxout["amount"] - half_change - Decimal("0.00001000") + tx.vout.append(CTxOut(int(half_change * COIN), P2SH_1)) + tx.vout.append(CTxOut(int(rem_change * COIN), P2SH_2)) # If this is the initial split we actually need to sign the transaction # Otherwise we just need to insert the proper ScriptSig - if (initial_split) : + if (initial_split): completetx = from_node.signrawtransaction(ToHex(tx))["hex"] - else : + else: tx.vin[0].scriptSig = SCRIPT_SIG[prevtxout["vout"]] completetx = ToHex(tx) txid = from_node.sendrawtransaction(completetx, True) - txouts.append({ "txid" : txid, "vout" : 0 , "amount" : half_change}) - txouts.append({ "txid" : txid, "vout" : 1 , "amount" : rem_change}) + txouts.append({"txid": txid, "vout": 0, "amount": half_change}) + 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: - log.info([str(all_estimates[e-1]) for e in [1,2,3,6,15,25]]) - delta = 1.0e-6 # account for rounding error - last_e = max(fees_seen) - for e in [x for x in all_estimates if x >= 0]: - # Estimates should be within the bounds of what transactions fees actually were: - if float(e)+delta < min(fees_seen) or float(e)-delta > max(fees_seen): +def check_estimates(node, fees_seen, max_invalid): + """Call estimatesmartfee and verify that the estimates meet certain invariants.""" + + delta = 1.0e-6 # account for rounding error + last_feerate = float(max(fees_seen)) + all_smart_estimates = [node.estimatesmartfee(i) for i in range(1, 26)] + for i, e in enumerate(all_smart_estimates): # estimate is for i+1 + feerate = float(e["feerate"]) + assert_greater_than(feerate, 0) + + if feerate + delta < min(fees_seen) or feerate - delta > max(fees_seen): raise AssertionError("Estimated fee (%f) out of range (%f,%f)" - %(float(e), min(fees_seen), max(fees_seen))) - # Estimates should be monotonically decreasing - if float(e)-delta > last_e: + % (feerate, min(fees_seen), max(fees_seen))) + if feerate - delta > last_feerate: raise AssertionError("Estimated fee (%f) larger than last fee (%f) for lower number of confirms" - %(float(e),float(last_e))) - last_e = e - valid_estimate = False - invalid_estimates = 0 - for i,e in enumerate(all_estimates): # estimate is for i+1 - if e >= 0: - valid_estimate = True - if i >= 13: # for n>=14 estimatesmartfee(n/2) should be at least as high as estimatefee(n) - assert(node.estimatesmartfee((i+1)//2)["feerate"] > float(e) - delta) + % (feerate, last_feerate)) + last_feerate = feerate + if i == 0: + assert_equal(e["blocks"], 2) else: - invalid_estimates += 1 - - # estimatesmartfee should still be valid - approx_estimate = node.estimatesmartfee(i+1)["feerate"] - answer_found = node.estimatesmartfee(i+1)["blocks"] - assert(approx_estimate > 0) - assert(answer_found > i+1) - - # Once we're at a high enough confirmation count that we can give an estimate - # We should have estimates for all higher confirmation counts - if valid_estimate: - raise AssertionError("Invalid estimate appears at higher confirm count than valid estimate") - - # Check on the expected number of different confirmation counts - # that we might not have valid estimates for - if invalid_estimates > max_invalid: - raise AssertionError("More than (%d) invalid estimates"%(max_invalid)) - return all_estimates - + assert_greater_than_or_equal(i + 1, e["blocks"]) class EstimateFeeTest(BitcoinTestFramework): def set_test_params(self): @@ -151,7 +133,7 @@ class EstimateFeeTest(BitcoinTestFramework): which we will use to generate our transactions. """ self.add_nodes(3, extra_args=[["-maxorphantx=1000", "-whitelist=127.0.0.1"], - ["-blockmaxsize=17000", "-maxorphantx=1000", "-deprecatedrpc=estimatefee"], + ["-blockmaxsize=17000", "-maxorphantx=1000"], ["-blockmaxsize=8000", "-maxorphantx=1000"]]) # Use node0 to mine blocks for input splitting # Node1 mines small blocks but that are bigger than the expected transaction rate. @@ -160,7 +142,6 @@ class EstimateFeeTest(BitcoinTestFramework): # Node2 is a stingy miner, that # produces too small blocks (room for only 55 or so transactions) - def transact_and_mine(self, numblocks, mining_node): min_fee = Decimal("0.00001") # We will now mine numblocks blocks generating on average 100 transactions between each block @@ -169,14 +150,14 @@ class EstimateFeeTest(BitcoinTestFramework): # resorting to tx's that depend on the mempool when those run out for i in range(numblocks): random.shuffle(self.confutxo) - for j in range(random.randrange(100-50,100+50)): - from_index = random.randint(1,2) + for j in range(random.randrange(100 - 50, 100 + 50)): + from_index = random.randint(1, 2) (txhex, fee) = small_txpuzzle_randfee(self.nodes[from_index], self.confutxo, self.memutxo, Decimal("0.005"), min_fee, min_fee) tx_kbytes = (len(txhex) // 2) / 1000.0 - self.fees_per_kb.append(float(fee)/tx_kbytes) + self.fees_per_kb.append(float(fee) / tx_kbytes) sync_mempools(self.nodes[0:3], wait=.1) - mined = mining_node.getblock(mining_node.generate(1)[0],True)["tx"] + mined = mining_node.getblock(mining_node.generate(1)[0], True)["tx"] sync_blocks(self.nodes[0:3], wait=.1) # update which txouts are confirmed newmem = [] @@ -191,10 +172,6 @@ class EstimateFeeTest(BitcoinTestFramework): self.log.info("This test is time consuming, please be patient") self.log.info("Splitting inputs so we can generate tx's") - # Make log handler available to helper functions - global log - log = self.log - # Start node0 self.start_node(0) self.txouts = [] @@ -210,13 +187,13 @@ class EstimateFeeTest(BitcoinTestFramework): # Use txouts to monitor the available utxo, since these won't be tracked in wallet reps = 0 while (reps < 5): - #Double txouts to txouts2 - while (len(self.txouts)>0): + # Double txouts to txouts2 + while (len(self.txouts) > 0): split_inputs(self.nodes[0], self.txouts, self.txouts2) while (len(self.nodes[0].getrawmempool()) > 0): self.nodes[0].generate(1) - #Double txouts2 to txouts - while (len(self.txouts2)>0): + # Double txouts2 to txouts + while (len(self.txouts2) > 0): split_inputs(self.nodes[0], self.txouts2, self.txouts) while (len(self.nodes[0].getrawmempool()) > 0): self.nodes[0].generate(1) @@ -235,7 +212,7 @@ class EstimateFeeTest(BitcoinTestFramework): self.fees_per_kb = [] self.memutxo = [] - self.confutxo = self.txouts # Start with the set of confirmed txouts after splitting + self.confutxo = self.txouts # Start with the set of confirmed txouts after splitting self.log.info("Will output estimates for 1/2/3/6/15/25 blocks") for i in range(2): diff --git a/test/functional/feature_logging.py b/test/functional/feature_logging.py new file mode 100755 index 0000000000..da4e7b0398 --- /dev/null +++ b/test/functional/feature_logging.py @@ -0,0 +1,59 @@ +#!/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 debug logging.""" + +import os + +from test_framework.test_framework import BitcoinTestFramework + +class LoggingTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.setup_clean_chain = True + + def run_test(self): + # test default log file name + assert os.path.isfile(os.path.join(self.nodes[0].datadir, "regtest", "debug.log")) + + # test alternative log file name in datadir + self.restart_node(0, ["-debuglogfile=foo.log"]) + assert os.path.isfile(os.path.join(self.nodes[0].datadir, "regtest", "foo.log")) + + # test alternative log file name outside datadir + tempname = os.path.join(self.options.tmpdir, "foo.log") + self.restart_node(0, ["-debuglogfile=%s" % tempname]) + assert os.path.isfile(tempname) + + # check that invalid log (relative) will cause error + invdir = os.path.join(self.nodes[0].datadir, "regtest", "foo") + invalidname = os.path.join("foo", "foo.log") + self.stop_node(0) + self.assert_start_raises_init_error(0, ["-debuglogfile=%s" % (invalidname)], + "Error: Could not open debug log file") + assert not os.path.isfile(os.path.join(invdir, "foo.log")) + + # check that invalid log (relative) works after path exists + self.stop_node(0) + os.mkdir(invdir) + self.start_node(0, ["-debuglogfile=%s" % (invalidname)]) + assert os.path.isfile(os.path.join(invdir, "foo.log")) + + # check that invalid log (absolute) will cause error + self.stop_node(0) + invdir = os.path.join(self.options.tmpdir, "foo") + invalidname = os.path.join(invdir, "foo.log") + self.assert_start_raises_init_error(0, ["-debuglogfile=%s" % invalidname], + "Error: Could not open debug log file") + assert not os.path.isfile(os.path.join(invdir, "foo.log")) + + # check that invalid log (absolute) works after path exists + self.stop_node(0) + os.mkdir(invdir) + self.start_node(0, ["-debuglogfile=%s" % (invalidname)]) + assert os.path.isfile(os.path.join(invdir, "foo.log")) + + +if __name__ == '__main__': + LoggingTest().main() diff --git a/test/functional/maxuploadtarget.py b/test/functional/feature_maxuploadtarget.py index 88e2ff2e16..45336ee801 100755 --- a/test/functional/maxuploadtarget.py +++ b/test/functional/feature_maxuploadtarget.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 behavior of -maxuploadtarget. @@ -17,15 +17,15 @@ from test_framework.mininode import * from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * -class TestNode(NodeConnCB): +class TestNode(P2PInterface): def __init__(self): super().__init__() self.block_receive_map = defaultdict(int) - def on_inv(self, conn, message): + def on_inv(self, message): pass - def on_block(self, conn, message): + def on_block(self, message): message.block.calc_sha256() self.block_receive_map[message.block.sha256] += 1 @@ -57,7 +57,7 @@ class MaxUploadTest(BitcoinTestFramework): for _ in range(3): p2p_conns.append(self.nodes[0].add_p2p_connection(TestNode())) - NetworkThread().start() # Start up network handling in another thread + network_thread_start() for p2pc in p2p_conns: p2pc.wait_for_verack() @@ -149,7 +149,7 @@ class MaxUploadTest(BitcoinTestFramework): # Reconnect to self.nodes[0] self.nodes[0].add_p2p_connection(TestNode()) - NetworkThread().start() # Start up network handling in another thread + network_thread_start() self.nodes[0].p2p.wait_for_verack() #retrieve 20 blocks which should be enough to break the 1MB limit diff --git a/test/functional/minchainwork.py b/test/functional/feature_minchainwork.py index 35cd7ad141..90a3de0e0d 100755 --- a/test/functional/minchainwork.py +++ b/test/functional/feature_minchainwork.py @@ -18,7 +18,7 @@ only succeeds past a given node once its nMinimumChainWork has been exceeded. import time from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import sync_blocks, connect_nodes, assert_equal +from test_framework.util import connect_nodes, assert_equal # 2 hashes required per regtest block (with no difficulty adjustment) REGTEST_WORK_PER_BLOCK = 2 diff --git a/test/functional/notifications.py b/test/functional/feature_notifications.py index c88972ab91..980bef5fc8 100755 --- a/test/functional/notifications.py +++ b/test/functional/feature_notifications.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 -alertnotify, -blocknotify and -walletnotify options.""" diff --git a/test/functional/nulldummy.py b/test/functional/feature_nulldummy.py index 7bc7c168f4..e4f413cc2a 100755 --- a/test/functional/nulldummy.py +++ b/test/functional/feature_nulldummy.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016 The Bitcoin Core developers +# Copyright (c) 2016-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 NULLDUMMY softfork. @@ -15,7 +15,7 @@ Generate 427 more blocks. from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * -from test_framework.mininode import CTransaction, NetworkThread +from test_framework.mininode import CTransaction, network_thread_start from test_framework.blocktools import create_coinbase, create_block, add_witness_commitment from test_framework.script import CScript from io import BytesIO @@ -42,15 +42,15 @@ class NULLDUMMYTest(BitcoinTestFramework): self.setup_clean_chain = True # This script tests NULLDUMMY activation, which is part of the 'segwit' deployment, so we go through # normal segwit activation here (and don't use the default always-on behaviour). - self.extra_args = [['-whitelist=127.0.0.1', '-walletprematurewitness', '-vbparams=segwit:0:999999999999']] + self.extra_args = [['-whitelist=127.0.0.1', '-walletprematurewitness', '-vbparams=segwit:0:999999999999', '-addresstype=legacy', "-deprecatedrpc=addwitnessaddress"]] def run_test(self): self.address = self.nodes[0].getnewaddress() - self.ms_address = self.nodes[0].addmultisigaddress(1,[self.address]) + self.ms_address = self.nodes[0].addmultisigaddress(1,[self.address])['address'] self.wit_address = self.nodes[0].addwitnessaddress(self.address) - self.wit_ms_address = self.nodes[0].addwitnessaddress(self.ms_address) + self.wit_ms_address = self.nodes[0].addmultisigaddress(1, [self.address], '', 'p2sh-segwit')['address'] - NetworkThread().start() # Start up network handling in another thread + network_thread_start() self.coinbase_blocks = self.nodes[0].generate(2) # Block 2 coinbase_txid = [] for i in self.coinbase_blocks: diff --git a/test/functional/proxy_test.py b/test/functional/feature_proxy.py index 81b99d1bf4..2eb1be47a5 100755 --- a/test/functional/proxy_test.py +++ b/test/functional/feature_proxy.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 bitcoind with different proxy configuration. diff --git a/test/functional/pruning.py b/test/functional/feature_pruning.py index 0101f61185..49ad7f838c 100755 --- a/test/functional/pruning.py +++ b/test/functional/feature_pruning.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 pruning code. diff --git a/test/functional/replace-by-fee.py b/test/functional/feature_rbf.py index 815e964848..6b7ab0f43e 100755 --- a/test/functional/replace-by-fee.py +++ b/test/functional/feature_rbf.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 RBF code.""" diff --git a/test/functional/reindex.py b/test/functional/feature_reindex.py index 1f684a1afe..ac67e6e9ba 100755 --- a/test/functional/reindex.py +++ b/test/functional/feature_reindex.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 running bitcoind with -reindex and -reindex-chainstate options. diff --git a/test/functional/segwit.py b/test/functional/feature_segwit.py index 338fa1bc52..7db05077c9 100755 --- a/test/functional/segwit.py +++ b/test/functional/feature_segwit.py @@ -1,13 +1,21 @@ #!/usr/bin/env python3 -# Copyright (c) 2016 The Bitcoin Core developers +# Copyright (c) 2016-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 SegWit changeover logic.""" +from test_framework.address import ( + key_to_p2sh_p2wpkh, + key_to_p2wpkh, + program_to_witness, + script_to_p2sh_p2wsh, + script_to_p2wsh, +) +from test_framework.blocktools import witness_script, send_to_witness from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * from test_framework.mininode import sha256, CTransaction, CTxIn, COutPoint, CTxOut, COIN, ToHex, FromHex -from test_framework.address import script_to_p2sh, key_to_p2pkh, key_to_p2sh_p2wpkh, key_to_p2wpkh, script_to_p2sh_p2wsh, script_to_p2wsh, program_to_witness +from test_framework.address import script_to_p2sh, key_to_p2pkh from test_framework.script import CScript, OP_HASH160, OP_CHECKSIG, OP_0, hash160, OP_EQUAL, OP_DUP, OP_EQUALVERIFY, OP_1, OP_2, OP_CHECKMULTISIG, OP_TRUE from io import BytesIO @@ -16,71 +24,27 @@ NODE_2 = 2 WIT_V0 = 0 WIT_V1 = 1 -# Create a scriptPubKey corresponding to either a P2WPKH output for the -# given pubkey, or a P2WSH output of a 1-of-1 multisig for the given -# pubkey. Returns the hex encoding of the scriptPubKey. -def witness_script(use_p2wsh, pubkey): - if (use_p2wsh == False): - # P2WPKH instead - pubkeyhash = hash160(hex_str_to_bytes(pubkey)) - pkscript = CScript([OP_0, pubkeyhash]) - else: - # 1-of-1 multisig - witness_program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG]) - scripthash = sha256(witness_program) - pkscript = CScript([OP_0, scripthash]) - return bytes_to_hex_str(pkscript) - -# Return a transaction (in hex) that spends the given utxo to a segwit output, -# optionally wrapping the segwit output using P2SH. -def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount): - if use_p2wsh: - program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG]) - addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program) - else: - addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey) - if not encode_p2sh: - assert_equal(node.validateaddress(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey)) - return node.createrawtransaction([utxo], {addr: amount}) - -# Create a transaction spending a given utxo to a segwit output corresponding -# to the given pubkey: use_p2wsh determines whether to use P2WPKH or P2WSH; -# encode_p2sh determines whether to wrap in P2SH. -# sign=True will have the given node sign the transaction. -# insert_redeem_script will be added to the scriptSig, if given. -def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""): - tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount) - if (sign): - signed = node.signrawtransaction(tx_to_witness) - assert("errors" not in signed or len(["errors"]) == 0) - return node.sendrawtransaction(signed["hex"]) - else: - if (insert_redeem_script): - tx = FromHex(CTransaction(), tx_to_witness) - tx.vin[0].scriptSig += CScript([hex_str_to_bytes(insert_redeem_script)]) - tx_to_witness = ToHex(tx) - - return node.sendrawtransaction(tx_to_witness) - def getutxo(txid): utxo = {} utxo["vout"] = 0 utxo["txid"] = txid return utxo -def find_unspent(node, min_value): - for utxo in node.listunspent(): - if utxo['amount'] >= min_value: +def find_spendable_utxo(node, min_value): + for utxo in node.listunspent(query_options={'minimumAmount': min_value}): + if utxo['spendable']: return utxo + raise AssertionError("Unspent output equal or higher than %s not found" % min_value) + class SegWitTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 # This test tests SegWit both pre and post-activation, so use the normal BIP9 activation. - self.extra_args = [["-walletprematurewitness", "-rpcserialversion=0", "-vbparams=segwit:0:999999999999"], - ["-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-rpcserialversion=1", "-vbparams=segwit:0:999999999999"], - ["-blockversion=536870915", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-vbparams=segwit:0:999999999999"]] + self.extra_args = [["-walletprematurewitness", "-rpcserialversion=0", "-vbparams=segwit:0:999999999999", "-addresstype=legacy", "-deprecatedrpc=addwitnessaddress"], + ["-blockversion=4", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-rpcserialversion=1", "-vbparams=segwit:0:999999999999", "-addresstype=legacy", "-deprecatedrpc=addwitnessaddress"], + ["-blockversion=536870915", "-promiscuousmempoolflags=517", "-prematurewitness", "-walletprematurewitness", "-vbparams=segwit:0:999999999999", "-addresstype=legacy", "-deprecatedrpc=addwitnessaddress"]] def setup_network(self): super().setup_network() @@ -133,12 +97,11 @@ class SegWitTest(BitcoinTestFramework): for i in range(3): newaddress = self.nodes[i].getnewaddress() self.pubkey.append(self.nodes[i].validateaddress(newaddress)["pubkey"]) - multiaddress = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]]) multiscript = CScript([OP_1, hex_str_to_bytes(self.pubkey[-1]), OP_1, OP_CHECKMULTISIG]) - p2sh_addr = self.nodes[i].addwitnessaddress(newaddress, True) + p2sh_addr = self.nodes[i].addwitnessaddress(newaddress) bip173_addr = self.nodes[i].addwitnessaddress(newaddress, False) - p2sh_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, True) - bip173_ms_addr = self.nodes[i].addwitnessaddress(multiaddress, False) + p2sh_ms_addr = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]], '', 'p2sh-segwit')['address'] + bip173_ms_addr = self.nodes[i].addmultisigaddress(1, [self.pubkey[-1]], '', 'bech32')['address'] assert_equal(p2sh_addr, key_to_p2sh_p2wpkh(self.pubkey[-1])) assert_equal(bip173_addr, key_to_p2wpkh(self.pubkey[-1])) assert_equal(p2sh_ms_addr, script_to_p2sh_p2wsh(multiscript)) @@ -152,8 +115,8 @@ class SegWitTest(BitcoinTestFramework): for i in range(5): for n in range(3): for v in range(2): - wit_ids[n][v].append(send_to_witness(v, self.nodes[0], find_unspent(self.nodes[0], 50), self.pubkey[n], False, Decimal("49.999"))) - p2sh_ids[n][v].append(send_to_witness(v, self.nodes[0], find_unspent(self.nodes[0], 50), self.pubkey[n], True, Decimal("49.999"))) + wit_ids[n][v].append(send_to_witness(v, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[n], False, Decimal("49.999"))) + p2sh_ids[n][v].append(send_to_witness(v, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[n], True, Decimal("49.999"))) self.nodes[0].generate(1) #block 163 sync_blocks(self.nodes) @@ -248,7 +211,7 @@ class SegWitTest(BitcoinTestFramework): # tx2 (segwit input, paying to a non-segwit output) -> # tx3 (non-segwit input, paying to a non-segwit output). # tx1 is allowed to appear in the block, but no others. - txid1 = send_to_witness(1, self.nodes[0], find_unspent(self.nodes[0], 50), self.pubkey[0], False, Decimal("49.996")) + txid1 = send_to_witness(1, self.nodes[0], find_spendable_utxo(self.nodes[0], 50), self.pubkey[0], False, Decimal("49.996")) hex_tx = self.nodes[0].gettransaction(txid)['hex'] tx = FromHex(CTransaction(), hex_tx) assert(tx.wit.is_null()) # This should not be a segwit input @@ -328,19 +291,19 @@ class SegWitTest(BitcoinTestFramework): solvable_anytime = [] # These outputs should be solvable after importpubkey unseen_anytime = [] # These outputs should never be seen - uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]])) - uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]])) - compressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_spendable_address[0]])) - uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], uncompressed_solvable_address[0]])) - compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]])) - compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], compressed_solvable_address[1]])) + uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]])['address']) + uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]])['address']) + compressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_spendable_address[0]])['address']) + uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], uncompressed_solvable_address[0]])['address']) + compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]])['address']) + compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], compressed_solvable_address[1]])['address']) unknown_address = ["mtKKyoHabkk6e4ppT7NaM7THqPUt7AzPrT", "2NDP3jLWAFT8NDAiUa9qiE6oBt2awmMq7Dx"] # Test multisig_without_privkey # We have 2 public keys without private keys, use addmultisigaddress to add to wallet. # Money sent to P2SH of multisig of this should only be seen after importaddress with the BASE58 P2SH address. - multisig_without_privkey_address = self.nodes[0].addmultisigaddress(2, [pubkeys[3], pubkeys[4]]) + multisig_without_privkey_address = self.nodes[0].addmultisigaddress(2, [pubkeys[3], pubkeys[4]])['address'] script = CScript([OP_2, hex_str_to_bytes(pubkeys[3]), hex_str_to_bytes(pubkeys[4]), OP_2, OP_CHECKMULTISIG]) solvable_after_importaddress.append(CScript([OP_HASH160, hash160(script), OP_EQUAL])) @@ -356,8 +319,10 @@ class SegWitTest(BitcoinTestFramework): [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v) # normal P2PKH and P2PK with compressed keys should always be spendable spendable_anytime.extend([p2pkh, p2pk]) - # P2SH_P2PK, P2SH_P2PKH, and witness with compressed keys are spendable after direct importaddress - spendable_after_importaddress.extend([p2wpkh, p2sh_p2wpkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh]) + # P2SH_P2PK, P2SH_P2PKH with compressed keys are spendable after direct importaddress + spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh]) + # P2WPKH and P2SH_P2WPKH with compressed keys should always be spendable + spendable_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in uncompressed_spendable_address: v = self.nodes[0].validateaddress(i) @@ -373,7 +338,7 @@ class SegWitTest(BitcoinTestFramework): spendable_anytime.extend([p2pkh, p2pk]) # P2SH_P2PK and P2SH_P2PKH are spendable after direct importaddress spendable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh]) - # witness with uncompressed keys are never seen + # Witness output types with uncompressed keys are never seen unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh]) for i in compressed_solvable_address: @@ -384,10 +349,10 @@ class SegWitTest(BitcoinTestFramework): solvable_after_importaddress.extend([bare, p2sh, p2wsh, p2sh_p2wsh]) else: [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v) - # normal P2PKH and P2PK with compressed keys should always be seen - solvable_anytime.extend([p2pkh, p2pk]) - # P2SH_P2PK, P2SH_P2PKH, and witness with compressed keys are seen after direct importaddress - solvable_after_importaddress.extend([p2wpkh, p2sh_p2wpkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh]) + # normal P2PKH, P2PK, P2WPKH and P2SH_P2WPKH with compressed keys should always be seen + solvable_anytime.extend([p2pkh, p2pk, p2wpkh, p2sh_p2wpkh]) + # P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after direct importaddress + solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh]) for i in uncompressed_solvable_address: v = self.nodes[0].validateaddress(i) @@ -403,7 +368,7 @@ class SegWitTest(BitcoinTestFramework): solvable_anytime.extend([p2pkh, p2pk]) # P2SH_P2PK, P2SH_P2PKH with uncompressed keys are seen after direct importaddress solvable_after_importaddress.extend([p2sh_p2pk, p2sh_p2pkh]) - # witness with uncompressed keys are never seen + # Witness output types with uncompressed keys are never seen unseen_anytime.extend([p2wpkh, p2sh_p2wpkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh]) op1 = CScript([OP_1]) @@ -496,12 +461,14 @@ class SegWitTest(BitcoinTestFramework): spendable_after_addwitnessaddress = [] # These outputs should be seen after importaddress solvable_after_addwitnessaddress=[] # These outputs should be seen after importaddress but not spendable unseen_anytime = [] # These outputs should never be seen + solvable_anytime = [] # These outputs should be solvable after importpubkey + unseen_anytime = [] # These outputs should never be seen - uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]])) - uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]])) - compressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_spendable_address[0]])) - uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], uncompressed_solvable_address[0]])) - compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]])) + uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], compressed_spendable_address[0]])['address']) + uncompressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [uncompressed_spendable_address[0], uncompressed_spendable_address[0]])['address']) + compressed_spendable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_spendable_address[0]])['address']) + uncompressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_solvable_address[0], uncompressed_solvable_address[0]])['address']) + compressed_solvable_address.append(self.nodes[0].addmultisigaddress(2, [compressed_spendable_address[0], compressed_solvable_address[0]])['address']) premature_witaddress = [] @@ -514,9 +481,8 @@ class SegWitTest(BitcoinTestFramework): premature_witaddress.append(script_to_p2sh(p2wsh)) else: [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v) - # P2WPKH, P2SH_P2WPKH are spendable after addwitnessaddress - spendable_after_addwitnessaddress.extend([p2wpkh, p2sh_p2wpkh]) - premature_witaddress.append(script_to_p2sh(p2wpkh)) + # P2WPKH, P2SH_P2WPKH are always spendable + spendable_anytime.extend([p2wpkh, p2sh_p2wpkh]) for i in uncompressed_spendable_address + uncompressed_solvable_address: v = self.nodes[0].validateaddress(i) @@ -538,10 +504,11 @@ class SegWitTest(BitcoinTestFramework): premature_witaddress.append(script_to_p2sh(p2wsh)) else: [p2wpkh, p2sh_p2wpkh, p2pk, p2pkh, p2sh_p2pk, p2sh_p2pkh, p2wsh_p2pk, p2wsh_p2pkh, p2sh_p2wsh_p2pk, p2sh_p2wsh_p2pkh] = self.p2pkh_address_to_script(v) - # P2SH_P2PK, P2SH_P2PKH with compressed keys are seen after addwitnessaddress - solvable_after_addwitnessaddress.extend([p2wpkh, p2sh_p2wpkh]) - premature_witaddress.append(script_to_p2sh(p2wpkh)) + # P2SH_P2PK, P2SH_P2PKH with compressed keys are always solvable + solvable_anytime.extend([p2wpkh, p2sh_p2wpkh]) + self.mine_and_test_listunspent(spendable_anytime, 2) + self.mine_and_test_listunspent(solvable_anytime, 1) self.mine_and_test_listunspent(spendable_after_addwitnessaddress + solvable_after_addwitnessaddress + unseen_anytime, 0) # addwitnessaddress should refuse to return a witness address if an uncompressed key is used @@ -558,8 +525,8 @@ class SegWitTest(BitcoinTestFramework): witaddress = self.nodes[0].addwitnessaddress(i) assert_equal(witaddress, self.nodes[0].addwitnessaddress(witaddress)) - spendable_txid.append(self.mine_and_test_listunspent(spendable_after_addwitnessaddress, 2)) - solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress, 1)) + spendable_txid.append(self.mine_and_test_listunspent(spendable_after_addwitnessaddress + spendable_anytime, 2)) + solvable_txid.append(self.mine_and_test_listunspent(solvable_after_addwitnessaddress + solvable_anytime, 1)) self.mine_and_test_listunspent(unseen_anytime, 0) # Check that createrawtransaction/decoderawtransaction with non-v0 Bech32 works @@ -605,7 +572,7 @@ class SegWitTest(BitcoinTestFramework): assert_equal(self.nodes[1].listtransactions("*", 1, 0, True)[0]["txid"], txid) def mine_and_test_listunspent(self, script_list, ismine): - utxo = find_unspent(self.nodes[0], 50) + utxo = find_spendable_utxo(self.nodes[0], 50) tx = CTransaction() tx.vin.append(CTxIn(COutPoint(int('0x'+utxo['txid'],0), utxo['vout']))) for i in script_list: diff --git a/test/functional/uacomment.py b/test/functional/feature_uacomment.py index 0b2c64ab69..bc3791508a 100755 --- a/test/functional/uacomment.py +++ b/test/functional/feature_uacomment.py @@ -23,7 +23,7 @@ class UacommentTest(BitcoinTestFramework): self.log.info("test -uacomment max length") self.stop_node(0) - expected = "Total length of network version string (286) exceeds maximum length (256). Reduce the number or size of uacomments." + expected = "exceeds maximum length (256). Reduce the number or size of uacomments." self.assert_start_raises_init_error(0, ["-uacomment=" + 'a' * 256], expected) self.log.info("test -uacomment unsafe characters") diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py new file mode 100755 index 0000000000..2985569a8f --- /dev/null +++ b/test/functional/feature_versionbits_warning.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +# Copyright (c) 2016-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 version bits warning system. + +Generate chains with block versions that appear to be signalling unknown +soft-forks, and test that warning alerts are generated. +""" +import os +import re + +from test_framework.blocktools import create_block, create_coinbase +from test_framework.messages import msg_block +from test_framework.mininode import P2PInterface, network_thread_start, mininode_lock +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import wait_until + +VB_PERIOD = 144 # versionbits period length for regtest +VB_THRESHOLD = 108 # versionbits activation threshold for regtest +VB_TOP_BITS = 0x20000000 +VB_UNKNOWN_BIT = 27 # Choose a bit unassigned to any deployment +VB_UNKNOWN_VERSION = VB_TOP_BITS | (1 << VB_UNKNOWN_BIT) + +WARN_UNKNOWN_RULES_MINED = "Unknown block versions being mined! It's possible unknown rules are in effect" +WARN_UNKNOWN_RULES_ACTIVE = "unknown new rules activated (versionbit {})".format(VB_UNKNOWN_BIT) +VB_PATTERN = re.compile("Warning: unknown new rules activated.*versionbit") + +class VersionBitsWarningTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 1 + + def setup_network(self): + self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt") + # Open and close to create zero-length file + with open(self.alert_filename, 'w', encoding='utf8'): + pass + self.extra_args = [["-alertnotify=echo %s >> \"" + self.alert_filename + "\""]] + self.setup_nodes() + + def send_blocks_with_version(self, peer, numblocks, version): + """Send numblocks blocks to peer with version set""" + tip = self.nodes[0].getbestblockhash() + height = self.nodes[0].getblockcount() + block_time = self.nodes[0].getblockheader(tip)["time"] + 1 + tip = int(tip, 16) + + for _ in range(numblocks): + block = create_block(tip, create_coinbase(height + 1), block_time) + block.nVersion = version + block.solve() + peer.send_message(msg_block(block)) + block_time += 1 + height += 1 + tip = block.sha256 + peer.sync_with_ping() + + def versionbits_in_alert_file(self): + """Test that the versionbits warning has been written to the alert file.""" + alert_text = open(self.alert_filename, 'r', encoding='utf8').read() + return VB_PATTERN.search(alert_text) is not None + + def run_test(self): + # Handy alias + node = self.nodes[0] + node.add_p2p_connection(P2PInterface()) + network_thread_start() + node.p2p.wait_for_verack() + + # Mine one period worth of blocks + node.generate(VB_PERIOD) + + self.log.info("Check that there is no warning if previous VB_BLOCKS have <VB_THRESHOLD blocks with unknown versionbits version.") + # Build one period of blocks with < VB_THRESHOLD blocks signaling some unknown bit + self.send_blocks_with_version(node.p2p, VB_THRESHOLD - 1, VB_UNKNOWN_VERSION) + node.generate(VB_PERIOD - VB_THRESHOLD + 1) + + # Check that we're not getting any versionbit-related errors in get*info() + assert(not VB_PATTERN.match(node.getmininginfo()["warnings"])) + assert(not VB_PATTERN.match(node.getnetworkinfo()["warnings"])) + + self.log.info("Check that there is a warning if >50 blocks in the last 100 were an unknown version") + # Build one period of blocks with VB_THRESHOLD blocks signaling some unknown bit + self.send_blocks_with_version(node.p2p, VB_THRESHOLD, VB_UNKNOWN_VERSION) + node.generate(VB_PERIOD - VB_THRESHOLD) + + # Check that get*info() shows the 51/100 unknown block version error. + assert(WARN_UNKNOWN_RULES_MINED in node.getmininginfo()["warnings"]) + assert(WARN_UNKNOWN_RULES_MINED in node.getnetworkinfo()["warnings"]) + + self.log.info("Check that there is a warning if previous VB_BLOCKS have >=VB_THRESHOLD blocks with unknown versionbits version.") + # Mine a period worth of expected blocks so the generic block-version warning + # is cleared. This will move the versionbit state to ACTIVE. + node.generate(VB_PERIOD) + + # Stop-start the node. This is required because bitcoind will only warn once about unknown versions or unknown rules activating. + self.restart_node(0) + + # Generating one block guarantees that we'll get out of IBD + node.generate(1) + wait_until(lambda: not node.getblockchaininfo()['initialblockdownload'], timeout=10, lock=mininode_lock) + # Generating one more block will be enough to generate an error. + node.generate(1) + # Check that get*info() shows the versionbits unknown rules warning + assert(WARN_UNKNOWN_RULES_ACTIVE in node.getmininginfo()["warnings"]) + assert(WARN_UNKNOWN_RULES_ACTIVE in node.getnetworkinfo()["warnings"]) + # Check that the alert file shows the versionbits unknown rules warning + wait_until(lambda: self.versionbits_in_alert_file(), timeout=60) + +if __name__ == '__main__': + VersionBitsWarningTest().main() diff --git a/test/functional/bitcoin_cli.py b/test/functional/interface_bitcoin_cli.py index 996cbb8a12..d8c80ab34f 100755 --- a/test/functional/bitcoin_cli.py +++ b/test/functional/interface_bitcoin_cli.py @@ -35,8 +35,11 @@ class TestBitcoinCli(BitcoinTestFramework): assert_equal(["foo", "bar"], self.nodes[0].cli('-rpcuser=%s' % user, '-stdin', '-stdinrpcpass', input=password + "\nfoo\nbar").echo()) assert_raises_process_error(1, "incorrect rpcuser or rpcpassword", self.nodes[0].cli('-rpcuser=%s' % user, '-stdin', '-stdinrpcpass', input="foo").echo) + self.log.info("Make sure that -getinfo with arguments fails") + assert_raises_process_error(1, "-getinfo takes no arguments", self.nodes[0].cli('-getinfo').help) + self.log.info("Compare responses from `bitcoin-cli -getinfo` and the RPCs data is retrieved from.") - cli_get_info = self.nodes[0].cli('-getinfo').help() + cli_get_info = self.nodes[0].cli('-getinfo').send_cli() wallet_info = self.nodes[0].getwalletinfo() network_info = self.nodes[0].getnetworkinfo() blockchain_info = self.nodes[0].getblockchaininfo() diff --git a/test/functional/httpbasics.py b/test/functional/interface_http.py index c7682cb49d..cd6d744545 100755 --- a/test/functional/httpbasics.py +++ b/test/functional/interface_http.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 RPC HTTP basics.""" diff --git a/test/functional/rest.py b/test/functional/interface_rest.py index 437111a4d7..9006e27cbe 100755 --- a/test/functional/rest.py +++ b/test/functional/interface_rest.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 REST API.""" diff --git a/test/functional/zmq_test.py b/test/functional/interface_zmq.py index fa30318416..86ccea4394 100755 --- a/test/functional/zmq_test.py +++ b/test/functional/interface_zmq.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 ZMQ notification interface.""" diff --git a/test/functional/invalidtxrequest.py b/test/functional/invalidtxrequest.py deleted file mode 100755 index a22bd8f8cd..0000000000 --- a/test/functional/invalidtxrequest.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python3 -# 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 -from test_framework.blocktools import * -import time - - - -# Use the ComparisonTestFramework with 1 node: only use --testbinary. -class InvalidTxRequestTest(ComparisonTestFramework): - - ''' Can either run this test as 1 node with expected answers, or two and compare them. - Change the "outcome" variable from each TestInstance object to only do the comparison. ''' - def set_test_params(self): - self.num_nodes = 1 - self.setup_clean_chain = True - - def run_test(self): - test = TestManager(self, self.options.tmpdir) - test.add_all_connections(self.nodes) - self.tip = None - self.block_time = None - NetworkThread().start() # Start up network handling in another thread - test.run() - - def get_tests(self): - if self.tip is None: - self.tip = int("0x" + self.nodes[0].getbestblockhash(), 0) - self.block_time = int(time.time())+1 - - ''' - Create a new block with an anyone-can-spend coinbase - ''' - height = 1 - block = create_block(self.tip, create_coinbase(height), self.block_time) - self.block_time += 1 - block.solve() - # Save the coinbase for later - self.block1 = block - self.tip = block.sha256 - height += 1 - yield TestInstance([[block, True]]) - - ''' - Now we need that block to mature so we can spend the coinbase. - ''' - test = TestInstance(sync_every_block=False) - for i in range(100): - block = create_block(self.tip, create_coinbase(height), self.block_time) - block.solve() - self.tip = block.sha256 - self.block_time += 1 - test.blocks_and_transactions.append([block, True]) - height += 1 - yield test - - # b'\x64' is OP_NOTIF - # Transaction will be rejected with code 16 (REJECT_INVALID) - tx1 = create_transaction(self.block1.vtx[0], 0, b'\x64', 50 * COIN - 12000) - yield TestInstance([[tx1, RejectResult(16, b'mandatory-script-verify-flag-failed')]]) - - # TODO: test further transactions... - -if __name__ == '__main__': - InvalidTxRequestTest().main() diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py index e24dc5a464..e7ce3820d2 100755 --- a/test/functional/mempool_limit.py +++ b/test/functional/mempool_limit.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 mempool limiting together/eviction with the wallet.""" @@ -17,10 +17,14 @@ class MempoolLimitTest(BitcoinTestFramework): txouts = gen_return_txouts() relayfee = self.nodes[0].getnetworkinfo()['relayfee'] + self.log.info('Check that mempoolminfee is minrelytxfee') + assert_equal(self.nodes[0].getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000')) + assert_equal(self.nodes[0].getmempoolinfo()['mempoolminfee'], Decimal('0.00001000')) + txids = [] utxos = create_confirmed_utxos(relayfee, self.nodes[0], 91) - #create a mempool tx that will be evicted + self.log.info('Create a mempool tx that will be evicted') us0 = utxos.pop() inputs = [{ "txid" : us0["txid"], "vout" : us0["vout"]}] outputs = {self.nodes[0].getnewaddress() : 0.0001} @@ -37,10 +41,14 @@ class MempoolLimitTest(BitcoinTestFramework): txids.append([]) txids[i] = create_lots_of_big_transactions(self.nodes[0], txouts, utxos[30*i:30*i+30], 30, (i+1)*base_fee) - # by now, the tx should be evicted, check confirmation state + self.log.info('The tx should be evicted by now') assert(txid not in self.nodes[0].getrawmempool()) txdata = self.nodes[0].gettransaction(txid) assert(txdata['confirmations'] == 0) #confirmation should still be 0 + self.log.info('Check that mempoolminfee is larger than minrelytxfee') + assert_equal(self.nodes[0].getmempoolinfo()['minrelaytxfee'], Decimal('0.00001000')) + assert_greater_than(self.nodes[0].getmempoolinfo()['mempoolminfee'], Decimal('0.00001000')) + if __name__ == '__main__': MempoolLimitTest().main() diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py index b845c75681..a3e872a8c6 100755 --- a/test/functional/mempool_packages.py +++ b/test/functional/mempool_packages.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 descendant package tracking code.""" diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py index 92f66be2ff..17f0967219 100755 --- a/test/functional/mempool_persist.py +++ b/test/functional/mempool_persist.py @@ -57,21 +57,28 @@ class MempoolPersistTest(BitcoinTestFramework): self.log.debug("Send 5 transactions from node2 (to its own address)") for i in range(5): self.nodes[2].sendtoaddress(self.nodes[2].getnewaddress(), Decimal("10")) + node2_balance = self.nodes[2].getbalance() self.sync_all() self.log.debug("Verify that node0 and node1 have 5 transactions in their mempools") assert_equal(len(self.nodes[0].getrawmempool()), 5) assert_equal(len(self.nodes[1].getrawmempool()), 5) - self.log.debug("Stop-start node0 and node1. Verify that node0 has the transactions in its mempool and node1 does not.") + self.log.debug("Stop-start the nodes. Verify that node0 has the transactions in its mempool and node1 does not. Verify that node2 calculates its balance correctly after loading wallet transactions.") self.stop_nodes() + self.start_node(1) # Give this one a head-start, so we can be "extra-sure" that it didn't load anything later self.start_node(0) - self.start_node(1) + self.start_node(2) # Give bitcoind a second to reload the mempool - time.sleep(1) - wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5) + wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5, timeout=1) + wait_until(lambda: len(self.nodes[2].getrawmempool()) == 5, timeout=1) + # The others have loaded their mempool. If node_1 loaded anything, we'd probably notice by now: assert_equal(len(self.nodes[1].getrawmempool()), 0) + # Verify accounting of mempool transactions after restart is correct + self.nodes[2].syncwithvalidationinterfacequeue() # Flush mempool to wallet + assert_equal(node2_balance, self.nodes[2].getbalance()) + self.log.debug("Stop-start node0 with -persistmempool=0. Verify that it doesn't load its mempool.dat file.") self.stop_nodes() self.start_node(0, extra_args=["-persistmempool=0"]) diff --git a/test/functional/mempool_reorg.py b/test/functional/mempool_reorg.py index 2803371f5b..d6bb292a58 100755 --- a/test/functional/mempool_reorg.py +++ b/test/functional/mempool_reorg.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 mempool re-org scenarios. diff --git a/test/functional/mempool_resurrect_test.py b/test/functional/mempool_resurrect.py index 1263c9306b..83e84da4bc 100755 --- a/test/functional/mempool_resurrect_test.py +++ b/test/functional/mempool_resurrect.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 resurrection of mined transactions when the blockchain is re-organized.""" diff --git a/test/functional/mempool_spendcoinbase.py b/test/functional/mempool_spend_coinbase.py index 6e8a635a76..db0738c08a 100755 --- a/test/functional/mempool_spendcoinbase.py +++ b/test/functional/mempool_spend_coinbase.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 spending coinbase transactions. diff --git a/test/functional/mining.py b/test/functional/mining_basic.py index 9aee06864e..569bf71933 100755 --- a/test/functional/mining.py +++ b/test/functional/mining_basic.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 mining RPCs diff --git a/test/functional/getblocktemplate_longpoll.py b/test/functional/mining_getblocktemplate_longpoll.py index 89768bd2fb..252ff4dbff 100755 --- a/test/functional/getblocktemplate_longpoll.py +++ b/test/functional/mining_getblocktemplate_longpoll.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 longpolling with getblocktemplate.""" diff --git a/test/functional/prioritise_transaction.py b/test/functional/mining_prioritisetransaction.py index bb56db9b40..57954ce321 100755 --- a/test/functional/prioritise_transaction.py +++ b/test/functional/mining_prioritisetransaction.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 prioritisetransaction mining RPC.""" @@ -15,6 +15,25 @@ class PrioritiseTransactionTest(BitcoinTestFramework): self.extra_args = [["-printpriority=1"], ["-printpriority=1"]] def run_test(self): + # Test `prioritisetransaction` required parameters + assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction) + assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '') + assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '', 0) + + # Test `prioritisetransaction` invalid extra parameters + assert_raises_rpc_error(-1, "prioritisetransaction", self.nodes[0].prioritisetransaction, '', 0, 0, 0) + + # Test `prioritisetransaction` invalid `txid` + assert_raises_rpc_error(-1, "txid must be hexadecimal string", self.nodes[0].prioritisetransaction, txid='foo', fee_delta=0) + + # Test `prioritisetransaction` invalid `dummy` + txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000' + assert_raises_rpc_error(-1, "JSON value is not a number as expected", self.nodes[0].prioritisetransaction, txid, 'foo', 0) + assert_raises_rpc_error(-8, "Priority is no longer supported, dummy argument to prioritisetransaction must be 0.", self.nodes[0].prioritisetransaction, txid, 1, 0) + + # Test `prioritisetransaction` invalid `fee_delta` + assert_raises_rpc_error(-1, "JSON value is not an integer as expected", self.nodes[0].prioritisetransaction, txid=txid, fee_delta='foo') + self.txouts = gen_return_txouts() self.relayfee = self.nodes[0].getnetworkinfo()['relayfee'] diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py deleted file mode 100755 index 7a0fbce477..0000000000 --- a/test/functional/multiwallet.py +++ /dev/null @@ -1,90 +0,0 @@ -#!/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 multiwallet. - -Verify that a bitcoind node can load multiple wallet files -""" -import os -import shutil - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, assert_raises_rpc_error - -class MultiWalletTest(BitcoinTestFramework): - def set_test_params(self): - self.setup_clean_chain = True - self.num_nodes = 1 - self.extra_args = [['-wallet=w1', '-wallet=w2', '-wallet=w3']] - - def run_test(self): - assert_equal(set(self.nodes[0].listwallets()), {"w1", "w2", "w3"}) - - self.stop_node(0) - - # should not initialize if there are duplicate wallets - self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.') - - # should not initialize if wallet file is a directory - os.mkdir(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w11')) - self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.') - - # should not initialize if one wallet is a copy of another - shutil.copyfile(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w2'), - os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w22')) - self.assert_start_raises_init_error(0, ['-wallet=w2', '-wallet=w22'], 'duplicates fileid') - - # should not initialize if wallet file is a symlink - os.symlink(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w1'), os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w12')) - self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.') - - self.start_node(0, self.extra_args[0]) - - w1 = self.nodes[0].get_wallet_rpc("w1") - w2 = self.nodes[0].get_wallet_rpc("w2") - w3 = self.nodes[0].get_wallet_rpc("w3") - wallet_bad = self.nodes[0].get_wallet_rpc("bad") - - w1.generate(1) - - # accessing invalid wallet fails - assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo) - - # accessing wallet RPC without using wallet endpoint fails - assert_raises_rpc_error(-19, "Wallet file not specified", self.nodes[0].getwalletinfo) - - # check w1 wallet balance - w1_info = w1.getwalletinfo() - assert_equal(w1_info['immature_balance'], 50) - w1_name = w1_info['walletname'] - assert_equal(w1_name, "w1") - - # check w2 wallet balance - w2_info = w2.getwalletinfo() - assert_equal(w2_info['immature_balance'], 0) - w2_name = w2_info['walletname'] - assert_equal(w2_name, "w2") - - w3_name = w3.getwalletinfo()['walletname'] - assert_equal(w3_name, "w3") - - assert_equal({"w1", "w2", "w3"}, {w1_name, w2_name, w3_name}) - - w1.generate(101) - assert_equal(w1.getbalance(), 100) - assert_equal(w2.getbalance(), 0) - assert_equal(w3.getbalance(), 0) - - w1.sendtoaddress(w2.getnewaddress(), 1) - w1.sendtoaddress(w3.getnewaddress(), 2) - w1.generate(1) - assert_equal(w2.getbalance(), 1) - assert_equal(w3.getbalance(), 2) - - batch = w1.batch([w1.getblockchaininfo.get_request(), w1.getwalletinfo.get_request()]) - assert_equal(batch[0]["result"]["chain"], "regtest") - assert_equal(batch[1]["result"]["walletname"], "w1") - -if __name__ == '__main__': - MultiWalletTest().main() diff --git a/test/functional/p2p-versionbits-warning.py b/test/functional/p2p-versionbits-warning.py deleted file mode 100755 index 464ca5a312..0000000000 --- a/test/functional/p2p-versionbits-warning.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/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 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 -from test_framework.util import * -import re -from test_framework.blocktools import create_block, create_coinbase - -VB_PERIOD = 144 # versionbits period length for regtest -VB_THRESHOLD = 108 # versionbits activation threshold for regtest -VB_TOP_BITS = 0x20000000 -VB_UNKNOWN_BIT = 27 # Choose a bit unassigned to any deployment - -WARN_UNKNOWN_RULES_MINED = "Unknown block versions being mined! It's possible unknown rules are in effect" -WARN_UNKNOWN_RULES_ACTIVE = "unknown new rules activated (versionbit {})".format(VB_UNKNOWN_BIT) -VB_PATTERN = re.compile("^Warning.*versionbit") - -class TestNode(NodeConnCB): - def on_inv(self, conn, message): - pass - -class VersionBitsWarningTest(BitcoinTestFramework): - def set_test_params(self): - self.setup_clean_chain = True - self.num_nodes = 1 - - def setup_network(self): - self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt") - # Open and close to create zero-length file - with open(self.alert_filename, 'w', encoding='utf8') as _: - pass - self.extra_args = [["-alertnotify=echo %s >> \"" + self.alert_filename + "\""]] - self.setup_nodes() - - # Send numblocks blocks via peer with nVersionToUse set. - def send_blocks_with_version(self, peer, numblocks, nVersionToUse): - tip = self.nodes[0].getbestblockhash() - height = self.nodes[0].getblockcount() - block_time = self.nodes[0].getblockheader(tip)["time"]+1 - tip = int(tip, 16) - - for _ in range(numblocks): - block = create_block(tip, create_coinbase(height+1), block_time) - block.nVersion = nVersionToUse - block.solve() - peer.send_message(msg_block(block)) - block_time += 1 - height += 1 - tip = block.sha256 - peer.sync_with_ping() - - def test_versionbits_in_alert_file(self): - with open(self.alert_filename, 'r', encoding='utf8') as f: - alert_text = f.read() - assert(VB_PATTERN.match(alert_text)) - - def run_test(self): - # Setup the p2p connection and start up the network thread. - self.nodes[0].add_p2p_connection(TestNode()) - - NetworkThread().start() # Start up network handling in another thread - - # Test logic begins here - self.nodes[0].p2p.wait_for_verack() - - # 1. Have the node mine one period worth of blocks - self.nodes[0].generate(VB_PERIOD) - - # 2. Now build one period of blocks on the tip, with < VB_THRESHOLD - # blocks signaling some unknown bit. - nVersion = VB_TOP_BITS | (1<<VB_UNKNOWN_BIT) - self.send_blocks_with_version(self.nodes[0].p2p, VB_THRESHOLD-1, nVersion) - - # Fill rest of period with regular version blocks - self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD + 1) - # Check that we're not getting any versionbit-related errors in - # get*info() - assert(not VB_PATTERN.match(self.nodes[0].getmininginfo()["warnings"])) - assert(not VB_PATTERN.match(self.nodes[0].getnetworkinfo()["warnings"])) - - # 3. Now build one period of blocks with >= VB_THRESHOLD blocks signaling - # some unknown bit - self.send_blocks_with_version(self.nodes[0].p2p, VB_THRESHOLD, nVersion) - self.nodes[0].generate(VB_PERIOD - VB_THRESHOLD) - # Might not get a versionbits-related alert yet, as we should - # have gotten a different alert due to more than 51/100 blocks - # being of unexpected version. - # Check that get*info() shows some kind of error. - assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getmininginfo()["warnings"]) - assert(WARN_UNKNOWN_RULES_MINED in self.nodes[0].getnetworkinfo()["warnings"]) - - # Mine a period worth of expected blocks so the generic block-version warning - # is cleared, and restart the node. This should move the versionbit state - # to ACTIVE. - self.nodes[0].generate(VB_PERIOD) - self.stop_nodes() - # Empty out the alert file - with open(self.alert_filename, 'w', encoding='utf8') as _: - pass - self.start_nodes() - - # Connecting one block should be enough to generate an error. - self.nodes[0].generate(1) - assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getmininginfo()["warnings"]) - assert(WARN_UNKNOWN_RULES_ACTIVE in self.nodes[0].getnetworkinfo()["warnings"]) - self.stop_nodes() - self.test_versionbits_in_alert_file() - - # Test framework expects the node to still be running... - self.start_nodes() - -if __name__ == '__main__': - VersionBitsWarningTest().main() diff --git a/test/functional/p2p-compactblocks.py b/test/functional/p2p_compactblocks.py index d2c4d39305..d9f461a049 100755 --- a/test/functional/p2p-compactblocks.py +++ b/test/functional/p2p_compactblocks.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016 The Bitcoin Core developers +# Copyright (c) 2016-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 compact blocks (BIP 152). @@ -15,7 +15,7 @@ from test_framework.blocktools import create_block, create_coinbase, add_witness from test_framework.script import CScript, OP_TRUE # TestNode: A peer we use to send messages to bitcoind, and store responses. -class TestNode(NodeConnCB): +class TestNode(P2PInterface): def __init__(self): super().__init__() self.last_sendcmpct = [] @@ -25,21 +25,21 @@ class TestNode(NodeConnCB): # so we can eg wait until a particular block is announced. self.announced_blockhashes = set() - def on_sendcmpct(self, conn, message): + def on_sendcmpct(self, message): self.last_sendcmpct.append(message) - def on_cmpctblock(self, conn, message): + def on_cmpctblock(self, message): self.block_announced = True self.last_message["cmpctblock"].header_and_shortids.header.calc_sha256() self.announced_blockhashes.add(self.last_message["cmpctblock"].header_and_shortids.header.sha256) - def on_headers(self, conn, message): + def on_headers(self, message): self.block_announced = True for x in self.last_message["headers"].headers: x.calc_sha256() self.announced_blockhashes.add(x.sha256) - def on_inv(self, conn, message): + def on_inv(self, message): for x in self.last_message["inv"].inv: if x.type == 2: self.block_announced = True @@ -60,7 +60,7 @@ class TestNode(NodeConnCB): msg = msg_getheaders() msg.locator.vHave = locator msg.hashstop = hashstop - self.connection.send_message(msg) + self.send_message(msg) def send_header_for_blocks(self, new_blocks): headers_message = msg_headers() @@ -86,7 +86,7 @@ class TestNode(NodeConnCB): This is used when we want to send a message into the node that we expect will get us disconnected, eg an invalid block.""" self.send_message(message) - wait_until(lambda: not self.connected, timeout=timeout, lock=mininode_lock) + wait_until(lambda: self.state != "connected", timeout=timeout, lock=mininode_lock) class CompactBlocksTest(BitcoinTestFramework): def set_test_params(self): @@ -95,7 +95,7 @@ class CompactBlocksTest(BitcoinTestFramework): self.num_nodes = 2 # This test was written assuming SegWit is activated using BIP9 at height 432 (3x confirmation window). # TODO: Rewrite this test to support SegWit being always active. - self.extra_args = [["-vbparams=segwit:0:0"], ["-vbparams=segwit:0:999999999999", "-txindex"]] + self.extra_args = [["-vbparams=segwit:0:0"], ["-vbparams=segwit:0:999999999999", "-txindex", "-deprecatedrpc=addwitnessaddress"]] self.utxos = [] def build_block_on_tip(self, node, segwit=False): @@ -792,7 +792,7 @@ class CompactBlocksTest(BitcoinTestFramework): self.segwit_node = self.nodes[1].add_p2p_connection(TestNode(), services=NODE_NETWORK|NODE_WITNESS) self.old_node = self.nodes[1].add_p2p_connection(TestNode(), services=NODE_NETWORK) - NetworkThread().start() # Start up network handling in another thread + network_thread_start() self.test_node.wait_for_verack() diff --git a/test/functional/disconnect_ban.py b/test/functional/p2p_disconnect_ban.py index 59655d37fb..c6067befb2 100755 --- a/test/functional/disconnect_ban.py +++ b/test/functional/p2p_disconnect_ban.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 node disconnect and ban behavior""" diff --git a/test/functional/p2p-feefilter.py b/test/functional/p2p_feefilter.py index 624278df40..47d9c55160 100755 --- a/test/functional/p2p-feefilter.py +++ b/test/functional/p2p_feefilter.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016 The Bitcoin Core developers +# Copyright (c) 2016-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 processing of feefilter messages.""" @@ -22,12 +22,12 @@ def allInvsMatch(invsExpected, testnode): time.sleep(1) return False -class TestNode(NodeConnCB): +class TestNode(P2PInterface): def __init__(self): super().__init__() self.txinvs = [] - def on_inv(self, conn, message): + def on_inv(self, message): for i in message.inv: if (i.type == 1): self.txinvs.append(hashToHex(i.hash)) @@ -49,7 +49,7 @@ class FeeFilterTest(BitcoinTestFramework): # Setup the p2p connections and start up the network thread. self.nodes[0].add_p2p_connection(TestNode()) - NetworkThread().start() + network_thread_start() self.nodes[0].p2p.wait_for_verack() # Test that invs are received for all txs at feerate of 20 sat/byte diff --git a/test/functional/p2p-fingerprint.py b/test/functional/p2p_fingerprint.py index 4b6446fc5b..516ce8555b 100755 --- a/test/functional/p2p-fingerprint.py +++ b/test/functional/p2p_fingerprint.py @@ -4,7 +4,7 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test various fingerprinting protections. -If an stale block more than a month old or its header are requested by a peer, +If a stale block more than a month old or its header are requested by a peer, the node should pretend that it does not have it to avoid fingerprinting. """ @@ -13,18 +13,17 @@ import time from test_framework.blocktools import (create_block, create_coinbase) from test_framework.mininode import ( CInv, - NetworkThread, - NodeConnCB, + P2PInterface, msg_headers, msg_block, msg_getdata, msg_getheaders, + network_thread_start, wait_until, ) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, - p2p_port, ) class P2PFingerprintTest(BitcoinTestFramework): @@ -76,9 +75,9 @@ class P2PFingerprintTest(BitcoinTestFramework): # This does not currently test that stale blocks timestamped within the # last month but that have over a month's worth of work are also withheld. def run_test(self): - node0 = self.nodes[0].add_p2p_connection(NodeConnCB()) + node0 = self.nodes[0].add_p2p_connection(P2PInterface()) - NetworkThread().start() + network_thread_start() node0.wait_for_verack() # Set node time to 60 days ago diff --git a/test/functional/invalidblockrequest.py b/test/functional/p2p_invalid_block.py index 9f44b44927..edcade63c1 100755 --- a/test/functional/invalidblockrequest.py +++ b/test/functional/p2p_invalid_block.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 node responses to invalid blocks. @@ -15,6 +15,7 @@ from test_framework.test_framework import ComparisonTestFramework from test_framework.util import * from test_framework.comptool import TestManager, TestInstance, RejectResult from test_framework.blocktools import * +from test_framework.mininode import network_thread_start import copy import time @@ -32,7 +33,7 @@ class InvalidBlockRequestTest(ComparisonTestFramework): test.add_all_connections(self.nodes) self.tip = None self.block_time = None - NetworkThread().start() # Start up network handling in another thread + network_thread_start() test.run() def get_tests(self): diff --git a/test/functional/p2p_invalid_tx.py b/test/functional/p2p_invalid_tx.py new file mode 100755 index 0000000000..64fada38e2 --- /dev/null +++ b/test/functional/p2p_invalid_tx.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# Copyright (c) 2015-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 node responses to invalid transactions. + +In this test we connect to one node over p2p, and test tx requests.""" +from test_framework.blocktools import create_block, create_coinbase, create_transaction +from test_framework.messages import COIN +from test_framework.mininode import network_thread_start, P2PDataStore +from test_framework.test_framework import BitcoinTestFramework + +class InvalidTxRequestTest(BitcoinTestFramework): + + def set_test_params(self): + self.num_nodes = 1 + self.setup_clean_chain = True + self.extra_args = [["-whitelist=127.0.0.1"]] + + def run_test(self): + # Add p2p connection to node0 + node = self.nodes[0] # convenience reference to the node + node.add_p2p_connection(P2PDataStore()) + + network_thread_start() + node.p2p.wait_for_verack() + + best_block = self.nodes[0].getbestblockhash() + tip = int(best_block, 16) + best_block_time = self.nodes[0].getblock(best_block)['time'] + block_time = best_block_time + 1 + + self.log.info("Create a new block with an anyone-can-spend coinbase.") + height = 1 + block = create_block(tip, create_coinbase(height), block_time) + block_time += 1 + block.solve() + # Save the coinbase for later + block1 = block + tip = block.sha256 + height += 1 + node.p2p.send_blocks_and_test([block], node, success=True) + + self.log.info("Mature the block.") + self.nodes[0].generate(100) + + # b'\x64' is OP_NOTIF + # Transaction will be rejected with code 16 (REJECT_INVALID) + tx1 = create_transaction(block1.vtx[0], 0, b'\x64', 50 * COIN - 12000) + node.p2p.send_txs_and_test([tx1], node, success=False, reject_code=16, reject_reason=b'mandatory-script-verify-flag-failed (Invalid OP_IF construction)') + + # TODO: test further transactions... + +if __name__ == '__main__': + InvalidTxRequestTest().main() diff --git a/test/functional/p2p-leaktests.py b/test/functional/p2p_leak.py index 719a03914d..ce4e6e9144 100755 --- a/test/functional/p2p-leaktests.py +++ b/test/functional/p2p_leak.py @@ -20,7 +20,7 @@ from test_framework.util import * banscore = 10 -class CLazyNode(NodeConnCB): +class CLazyNode(P2PInterface): def __init__(self): super().__init__() self.unexpected_msg = False @@ -30,43 +30,42 @@ class CLazyNode(NodeConnCB): self.unexpected_msg = True self.log.info("should not have received message: %s" % message.command) - def on_open(self, conn): - self.connected = True + def on_open(self): self.ever_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_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) + def on_version(self, message): self.bad_message(message) + def on_verack(self, message): self.bad_message(message) + def on_reject(self, message): self.bad_message(message) + def on_inv(self, message): self.bad_message(message) + def on_addr(self, message): self.bad_message(message) + def on_getdata(self, message): self.bad_message(message) + def on_getblocks(self, message): self.bad_message(message) + def on_tx(self, message): self.bad_message(message) + def on_block(self, message): self.bad_message(message) + def on_getaddr(self, message): self.bad_message(message) + def on_headers(self, message): self.bad_message(message) + def on_getheaders(self, message): self.bad_message(message) + def on_ping(self, message): self.bad_message(message) + def on_mempool(self, message): self.bad_message(message) + def on_pong(self, message): self.bad_message(message) + def on_feefilter(self, message): self.bad_message(message) + def on_sendheaders(self, message): self.bad_message(message) + def on_sendcmpct(self, message): self.bad_message(message) + def on_cmpctblock(self, message): self.bad_message(message) + def on_getblocktxn(self, message): self.bad_message(message) + def on_blocktxn(self, 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): # 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) + def on_open(self): + super().on_open() for i in range(banscore): self.send_message(msg_verack()) - def on_reject(self, conn, message): pass + def on_reject(self, message): pass # Node that never sends a version. This one just sits idle and hopes to receive # any message (it shouldn't!) @@ -80,15 +79,15 @@ class CNodeNoVerackIdle(CLazyNode): self.version_received = False super().__init__() - def on_reject(self, conn, message): pass - def on_verack(self, conn, message): pass + def on_reject(self, message): pass + def on_verack(self, 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): + def on_version(self, message): self.version_received = True - conn.send_message(msg_ping()) - conn.send_message(msg_getaddr()) + self.send_message(msg_ping()) + self.send_message(msg_getaddr()) class P2PLeakTest(BitcoinTestFramework): def set_test_params(self): @@ -104,7 +103,7 @@ class P2PLeakTest(BitcoinTestFramework): unsupported_service_bit5_node = self.nodes[0].add_p2p_connection(CLazyNode(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5) unsupported_service_bit7_node = self.nodes[0].add_p2p_connection(CLazyNode(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7) - NetworkThread().start() # Start up network handling in another thread + network_thread_start() wait_until(lambda: no_version_bannode.ever_connected, timeout=10, lock=mininode_lock) wait_until(lambda: no_version_idlenode.ever_connected, timeout=10, lock=mininode_lock) @@ -119,16 +118,17 @@ class P2PLeakTest(BitcoinTestFramework): time.sleep(5) #This node should have been banned - assert not no_version_bannode.connected + assert no_version_bannode.state != "connected" # These nodes should have been disconnected - assert not unsupported_service_bit5_node.connected - assert not unsupported_service_bit7_node.connected + assert unsupported_service_bit5_node.state != "connected" + assert unsupported_service_bit7_node.state != "connected" self.nodes[0].disconnect_p2ps() - # Wait until all connections are closed + # Wait until all connections are closed and the network thread has terminated wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 0) + network_thread_join() # Make sure no unexpected messages came in assert(no_version_bannode.unexpected_msg == False) @@ -140,10 +140,11 @@ class P2PLeakTest(BitcoinTestFramework): self.log.info("Service bits 5 and 7 are allowed after August 1st 2018") self.nodes[0].setmocktime(1533168000) # August 2nd 2018 - allowed_service_bit5_node = self.nodes[0].add_p2p_connection(NodeConnCB(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5) - allowed_service_bit7_node = self.nodes[0].add_p2p_connection(NodeConnCB(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7) + allowed_service_bit5_node = self.nodes[0].add_p2p_connection(P2PInterface(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_5) + allowed_service_bit7_node = self.nodes[0].add_p2p_connection(P2PInterface(), services=NODE_NETWORK|NODE_UNSUPPORTED_SERVICE_BIT_7) - NetworkThread().start() # Network thread stopped when all previous NodeConnCBs disconnected. Restart it + # Network thread stopped when all previous P2PInterfaces disconnected. Restart it + network_thread_start() wait_until(lambda: allowed_service_bit5_node.message_count["verack"], lock=mininode_lock) wait_until(lambda: allowed_service_bit7_node.message_count["verack"], lock=mininode_lock) diff --git a/test/functional/p2p-mempool.py b/test/functional/p2p_mempool.py index be467c4223..485a8af3d0 100755 --- a/test/functional/p2p-mempool.py +++ b/test/functional/p2p_mempool.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 p2p mempool message. @@ -20,8 +20,8 @@ class P2PMempoolTests(BitcoinTestFramework): def run_test(self): # Add a p2p connection - self.nodes[0].add_p2p_connection(NodeConnCB()) - NetworkThread().start() + self.nodes[0].add_p2p_connection(P2PInterface()) + network_thread_start() self.nodes[0].p2p.wait_for_verack() #request mempool diff --git a/test/functional/p2p_node_network_limited.py b/test/functional/p2p_node_network_limited.py new file mode 100755 index 0000000000..70415e0168 --- /dev/null +++ b/test/functional/p2p_node_network_limited.py @@ -0,0 +1,57 @@ +#!/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. +"""Tests NODE_NETWORK_LIMITED. + +Tests that a node configured with -prune=550 signals NODE_NETWORK_LIMITED correctly +and that it responds to getdata requests for blocks correctly: + - send a block within 288 + 2 of the tip + - disconnect peers who request blocks older than that.""" +from test_framework.messages import CInv, msg_getdata +from test_framework.mininode import NODE_BLOOM, NODE_NETWORK_LIMITED, NODE_WITNESS, NetworkThread, P2PInterface +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal + +class P2PIgnoreInv(P2PInterface): + def on_inv(self, message): + # The node will send us invs for other blocks. Ignore them. + pass + + def send_getdata_for_block(self, blockhash): + getdata_request = msg_getdata() + getdata_request.inv.append(CInv(2, int(blockhash, 16))) + self.send_message(getdata_request) + +class NodeNetworkLimitedTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 1 + self.extra_args = [['-prune=550']] + + def run_test(self): + node = self.nodes[0].add_p2p_connection(P2PIgnoreInv()) + NetworkThread().start() + node.wait_for_verack() + + expected_services = NODE_BLOOM | NODE_WITNESS | NODE_NETWORK_LIMITED + + self.log.info("Check that node has signalled expected services.") + assert_equal(node.nServices, expected_services) + + self.log.info("Check that the localservices is as expected.") + assert_equal(int(self.nodes[0].getnetworkinfo()['localservices'], 16), expected_services) + + self.log.info("Mine enough blocks to reach the NODE_NETWORK_LIMITED range.") + blocks = self.nodes[0].generate(292) + + self.log.info("Make sure we can max retrive block at tip-288.") + node.send_getdata_for_block(blocks[1]) # last block in valid range + node.wait_for_block(int(blocks[1], 16), timeout=3) + + self.log.info("Requesting block at height 2 (tip-289) must fail (ignored).") + node.send_getdata_for_block(blocks[0]) # first block outside of the 288+2 limit + node.wait_for_disconnect(5) + +if __name__ == '__main__': + NodeNetworkLimitedTest().main() diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p_segwit.py index b940bc4096..20e4805df0 100755 --- a/test/functional/p2p-segwit.py +++ b/test/functional/p2p_segwit.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016 The Bitcoin Core developers +# Copyright (c) 2016-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 segwit transactions and blocks on P2P network.""" @@ -25,19 +25,46 @@ MAX_SIGOP_COST = 80000 # Calculate the virtual size of a witness block: # (base + witness/4) def get_virtual_size(witness_block): - base_size = len(witness_block.serialize()) + base_size = len(witness_block.serialize(with_witness=False)) total_size = len(witness_block.serialize(with_witness=True)) # the "+3" is so we round up vsize = int((3*base_size + total_size + 3)/4) return vsize -class TestNode(NodeConnCB): - def __init__(self, rpc): +def test_transaction_acceptance(rpc, p2p, tx, with_witness, accepted, reason=None): + """Send a transaction to the node and check that it's accepted to the mempool + + - Submit the transaction over the p2p interface + - use the getrawmempool rpc to check for acceptance.""" + tx_message = msg_tx(tx) + if with_witness: + tx_message = msg_witness_tx(tx) + p2p.send_message(tx_message) + p2p.sync_with_ping() + assert_equal(tx.hash in rpc.getrawmempool(), accepted) + if (reason != None and not accepted): + # Check the rejection reason as well. + with mininode_lock: + assert_equal(p2p.last_message["reject"].reason, reason) + +def test_witness_block(rpc, p2p, block, accepted, with_witness=True): + """Send a block to the node and check that it's accepted + + - Submit the block over the p2p interface + - use the getbestblockhash rpc to check for acceptance.""" + if with_witness: + p2p.send_message(msg_witness_block(block)) + else: + p2p.send_message(msg_block(block)) + p2p.sync_with_ping() + assert_equal(rpc.getbestblockhash() == block.hash, accepted) + +class TestNode(P2PInterface): + def __init__(self): super().__init__() self.getdataset = set() - self.rpc = rpc - def on_getdata(self, conn, message): + def on_getdata(self, message): for inv in message.inv: self.getdataset.add(inv.hash) @@ -68,27 +95,6 @@ class TestNode(NodeConnCB): self.wait_for_block(blockhash, timeout) return self.last_message["block"].block - def test_transaction_acceptance(self, tx, with_witness, accepted, reason=None): - tx_message = msg_tx(tx) - if with_witness: - tx_message = msg_witness_tx(tx) - self.send_message(tx_message) - self.sync_with_ping() - assert_equal(tx.hash in self.rpc.getrawmempool(), accepted) - if (reason != None and not accepted): - # Check the rejection reason as well. - with mininode_lock: - assert_equal(self.last_message["reject"].reason, reason) - - # Test whether a witness block had the correct effect on the tip - def test_witness_block(self, block, accepted, with_witness=True): - if with_witness: - self.send_message(msg_witness_block(block)) - else: - self.send_message(msg_block(block)) - self.sync_with_ping() - assert_equal(self.rpc.getbestblockhash() == block.hash, accepted) - # Used to keep track of anyone-can-spend outputs that we can use in the tests class UTXO(): def __init__(self, sha256, n, nValue): @@ -142,7 +148,7 @@ class SegWitTest(BitcoinTestFramework): ''' Individual tests ''' def test_witness_services(self): self.log.info("Verifying NODE_WITNESS service bit") - assert((self.test_node.connection.nServices & NODE_WITNESS) != 0) + assert((self.test_node.nServices & NODE_WITNESS) != 0) # See if sending a regular transaction works, and create a utxo @@ -201,7 +207,7 @@ class SegWitTest(BitcoinTestFramework): self.update_witness_block_with_transactions(block, [tx]) # Sending witness data before activation is not allowed (anti-spam # rule). - self.test_node.test_witness_block(block, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # TODO: fix synchronization so we can test reject reason # Right now, bitcoind delays sending reject messages for blocks # until the future, making synchronization here difficult. @@ -228,7 +234,7 @@ class SegWitTest(BitcoinTestFramework): tx2.vin.append(CTxIn(COutPoint(tx.sha256, 0), b"")) tx2.vout.append(CTxOut(tx.vout[0].nValue-1000, scriptPubKey)) tx2.rehash() - self.test_node.test_transaction_acceptance(tx2, False, True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, False, True) self.nodes[0].generate(1) sync_blocks(self.nodes) @@ -245,18 +251,18 @@ class SegWitTest(BitcoinTestFramework): tx3.rehash() # Note that this should be rejected for the premature witness reason, # rather than a policy check, since segwit hasn't activated yet. - self.std_node.test_transaction_acceptance(tx3, True, False, b'no-witness-yet') + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, True, False, b'no-witness-yet') # If we send without witness, it should be accepted. - self.std_node.test_transaction_acceptance(tx3, False, True) + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, False, True) # Now create a new anyone-can-spend utxo for the next test. tx4 = CTransaction() tx4.vin.append(CTxIn(COutPoint(tx3.sha256, 0), CScript([p2sh_program]))) tx4.vout.append(CTxOut(tx3.vout[0].nValue-1000, CScript([OP_TRUE]))) tx4.rehash() - self.test_node.test_transaction_acceptance(tx3, False, True) - self.test_node.test_transaction_acceptance(tx4, False, True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, False, True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx4, False, True) self.nodes[0].generate(1) sync_blocks(self.nodes) @@ -317,7 +323,7 @@ class SegWitTest(BitcoinTestFramework): assert(msg_witness_block(block).serialize() != msg_block(block).serialize()) # This empty block should be valid. - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) # Try to tweak the nonce block_2 = self.build_next_block() @@ -328,7 +334,7 @@ class SegWitTest(BitcoinTestFramework): assert(block_2.vtx[0].vout[-1] != block.vtx[0].vout[-1]) # This should also be valid. - self.test_node.test_witness_block(block_2, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block_2, accepted=True) # Now test commitments with actual transactions assert (len(self.utxo) > 0) @@ -361,7 +367,7 @@ class SegWitTest(BitcoinTestFramework): block_3.rehash() block_3.solve() - self.test_node.test_witness_block(block_3, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block_3, accepted=False) # Add a different commitment with different nonce, but in the # right location, and with some funds burned(!). @@ -375,7 +381,7 @@ class SegWitTest(BitcoinTestFramework): block_3.rehash() assert(len(block_3.vtx[0].vout) == 4) # 3 OP_returns block_3.solve() - self.test_node.test_witness_block(block_3, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block_3, accepted=True) # Finally test that a block with no witness transactions can # omit the commitment. @@ -387,7 +393,7 @@ class SegWitTest(BitcoinTestFramework): block_4.vtx.append(tx3) block_4.hashMerkleRoot = block_4.calc_merkle_root() block_4.solve() - self.test_node.test_witness_block(block_4, with_witness=False, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block_4, with_witness=False, accepted=True) # Update available utxo's for use in later test. self.utxo.pop(0) @@ -428,11 +434,11 @@ class SegWitTest(BitcoinTestFramework): # Change the nonce -- should not cause the block to be permanently # failed block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ ser_uint256(1) ] - self.test_node.test_witness_block(block, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Changing the witness nonce doesn't change the block hash block.vtx[0].wit.vtxinwit[0].scriptWitness.stack = [ ser_uint256(0) ] - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) def test_witness_block_size(self): @@ -497,7 +503,7 @@ class SegWitTest(BitcoinTestFramework): # limit assert(len(block.serialize(True)) > 2*1024*1024) - self.test_node.test_witness_block(block, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Now resize the second transaction to make the block fit. cur_length = len(block.vtx[-1].wit.vtxinwit[0].scriptWitness.stack[0]) @@ -507,7 +513,7 @@ class SegWitTest(BitcoinTestFramework): block.solve() assert(get_virtual_size(block) == MAX_BLOCK_BASE_SIZE) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) # Update available utxo's self.utxo.pop(0) @@ -574,7 +580,7 @@ class SegWitTest(BitcoinTestFramework): self.update_witness_block_with_transactions(block, [tx]) # Extra witness data should not be allowed. - self.test_node.test_witness_block(block, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Try extra signature data. Ok if we're not spending a witness output. block.vtx[1].wit.vtxinwit = [] @@ -583,7 +589,7 @@ class SegWitTest(BitcoinTestFramework): add_witness_commitment(block) block.solve() - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) # Now try extra witness/signature data on an input that DOES require a # witness @@ -599,7 +605,7 @@ class SegWitTest(BitcoinTestFramework): self.update_witness_block_with_transactions(block, [tx2]) # This has extra witness data, so it should fail. - self.test_node.test_witness_block(block, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Now get rid of the extra witness, but add extra scriptSig data tx2.vin[0].scriptSig = CScript([OP_TRUE]) @@ -611,7 +617,7 @@ class SegWitTest(BitcoinTestFramework): block.solve() # This has extra signature data for a witness input, so it should fail. - self.test_node.test_witness_block(block, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Now get rid of the extra scriptsig on the witness input, and verify # success (even with extra scriptsig data in the non-witness input) @@ -620,7 +626,7 @@ class SegWitTest(BitcoinTestFramework): add_witness_commitment(block) block.solve() - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) # Update utxo for later tests self.utxo.pop(0) @@ -653,14 +659,14 @@ class SegWitTest(BitcoinTestFramework): tx2.rehash() self.update_witness_block_with_transactions(block, [tx, tx2]) - self.test_node.test_witness_block(block, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Now reduce the length of the stack element tx2.wit.vtxinwit[0].scriptWitness.stack[0] = b'a'*(MAX_SCRIPT_ELEMENT_SIZE) add_witness_commitment(block) block.solve() - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) # Update the utxo for later tests self.utxo.pop() @@ -695,7 +701,7 @@ class SegWitTest(BitcoinTestFramework): self.update_witness_block_with_transactions(block, [tx, tx2]) - self.test_node.test_witness_block(block, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Try again with one less byte in the witness program witness_program = CScript([b'a'*520]*19 + [OP_DROP]*62 + [OP_TRUE]) @@ -710,7 +716,7 @@ class SegWitTest(BitcoinTestFramework): tx2.rehash() block.vtx = [block.vtx[0]] self.update_witness_block_with_transactions(block, [tx, tx2]) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) self.utxo.pop() self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue)) @@ -736,7 +742,7 @@ class SegWitTest(BitcoinTestFramework): block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx]) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) # Try various ways to spend tx that should all break. # This "broken" transaction serializer will not normalize @@ -771,7 +777,7 @@ class SegWitTest(BitcoinTestFramework): block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx2]) - self.test_node.test_witness_block(block, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Now try using a too short vtxinwit tx2.wit.vtxinwit.pop() @@ -779,7 +785,7 @@ class SegWitTest(BitcoinTestFramework): block.vtx = [block.vtx[0]] self.update_witness_block_with_transactions(block, [tx2]) - self.test_node.test_witness_block(block, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Now make one of the intermediate witnesses be incorrect tx2.wit.vtxinwit.append(CTxInWitness()) @@ -788,13 +794,13 @@ class SegWitTest(BitcoinTestFramework): block.vtx = [block.vtx[0]] self.update_witness_block_with_transactions(block, [tx2]) - self.test_node.test_witness_block(block, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Fix the broken witness and the block should be accepted. tx2.wit.vtxinwit[5].scriptWitness.stack = [b'a', witness_program] block.vtx = [block.vtx[0]] self.update_witness_block_with_transactions(block, [tx2]) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) self.utxo.pop() self.utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue)) @@ -834,11 +840,11 @@ class SegWitTest(BitcoinTestFramework): # its from) assert_equal(len(self.nodes[0].getrawmempool()), 0) assert_equal(len(self.nodes[1].getrawmempool()), 0) - self.old_node.test_transaction_acceptance(tx, with_witness=True, accepted=False) - self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=False) + test_transaction_acceptance(self.nodes[0].rpc, self.old_node, tx, with_witness=True, accepted=False) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=False) # But eliminating the witness should fix it - self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True) # Cleanup: mine the first transaction and update utxo self.nodes[0].generate(1) @@ -870,11 +876,11 @@ class SegWitTest(BitcoinTestFramework): # Verify that unnecessary witnesses are rejected. self.test_node.announce_tx_and_wait_for_getdata(tx) assert_equal(len(self.nodes[0].getrawmempool()), 0) - self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=False) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=False) # Verify that removing the witness succeeds. self.test_node.announce_tx_and_wait_for_getdata(tx) - self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True) # Now try to add extra witness data to a valid witness tx. witness_program = CScript([OP_TRUE]) @@ -899,24 +905,24 @@ class SegWitTest(BitcoinTestFramework): # Node will not be blinded to the transaction self.std_node.announce_tx_and_wait_for_getdata(tx3) - self.std_node.test_transaction_acceptance(tx3, True, False, b'tx-size') + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, True, False, b'tx-size') self.std_node.announce_tx_and_wait_for_getdata(tx3) - self.std_node.test_transaction_acceptance(tx3, True, False, b'tx-size') + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx3, True, False, b'tx-size') # Remove witness stuffing, instead add extra witness push on stack tx3.vout[0] = CTxOut(tx2.vout[0].nValue-1000, CScript([OP_TRUE])) tx3.wit.vtxinwit[0].scriptWitness.stack = [CScript([CScriptNum(1)]), witness_program ] tx3.rehash() - self.test_node.test_transaction_acceptance(tx2, with_witness=True, accepted=True) - self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=False) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=False) # Get rid of the extra witness, and verify acceptance. tx3.wit.vtxinwit[0].scriptWitness.stack = [ witness_program ] # Also check that old_node gets a tx announcement, even though this is # a witness transaction. self.old_node.wait_for_inv([CInv(1, tx2.sha256)]) # wait until tx2 was inv'ed - self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True) self.old_node.wait_for_inv([CInv(1, tx3.sha256)]) # Test that getrawtransaction returns correct witness information @@ -955,20 +961,20 @@ class SegWitTest(BitcoinTestFramework): self.test_node.announce_block_and_wait_for_getdata(block1, use_header=False) assert(self.test_node.last_message["getdata"].inv[0].type == blocktype) - self.test_node.test_witness_block(block1, True) + test_witness_block(self.nodes[0].rpc, self.test_node, block1, True) block2 = self.build_next_block(nVersion=4) block2.solve() self.test_node.announce_block_and_wait_for_getdata(block2, use_header=True) assert(self.test_node.last_message["getdata"].inv[0].type == blocktype) - self.test_node.test_witness_block(block2, True) + test_witness_block(self.nodes[0].rpc, self.test_node, block2, True) block3 = self.build_next_block(nVersion=(VB_TOP_BITS | (1<<15))) block3.solve() self.test_node.announce_block_and_wait_for_getdata(block3, use_header=True) assert(self.test_node.last_message["getdata"].inv[0].type == blocktype) - self.test_node.test_witness_block(block3, True) + test_witness_block(self.nodes[0].rpc, self.test_node, block3, True) # Check that we can getdata for witness blocks or regular blocks, # and the right thing happens. @@ -998,7 +1004,7 @@ class SegWitTest(BitcoinTestFramework): # This gives us a witness commitment. assert(len(block.vtx[0].wit.vtxinwit) == 1) assert(len(block.vtx[0].wit.vtxinwit[0].scriptWitness.stack) == 1) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) # Now try to retrieve it... rpc_block = self.nodes[0].getblock(block.hash, False) non_wit_block = self.test_node.request_block(block.sha256, 2) @@ -1052,7 +1058,7 @@ class SegWitTest(BitcoinTestFramework): p2sh_tx.rehash() # Mine it on test_node to create the confirmed output. - self.test_node.test_transaction_acceptance(p2sh_tx, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_tx, with_witness=True, accepted=True) self.nodes[0].generate(1) sync_blocks(self.nodes) @@ -1064,7 +1070,7 @@ class SegWitTest(BitcoinTestFramework): tx.vout.append(CTxOut(8000, scriptPubKey)) # Might burn this later tx.rehash() - self.std_node.test_transaction_acceptance(tx, with_witness=True, accepted=segwit_activated) + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx, with_witness=True, accepted=segwit_activated) # Now create something that looks like a P2PKH output. This won't be spendable. scriptPubKey = CScript([OP_0, hash160(witness_hash)]) @@ -1081,7 +1087,7 @@ class SegWitTest(BitcoinTestFramework): tx2.vout = [CTxOut(p2sh_tx.vout[0].nValue-1000, scriptPubKey)] tx2.rehash() - self.std_node.test_transaction_acceptance(tx2, with_witness=True, accepted=segwit_activated) + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, with_witness=True, accepted=segwit_activated) # Now update self.utxo for later tests. tx3 = CTransaction() @@ -1094,13 +1100,13 @@ class SegWitTest(BitcoinTestFramework): tx3.wit.vtxinwit.append(CTxInWitness()) tx3.wit.vtxinwit[0].scriptWitness.stack = [witness_program] tx3.rehash() - self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True) else: # tx and tx2 didn't go anywhere; just clean up the p2sh_tx output. tx3.vin = [CTxIn(COutPoint(p2sh_tx.sha256, 0), CScript([witness_program]))] tx3.vout = [CTxOut(p2sh_tx.vout[0].nValue-1000, witness_program)] tx3.rehash() - self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=True) self.nodes[0].generate(1) sync_blocks(self.nodes) @@ -1124,7 +1130,7 @@ class SegWitTest(BitcoinTestFramework): tx.rehash() block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx]) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) self.utxo.pop(0) for i in range(NUM_TESTS): self.utxo.append(UTXO(tx.sha256, i, split_value)) @@ -1143,8 +1149,8 @@ class SegWitTest(BitcoinTestFramework): tx.vin = [CTxIn(COutPoint(self.utxo[0].sha256, self.utxo[0].n), b"")] tx.vout = [CTxOut(self.utxo[0].nValue-1000, scriptPubKey)] tx.rehash() - self.std_node.test_transaction_acceptance(tx, with_witness=True, accepted=False) - self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx, with_witness=True, accepted=False) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=True) self.utxo.pop(0) temp_utxo.append(UTXO(tx.sha256, 0, tx.vout[0].nValue)) @@ -1163,8 +1169,8 @@ class SegWitTest(BitcoinTestFramework): tx2.rehash() # Gets accepted to test_node, because standardness of outputs isn't # checked with fRequireStandard - self.test_node.test_transaction_acceptance(tx2, with_witness=True, accepted=True) - self.std_node.test_transaction_acceptance(tx2, with_witness=True, accepted=False) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, tx2, with_witness=True, accepted=False) temp_utxo.pop() # last entry in temp_utxo was the output we just spent temp_utxo.append(UTXO(tx2.sha256, 0, tx2.vout[0].nValue)) @@ -1180,7 +1186,7 @@ class SegWitTest(BitcoinTestFramework): tx3.rehash() # Spending a higher version witness output is not allowed by policy, # even with fRequireStandard=false. - self.test_node.test_transaction_acceptance(tx3, with_witness=True, accepted=False) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, with_witness=True, accepted=False) self.test_node.sync_with_ping() with mininode_lock: assert(b"reserved for soft-fork upgrades" in self.test_node.last_message["reject"].reason) @@ -1188,7 +1194,7 @@ class SegWitTest(BitcoinTestFramework): # Building a block with the transaction must be valid, however. block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx2, tx3]) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) sync_blocks(self.nodes) # Add utxo to our list @@ -1206,7 +1212,7 @@ class SegWitTest(BitcoinTestFramework): # This next line will rehash the coinbase and update the merkle # root, and solve. self.update_witness_block_with_transactions(block, []) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) spend_tx = CTransaction() spend_tx.vin = [CTxIn(COutPoint(block.vtx[0].sha256, 0), b"")] @@ -1220,13 +1226,13 @@ class SegWitTest(BitcoinTestFramework): sync_blocks(self.nodes) block2 = self.build_next_block() self.update_witness_block_with_transactions(block2, [spend_tx]) - self.test_node.test_witness_block(block2, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block2, accepted=False) # Advancing one more block should allow the spend. self.nodes[0].generate(1) block2 = self.build_next_block() self.update_witness_block_with_transactions(block2, [spend_tx]) - self.test_node.test_witness_block(block2, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block2, accepted=True) sync_blocks(self.nodes) @@ -1247,11 +1253,11 @@ class SegWitTest(BitcoinTestFramework): tx.vout.append(CTxOut(self.utxo[0].nValue-1000, scriptPubKey)) tx.rehash() - self.test_node.test_transaction_acceptance(tx, with_witness=True, accepted=True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=True, accepted=True) # Mine this transaction in preparation for following tests. block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx]) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) sync_blocks(self.nodes) self.utxo.pop(0) @@ -1268,19 +1274,19 @@ class SegWitTest(BitcoinTestFramework): # Too-large input value sign_P2PK_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue+1, key) self.update_witness_block_with_transactions(block, [tx]) - self.test_node.test_witness_block(block, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Too-small input value sign_P2PK_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue-1, key) block.vtx.pop() # remove last tx self.update_witness_block_with_transactions(block, [tx]) - self.test_node.test_witness_block(block, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Now try correct value sign_P2PK_witness_input(witness_program, tx, 0, hashtype, prev_utxo.nValue, key) block.vtx.pop() self.update_witness_block_with_transactions(block, [tx]) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) prev_utxo = UTXO(tx.sha256, 0, tx.vout[0].nValue) @@ -1304,7 +1310,7 @@ class SegWitTest(BitcoinTestFramework): block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx]) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) block = self.build_next_block() used_sighash_single_out_of_bounds = False @@ -1346,7 +1352,7 @@ class SegWitTest(BitcoinTestFramework): # Test the block periodically, if we're close to maxblocksize if (get_virtual_size(block) > MAX_BLOCK_BASE_SIZE - 1000): self.update_witness_block_with_transactions(block, []) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) block = self.build_next_block() if (not used_sighash_single_out_of_bounds): @@ -1354,7 +1360,7 @@ class SegWitTest(BitcoinTestFramework): # Test the transactions we've added to the block if (len(block.vtx) > 1): self.update_witness_block_with_transactions(block, []) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) # Now test witness version 0 P2PKH transactions pubkeyhash = hash160(pubkey) @@ -1376,7 +1382,7 @@ class SegWitTest(BitcoinTestFramework): tx2.vin[0].scriptSig = CScript([signature, pubkey]) block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx, tx2]) - self.test_node.test_witness_block(block, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=False) # Move the signature to the witness. block.vtx.pop() @@ -1386,26 +1392,28 @@ class SegWitTest(BitcoinTestFramework): tx2.rehash() self.update_witness_block_with_transactions(block, [tx2]) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) temp_utxos.pop(0) - # Update self.utxos for later tests. Just spend everything in - # temp_utxos to a corresponding entry in self.utxos + # Update self.utxos for later tests by creating two outputs + # that consolidate all the coins in temp_utxos. + output_value = sum(i.nValue for i in temp_utxos) // 2 + tx = CTransaction() index = 0 + # Just spend to our usual anyone-can-spend output + tx.vout = [CTxOut(output_value, CScript([OP_TRUE]))] * 2 for i in temp_utxos: - # Just spend to our usual anyone-can-spend output - # Use SIGHASH_SINGLE|SIGHASH_ANYONECANPAY so we can build up + # Use SIGHASH_ALL|SIGHASH_ANYONECANPAY so we can build up # the signatures as we go. tx.vin.append(CTxIn(COutPoint(i.sha256, i.n), b"")) - tx.vout.append(CTxOut(i.nValue, CScript([OP_TRUE]))) tx.wit.vtxinwit.append(CTxInWitness()) - sign_P2PK_witness_input(witness_program, tx, index, SIGHASH_SINGLE|SIGHASH_ANYONECANPAY, i.nValue, key) + sign_P2PK_witness_input(witness_program, tx, index, SIGHASH_ALL|SIGHASH_ANYONECANPAY, i.nValue, key) index += 1 block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx]) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) for i in range(len(tx.vout)): self.utxo.append(UTXO(tx.sha256, i, tx.vout[i].nValue)) @@ -1432,10 +1440,10 @@ class SegWitTest(BitcoinTestFramework): tx.rehash() # Verify mempool acceptance and block validity - self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True) block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx]) - self.test_node.test_witness_block(block, accepted=True, with_witness=segwit_activated) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True, with_witness=segwit_activated) sync_blocks(self.nodes) # Now test attempts to spend the output. @@ -1449,12 +1457,12 @@ class SegWitTest(BitcoinTestFramework): # will require a witness to spend a witness program regardless of # segwit activation. Note that older bitcoind's that are not # segwit-aware would also reject this for failing CLEANSTACK. - self.test_node.test_transaction_acceptance(spend_tx, with_witness=False, accepted=False) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=False, accepted=False) # Try to put the witness script in the scriptSig, should also fail. spend_tx.vin[0].scriptSig = CScript([p2wsh_pubkey, b'a']) spend_tx.rehash() - self.test_node.test_transaction_acceptance(spend_tx, with_witness=False, accepted=False) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=False, accepted=False) # Now put the witness script in the witness, should succeed after # segwit activates. @@ -1464,7 +1472,7 @@ class SegWitTest(BitcoinTestFramework): spend_tx.wit.vtxinwit[0].scriptWitness.stack = [ b'a', witness_program ] # Verify mempool acceptance - self.test_node.test_transaction_acceptance(spend_tx, with_witness=True, accepted=segwit_activated) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, spend_tx, with_witness=True, accepted=segwit_activated) block = self.build_next_block() self.update_witness_block_with_transactions(block, [spend_tx]) @@ -1472,9 +1480,9 @@ class SegWitTest(BitcoinTestFramework): # should be valid. If we're after activation, then sending this with # witnesses should be valid. if segwit_activated: - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) else: - self.test_node.test_witness_block(block, accepted=True, with_witness=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True, with_witness=False) # Update self.utxo self.utxo.pop(0) @@ -1558,7 +1566,7 @@ class SegWitTest(BitcoinTestFramework): block_1 = self.build_next_block() self.update_witness_block_with_transactions(block_1, [tx]) - self.test_node.test_witness_block(block_1, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block_1, accepted=True) tx2 = CTransaction() # If we try to spend the first n-1 outputs from tx, that should be @@ -1575,7 +1583,7 @@ class SegWitTest(BitcoinTestFramework): block_2 = self.build_next_block() self.update_witness_block_with_transactions(block_2, [tx2]) - self.test_node.test_witness_block(block_2, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block_2, accepted=False) # Try dropping the last input in tx2, and add an output that has # too many sigops (contributing to legacy sigop count). @@ -1588,14 +1596,14 @@ class SegWitTest(BitcoinTestFramework): tx2.rehash() block_3 = self.build_next_block() self.update_witness_block_with_transactions(block_3, [tx2]) - self.test_node.test_witness_block(block_3, accepted=False) + test_witness_block(self.nodes[0].rpc, self.test_node, block_3, accepted=False) # If we drop the last checksig in this output, the tx should succeed. block_4 = self.build_next_block() tx2.vout[-1].scriptPubKey = CScript([OP_CHECKSIG]*(checksig_count-1)) tx2.rehash() self.update_witness_block_with_transactions(block_4, [tx2]) - self.test_node.test_witness_block(block_4, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block_4, accepted=True) # Reset the tip back down for the next test sync_blocks(self.nodes) @@ -1611,7 +1619,7 @@ class SegWitTest(BitcoinTestFramework): tx2.wit.vtxinwit[-1].scriptWitness.stack = [ witness_program_justright ] tx2.rehash() self.update_witness_block_with_transactions(block_5, [tx2]) - self.test_node.test_witness_block(block_5, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block_5, accepted=True) # TODO: test p2sh sigop counting @@ -1689,7 +1697,7 @@ class SegWitTest(BitcoinTestFramework): # Confirm it in a block. block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx]) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) # Now try to spend it. Send it to a P2WSH output, which we'll # use in the next test. @@ -1708,11 +1716,11 @@ class SegWitTest(BitcoinTestFramework): tx2.rehash() # Should fail policy test. - self.test_node.test_transaction_acceptance(tx2, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx2, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') # But passes consensus. block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx2]) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) # Test 2: P2WSH # Try to spend the P2WSH output created in last test. @@ -1728,11 +1736,11 @@ class SegWitTest(BitcoinTestFramework): sign_P2PK_witness_input(witness_program, tx3, 0, SIGHASH_ALL, tx2.vout[0].nValue, key) # Should fail policy test. - self.test_node.test_transaction_acceptance(tx3, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx3, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') # But passes consensus. block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx3]) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) # Test 3: P2SH(P2WSH) # Try to spend the P2SH output created in the last test. @@ -1745,10 +1753,10 @@ class SegWitTest(BitcoinTestFramework): sign_P2PK_witness_input(witness_program, tx4, 0, SIGHASH_ALL, tx3.vout[0].nValue, key) # Should fail policy test. - self.test_node.test_transaction_acceptance(tx4, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx4, True, False, b'non-mandatory-script-verify-flag (Using non-compressed keys in segwit)') block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx4]) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) # Test 4: Uncompressed pubkeys should still be valid in non-segwit # transactions. @@ -1760,10 +1768,10 @@ class SegWitTest(BitcoinTestFramework): tx5.vin[0].scriptSig = CScript([signature, pubkey]) tx5.rehash() # Should pass policy and consensus. - self.test_node.test_transaction_acceptance(tx5, True, True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx5, True, True) block = self.build_next_block() self.update_witness_block_with_transactions(block, [tx5]) - self.test_node.test_witness_block(block, accepted=True) + test_witness_block(self.nodes[0].rpc, self.test_node, block, accepted=True) self.utxo.append(UTXO(tx5.sha256, 0, tx5.vout[0].nValue)) def test_non_standard_witness(self): @@ -1793,7 +1801,7 @@ class SegWitTest(BitcoinTestFramework): tx.vout.append(CTxOut(outputvalue, CScript([OP_HASH160, p2sh, OP_EQUAL]))) tx.rehash() txid = tx.sha256 - self.test_node.test_transaction_acceptance(tx, with_witness=False, accepted=True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, tx, with_witness=False, accepted=True) self.nodes[0].generate(1) sync_blocks(self.nodes) @@ -1818,45 +1826,45 @@ class SegWitTest(BitcoinTestFramework): # Testing native P2WSH # Witness stack size, excluding witnessScript, over 100 is non-standard p2wsh_txs[0].wit.vtxinwit[0].scriptWitness.stack = [pad] * 101 + [scripts[0]] - self.std_node.test_transaction_acceptance(p2wsh_txs[0], True, False, b'bad-witness-nonstandard') + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2wsh_txs[0], True, False, b'bad-witness-nonstandard') # Non-standard nodes should accept - self.test_node.test_transaction_acceptance(p2wsh_txs[0], True, True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2wsh_txs[0], True, True) # Stack element size over 80 bytes is non-standard p2wsh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 81] * 100 + [scripts[1]] - self.std_node.test_transaction_acceptance(p2wsh_txs[1], True, False, b'bad-witness-nonstandard') + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2wsh_txs[1], True, False, b'bad-witness-nonstandard') # Non-standard nodes should accept - self.test_node.test_transaction_acceptance(p2wsh_txs[1], True, True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2wsh_txs[1], True, True) # Standard nodes should accept if element size is not over 80 bytes p2wsh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 80] * 100 + [scripts[1]] - self.std_node.test_transaction_acceptance(p2wsh_txs[1], True, True) + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2wsh_txs[1], True, True) # witnessScript size at 3600 bytes is standard p2wsh_txs[2].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, scripts[2]] - self.test_node.test_transaction_acceptance(p2wsh_txs[2], True, True) - self.std_node.test_transaction_acceptance(p2wsh_txs[2], True, True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2wsh_txs[2], True, True) + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2wsh_txs[2], True, True) # witnessScript size at 3601 bytes is non-standard p2wsh_txs[3].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, pad, scripts[3]] - self.std_node.test_transaction_acceptance(p2wsh_txs[3], True, False, b'bad-witness-nonstandard') + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2wsh_txs[3], True, False, b'bad-witness-nonstandard') # Non-standard nodes should accept - self.test_node.test_transaction_acceptance(p2wsh_txs[3], True, True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2wsh_txs[3], True, True) # Repeating the same tests with P2SH-P2WSH p2sh_txs[0].wit.vtxinwit[0].scriptWitness.stack = [pad] * 101 + [scripts[0]] - self.std_node.test_transaction_acceptance(p2sh_txs[0], True, False, b'bad-witness-nonstandard') - self.test_node.test_transaction_acceptance(p2sh_txs[0], True, True) + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[0], True, False, b'bad-witness-nonstandard') + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_txs[0], True, True) p2sh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 81] * 100 + [scripts[1]] - self.std_node.test_transaction_acceptance(p2sh_txs[1], True, False, b'bad-witness-nonstandard') - self.test_node.test_transaction_acceptance(p2sh_txs[1], True, True) + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[1], True, False, b'bad-witness-nonstandard') + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_txs[1], True, True) p2sh_txs[1].wit.vtxinwit[0].scriptWitness.stack = [pad * 80] * 100 + [scripts[1]] - self.std_node.test_transaction_acceptance(p2sh_txs[1], True, True) + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[1], True, True) p2sh_txs[2].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, scripts[2]] - self.test_node.test_transaction_acceptance(p2sh_txs[2], True, True) - self.std_node.test_transaction_acceptance(p2sh_txs[2], True, True) + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_txs[2], True, True) + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[2], True, True) p2sh_txs[3].wit.vtxinwit[0].scriptWitness.stack = [pad, pad, pad, scripts[3]] - self.std_node.test_transaction_acceptance(p2sh_txs[3], True, False, b'bad-witness-nonstandard') - self.test_node.test_transaction_acceptance(p2sh_txs[3], True, True) + test_transaction_acceptance(self.nodes[1].rpc, self.std_node, p2sh_txs[3], True, False, b'bad-witness-nonstandard') + test_transaction_acceptance(self.nodes[0].rpc, self.test_node, p2sh_txs[3], True, True) self.nodes[0].generate(1) # Mine and clean up the mempool of non-standard node # Valid but non-standard transactions in a block should be accepted by standard node @@ -1870,13 +1878,13 @@ class SegWitTest(BitcoinTestFramework): def run_test(self): # Setup the p2p connections and start up the network thread. # self.test_node sets NODE_WITNESS|NODE_NETWORK - self.test_node = self.nodes[0].add_p2p_connection(TestNode(self.nodes[0].rpc), services=NODE_NETWORK|NODE_WITNESS) + self.test_node = self.nodes[0].add_p2p_connection(TestNode(), services=NODE_NETWORK|NODE_WITNESS) # self.old_node sets only NODE_NETWORK - self.old_node = self.nodes[0].add_p2p_connection(TestNode(self.nodes[0].rpc), services=NODE_NETWORK) + self.old_node = self.nodes[0].add_p2p_connection(TestNode(), services=NODE_NETWORK) # self.std_node is for testing node1 (fRequireStandard=true) - self.std_node = self.nodes[1].add_p2p_connection(TestNode(self.nodes[1].rpc), services=NODE_NETWORK|NODE_WITNESS) + self.std_node = self.nodes[1].add_p2p_connection(TestNode(), services=NODE_NETWORK|NODE_WITNESS) - NetworkThread().start() # Start up network handling in another thread + network_thread_start() # Keep a place to store utxo's that can be used in later tests self.utxo = [] diff --git a/test/functional/sendheaders.py b/test/functional/p2p_sendheaders.py index 55bb80ea00..8869aeaaea 100755 --- a/test/functional/sendheaders.py +++ b/test/functional/p2p_sendheaders.py @@ -1,14 +1,16 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 behavior of headers messages to announce blocks. -Setup: +Setup: -- Two nodes, two p2p connections to node0. One p2p connection should only ever - receive inv's (omitted from testing description below, this is our control). - Second node is used for creating reorgs. +- Two nodes: + - node0 is the node-under-test. We create two p2p connections to it. The + first p2p connection is a control and should only ever receive inv's. The + second p2p connection tests the headers sending logic. + - node1 is used to create reorgs. test_null_locators ================== @@ -83,146 +85,160 @@ d. Announce 49 headers that don't connect. 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 +from test_framework.mininode import ( + CBlockHeader, + CInv, + NODE_WITNESS, + network_thread_start, + P2PInterface, + mininode_lock, + msg_block, + msg_getblocks, + msg_getdata, + msg_getheaders, + msg_headers, + msg_inv, + msg_sendheaders, +) +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + sync_blocks, + wait_until, +) +DIRECT_FETCH_RESPONSE_TIME = 0.05 -direct_fetch_response_time = 0.05 - -class TestNode(NodeConnCB): +class BaseNode(P2PInterface): def __init__(self): super().__init__() + self.block_announced = False self.last_blockhash_announced = None - def clear_last_announcement(self): - with mininode_lock: - self.block_announced = False - self.last_message.pop("inv", None) - self.last_message.pop("headers", None) - - # Request data for a list of block hashes - def get_data(self, block_hashes): + def send_get_data(self, block_hashes): + """Request data for a list of block hashes.""" msg = msg_getdata() for x in block_hashes: msg.inv.append(CInv(2, x)) - self.connection.send_message(msg) + self.send_message(msg) - def get_headers(self, locator, hashstop): + def send_get_headers(self, locator, hashstop): msg = msg_getheaders() msg.locator.vHave = locator msg.hashstop = hashstop - self.connection.send_message(msg) + self.send_message(msg) def send_block_inv(self, blockhash): msg = msg_inv() msg.inv = [CInv(2, blockhash)] - self.connection.send_message(msg) + self.send_message(msg) + + 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) - def on_inv(self, conn, message): + def send_getblocks(self, locator): + getblocks_message = msg_getblocks() + getblocks_message.locator.vHave = locator + self.send_message(getblocks_message) + + def wait_for_getdata(self, hash_list, timeout=60): + if hash_list == []: + return + + test_function = lambda: "getdata" in self.last_message and [x.hash for x in self.last_message["getdata"].inv] == hash_list + wait_until(test_function, timeout=timeout, lock=mininode_lock) + + def wait_for_block_announcement(self, block_hash, timeout=60): + test_function = lambda: self.last_blockhash_announced == block_hash + wait_until(test_function, timeout=timeout, lock=mininode_lock) + + def on_inv(self, message): self.block_announced = True self.last_blockhash_announced = message.inv[-1].hash - def on_headers(self, conn, message): + def on_headers(self, message): if len(message.headers): self.block_announced = True message.headers[-1].calc_sha256() self.last_blockhash_announced = message.headers[-1].sha256 - # Test whether the last announcement we received had the - # right header or the right inv - # inv and headers should be lists of block hashes + def clear_last_announcement(self): + with mininode_lock: + self.block_announced = False + self.last_message.pop("inv", None) + self.last_message.pop("headers", None) + def check_last_announcement(self, headers=None, inv=None): - expect_headers = headers if headers != None else [] - expect_inv = inv if inv != None else [] + """Test whether the last announcement received had the right header or the right inv. + + inv and headers should be lists of block hashes.""" + test_function = lambda: self.block_announced wait_until(test_function, timeout=60, lock=mininode_lock) + with mininode_lock: self.block_announced = False - success = True compare_inv = [] if "inv" in self.last_message: compare_inv = [x.hash for x in self.last_message["inv"].inv] - if compare_inv != expect_inv: - success = False + if inv is not None: + assert_equal(compare_inv, inv) - hash_headers = [] + compare_headers = [] if "headers" in self.last_message: - # treat headers as a list of block hashes - hash_headers = [ x.sha256 for x in self.last_message["headers"].headers ] - if hash_headers != expect_headers: - success = False + compare_headers = [x.sha256 for x in self.last_message["headers"].headers] + if headers is not None: + assert_equal(compare_headers, headers) self.last_message.pop("inv", None) self.last_message.pop("headers", None) - return success - - def wait_for_getdata(self, hash_list, timeout=60): - if hash_list == []: - return - - test_function = lambda: "getdata" in self.last_message and [x.hash for x in self.last_message["getdata"].inv] == hash_list - wait_until(test_function, timeout=timeout, lock=mininode_lock) - return - - def wait_for_block_announcement(self, block_hash, timeout=60): - test_function = lambda: self.last_blockhash_announced == block_hash - wait_until(test_function, timeout=timeout, lock=mininode_lock) - 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) - - def send_getblocks(self, locator): - getblocks_message = msg_getblocks() - getblocks_message.locator.vHave = locator - self.send_message(getblocks_message) class SendHeadersTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 - # mine count blocks and return the new tip def mine_blocks(self, count): + """Mine count blocks and return the new tip.""" + # Clear out last block announcement from each p2p listener [x.clear_last_announcement() for x in self.nodes[0].p2ps] self.nodes[0].generate(count) return int(self.nodes[0].getbestblockhash(), 16) - # mine a reorg that invalidates length blocks (replacing them with - # length+1 blocks). - # Note: we clear the state of our p2p connections after the - # to-be-reorged-out blocks are mined, so that we don't break later tests. - # return the list of block hashes newly mined def mine_reorg(self, length): - self.nodes[0].generate(length) # make sure all invalidated blocks are node0's + """Mine a reorg that invalidates length blocks (replacing them with # length+1 blocks). + + Note: we clear the state of our p2p connections after the + to-be-reorged-out blocks are mined, so that we don't break later tests. + return the list of block hashes newly mined.""" + + self.nodes[0].generate(length) # make sure all invalidated blocks are node0's sync_blocks(self.nodes, wait=0.1) for x in self.nodes[0].p2ps: x.wait_for_block_announcement(int(self.nodes[0].getbestblockhash(), 16)) x.clear_last_announcement() tip_height = self.nodes[1].getblockcount() - hash_to_invalidate = self.nodes[1].getblockhash(tip_height-(length-1)) + hash_to_invalidate = self.nodes[1].getblockhash(tip_height - (length - 1)) self.nodes[1].invalidateblock(hash_to_invalidate) - all_hashes = self.nodes[1].generate(length+1) # Must be longer than the orig chain + all_hashes = self.nodes[1].generate(length + 1) # Must be longer than the orig chain sync_blocks(self.nodes, wait=0.1) return [int(x, 16) for x in all_hashes] def run_test(self): # Setup the p2p connections and start up the network thread. - inv_node = self.nodes[0].add_p2p_connection(TestNode()) - # Set nServices to 0 for test_node, so no block download will occur outside of - # direct fetching - test_node = self.nodes[0].add_p2p_connection(TestNode(), services=NODE_WITNESS) + inv_node = self.nodes[0].add_p2p_connection(BaseNode()) + # Make sure NODE_NETWORK is not set for test_node, so no block download + # will occur outside of direct fetching + test_node = self.nodes[0].add_p2p_connection(BaseNode(), services=NODE_WITNESS) - NetworkThread().start() # Start up network handling in another thread + network_thread_start() # Test logic begins here inv_node.wait_for_verack() @@ -232,27 +248,32 @@ class SendHeadersTest(BitcoinTestFramework): inv_node.sync_with_ping() test_node.sync_with_ping() - self.test_null_locators(test_node) + self.test_null_locators(test_node, inv_node) self.test_nonnull_locators(test_node, inv_node) - def test_null_locators(self, test_node): + def test_null_locators(self, test_node, inv_node): tip = self.nodes[0].getblockheader(self.nodes[0].generate(1)[0]) tip_hash = int(tip["hash"], 16) + inv_node.check_last_announcement(inv=[tip_hash], headers=[]) + test_node.check_last_announcement(inv=[tip_hash], headers=[]) + self.log.info("Verify getheaders with null locator and valid hashstop returns headers.") test_node.clear_last_announcement() - test_node.get_headers(locator=[], hashstop=tip_hash) - assert_equal(test_node.check_last_announcement(headers=[tip_hash]), True) + test_node.send_get_headers(locator=[], hashstop=tip_hash) + test_node.check_last_announcement(headers=[tip_hash]) self.log.info("Verify getheaders with null locator and invalid hashstop does not return headers.") block = create_block(int(tip["hash"], 16), create_coinbase(tip["height"] + 1), tip["mediantime"] + 1) block.solve() test_node.send_header_for_blocks([block]) test_node.clear_last_announcement() - test_node.get_headers(locator=[], hashstop=int(block.hash, 16)) + test_node.send_get_headers(locator=[], hashstop=int(block.hash, 16)) test_node.sync_with_ping() assert_equal(test_node.block_announced, False) + inv_node.clear_last_announcement() test_node.send_message(msg_block(block)) + inv_node.check_last_announcement(inv=[int(block.hash, 16)], headers=[]) def test_nonnull_locators(self, test_node, inv_node): tip = int(self.nodes[0].getbestblockhash(), 16) @@ -263,30 +284,30 @@ class SendHeadersTest(BitcoinTestFramework): for i in range(4): old_tip = tip tip = self.mine_blocks(1) - assert_equal(inv_node.check_last_announcement(inv=[tip]), True) - assert_equal(test_node.check_last_announcement(inv=[tip]), True) + inv_node.check_last_announcement(inv=[tip], headers=[]) + test_node.check_last_announcement(inv=[tip], headers=[]) # Try a few different responses; none should affect next announcement if i == 0: # first request the block - test_node.get_data([tip]) + test_node.send_get_data([tip]) test_node.wait_for_block(tip) elif i == 1: # next try requesting header and block - test_node.get_headers(locator=[old_tip], hashstop=tip) - test_node.get_data([tip]) + test_node.send_get_headers(locator=[old_tip], hashstop=tip) + test_node.send_get_data([tip]) test_node.wait_for_block(tip) - test_node.clear_last_announcement() # since we requested headers... + test_node.clear_last_announcement() # since we requested headers... elif i == 2: # this time announce own block via headers height = self.nodes[0].getblockcount() last_time = self.nodes[0].getblock(self.nodes[0].getbestblockhash())['time'] block_time = last_time + 1 - new_block = create_block(tip, create_coinbase(height+1), block_time) + new_block = create_block(tip, create_coinbase(height + 1), block_time) new_block.solve() test_node.send_header_for_blocks([new_block]) test_node.wait_for_getdata([new_block.sha256]) test_node.send_message(msg_block(new_block)) - test_node.sync_with_ping() # make sure this block is processed + test_node.sync_with_ping() # make sure this block is processed inv_node.clear_last_announcement() test_node.clear_last_announcement() @@ -297,15 +318,15 @@ class SendHeadersTest(BitcoinTestFramework): # commence and keep working. test_node.send_message(msg_sendheaders()) prev_tip = int(self.nodes[0].getbestblockhash(), 16) - test_node.get_headers(locator=[prev_tip], hashstop=0) + test_node.send_get_headers(locator=[prev_tip], hashstop=0) test_node.sync_with_ping() # Now that we've synced headers, headers announcements should work tip = self.mine_blocks(1) - assert_equal(inv_node.check_last_announcement(inv=[tip]), True) - assert_equal(test_node.check_last_announcement(headers=[tip]), True) + inv_node.check_last_announcement(inv=[tip], headers=[]) + test_node.check_last_announcement(headers=[tip]) - height = self.nodes[0].getblockcount()+1 + height = self.nodes[0].getblockcount() + 1 block_time += 10 # Advance far enough ahead for i in range(10): # Mine i blocks, and alternate announcing either via @@ -314,7 +335,7 @@ class SendHeadersTest(BitcoinTestFramework): # with block header, even though the blocks are never requested for j in range(2): blocks = [] - for b in range(i+1): + for b in range(i + 1): blocks.append(create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 @@ -328,7 +349,7 @@ class SendHeadersTest(BitcoinTestFramework): test_node.send_header_for_blocks(blocks) # Test that duplicate inv's won't result in duplicate # getdata requests, or duplicate headers announcements - [ inv_node.send_block_inv(x.sha256) for x in blocks ] + [inv_node.send_block_inv(x.sha256) for x in blocks] test_node.wait_for_getdata([x.sha256 for x in blocks]) inv_node.sync_with_ping() else: @@ -339,7 +360,7 @@ class SendHeadersTest(BitcoinTestFramework): # getdata requests (the check is further down) inv_node.send_header_for_blocks(blocks) inv_node.sync_with_ping() - [ test_node.send_message(msg_block(x)) for x in blocks ] + [test_node.send_message(msg_block(x)) for x in blocks] test_node.sync_with_ping() inv_node.sync_with_ping() # This block should not be announced to the inv node (since it also @@ -347,8 +368,8 @@ class SendHeadersTest(BitcoinTestFramework): assert "inv" not in inv_node.last_message assert "headers" not in inv_node.last_message tip = self.mine_blocks(1) - assert_equal(inv_node.check_last_announcement(inv=[tip]), True) - assert_equal(test_node.check_last_announcement(headers=[tip]), True) + inv_node.check_last_announcement(inv=[tip], headers=[]) + test_node.check_last_announcement(headers=[tip]) height += 1 block_time += 1 @@ -362,16 +383,16 @@ class SendHeadersTest(BitcoinTestFramework): # First try mining a reorg that can propagate with header announcement new_block_hashes = self.mine_reorg(length=7) tip = new_block_hashes[-1] - assert_equal(inv_node.check_last_announcement(inv=[tip]), True) - assert_equal(test_node.check_last_announcement(headers=new_block_hashes), True) + inv_node.check_last_announcement(inv=[tip], headers=[]) + test_node.check_last_announcement(headers=new_block_hashes) - block_time += 8 + block_time += 8 # Mine a too-large reorg, which should be announced with a single inv new_block_hashes = self.mine_reorg(length=8) tip = new_block_hashes[-1] - assert_equal(inv_node.check_last_announcement(inv=[tip]), True) - assert_equal(test_node.check_last_announcement(inv=[tip]), True) + inv_node.check_last_announcement(inv=[tip], headers=[]) + test_node.check_last_announcement(inv=[tip], headers=[]) block_time += 9 @@ -379,42 +400,42 @@ class SendHeadersTest(BitcoinTestFramework): fork_point = int(fork_point, 16) # Use getblocks/getdata - test_node.send_getblocks(locator = [fork_point]) - assert_equal(test_node.check_last_announcement(inv=new_block_hashes), True) - test_node.get_data(new_block_hashes) + test_node.send_getblocks(locator=[fork_point]) + test_node.check_last_announcement(inv=new_block_hashes, headers=[]) + test_node.send_get_data(new_block_hashes) test_node.wait_for_block(new_block_hashes[-1]) for i in range(3): # Mine another block, still should get only an inv tip = self.mine_blocks(1) - assert_equal(inv_node.check_last_announcement(inv=[tip]), True) - assert_equal(test_node.check_last_announcement(inv=[tip]), True) + inv_node.check_last_announcement(inv=[tip], headers=[]) + test_node.check_last_announcement(inv=[tip], headers=[]) if i == 0: # Just get the data -- shouldn't cause headers announcements to resume - test_node.get_data([tip]) + test_node.send_get_data([tip]) test_node.wait_for_block(tip) elif i == 1: # Send a getheaders message that shouldn't trigger headers announcements # to resume (best header sent will be too old) - test_node.get_headers(locator=[fork_point], hashstop=new_block_hashes[1]) - test_node.get_data([tip]) + test_node.send_get_headers(locator=[fork_point], hashstop=new_block_hashes[1]) + test_node.send_get_data([tip]) test_node.wait_for_block(tip) elif i == 2: - test_node.get_data([tip]) + test_node.send_get_data([tip]) test_node.wait_for_block(tip) # This time, try sending either a getheaders to trigger resumption - # of headers announcements, or mine a new block and inv it, also + # of headers announcements, or mine a new block and inv it, also # triggering resumption of headers announcements. if j == 0: - test_node.get_headers(locator=[tip], hashstop=0) + test_node.send_get_headers(locator=[tip], hashstop=0) test_node.sync_with_ping() else: test_node.send_block_inv(tip) test_node.sync_with_ping() # New blocks should now be announced with header tip = self.mine_blocks(1) - assert_equal(inv_node.check_last_announcement(inv=[tip]), True) - assert_equal(test_node.check_last_announcement(headers=[tip]), True) + inv_node.check_last_announcement(inv=[tip], headers=[]) + test_node.check_last_announcement(headers=[tip]) self.log.info("Part 3: success!") @@ -434,7 +455,7 @@ class SendHeadersTest(BitcoinTestFramework): height += 1 inv_node.send_message(msg_block(blocks[-1])) - inv_node.sync_with_ping() # Make sure blocks are processed + inv_node.sync_with_ping() # Make sure blocks are processed test_node.last_message.pop("getdata", None) test_node.send_header_for_blocks(blocks) test_node.sync_with_ping() @@ -453,9 +474,9 @@ class SendHeadersTest(BitcoinTestFramework): test_node.send_header_for_blocks(blocks) test_node.sync_with_ping() - test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=direct_fetch_response_time) + test_node.wait_for_getdata([x.sha256 for x in blocks], timeout=DIRECT_FETCH_RESPONSE_TIME) - [ test_node.send_message(msg_block(x)) for x in blocks ] + [test_node.send_message(msg_block(x)) for x in blocks] test_node.sync_with_ping() @@ -484,13 +505,13 @@ class SendHeadersTest(BitcoinTestFramework): # both blocks (same work as tip) test_node.send_header_for_blocks(blocks[1:2]) test_node.sync_with_ping() - test_node.wait_for_getdata([x.sha256 for x in blocks[0:2]], timeout=direct_fetch_response_time) + test_node.wait_for_getdata([x.sha256 for x in blocks[0:2]], timeout=DIRECT_FETCH_RESPONSE_TIME) # Announcing 16 more headers should trigger direct fetch for 14 more # blocks test_node.send_header_for_blocks(blocks[2:18]) test_node.sync_with_ping() - test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=direct_fetch_response_time) + test_node.wait_for_getdata([x.sha256 for x in blocks[2:16]], timeout=DIRECT_FETCH_RESPONSE_TIME) # Announcing 1 more header should not trigger any response test_node.last_message.pop("getdata", None) @@ -502,7 +523,7 @@ class SendHeadersTest(BitcoinTestFramework): self.log.info("Part 4: success!") # Now deliver all those blocks we announced. - [ test_node.send_message(msg_block(x)) for x in blocks ] + [test_node.send_message(msg_block(x)) for x in blocks] self.log.info("Part 5: Testing handling of unconnecting headers") # First we test that receipt of an unconnecting header doesn't prevent @@ -524,7 +545,7 @@ class SendHeadersTest(BitcoinTestFramework): test_node.wait_for_getheaders() test_node.send_header_for_blocks(blocks) test_node.wait_for_getdata([x.sha256 for x in blocks]) - [ test_node.send_message(msg_block(x)) for x in blocks ] + [test_node.send_message(msg_block(x)) for x in blocks] test_node.sync_with_ping() assert_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256) @@ -532,7 +553,7 @@ class SendHeadersTest(BitcoinTestFramework): # Now we test that if we repeatedly don't send connecting headers, we # don't go into an infinite loop trying to get them to connect. MAX_UNCONNECTING_HEADERS = 10 - for j in range(MAX_UNCONNECTING_HEADERS+1): + for j in range(MAX_UNCONNECTING_HEADERS + 1): blocks.append(create_block(tip, create_coinbase(height), block_time)) blocks[-1].solve() tip = blocks[-1].sha256 @@ -554,11 +575,11 @@ class SendHeadersTest(BitcoinTestFramework): # Now try to see how many unconnecting headers we can send # before we get disconnected. Should be 5*MAX_UNCONNECTING_HEADERS - for i in range(5*MAX_UNCONNECTING_HEADERS - 1): + for i in range(5 * MAX_UNCONNECTING_HEADERS - 1): # Send a header that doesn't connect, check that we get a getheaders. with mininode_lock: test_node.last_message.pop("getheaders", None) - test_node.send_header_for_blocks([blocks[i%len(blocks)]]) + test_node.send_header_for_blocks([blocks[i % len(blocks)]]) test_node.wait_for_getheaders() # Eventually this stops working. diff --git a/test/functional/p2p-timeouts.py b/test/functional/p2p_timeouts.py index 14a3bf48fb..6d21095cc6 100755 --- a/test/functional/p2p-timeouts.py +++ b/test/functional/p2p_timeouts.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016 The Bitcoin Core developers +# Copyright (c) 2016-2017 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test various net timeouts. @@ -27,8 +27,8 @@ from test_framework.mininode import * from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * -class TestNode(NodeConnCB): - def on_version(self, conn, message): +class TestNode(P2PInterface): + def on_version(self, message): # Don't send a verack in response pass @@ -43,7 +43,7 @@ class TimeoutsTest(BitcoinTestFramework): no_version_node = self.nodes[0].add_p2p_connection(TestNode(), send_version=False) no_send_node = self.nodes[0].add_p2p_connection(TestNode(), send_version=False) - NetworkThread().start() # Start up network handling in another thread + network_thread_start() sleep(1) diff --git a/test/functional/p2p-acceptblock.py b/test/functional/p2p_unrequested_blocks.py index ca0e0080a1..672626f15b 100755 --- a/test/functional/p2p-acceptblock.py +++ b/test/functional/p2p_unrequested_blocks.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 processing of unrequested blocks. @@ -7,7 +7,7 @@ Setup: two nodes, node0+node1, not connected to each other. Node1 will have nMinimumChainWork set to 0x10, so it won't process low-work unrequested blocks. -We have one NodeConn connection to node0 called test_node, and one to node1 +We have one P2PInterface connection to node0 called test_node, and one to node1 called min_work_node. The test: @@ -79,11 +79,11 @@ class AcceptBlockTest(BitcoinTestFramework): def run_test(self): # Setup the p2p connections and start up the network thread. # test_node connects to node0 (not whitelisted) - test_node = self.nodes[0].add_p2p_connection(NodeConnCB()) - # min_work_node connects to node1 - min_work_node = self.nodes[1].add_p2p_connection(NodeConnCB()) + test_node = self.nodes[0].add_p2p_connection(P2PInterface()) + # min_work_node connects to node1 (whitelisted) + min_work_node = self.nodes[1].add_p2p_connection(P2PInterface()) - NetworkThread().start() # Start up network handling in another thread + network_thread_start() # Test logic begins here test_node.wait_for_verack() @@ -207,9 +207,13 @@ class AcceptBlockTest(BitcoinTestFramework): # disconnect/reconnect first self.nodes[0].disconnect_p2ps() - test_node = self.nodes[0].add_p2p_connection(NodeConnCB()) + self.nodes[1].disconnect_p2ps() + network_thread_join() + test_node = self.nodes[0].add_p2p_connection(P2PInterface()) + network_thread_start() test_node.wait_for_verack() + test_node.send_message(msg_block(block_h1f)) test_node.sync_with_ping() @@ -292,9 +296,9 @@ class AcceptBlockTest(BitcoinTestFramework): test_node.wait_for_disconnect() self.nodes[0].disconnect_p2ps() - test_node = self.nodes[0].add_p2p_connection(NodeConnCB()) + test_node = self.nodes[0].add_p2p_connection(P2PInterface()) - NetworkThread().start() # Start up network handling in another thread + network_thread_start() test_node.wait_for_verack() # We should have failed reorg and switched back to 290 (but have block 291) diff --git a/test/functional/rpcbind_test.py b/test/functional/rpc_bind.py index 0e8c3fa209..05433c7e24 100755 --- a/test/functional/rpcbind_test.py +++ b/test/functional/rpc_bind.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 running bitcoind with the -rpcbind and -rpcallowip options.""" diff --git a/test/functional/blockchain.py b/test/functional/rpc_blockchain.py index 49fafbc9aa..7cf2abe6f0 100755 --- a/test/functional/blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 RPCs related to blockchainstate. @@ -100,6 +100,8 @@ class BlockchainTest(BitcoinTestFramework): assert_greater_than(res['size_on_disk'], 0) def _test_getchaintxstats(self): + self.log.info("Test getchaintxstats") + chaintxstats = self.nodes[0].getchaintxstats(1) # 200 txs plus genesis tx assert_equal(chaintxstats['txcount'], 201) @@ -107,21 +109,25 @@ class BlockchainTest(BitcoinTestFramework): # we have to round because of binary math assert_equal(round(chaintxstats['txrate'] * 600, 10), Decimal(1)) - b1 = self.nodes[0].getblock(self.nodes[0].getblockhash(1)) - b200 = self.nodes[0].getblock(self.nodes[0].getblockhash(200)) + b1_hash = self.nodes[0].getblockhash(1) + b1 = self.nodes[0].getblock(b1_hash) + b200_hash = self.nodes[0].getblockhash(200) + b200 = self.nodes[0].getblock(b200_hash) time_diff = b200['mediantime'] - b1['mediantime'] chaintxstats = self.nodes[0].getchaintxstats() assert_equal(chaintxstats['time'], b200['time']) assert_equal(chaintxstats['txcount'], 201) + assert_equal(chaintxstats['window_final_block_hash'], b200_hash) assert_equal(chaintxstats['window_block_count'], 199) assert_equal(chaintxstats['window_tx_count'], 199) assert_equal(chaintxstats['window_interval'], time_diff) assert_equal(round(chaintxstats['txrate'] * time_diff, 10), Decimal(199)) - chaintxstats = self.nodes[0].getchaintxstats(blockhash=b1['hash']) + chaintxstats = self.nodes[0].getchaintxstats(blockhash=b1_hash) assert_equal(chaintxstats['time'], b1['time']) assert_equal(chaintxstats['txcount'], 2) + assert_equal(chaintxstats['window_final_block_hash'], b1_hash) assert_equal(chaintxstats['window_block_count'], 0) assert('window_tx_count' not in chaintxstats) assert('window_interval' not in chaintxstats) @@ -173,8 +179,7 @@ class BlockchainTest(BitcoinTestFramework): def _test_getblockheader(self): node = self.nodes[0] - assert_raises_rpc_error(-5, "Block not found", - node.getblockheader, "nonsense") + assert_raises_rpc_error(-5, "Block not found", node.getblockheader, "nonsense") besthash = node.getbestblockhash() secondbesthash = node.getblockhash(199) diff --git a/test/functional/decodescript.py b/test/functional/rpc_decodescript.py index 6611da8831..1ffc570437 100755 --- a/test/functional/decodescript.py +++ b/test/functional/rpc_decodescript.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 decoding scripts via decodescript RPC command.""" diff --git a/test/functional/rpc_deprecated.py b/test/functional/rpc_deprecated.py new file mode 100755 index 0000000000..90183474bb --- /dev/null +++ b/test/functional/rpc_deprecated.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test deprecation of RPC calls.""" +from test_framework.test_framework import BitcoinTestFramework + +class DeprecatedRpcTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 2 + self.setup_clean_chain = True + self.extra_args = [[], []] + + def run_test(self): + # This test should be used to verify correct behaviour of deprecated + # RPC methods with and without the -deprecatedrpc flags. For example: + # + # self.log.info("Make sure that -deprecatedrpc=createmultisig allows it to take addresses") + # assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 1, [self.nodes[0].getnewaddress()]) + # self.nodes[1].createmultisig(1, [self.nodes[1].getnewaddress()]) + # + # There are currently no deprecated RPC methods in master, so this + # test is currently empty. + pass + +if __name__ == '__main__': + DeprecatedRpcTest().main() diff --git a/test/functional/fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py index d446f56d0e..4d3be18516 100755 --- a/test/functional/fundrawtransaction.py +++ b/test/functional/rpc_fundrawtransaction.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 fundrawtransaction RPC.""" @@ -181,6 +181,9 @@ class RawTransactionsTest(BitcoinTestFramework): assert_raises_rpc_error(-3, "Unexpected key foo", self.nodes[2].fundrawtransaction, rawtx, {'foo':'bar'}) + # reserveChangeKey was deprecated and is now removed + assert_raises_rpc_error(-3, "Unexpected key reserveChangeKey", lambda: self.nodes[2].fundrawtransaction(hexstring=rawtx, options={'reserveChangeKey': True})) + ############################################################ # test a fundrawtransaction with an invalid change address # ############################################################ @@ -212,6 +215,19 @@ class RawTransactionsTest(BitcoinTestFramework): out = dec_tx['vout'][0] assert_equal(change, out['scriptPubKey']['addresses'][0]) + ######################################################### + # test a fundrawtransaction with a provided change type # + ######################################################### + utx = get_unspent(self.nodes[2].listunspent(), 5) + + inputs = [ {'txid' : utx['txid'], 'vout' : utx['vout']} ] + outputs = { self.nodes[0].getnewaddress() : Decimal(4.0) } + rawtx = self.nodes[2].createrawtransaction(inputs, outputs) + assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[2].fundrawtransaction, rawtx, {'change_type': None}) + assert_raises_rpc_error(-5, "Unknown change type", self.nodes[2].fundrawtransaction, rawtx, {'change_type': ''}) + rawtx = self.nodes[2].fundrawtransaction(rawtx, {'change_type': 'bech32'}) + dec_tx = self.nodes[2].decoderawtransaction(rawtx['hex']) + assert_equal('witness_v0_keyhash', dec_tx['vout'][rawtx['changepos']]['scriptPubKey']['type']) ######################################################################### # test a fundrawtransaction with a VIN smaller than the required amount # @@ -358,7 +374,7 @@ class RawTransactionsTest(BitcoinTestFramework): addr1Obj = self.nodes[1].validateaddress(addr1) addr2Obj = self.nodes[1].validateaddress(addr2) - mSigObj = self.nodes[1].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']]) + mSigObj = self.nodes[1].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] inputs = [] outputs = {mSigObj:1.1} @@ -391,7 +407,7 @@ class RawTransactionsTest(BitcoinTestFramework): addr4Obj = self.nodes[1].validateaddress(addr4) addr5Obj = self.nodes[1].validateaddress(addr5) - mSigObj = self.nodes[1].addmultisigaddress(4, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey'], addr4Obj['pubkey'], addr5Obj['pubkey']]) + mSigObj = self.nodes[1].addmultisigaddress(4, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey'], addr4Obj['pubkey'], addr5Obj['pubkey']])['address'] inputs = [] outputs = {mSigObj:1.1} @@ -418,7 +434,7 @@ class RawTransactionsTest(BitcoinTestFramework): addr1Obj = self.nodes[2].validateaddress(addr1) addr2Obj = self.nodes[2].validateaddress(addr2) - mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']]) + mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] # send 1.2 BTC to msig addr @@ -663,7 +679,7 @@ class RawTransactionsTest(BitcoinTestFramework): 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] + 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)] diff --git a/test/functional/getchaintips.py b/test/functional/rpc_getchaintips.py index 21b67bfc64..277930bb1a 100755 --- a/test/functional/getchaintips.py +++ b/test/functional/rpc_getchaintips.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 getchaintips RPC. diff --git a/test/functional/invalidateblock.py b/test/functional/rpc_invalidateblock.py index dd3daf1e07..b037c2431d 100755 --- a/test/functional/invalidateblock.py +++ b/test/functional/rpc_invalidateblock.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 invalidateblock RPC.""" diff --git a/test/functional/listtransactions.py b/test/functional/rpc_listtransactions.py index e4522cc3b5..ba71ac0967 100755 --- a/test/functional/listtransactions.py +++ b/test/functional/rpc_listtransactions.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 listtransactions API.""" @@ -81,7 +81,8 @@ class ListTransactionsTest(BitcoinTestFramework): {"category":"receive","amount":Decimal("0.44")}, {"txid":txid, "account" : "toself"} ) - multisig = self.nodes[1].createmultisig(1, [self.nodes[1].getnewaddress()]) + pubkey = self.nodes[1].validateaddress(self.nodes[1].getnewaddress())['pubkey'] + multisig = self.nodes[1].createmultisig(1, [pubkey]) self.nodes[0].importaddress(multisig["redeemScript"], "watchonly", False, True) txid = self.nodes[1].sendtoaddress(multisig["address"], 0.1) self.nodes[1].generate(1) diff --git a/test/functional/rpcnamedargs.py b/test/functional/rpc_named_arguments.py index c47212bddb..97bee39614 100755 --- a/test/functional/rpcnamedargs.py +++ b/test/functional/rpc_named_arguments.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016 The Bitcoin Core developers +# Copyright (c) 2016-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 using named arguments for RPCs.""" diff --git a/test/functional/net.py b/test/functional/rpc_net.py index 16e4f6adb4..16e4f6adb4 100755 --- a/test/functional/net.py +++ b/test/functional/rpc_net.py diff --git a/test/functional/preciousblock.py b/test/functional/rpc_preciousblock.py index 1466f901c0..960cd0ad12 100755 --- a/test/functional/preciousblock.py +++ b/test/functional/rpc_preciousblock.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 preciousblock RPC.""" diff --git a/test/functional/rawtransactions.py b/test/functional/rpc_rawtransaction.py index 2777cb9693..92126ef4b7 100755 --- a/test/functional/rawtransactions.py +++ b/test/functional/rpc_rawtransaction.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 rawtransaction RPCs. @@ -15,11 +15,31 @@ Test the following RPCs: from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * + +class multidict(dict): + """Dictionary that allows duplicate keys. + + Constructed with a list of (key, value) tuples. When dumped by the json module, + will output invalid json with repeated keys, eg: + >>> json.dumps(multidict([(1,2),(1,2)]) + '{"1": 2, "1": 2}' + + Used to test calls to rpc methods with repeated keys in the json object.""" + + def __init__(self, x): + dict.__init__(self, x) + self.x = x + + def items(self): + return self.x + + # Create one-input, one-output, no-fee transaction: class RawTransactionsTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 + self.extra_args = [["-addresstype=legacy"], ["-addresstype=legacy"], ["-addresstype=legacy"]] def setup_network(self, split=False): super().setup_network() @@ -39,6 +59,45 @@ class RawTransactionsTest(BitcoinTestFramework): self.nodes[0].generate(5) self.sync_all() + # Test getrawtransaction on genesis block coinbase returns an error + block = self.nodes[0].getblock(self.nodes[0].getblockhash(0)) + assert_raises_rpc_error(-5, "The genesis block coinbase is not considered an ordinary transaction", self.nodes[0].getrawtransaction, block['merkleroot']) + + # Test `createrawtransaction` required parameters + assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction) + assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, []) + + # Test `createrawtransaction` invalid extra parameters + assert_raises_rpc_error(-1, "createrawtransaction", self.nodes[0].createrawtransaction, [], {}, 0, False, 'foo') + + # Test `createrawtransaction` invalid `inputs` + txid = '1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000' + assert_raises_rpc_error(-3, "Expected type array", self.nodes[0].createrawtransaction, 'foo', {}) + assert_raises_rpc_error(-1, "JSON value is not an object as expected", self.nodes[0].createrawtransaction, ['foo'], {}) + assert_raises_rpc_error(-8, "txid must be hexadecimal string", self.nodes[0].createrawtransaction, [{}], {}) + assert_raises_rpc_error(-8, "txid must be hexadecimal string", self.nodes[0].createrawtransaction, [{'txid': 'foo'}], {}) + assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid}], {}) + assert_raises_rpc_error(-8, "Invalid parameter, missing vout key", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 'foo'}], {}) + assert_raises_rpc_error(-8, "Invalid parameter, vout must be positive", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': -1}], {}) + assert_raises_rpc_error(-8, "Invalid parameter, sequence number is out of range", self.nodes[0].createrawtransaction, [{'txid': txid, 'vout': 0, 'sequence': -1}], {}) + + # Test `createrawtransaction` invalid `outputs` + address = self.nodes[0].getnewaddress() + assert_raises_rpc_error(-3, "Expected type object", self.nodes[0].createrawtransaction, [], 'foo') + assert_raises_rpc_error(-8, "Data must be hexadecimal string", self.nodes[0].createrawtransaction, [], {'data': 'foo'}) + assert_raises_rpc_error(-5, "Invalid Bitcoin address", self.nodes[0].createrawtransaction, [], {'foo': 0}) + assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].createrawtransaction, [], {address: 'foo'}) + assert_raises_rpc_error(-3, "Amount out of range", self.nodes[0].createrawtransaction, [], {address: -1}) + assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], multidict([(address, 1), (address, 1)])) + + # Test `createrawtransaction` invalid `locktime` + assert_raises_rpc_error(-3, "Expected type number", self.nodes[0].createrawtransaction, [], {}, 'foo') + assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, -1) + assert_raises_rpc_error(-8, "Invalid parameter, locktime out of range", self.nodes[0].createrawtransaction, [], {}, 4294967296) + + # Test `createrawtransaction` invalid `replaceable` + assert_raises_rpc_error(-3, "Expected type bool", self.nodes[0].createrawtransaction, [], {}, 0, 'foo') + ######################################### # sendrawtransaction with missing input # ######################################### @@ -50,6 +109,36 @@ class RawTransactionsTest(BitcoinTestFramework): # This will raise an exception since there are missing inputs assert_raises_rpc_error(-25, "Missing inputs", self.nodes[2].sendrawtransaction, rawtx['hex']) + ##################################### + # getrawtransaction with block hash # + ##################################### + + # make a tx by sending then generate 2 blocks; block1 has the tx in it + tx = self.nodes[2].sendtoaddress(self.nodes[1].getnewaddress(), 1) + block1, block2 = self.nodes[2].generate(2) + self.sync_all() + # We should be able to get the raw transaction by providing the correct block + gottx = self.nodes[0].getrawtransaction(tx, True, block1) + assert_equal(gottx['txid'], tx) + assert_equal(gottx['in_active_chain'], True) + # We should not have the 'in_active_chain' flag when we don't provide a block + gottx = self.nodes[0].getrawtransaction(tx, True) + assert_equal(gottx['txid'], tx) + assert 'in_active_chain' not in gottx + # We should not get the tx if we provide an unrelated block + assert_raises_rpc_error(-5, "No such transaction found", self.nodes[0].getrawtransaction, tx, True, block2) + # An invalid block hash should raise the correct errors + assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", self.nodes[0].getrawtransaction, tx, True, True) + assert_raises_rpc_error(-8, "parameter 3 must be hexadecimal", self.nodes[0].getrawtransaction, tx, True, "foobar") + assert_raises_rpc_error(-8, "parameter 3 must be of length 64", self.nodes[0].getrawtransaction, tx, True, "abcd1234") + assert_raises_rpc_error(-5, "Block hash not found", self.nodes[0].getrawtransaction, tx, True, "0000000000000000000000000000000000000000000000000000000000000000") + # Undo the blocks and check in_active_chain + self.nodes[0].invalidateblock(block1) + gottx = self.nodes[0].getrawtransaction(txid=tx, verbose=True, blockhash=block1) + assert_equal(gottx['in_active_chain'], False) + self.nodes[0].reconsiderblock(block1) + assert_equal(self.nodes[0].getbestblockhash(), block2) + ######################### # RAW TX MULTISIG TESTS # ######################### @@ -60,7 +149,12 @@ class RawTransactionsTest(BitcoinTestFramework): addr1Obj = self.nodes[2].validateaddress(addr1) addr2Obj = self.nodes[2].validateaddress(addr2) - mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']]) + # Tests for createmultisig and addmultisigaddress + assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 1, ["01020304"]) + self.nodes[0].createmultisig(2, [addr1Obj['pubkey'], addr2Obj['pubkey']]) # createmultisig can only take public keys + assert_raises_rpc_error(-5, "Invalid public key", self.nodes[0].createmultisig, 2, [addr1Obj['pubkey'], addr1]) # addmultisigaddress can take both pubkeys and addresses so long as they are in the wallet, which is tested here. + + mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr1])['address'] #use balance deltas instead of absolute values bal = self.nodes[2].getbalance() @@ -83,7 +177,7 @@ class RawTransactionsTest(BitcoinTestFramework): addr2Obj = self.nodes[2].validateaddress(addr2) addr3Obj = self.nodes[2].validateaddress(addr3) - mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey']]) + mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey'], addr3Obj['pubkey']])['address'] txId = self.nodes[0].sendtoaddress(mSigObj, 2.2) decTx = self.nodes[0].gettransaction(txId) @@ -92,7 +186,7 @@ class RawTransactionsTest(BitcoinTestFramework): self.nodes[0].generate(1) self.sync_all() - #THIS IS A INCOMPLETE FEATURE + #THIS IS AN INCOMPLETE FEATURE #NODE2 HAS TWO OF THREE KEY AND THE FUNDS SHOULD BE SPENDABLE AND COUNT AT BALANCE CALCULATION assert_equal(self.nodes[2].getbalance(), bal) #for now, assume the funds of a 2of3 multisig tx are not marked as spendable @@ -105,7 +199,7 @@ class RawTransactionsTest(BitcoinTestFramework): break bal = self.nodes[0].getbalance() - inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex']}] + inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "amount" : vout['value']}] outputs = { self.nodes[0].getnewaddress() : 2.19 } rawTx = self.nodes[2].createrawtransaction(inputs, outputs) rawTxPartialSigned = self.nodes[1].signrawtransaction(rawTx, inputs) @@ -128,8 +222,8 @@ class RawTransactionsTest(BitcoinTestFramework): addr1Obj = self.nodes[1].validateaddress(addr1) addr2Obj = self.nodes[2].validateaddress(addr2) - self.nodes[1].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']]) - mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']]) + self.nodes[1].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] + mSigObj = self.nodes[2].addmultisigaddress(2, [addr1Obj['pubkey'], addr2Obj['pubkey']])['address'] mSigObjValid = self.nodes[2].validateaddress(mSigObj) txId = self.nodes[0].sendtoaddress(mSigObj, 2.2) @@ -150,7 +244,7 @@ class RawTransactionsTest(BitcoinTestFramework): break bal = self.nodes[0].getbalance() - inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "redeemScript" : mSigObjValid['hex']}] + inputs = [{ "txid" : txId, "vout" : vout['n'], "scriptPubKey" : vout['scriptPubKey']['hex'], "redeemScript" : mSigObjValid['hex'], "amount" : vout['value']}] outputs = { self.nodes[0].getnewaddress() : 2.19 } rawTx2 = self.nodes[2].createrawtransaction(inputs, outputs) rawTxPartialSigned1 = self.nodes[1].signrawtransaction(rawTx2, inputs) @@ -169,6 +263,17 @@ class RawTransactionsTest(BitcoinTestFramework): self.sync_all() assert_equal(self.nodes[0].getbalance(), bal+Decimal('50.00000000')+Decimal('2.19000000')) #block reward + tx + # decoderawtransaction tests + # witness transaction + encrawtx = "010000000001010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f50500000000000000000000" + decrawtx = self.nodes[0].decoderawtransaction(encrawtx, True) # decode as witness transaction + assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000')) + assert_raises_rpc_error(-22, 'TX decode failed', self.nodes[0].decoderawtransaction, encrawtx, False) # force decode as non-witness transaction + # non-witness transaction + encrawtx = "01000000010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f505000000000000000000" + decrawtx = self.nodes[0].decoderawtransaction(encrawtx, False) # decode as non-witness transaction + assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000')) + # getrawtransaction tests # 1. valid parameters - only supply txid txHash = rawTx["hash"] @@ -188,13 +293,13 @@ class RawTransactionsTest(BitcoinTestFramework): assert_equal(self.nodes[0].getrawtransaction(txHash, True)["hex"], rawTxSigned['hex']) # 6. invalid parameters - supply txid and string "Flase" - assert_raises_rpc_error(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, "Flase") + assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, "Flase") # 7. invalid parameters - supply txid and empty array - assert_raises_rpc_error(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, []) + assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, []) # 8. invalid parameters - supply txid and empty dict - assert_raises_rpc_error(-3,"Invalid type", self.nodes[0].getrawtransaction, txHash, {}) + assert_raises_rpc_error(-1, "not a boolean", self.nodes[0].getrawtransaction, txHash, {}) inputs = [ {'txid' : "1d1d4e24ed99057e84c3f80fd8fbec79ed9e1acee37da269356ecea000000000", 'vout' : 1, 'sequence' : 1000}] outputs = { self.nodes[0].getnewaddress() : 1 } diff --git a/test/functional/signmessages.py b/test/functional/rpc_signmessage.py index 52ba6a5ad7..5b6935ceea 100755 --- a/test/functional/signmessages.py +++ b/test/functional/rpc_signmessage.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016 The Bitcoin Core developers +# Copyright (c) 2016-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 RPC commands for signing and verifying messages.""" @@ -11,6 +11,7 @@ class SignMessagesTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 + self.extra_args = [["-addresstype=legacy"]] def run_test(self): message = 'This is just a test message' diff --git a/test/functional/signrawtransactions.py b/test/functional/rpc_signrawtransaction.py index 9a45d53cb8..dd0fa6c02c 100755 --- a/test/functional/signrawtransactions.py +++ b/test/functional/rpc_signrawtransaction.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 transaction signing using the signrawtransaction RPC.""" diff --git a/test/functional/merkle_blocks.py b/test/functional/rpc_txoutproof.py index b3989a4c54..50e0371fdf 100755 --- a/test/functional/merkle_blocks.py +++ b/test/functional/rpc_txoutproof.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 gettxoutproof and verifytxoutproof RPCs.""" diff --git a/test/functional/uptime.py b/test/functional/rpc_uptime.py index 78236b2393..78236b2393 100755 --- a/test/functional/uptime.py +++ b/test/functional/rpc_uptime.py diff --git a/test/functional/multi_rpc.py b/test/functional/rpc_users.py index a2b346f274..01f68344ae 100755 --- a/test/functional/multi_rpc.py +++ b/test/functional/rpc_users.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 multiple RPC users.""" diff --git a/test/functional/test_framework/address.py b/test/functional/test_framework/address.py index 2e2db5ffb2..b076c9dd43 100644 --- a/test/functional/test_framework/address.py +++ b/test/functional/test_framework/address.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016 The Bitcoin Core developers +# Copyright (c) 2016-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. """Encode and decode BASE58, P2PKH and P2SH addresses.""" diff --git a/test/functional/test_framework/blockstore.py b/test/functional/test_framework/blockstore.py index 051c57a6c7..6067a407cc 100644 --- a/test/functional/test_framework/blockstore.py +++ b/test/functional/test_framework/blockstore.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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. """BlockStore and TxStore helper classes.""" diff --git a/test/functional/test_framework/blocktools.py b/test/functional/test_framework/blocktools.py index 5dcf516dc6..642ef98a27 100644 --- a/test/functional/test_framework/blocktools.py +++ b/test/functional/test_framework/blocktools.py @@ -1,11 +1,27 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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. """Utilities for manipulating blocks and transactions.""" +from .address import ( + key_to_p2sh_p2wpkh, + key_to_p2wpkh, + script_to_p2sh_p2wsh, + script_to_p2wsh, +) from .mininode import * -from .script import CScript, OP_TRUE, OP_CHECKSIG, OP_RETURN +from .script import ( + CScript, + OP_0, + OP_1, + OP_CHECKMULTISIG, + OP_CHECKSIG, + OP_RETURN, + OP_TRUE, + hash160, +) +from .util import assert_equal # Create a block (with regtest difficulty) def create_block(hashprev, coinbase, nTime=None): @@ -108,3 +124,49 @@ def get_legacy_sigopcount_tx(tx, fAccurate=True): # scriptSig might be of type bytes, so convert to CScript for the moment count += CScript(j.scriptSig).GetSigOpCount(fAccurate) return count + +# Create a scriptPubKey corresponding to either a P2WPKH output for the +# given pubkey, or a P2WSH output of a 1-of-1 multisig for the given +# pubkey. Returns the hex encoding of the scriptPubKey. +def witness_script(use_p2wsh, pubkey): + if (use_p2wsh == False): + # P2WPKH instead + pubkeyhash = hash160(hex_str_to_bytes(pubkey)) + pkscript = CScript([OP_0, pubkeyhash]) + else: + # 1-of-1 multisig + witness_program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG]) + scripthash = sha256(witness_program) + pkscript = CScript([OP_0, scripthash]) + return bytes_to_hex_str(pkscript) + +# Return a transaction (in hex) that spends the given utxo to a segwit output, +# optionally wrapping the segwit output using P2SH. +def create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount): + if use_p2wsh: + program = CScript([OP_1, hex_str_to_bytes(pubkey), OP_1, OP_CHECKMULTISIG]) + addr = script_to_p2sh_p2wsh(program) if encode_p2sh else script_to_p2wsh(program) + else: + addr = key_to_p2sh_p2wpkh(pubkey) if encode_p2sh else key_to_p2wpkh(pubkey) + if not encode_p2sh: + assert_equal(node.validateaddress(addr)['scriptPubKey'], witness_script(use_p2wsh, pubkey)) + return node.createrawtransaction([utxo], {addr: amount}) + +# Create a transaction spending a given utxo to a segwit output corresponding +# to the given pubkey: use_p2wsh determines whether to use P2WPKH or P2WSH; +# encode_p2sh determines whether to wrap in P2SH. +# sign=True will have the given node sign the transaction. +# insert_redeem_script will be added to the scriptSig, if given. +def send_to_witness(use_p2wsh, node, utxo, pubkey, encode_p2sh, amount, sign=True, insert_redeem_script=""): + tx_to_witness = create_witness_tx(node, use_p2wsh, utxo, pubkey, encode_p2sh, amount) + if (sign): + signed = node.signrawtransaction(tx_to_witness) + assert("errors" not in signed or len(["errors"]) == 0) + return node.sendrawtransaction(signed["hex"]) + else: + if (insert_redeem_script): + tx = FromHex(CTransaction(), tx_to_witness) + tx.vin[0].scriptSig += CScript([hex_str_to_bytes(insert_redeem_script)]) + tx_to_witness = ToHex(tx) + + return node.sendrawtransaction(tx_to_witness) diff --git a/test/functional/test_framework/comptool.py b/test/functional/test_framework/comptool.py index 723826bae4..61ea2280e2 100755 --- a/test/functional/test_framework/comptool.py +++ b/test/functional/test_framework/comptool.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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. """Compare two or more bitcoinds to each other. @@ -39,11 +39,10 @@ class RejectResult(): def __repr__(self): return '%i:%s' % (self.code,self.reason or '*') -class TestNode(NodeConnCB): +class TestNode(P2PInterface): def __init__(self, block_store, tx_store): super().__init__() - self.conn = None self.bestblockhash = None self.block_store = block_store self.block_request_map = {} @@ -58,26 +57,23 @@ class TestNode(NodeConnCB): self.lastInv = [] self.closed = False - def on_close(self, conn): + def on_close(self): self.closed = True - def add_connection(self, conn): - self.conn = conn - - def on_headers(self, conn, message): + def on_headers(self, message): if len(message.headers) > 0: best_header = message.headers[-1] best_header.calc_sha256() self.bestblockhash = best_header.sha256 - def on_getheaders(self, conn, message): + def on_getheaders(self, message): response = self.block_store.headers_for(message.locator, message.hashstop) if response is not None: - conn.send_message(response) + self.send_message(response) - def on_getdata(self, conn, message): - [conn.send_message(r) for r in self.block_store.get_blocks(message.inv)] - [conn.send_message(r) for r in self.tx_store.get_transactions(message.inv)] + def on_getdata(self, message): + [self.send_message(r) for r in self.block_store.get_blocks(message.inv)] + [self.send_message(r) for r in self.tx_store.get_transactions(message.inv)] for i in message.inv: if i.type == 1 or i.type == 1 | (1 << 30): # MSG_TX or MSG_WITNESS_TX @@ -85,16 +81,16 @@ class TestNode(NodeConnCB): elif i.type == 2 or i.type == 2 | (1 << 30): # MSG_BLOCK or MSG_WITNESS_BLOCK self.block_request_map[i.hash] = True - def on_inv(self, conn, message): + def on_inv(self, message): self.lastInv = [x.hash for x in message.inv] - def on_pong(self, conn, message): + def on_pong(self, message): try: del self.pingMap[message.nonce] except KeyError: raise AssertionError("Got pong for unknown ping [%s]" % repr(message)) - def on_reject(self, conn, message): + def on_reject(self, message): if message.message == b'tx': self.tx_reject_map[message.data] = RejectResult(message.code, message.reason) if message.message == b'block': @@ -102,30 +98,30 @@ class TestNode(NodeConnCB): def send_inv(self, obj): mtype = 2 if isinstance(obj, CBlock) else 1 - self.conn.send_message(msg_inv([CInv(mtype, obj.sha256)])) + self.send_message(msg_inv([CInv(mtype, obj.sha256)])) def send_getheaders(self): # We ask for headers from their last tip. m = msg_getheaders() m.locator = self.block_store.get_locator(self.bestblockhash) - self.conn.send_message(m) + self.send_message(m) def send_header(self, header): m = msg_headers() m.headers.append(header) - self.conn.send_message(m) + self.send_message(m) # This assumes BIP31 def send_ping(self, nonce): self.pingMap[nonce] = True - self.conn.send_message(msg_ping(nonce)) + self.send_message(msg_ping(nonce)) def received_ping_response(self, nonce): return nonce not in self.pingMap def send_mempool(self): self.lastInv = [] - self.conn.send_message(msg_mempool()) + self.send_message(msg_mempool()) # TestInstance: # @@ -166,8 +162,7 @@ class TestManager(): def __init__(self, testgen, datadir): self.test_generator = testgen - self.connections = [] - self.test_nodes = [] + self.p2p_connections= [] self.block_store = BlockStore(datadir) self.tx_store = TxStore(datadir) self.ping_counter = 1 @@ -175,28 +170,24 @@ class TestManager(): def add_all_connections(self, nodes): for i in range(len(nodes)): # Create a p2p connection to each node - test_node = TestNode(self.block_store, self.tx_store) - self.test_nodes.append(test_node) - self.connections.append(NodeConn('127.0.0.1', p2p_port(i), test_node)) - # Make sure the TestNode (callback class) has a reference to its - # associated NodeConn - test_node.add_connection(self.connections[-1]) + node = TestNode(self.block_store, self.tx_store) + node.peer_connect('127.0.0.1', p2p_port(i)) + self.p2p_connections.append(node) def clear_all_connections(self): - self.connections = [] - self.test_nodes = [] + self.p2p_connections = [] def wait_for_disconnections(self): def disconnected(): - return all(node.closed for node in self.test_nodes) + return all(node.closed for node in self.p2p_connections) wait_until(disconnected, timeout=10, lock=mininode_lock) def wait_for_verack(self): - return all(node.wait_for_verack() for node in self.test_nodes) + return all(node.wait_for_verack() for node in self.p2p_connections) def wait_for_pings(self, counter): def received_pongs(): - return all(node.received_ping_response(counter) for node in self.test_nodes) + return all(node.received_ping_response(counter) for node in self.p2p_connections) wait_until(received_pongs, lock=mininode_lock) # sync_blocks: Wait for all connections to request the blockhash given @@ -206,17 +197,17 @@ class TestManager(): def blocks_requested(): return all( blockhash in node.block_request_map and node.block_request_map[blockhash] - for node in self.test_nodes + for node in self.p2p_connections ) # --> error if not requested wait_until(blocks_requested, attempts=20*num_blocks, lock=mininode_lock) # Send getheaders message - [ c.cb.send_getheaders() for c in self.connections ] + [ c.send_getheaders() for c in self.p2p_connections ] # Send ping and wait for response -- synchronization hack - [ c.cb.send_ping(self.ping_counter) for c in self.connections ] + [ c.send_ping(self.ping_counter) for c in self.p2p_connections ] self.wait_for_pings(self.ping_counter) self.ping_counter += 1 @@ -226,42 +217,42 @@ class TestManager(): def transaction_requested(): return all( txhash in node.tx_request_map and node.tx_request_map[txhash] - for node in self.test_nodes + for node in self.p2p_connections ) # --> error if not requested wait_until(transaction_requested, attempts=20*num_events, lock=mininode_lock) # Get the mempool - [ c.cb.send_mempool() for c in self.connections ] + [ c.send_mempool() for c in self.p2p_connections ] # Send ping and wait for response -- synchronization hack - [ c.cb.send_ping(self.ping_counter) for c in self.connections ] + [ c.send_ping(self.ping_counter) for c in self.p2p_connections ] self.wait_for_pings(self.ping_counter) self.ping_counter += 1 # Sort inv responses from each node with mininode_lock: - [ c.cb.lastInv.sort() for c in self.connections ] + [ c.lastInv.sort() for c in self.p2p_connections ] # Verify that the tip of each connection all agree with each other, and # with the expected outcome (if given) def check_results(self, blockhash, outcome): with mininode_lock: - for c in self.connections: + for c in self.p2p_connections: if outcome is None: - if c.cb.bestblockhash != self.connections[0].cb.bestblockhash: + if c.bestblockhash != self.p2p_connections[0].bestblockhash: return False elif isinstance(outcome, RejectResult): # Check that block was rejected w/ code - if c.cb.bestblockhash == blockhash: + if c.bestblockhash == blockhash: return False - if blockhash not in c.cb.block_reject_map: + if blockhash not in c.block_reject_map: logger.error('Block not in reject map: %064x' % (blockhash)) return False - if not outcome.match(c.cb.block_reject_map[blockhash]): - logger.error('Block rejected with %s instead of expected %s: %064x' % (c.cb.block_reject_map[blockhash], outcome, blockhash)) + if not outcome.match(c.block_reject_map[blockhash]): + logger.error('Block rejected with %s instead of expected %s: %064x' % (c.block_reject_map[blockhash], outcome, blockhash)) return False - elif ((c.cb.bestblockhash == blockhash) != outcome): + elif ((c.bestblockhash == blockhash) != outcome): return False return True @@ -273,21 +264,21 @@ class TestManager(): # a particular tx's existence in the mempool is the same across all nodes. def check_mempool(self, txhash, outcome): with mininode_lock: - for c in self.connections: + for c in self.p2p_connections: if outcome is None: # Make sure the mempools agree with each other - if c.cb.lastInv != self.connections[0].cb.lastInv: + if c.lastInv != self.p2p_connections[0].lastInv: return False elif isinstance(outcome, RejectResult): # Check that tx was rejected w/ code - if txhash in c.cb.lastInv: + if txhash in c.lastInv: return False - if txhash not in c.cb.tx_reject_map: + if txhash not in c.tx_reject_map: logger.error('Tx not in reject map: %064x' % (txhash)) return False - if not outcome.match(c.cb.tx_reject_map[txhash]): - logger.error('Tx rejected with %s instead of expected %s: %064x' % (c.cb.tx_reject_map[txhash], outcome, txhash)) + if not outcome.match(c.tx_reject_map[txhash]): + logger.error('Tx rejected with %s instead of expected %s: %064x' % (c.tx_reject_map[txhash], outcome, txhash)) return False - elif ((txhash in c.cb.lastInv) != outcome): + elif ((txhash in c.lastInv) != outcome): return False return True @@ -332,25 +323,25 @@ class TestManager(): first_block_with_hash = False with mininode_lock: self.block_store.add_block(block) - for c in self.connections: - if first_block_with_hash and block.sha256 in c.cb.block_request_map and c.cb.block_request_map[block.sha256] == True: + for c in self.p2p_connections: + if first_block_with_hash and block.sha256 in c.block_request_map and c.block_request_map[block.sha256] == True: # There was a previous request for this block hash # Most likely, we delivered a header for this block # but never had the block to respond to the getdata c.send_message(msg_block(block)) else: - c.cb.block_request_map[block.sha256] = False + c.block_request_map[block.sha256] = False # Either send inv's to each node and sync, or add # to invqueue for later inv'ing. if (test_instance.sync_every_block): # if we expect success, send inv and sync every block # if we expect failure, just push the block and see what happens. if outcome == True: - [ c.cb.send_inv(block) for c in self.connections ] + [ c.send_inv(block) for c in self.p2p_connections ] self.sync_blocks(block.sha256, 1) else: - [ c.send_message(msg_block(block)) for c in self.connections ] - [ c.cb.send_ping(self.ping_counter) for c in self.connections ] + [ c.send_message(msg_block(block)) for c in self.p2p_connections ] + [ c.send_ping(self.ping_counter) for c in self.p2p_connections ] self.wait_for_pings(self.ping_counter) self.ping_counter += 1 if (not self.check_results(tip, outcome)): @@ -360,7 +351,7 @@ class TestManager(): elif isinstance(b_or_t, CBlockHeader): block_header = b_or_t self.block_store.add_header(block_header) - [ c.cb.send_header(block_header) for c in self.connections ] + [ c.send_header(block_header) for c in self.p2p_connections ] else: # Tx test runner assert(isinstance(b_or_t, CTransaction)) @@ -369,11 +360,11 @@ class TestManager(): # Add to shared tx store and clear map entry with mininode_lock: self.tx_store.add_transaction(tx) - for c in self.connections: - c.cb.tx_request_map[tx.sha256] = False + for c in self.p2p_connections: + c.tx_request_map[tx.sha256] = False # Again, either inv to all nodes or save for later if (test_instance.sync_every_tx): - [ c.cb.send_inv(tx) for c in self.connections ] + [ c.send_inv(tx) for c in self.p2p_connections ] self.sync_transaction(tx.sha256, 1) if (not self.check_mempool(tx.sha256, outcome)): raise AssertionError("Test failed at test %d" % test_number) @@ -381,26 +372,26 @@ class TestManager(): invqueue.append(CInv(1, tx.sha256)) # Ensure we're not overflowing the inv queue if len(invqueue) == MAX_INV_SZ: - [ c.send_message(msg_inv(invqueue)) for c in self.connections ] + [ c.send_message(msg_inv(invqueue)) for c in self.p2p_connections ] invqueue = [] # Do final sync if we weren't syncing on every block or every tx. if (not test_instance.sync_every_block and block is not None): if len(invqueue) > 0: - [ c.send_message(msg_inv(invqueue)) for c in self.connections ] + [ c.send_message(msg_inv(invqueue)) for c in self.p2p_connections ] invqueue = [] self.sync_blocks(block.sha256, len(test_instance.blocks_and_transactions)) if (not self.check_results(tip, block_outcome)): raise AssertionError("Block test failed at test %d" % test_number) if (not test_instance.sync_every_tx and tx is not None): if len(invqueue) > 0: - [ c.send_message(msg_inv(invqueue)) for c in self.connections ] + [ c.send_message(msg_inv(invqueue)) for c in self.p2p_connections ] invqueue = [] self.sync_transaction(tx.sha256, len(test_instance.blocks_and_transactions)) if (not self.check_mempool(tx.sha256, tx_outcome)): raise AssertionError("Mempool test failed at test %d" % test_number) - [ c.disconnect_node() for c in self.connections ] + [ c.disconnect_node() for c in self.p2p_connections ] self.wait_for_disconnections() self.block_store.close() self.tx_store.close() diff --git a/test/functional/test_framework/coverage.py b/test/functional/test_framework/coverage.py index ddc3c515b2..f8761f2bb3 100644 --- a/test/functional/test_framework/coverage.py +++ b/test/functional/test_framework/coverage.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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. """Utilities for doing coverage analysis on the RPC interface. diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index 40d02f3ee0..46ef7521e0 100644 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -24,7 +24,7 @@ import struct import time from test_framework.siphash import siphash256 -from test_framework.util import hex_str_to_bytes, bytes_to_hex_str, wait_until +from test_framework.util import hex_str_to_bytes, bytes_to_hex_str MIN_VERSION_SUPPORTED = 60001 MY_VERSION = 70014 # past bip-31 for ping/pong @@ -38,10 +38,16 @@ COIN = 100000000 # 1 btc in satoshis NODE_NETWORK = (1 << 0) # NODE_GETUTXO = (1 << 1) -# NODE_BLOOM = (1 << 2) +NODE_BLOOM = (1 << 2) NODE_WITNESS = (1 << 3) NODE_UNSUPPORTED_SERVICE_BIT_5 = (1 << 5) NODE_UNSUPPORTED_SERVICE_BIT_7 = (1 << 7) +NODE_NETWORK_LIMITED = (1 << 10) + +MSG_TX = 1 +MSG_BLOCK = 2 +MSG_WITNESS_FLAG = 1 << 30 +MSG_TYPE_MASK = 0xffffffff >> 2 # Serialization/deserialization tools def sha256(s): @@ -167,21 +173,6 @@ def ser_string_vector(l): return r -def deser_int_vector(f): - nit = deser_compact_size(f) - r = [] - for i in range(nit): - t = struct.unpack("<i", f.read(4))[0] - r.append(t) - return r - - -def ser_int_vector(l): - r = ser_compact_size(len(l)) - for i in l: - r += struct.pack("<i", i) - return r - # Deserialize from a hex string representation (eg from RPC) def FromHex(obj, hex_string): obj.deserialize(BytesIO(hex_str_to_bytes(hex_string))) @@ -218,8 +209,6 @@ class CAddress(): return "CAddress(nServices=%i ip=%s port=%i)" % (self.nServices, self.ip, self.port) -MSG_WITNESS_FLAG = 1<<30 - class CInv(): typemap = { 0: "Error", @@ -467,10 +456,10 @@ class CTransaction(): r += struct.pack("<I", self.nLockTime) return r - # Regular serialization is without witness -- must explicitly - # call serialize_with_witness to include witness data. + # Regular serialization is with witness -- must explicitly + # call serialize_without_witness to exclude witness data. def serialize(self): - return self.serialize_without_witness() + return self.serialize_with_witness() # Recalculate the txid (transaction hash without witness) def rehash(self): @@ -486,7 +475,7 @@ class CTransaction(): if self.sha256 is None: self.sha256 = uint256_from_str(hash256(self.serialize_without_witness())) - self.hash = encode(hash256(self.serialize())[::-1], 'hex_codec').decode('ascii') + self.hash = encode(hash256(self.serialize_without_witness())[::-1], 'hex_codec').decode('ascii') def is_valid(self): self.calc_sha256() @@ -583,7 +572,7 @@ class CBlock(CBlockHeader): if with_witness: r += ser_vector(self.vtx, "serialize_with_witness") else: - r += ser_vector(self.vtx) + r += ser_vector(self.vtx, "serialize_without_witness") return r # Calculate the merkle root given a vector of transaction hashes @@ -650,7 +639,7 @@ class PrefilledTransaction(): self.tx = CTransaction() self.tx.deserialize(f) - def serialize(self, with_witness=False): + def serialize(self, with_witness=True): r = b"" r += ser_compact_size(self.index) if with_witness: @@ -659,6 +648,9 @@ class PrefilledTransaction(): r += self.tx.serialize_without_witness() return r + def serialize_without_witness(self): + return self.serialize(with_witness=False) + def serialize_with_witness(self): return self.serialize(with_witness=True) @@ -698,7 +690,7 @@ class P2PHeaderAndShortIDs(): if with_witness: r += ser_vector(self.prefilled_txn, "serialize_with_witness") else: - r += ser_vector(self.prefilled_txn) + r += ser_vector(self.prefilled_txn, "serialize_without_witness") return r def __repr__(self): @@ -829,13 +821,13 @@ class BlockTransactions(): self.blockhash = deser_uint256(f) self.transactions = deser_vector(f, CTransaction) - def serialize(self, with_witness=False): + def serialize(self, with_witness=True): r = b"" r += ser_uint256(self.blockhash) if with_witness: r += ser_vector(self.transactions, "serialize_with_witness") else: - r += ser_vector(self.transactions) + r += ser_vector(self.transactions, "serialize_without_witness") return r def __repr__(self): @@ -1035,7 +1027,7 @@ class msg_block(): self.block.deserialize(f) def serialize(self): - return self.block.serialize() + return self.block.serialize(with_witness=False) def __repr__(self): return "msg_block(block=%s)" % (repr(self.block)) @@ -1306,7 +1298,7 @@ class msg_blocktxn(): def serialize(self): r = b"" - r += self.block_transactions.serialize() + r += self.block_transactions.serialize(with_witness=False) return r def __repr__(self): diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py index 24c96b5681..99d0abc3f9 100755 --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -1,18 +1,18 @@ #!/usr/bin/env python3 # Copyright (c) 2010 ArtForz -- public domain half-a-node # Copyright (c) 2012 Jeff Garzik -# Copyright (c) 2010-2016 The Bitcoin Core developers +# Copyright (c) 2010-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. """Bitcoin P2P network half-a-node. -This python code was modified from ArtForz' public domain half-a-node, as +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 -""" +P2PConnection: A low-level connection object to a node's P2P interface +P2PInterface: A high-level interface object for communicating to a node over P2P +P2PDataStore: A p2p interface class that keeps a store of transactions and blocks + and can respond correctly to getdata and getheaders messages""" import asyncore from collections import defaultdict from io import BytesIO @@ -20,10 +20,10 @@ import logging import socket import struct import sys -import time -from threading import RLock, Thread +import threading from test_framework.messages import * +from test_framework.util import wait_until logger = logging.getLogger("TestFramework.mininode") @@ -57,190 +57,60 @@ MAGIC_BYTES = { "regtest": b"\xfa\xbf\xb5\xda", # regtest } -class NodeConnCB(): - """Callback and helper functions for P2P connection to a bitcoind node. +class P2PConnection(asyncore.dispatcher): + """A low-level connection object to a node's P2P interface. - Individual testcases should subclass this and override the on_* methods - if they want to alter message handling behaviour.""" - def __init__(self): - # Track whether we have a P2P connection open to the node - self.connected = False - self.connection = None - - # Track number of messages of each type received and the most recent - # message of each type - self.message_count = defaultdict(int) - self.last_message = {} - - # A count of the number of ping messages we've sent to the node - self.ping_counter = 1 - - # Message receiving methods - - def deliver(self, conn, message): - """Receive message and dispatch message to appropriate callback. - - We keep a count of how many of each message type has been received - and the most recent message of each type.""" - with mininode_lock: - try: - command = message.command.decode('ascii') - self.message_count[command] += 1 - self.last_message[command] = message - getattr(self, 'on_' + command)(conn, message) - except: - print("ERROR delivering %s (%s)" % (repr(message), - sys.exc_info()[0])) - raise - - # Callback methods. Can be overridden by subclasses in individual test - # cases to provide custom message handling behaviour. - - def on_open(self, conn): - self.connected = True - - def on_close(self, conn): - self.connected = False - self.connection = None - - def on_addr(self, conn, message): pass - def on_block(self, conn, message): pass - def on_blocktxn(self, conn, message): pass - def on_cmpctblock(self, conn, message): pass - def on_feefilter(self, conn, message): pass - def on_getaddr(self, conn, message): pass - def on_getblocks(self, conn, message): pass - def on_getblocktxn(self, conn, message): pass - def on_getdata(self, conn, message): pass - def on_getheaders(self, conn, message): pass - def on_headers(self, conn, message): pass - def on_mempool(self, conn): pass - def on_pong(self, conn, message): pass - def on_reject(self, conn, message): pass - def on_sendcmpct(self, conn, message): pass - def on_sendheaders(self, conn, message): pass - def on_tx(self, conn, message): pass - - def on_inv(self, conn, message): - want = msg_getdata() - for i in message.inv: - if i.type != 0: - want.inv.append(i) - if len(want.inv): - conn.send_message(want) - - def on_ping(self, conn, message): - conn.send_message(msg_pong(message.nonce)) - - def on_verack(self, conn, message): - self.verack_received = True - - def on_version(self, conn, message): - assert message.nVersion >= MIN_VERSION_SUPPORTED, "Version {} received. Test framework only supports versions greater than {}".format(message.nVersion, MIN_VERSION_SUPPORTED) - conn.send_message(msg_verack()) - conn.nServices = message.nServices - - # Connection helper methods - - def add_connection(self, conn): - self.connection = conn - - def wait_for_disconnect(self, timeout=60): - test_function = lambda: not self.connected - wait_until(test_function, timeout=timeout, lock=mininode_lock) + This class is responsible for: - # Message receiving helper methods - - def wait_for_block(self, blockhash, timeout=60): - test_function = lambda: self.last_message.get("block") and self.last_message["block"].block.rehash() == blockhash - wait_until(test_function, timeout=timeout, lock=mininode_lock) + - opening and closing the TCP connection to the node + - reading bytes from and writing bytes to the socket + - deserializing and serializing the P2P message header + - logging messages as they are sent and received - def wait_for_getdata(self, timeout=60): - test_function = lambda: self.last_message.get("getdata") - wait_until(test_function, timeout=timeout, lock=mininode_lock) + This class contains no logic for handing the P2P message payloads. It must be + sub-classed and the on_message() callback overridden.""" - def wait_for_getheaders(self, timeout=60): - test_function = lambda: self.last_message.get("getheaders") - wait_until(test_function, timeout=timeout, lock=mininode_lock) - - def wait_for_inv(self, expected_inv, timeout=60): - """Waits for an INV message and checks that the first inv object in the message was as expected.""" - if len(expected_inv) > 1: - raise NotImplementedError("wait_for_inv() will only verify the first inv object") - test_function = lambda: self.last_message.get("inv") and \ - self.last_message["inv"].inv[0].type == expected_inv[0].type and \ - self.last_message["inv"].inv[0].hash == expected_inv[0].hash - wait_until(test_function, timeout=timeout, lock=mininode_lock) - - def wait_for_verack(self, timeout=60): - test_function = lambda: self.message_count["verack"] - wait_until(test_function, timeout=timeout, lock=mininode_lock) - - # Message sending helper functions - - def send_message(self, message): - if self.connection: - self.connection.send_message(message) - else: - logger.error("Cannot send message. No connection to node!") - - def send_and_ping(self, message): - self.send_message(message) - self.sync_with_ping() - - # Sync up with the node - def sync_with_ping(self, timeout=60): - self.send_message(msg_ping(nonce=self.ping_counter)) - test_function = lambda: self.last_message.get("pong") and self.last_message["pong"].nonce == self.ping_counter - wait_until(test_function, timeout=timeout, lock=mininode_lock) - self.ping_counter += 1 - -class NodeConn(asyncore.dispatcher): - """The actual NodeConn class + def __init__(self): + # All P2PConnections must be created before starting the NetworkThread. + # assert that the network thread is not running. + assert not network_thread_running() - This class provides an interface for a p2p connection to a specified node.""" + super().__init__(map=mininode_socket_map) - def __init__(self, dstaddr, dstport, callback, net="regtest", services=NODE_NETWORK|NODE_WITNESS, send_version=True): - asyncore.dispatcher.__init__(self, map=mininode_socket_map) + def peer_connect(self, dstaddr, dstport, net="regtest"): self.dstaddr = dstaddr self.dstport = dstport self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) self.sendbuf = b"" self.recvbuf = b"" - self.last_sent = 0 self.state = "connecting" self.network = net - self.cb = callback self.disconnect = False - self.nServices = 0 - 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) - - logger.info('Connecting to Bitcoin Node: %s:%d' % (self.dstaddr, self.dstport)) + logger.debug('Connecting to Bitcoin Node: %s:%d' % (self.dstaddr, self.dstport)) try: self.connect((dstaddr, dstport)) except: self.handle_close() + def peer_disconnect(self): + # Connection could have already been closed by other end. + if self.state == "connected": + self.disconnect_node() + # Connection and disconnection methods def handle_connect(self): + """asyncore callback when a connection is opened.""" if self.state != "connected": logger.debug("Connected & Listening: %s:%d" % (self.dstaddr, self.dstport)) self.state = "connected" - self.cb.on_open(self) + self.on_open() def handle_close(self): + """asyncore callback when a connection is closed.""" logger.debug("Closing connection to: %s:%d" % (self.dstaddr, self.dstport)) self.state = "closed" self.recvbuf = b"" @@ -249,10 +119,10 @@ class NodeConn(asyncore.dispatcher): self.close() except: pass - self.cb.on_close(self) + self.on_close() def disconnect_node(self): - """ Disconnect the p2p connection. + """Disconnect the p2p connection. Called by the test logic thread. Causes the p2p connection to be disconnected on the next iteration of the asyncore loop.""" @@ -260,16 +130,19 @@ class NodeConn(asyncore.dispatcher): # Socket read methods - def readable(self): - return True - def handle_read(self): + """asyncore callback when data is read from the socket.""" t = self.recv(8192) if len(t) > 0: self.recvbuf += t - self.got_data() + self._on_data() - def got_data(self): + def _on_data(self): + """Try to read P2P messages from the recv buffer. + + This method reads data from the buffer in a loop. It deserializes, + parses and verifies the P2P header, then passes the P2P payload to + the on_message callback for processing.""" try: while True: if len(self.recvbuf) < 4: @@ -294,26 +167,27 @@ class NodeConn(asyncore.dispatcher): f = BytesIO(msg) t = MESSAGEMAP[command]() t.deserialize(f) - self.got_message(t) + self._log_message("receive", t) + self.on_message(t) except Exception as e: logger.exception('Error reading message:', repr(e)) raise - def got_message(self, message): - if self.last_sent + 30 * 60 < time.time(): - self.send_message(MESSAGEMAP[b'ping']()) - self._log_message("receive", message) - self.cb.deliver(self, message) + def on_message(self, message): + """Callback for processing a P2P payload. Must be overridden by derived class.""" + raise NotImplementedError # Socket write methods def writable(self): + """asyncore method to determine whether the handle_write() callback should be called on the next loop.""" with mininode_lock: pre_connection = self.state == "connecting" length = len(self.sendbuf) return (length > 0 or pre_connection) def handle_write(self): + """asyncore callback when data should be written to the socket.""" 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 @@ -331,6 +205,10 @@ class NodeConn(asyncore.dispatcher): self.sendbuf = self.sendbuf[sent:] def send_message(self, message, pushbuf=False): + """Send a P2P message over the socket. + + This method takes a P2P payload, builds the P2P header and adds + the message to the send buffer to be sent over the socket.""" if self.state != "connected" and not pushbuf: raise IOError('Not connected, no pushbuf') self._log_message("send", message) @@ -353,11 +231,11 @@ class NodeConn(asyncore.dispatcher): self.sendbuf = tmsg else: self.sendbuf += tmsg - self.last_sent = time.time() # Class utility methods def _log_message(self, direction, msg): + """Logs a message being sent or received over the connection.""" if direction == "send": log_message = "Send message to " elif direction == "receive": @@ -368,6 +246,164 @@ class NodeConn(asyncore.dispatcher): logger.debug(log_message) +class P2PInterface(P2PConnection): + """A high-level P2P interface class for communicating with a Bitcoin node. + + This class provides high-level callbacks for processing P2P message + payloads, as well as convenience methods for interacting with the + node over P2P. + + Individual testcases should subclass this and override the on_* methods + if they want to alter message handling behaviour.""" + def __init__(self): + super().__init__() + + # Track number of messages of each type received and the most recent + # message of each type + self.message_count = defaultdict(int) + self.last_message = {} + + # A count of the number of ping messages we've sent to the node + self.ping_counter = 1 + + # The network services received from the peer + self.nServices = 0 + + def peer_connect(self, *args, services=NODE_NETWORK|NODE_WITNESS, send_version=True, **kwargs): + super().peer_connect(*args, **kwargs) + + if send_version: + # Send a version msg + 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) + + # Message receiving methods + + def on_message(self, message): + """Receive message and dispatch message to appropriate callback. + + We keep a count of how many of each message type has been received + and the most recent message of each type.""" + with mininode_lock: + try: + command = message.command.decode('ascii') + self.message_count[command] += 1 + self.last_message[command] = message + getattr(self, 'on_' + command)(message) + except: + print("ERROR delivering %s (%s)" % (repr(message), sys.exc_info()[0])) + raise + + # Callback methods. Can be overridden by subclasses in individual test + # cases to provide custom message handling behaviour. + + def on_open(self): + pass + + def on_close(self): + pass + + def on_addr(self, message): pass + def on_block(self, message): pass + def on_blocktxn(self, message): pass + def on_cmpctblock(self, message): pass + def on_feefilter(self, message): pass + def on_getaddr(self, message): pass + def on_getblocks(self, message): pass + def on_getblocktxn(self, message): pass + def on_getdata(self, message): pass + def on_getheaders(self, message): pass + def on_headers(self, message): pass + def on_mempool(self, message): pass + def on_pong(self, message): pass + def on_reject(self, message): pass + def on_sendcmpct(self, message): pass + def on_sendheaders(self, message): pass + def on_tx(self, message): pass + + def on_inv(self, message): + want = msg_getdata() + for i in message.inv: + if i.type != 0: + want.inv.append(i) + if len(want.inv): + self.send_message(want) + + def on_ping(self, message): + self.send_message(msg_pong(message.nonce)) + + def on_verack(self, message): + self.verack_received = True + + def on_version(self, message): + assert message.nVersion >= MIN_VERSION_SUPPORTED, "Version {} received. Test framework only supports versions greater than {}".format(message.nVersion, MIN_VERSION_SUPPORTED) + self.send_message(msg_verack()) + self.nServices = message.nServices + + # Connection helper methods + + def wait_for_disconnect(self, timeout=60): + test_function = lambda: self.state != "connected" + wait_until(test_function, timeout=timeout, lock=mininode_lock) + + # Message receiving helper methods + + def wait_for_block(self, blockhash, timeout=60): + test_function = lambda: self.last_message.get("block") and self.last_message["block"].block.rehash() == blockhash + wait_until(test_function, timeout=timeout, lock=mininode_lock) + + def wait_for_getdata(self, timeout=60): + """Waits for a getdata message. + + Receiving any getdata message will satisfy the predicate. the last_message["getdata"] + value must be explicitly cleared before calling this method, or this will return + immediately with success. TODO: change this method to take a hash value and only + return true if the correct block/tx has been requested.""" + test_function = lambda: self.last_message.get("getdata") + wait_until(test_function, timeout=timeout, lock=mininode_lock) + + def wait_for_getheaders(self, timeout=60): + """Waits for a getheaders message. + + Receiving any getheaders message will satisfy the predicate. the last_message["getheaders"] + value must be explicitly cleared before calling this method, or this will return + immediately with success. TODO: change this method to take a hash value and only + return true if the correct block header has been requested.""" + test_function = lambda: self.last_message.get("getheaders") + wait_until(test_function, timeout=timeout, lock=mininode_lock) + + def wait_for_inv(self, expected_inv, timeout=60): + """Waits for an INV message and checks that the first inv object in the message was as expected.""" + if len(expected_inv) > 1: + raise NotImplementedError("wait_for_inv() will only verify the first inv object") + test_function = lambda: self.last_message.get("inv") and \ + self.last_message["inv"].inv[0].type == expected_inv[0].type and \ + self.last_message["inv"].inv[0].hash == expected_inv[0].hash + wait_until(test_function, timeout=timeout, lock=mininode_lock) + + def wait_for_verack(self, timeout=60): + test_function = lambda: self.message_count["verack"] + wait_until(test_function, timeout=timeout, lock=mininode_lock) + + # Message sending helper functions + + def send_and_ping(self, message): + self.send_message(message) + self.sync_with_ping() + + # Sync up with the node + def sync_with_ping(self, timeout=60): + self.send_message(msg_ping(nonce=self.ping_counter)) + test_function = lambda: self.last_message.get("pong") and self.last_message["pong"].nonce == self.ping_counter + wait_until(test_function, timeout=timeout, lock=mininode_lock) + self.ping_counter += 1 + + # Keep our own socket map for asyncore, so that we can track disconnects # ourselves (to workaround an issue with closing an asyncore socket when # using select) @@ -375,13 +411,16 @@ mininode_socket_map = dict() # One lock for synchronizing all data access between the networking thread (see # NetworkThread below) and the thread running the test logic. For simplicity, -# NodeConn acquires this lock whenever delivering a message to a NodeConnCB, +# P2PConnection acquires this lock whenever delivering a message to a P2PInterface, # and whenever adding anything to the send buffer (in send_message()). This # lock should be acquired in the thread running the test logic to synchronize -# access to any data shared with the NodeConnCB or NodeConn. -mininode_lock = RLock() +# access to any data shared with the P2PInterface or P2PConnection. +mininode_lock = threading.RLock() + +class NetworkThread(threading.Thread): + def __init__(self): + super().__init__(name="NetworkThread") -class NetworkThread(Thread): def run(self): while mininode_socket_map: # We check for whether to disconnect outside of the asyncore @@ -394,3 +433,159 @@ class NetworkThread(Thread): [obj.handle_close() for obj in disconnected] asyncore.loop(0.1, use_poll=True, map=mininode_socket_map, count=1) logger.debug("Network thread closing") + +def network_thread_start(): + """Start the network thread.""" + # Only one network thread may run at a time + assert not network_thread_running() + + NetworkThread().start() + +def network_thread_running(): + """Return whether the network thread is running.""" + return any([thread.name == "NetworkThread" for thread in threading.enumerate()]) + +def network_thread_join(timeout=10): + """Wait timeout seconds for the network thread to terminate. + + Throw if the network thread doesn't terminate in timeout seconds.""" + network_threads = [thread for thread in threading.enumerate() if thread.name == "NetworkThread"] + assert len(network_threads) <= 1 + for thread in network_threads: + thread.join(timeout) + assert not thread.is_alive() + +class P2PDataStore(P2PInterface): + """A P2P data store class. + + Keeps a block and transaction store and responds correctly to getdata and getheaders requests.""" + + def __init__(self): + super().__init__() + self.reject_code_received = None + self.reject_reason_received = None + # store of blocks. key is block hash, value is a CBlock object + self.block_store = {} + self.last_block_hash = '' + # store of txs. key is txid, value is a CTransaction object + self.tx_store = {} + self.getdata_requests = [] + + def on_getdata(self, message): + """Check for the tx/block in our stores and if found, reply with an inv message.""" + for inv in message.inv: + self.getdata_requests.append(inv.hash) + if (inv.type & MSG_TYPE_MASK) == MSG_TX and inv.hash in self.tx_store.keys(): + self.send_message(msg_tx(self.tx_store[inv.hash])) + elif (inv.type & MSG_TYPE_MASK) == MSG_BLOCK and inv.hash in self.block_store.keys(): + self.send_message(msg_block(self.block_store[inv.hash])) + else: + logger.debug('getdata message type {} received.'.format(hex(inv.type))) + + def on_getheaders(self, message): + """Search back through our block store for the locator, and reply with a headers message if found.""" + + locator, hash_stop = message.locator, message.hashstop + + # Assume that the most recent block added is the tip + if not self.block_store: + return + + headers_list = [self.block_store[self.last_block_hash]] + maxheaders = 2000 + while headers_list[-1].sha256 not in locator.vHave: + # Walk back through the block store, adding headers to headers_list + # as we go. + prev_block_hash = headers_list[-1].hashPrevBlock + if prev_block_hash in self.block_store: + prev_block_header = self.block_store[prev_block_hash] + headers_list.append(prev_block_header) + if prev_block_header.sha256 == hash_stop: + # if this is the hashstop header, stop here + break + else: + logger.debug('block hash {} not found in block store'.format(hex(prev_block_hash))) + break + + # Truncate the list if there are too many headers + headers_list = headers_list[:-maxheaders - 1:-1] + response = msg_headers(headers_list) + + if response is not None: + self.send_message(response) + + def on_reject(self, message): + """Store reject reason and code for testing.""" + self.reject_code_received = message.code + self.reject_reason_received = message.reason + + def send_blocks_and_test(self, blocks, rpc, success=True, request_block=True, reject_code=None, reject_reason=None, timeout=60): + """Send blocks to test node and test whether the tip advances. + + - add all blocks to our block_store + - send a headers message for the final block + - the on_getheaders handler will ensure that any getheaders are responded to + - if request_block is True: wait for getdata for each of the blocks. The on_getdata handler will + ensure that any getdata messages are responded to + - if success is True: assert that the node's tip advances to the most recent block + - if success is False: assert that the node's tip doesn't advance + - if reject_code and reject_reason are set: assert that the correct reject message is received""" + + with mininode_lock: + self.reject_code_received = None + self.reject_reason_received = None + + for block in blocks: + self.block_store[block.sha256] = block + self.last_block_hash = block.sha256 + + self.send_message(msg_headers([blocks[-1]])) + + if request_block: + wait_until(lambda: blocks[-1].sha256 in self.getdata_requests, timeout=timeout, lock=mininode_lock) + + if success: + wait_until(lambda: rpc.getbestblockhash() == blocks[-1].hash, timeout=timeout) + else: + assert rpc.getbestblockhash() != blocks[-1].hash + + if reject_code is not None: + wait_until(lambda: self.reject_code_received == reject_code, lock=mininode_lock) + if reject_reason is not None: + wait_until(lambda: self.reject_reason_received == reject_reason, lock=mininode_lock) + + def send_txs_and_test(self, txs, rpc, success=True, reject_code=None, reject_reason=None): + """Send txs to test node and test whether they're accepted to the mempool. + + - add all txs to our tx_store + - send tx messages for all txs + - if success is True: assert that the tx is accepted to the mempool + - if success is False: assert that the tx is not accepted to the mempool + - if reject_code and reject_reason are set: assert that the correct reject message is received.""" + + with mininode_lock: + self.reject_code_received = None + self.reject_reason_received = None + + for tx in txs: + self.tx_store[tx.sha256] = tx + + for tx in txs: + self.send_message(msg_tx(tx)) + + self.sync_with_ping() + + raw_mempool = rpc.getrawmempool() + if success: + # Check that all txs are now in the mempool + for tx in txs: + assert tx.hash in raw_mempool, "{} not found in mempool".format(tx.hash) + else: + # Check that none of the txs are now in the mempool + for tx in txs: + assert tx.hash not in raw_mempool, "{} tx found in mempool".format(tx.hash) + + if reject_code is not None: + wait_until(lambda: self.reject_code_received == reject_code, lock=mininode_lock) + if reject_reason is not None: + wait_until(lambda: self.reject_reason_received == reject_reason, lock=mininode_lock) diff --git a/test/functional/test_framework/netutil.py b/test/functional/test_framework/netutil.py index e5d415788f..96fe283347 100644 --- a/test/functional/test_framework/netutil.py +++ b/test/functional/test_framework/netutil.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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. """Linux network utilities. diff --git a/test/functional/test_framework/script.py b/test/functional/test_framework/script.py index a4c046bd3d..dae8a4e569 100644 --- a/test/functional/test_framework/script.py +++ b/test/functional/test_framework/script.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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. """Functionality to build scripts, as well as SignatureHash(). @@ -641,7 +641,7 @@ def SignatureHash(script, txTo, inIdx, hashtype): txtmp.vin = [] txtmp.vin.append(tmp) - s = txtmp.serialize() + s = txtmp.serialize_without_witness() s += struct.pack(b"<I", hashtype) hash = hash256(s) diff --git a/test/functional/test_framework/siphash.py b/test/functional/test_framework/siphash.py index f68ecad36b..6ffc982cea 100644 --- a/test/functional/test_framework/siphash.py +++ b/test/functional/test_framework/siphash.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016 The Bitcoin Core developers +# Copyright (c) 2016-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. """Specialized SipHash-2-4 implementations. diff --git a/test/functional/test_framework/socks5.py b/test/functional/test_framework/socks5.py index 7b40c47fbf..4721809a3b 100644 --- a/test/functional/test_framework/socks5.py +++ b/test/functional/test_framework/socks5.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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. """Dummy Socks5 server for testing.""" diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 8df50474f3..a5e66bd959 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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. """Base class for RPC testing.""" -from collections import deque from enum import Enum import logging import optparse @@ -14,7 +13,6 @@ import shutil import sys import tempfile import time -import traceback from .authproxy import JSONRPCException from . import coverage @@ -64,6 +62,7 @@ class BitcoinTestFramework(): self.setup_clean_chain = False self.nodes = [] self.mocktime = 0 + self.supports_cli = False self.set_test_params() assert hasattr(self, "num_nodes"), "Test must set self.num_nodes in set_test_params()" @@ -93,12 +92,16 @@ class BitcoinTestFramework(): help="Location of the test framework config file") parser.add_option("--pdbonfailure", dest="pdbonfailure", default=False, action="store_true", help="Attach a python debugger if test fails") + parser.add_option("--usecli", dest="usecli", default=False, action="store_true", + help="use bitcoin-cli instead of RPC for all commands") self.add_options(parser) (self.options, self.args) = parser.parse_args() PortSeed.n = self.options.port_seed - os.environ['PATH'] = self.options.srcdir + ":" + self.options.srcdir + "/qt:" + os.environ['PATH'] + os.environ['PATH'] = self.options.srcdir + os.pathsep + \ + self.options.srcdir + os.path.sep + "qt" + os.pathsep + \ + os.environ['PATH'] check_json_precision() @@ -115,6 +118,8 @@ class BitcoinTestFramework(): success = TestStatus.FAILED try: + if self.options.usecli and not self.supports_cli: + raise SkipTest("--usecli specified but test does not support using CLI") self.setup_chain() self.setup_network() self.run_test() @@ -145,36 +150,26 @@ class BitcoinTestFramework(): self.log.info("Note: bitcoinds were not stopped and may still be running") if not self.options.nocleanup and not self.options.noshutdown and success != TestStatus.FAILED: - self.log.info("Cleaning up") - shutil.rmtree(self.options.tmpdir) + self.log.info("Cleaning up {} on exit".format(self.options.tmpdir)) + cleanup_tree_on_exit = True else: self.log.warning("Not cleaning up dir %s" % self.options.tmpdir) - if os.getenv("PYTHON_DEBUG", ""): - # Dump the end of the debug logs, to aid in debugging rare - # travis failures. - import glob - filenames = [self.options.tmpdir + "/test_framework.log"] - filenames += glob.glob(self.options.tmpdir + "/node*/regtest/debug.log") - MAX_LINES_TO_PRINT = 1000 - for fn in filenames: - try: - with open(fn, 'r') as f: - print("From", fn, ":") - print("".join(deque(f, MAX_LINES_TO_PRINT))) - except OSError: - print("Opening file %s failed." % fn) - traceback.print_exc() + cleanup_tree_on_exit = False if success == TestStatus.PASSED: self.log.info("Tests successful") - sys.exit(TEST_EXIT_PASSED) + exit_code = TEST_EXIT_PASSED elif success == TestStatus.SKIPPED: self.log.info("Test skipped") - sys.exit(TEST_EXIT_SKIPPED) + exit_code = TEST_EXIT_SKIPPED else: self.log.error("Test failed. Test logging available at %s/test_framework.log", self.options.tmpdir) - logging.shutdown() - sys.exit(TEST_EXIT_FAILED) + self.log.error("Hint: Call {} '{}' to consolidate all logs".format(os.path.normpath(os.path.dirname(os.path.realpath(__file__)) + "/../combine_logs.py"), self.options.tmpdir)) + exit_code = TEST_EXIT_FAILED + logging.shutdown() + if cleanup_tree_on_exit: + shutil.rmtree(self.options.tmpdir) + sys.exit(exit_code) # Methods to override in subclass test scripts. def set_test_params(self): @@ -228,20 +223,20 @@ class BitcoinTestFramework(): assert_equal(len(extra_args), num_nodes) assert_equal(len(binary), num_nodes) for i in range(num_nodes): - self.nodes.append(TestNode(i, self.options.tmpdir, extra_args[i], rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir)) + self.nodes.append(TestNode(i, self.options.tmpdir, extra_args[i], rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir, use_cli=self.options.usecli)) - def start_node(self, i, extra_args=None, stderr=None): + def start_node(self, i, *args, **kwargs): """Start a bitcoind""" node = self.nodes[i] - node.start(extra_args, stderr) + node.start(*args, **kwargs) node.wait_for_rpc_connection() if self.options.coveragedir is not None: coverage.write_all_rpc_commands(self.options.coveragedir, node.rpc) - def start_nodes(self, extra_args=None): + def start_nodes(self, extra_args=None, *args, **kwargs): """Start multiple bitcoinds""" if extra_args is None: @@ -249,7 +244,7 @@ class BitcoinTestFramework(): assert_equal(len(extra_args), self.num_nodes) try: for i, node in enumerate(self.nodes): - node.start(extra_args[i]) + node.start(extra_args[i], *args, **kwargs) for node in self.nodes: node.wait_for_rpc_connection() except: @@ -281,10 +276,10 @@ class BitcoinTestFramework(): self.stop_node(i) self.start_node(i, extra_args) - def assert_start_raises_init_error(self, i, extra_args=None, expected_msg=None): + def assert_start_raises_init_error(self, i, extra_args=None, expected_msg=None, *args, **kwargs): with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr: try: - self.start_node(i, extra_args, stderr=log_stderr) + self.start_node(i, extra_args, stderr=log_stderr, *args, **kwargs) self.stop_node(i) except Exception as e: assert 'bitcoind exited' in str(e) # node must have shutdown @@ -432,7 +427,7 @@ class BitcoinTestFramework(): self.disable_mocktime() for i in range(MAX_NODES): os.remove(log_filename(self.options.cachedir, i, "debug.log")) - os.remove(log_filename(self.options.cachedir, i, "db.log")) + os.remove(log_filename(self.options.cachedir, i, "wallets/db.log")) os.remove(log_filename(self.options.cachedir, i, "peers.dat")) os.remove(log_filename(self.options.cachedir, i, "fee_estimates.dat")) diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 34b458482a..1054e6d028 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -10,11 +10,11 @@ import http.client import json import logging import os +import re import subprocess import time from .authproxy import JSONRPCException -from .mininode import NodeConn from .util import ( assert_equal, get_rpc_proxy, @@ -23,6 +23,9 @@ from .util import ( p2p_port, ) +# For Python 3.4 compatibility +JSONDecodeError = getattr(json, "JSONDecodeError", ValueError) + BITCOIND_PROC_WAIT_TIMEOUT = 60 class TestNode(): @@ -39,7 +42,7 @@ class TestNode(): To make things easier for the test writer, any unrecognised messages will be dispatched to the RPC connection.""" - def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mocktime, coverage_dir): + def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mocktime, coverage_dir, use_cli=False): self.index = i self.datadir = os.path.join(dirname, "node" + str(i)) self.rpchost = rpchost @@ -59,6 +62,7 @@ class TestNode(): self.args = [self.binary, "-datadir=" + self.datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(mocktime), "-uacomment=testnode%d" % i] self.cli = TestNodeCLI(os.getenv("BITCOINCLI", "bitcoin-cli"), self.datadir) + self.use_cli = use_cli self.running = False self.process = None @@ -70,17 +74,20 @@ class TestNode(): self.p2ps = [] def __getattr__(self, name): - """Dispatches any unrecognised messages to the RPC connection.""" - assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection" - return getattr(self.rpc, name) + """Dispatches any unrecognised messages to the RPC connection or a CLI instance.""" + if self.use_cli: + return getattr(self.cli, name) + else: + assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection" + return getattr(self.rpc, name) - def start(self, extra_args=None, stderr=None): + def start(self, extra_args=None, stderr=None, *args, **kwargs): """Start the node.""" if extra_args is None: extra_args = self.extra_args if stderr is None: stderr = self.stderr - self.process = subprocess.Popen(self.args + extra_args, stderr=stderr) + self.process = subprocess.Popen(self.args + extra_args, stderr=stderr, *args, **kwargs) self.running = True self.log.debug("bitcoind started, waiting for RPC to come up") @@ -111,10 +118,13 @@ class TestNode(): raise AssertionError("Unable to connect to bitcoind") def get_wallet_rpc(self, wallet_name): - assert self.rpc_connected - assert self.rpc - wallet_path = "wallet/%s" % wallet_name - return self.rpc / wallet_path + if self.use_cli: + return self.cli("-rpcwallet={}".format(wallet_name)) + else: + assert self.rpc_connected + assert self.rpc + wallet_path = "wallet/%s" % wallet_name + return self.rpc / wallet_path def stop_node(self): """Stop the node.""" @@ -158,7 +168,7 @@ class TestNode(): self.encryptwallet(passphrase) self.wait_until_stopped() - def add_p2p_connection(self, p2p_conn, **kwargs): + def add_p2p_connection(self, p2p_conn, *args, **kwargs): """Add a p2p connection to the node. This method adds the p2p connection to the self.p2ps list and also @@ -167,9 +177,9 @@ class TestNode(): kwargs['dstport'] = p2p_port(self.index) if 'dstaddr' not in kwargs: kwargs['dstaddr'] = '127.0.0.1' + + p2p_conn.peer_connect(*args, **kwargs) self.p2ps.append(p2p_conn) - kwargs.update({'callback': p2p_conn}) - p2p_conn.add_connection(NodeConn(**kwargs)) return p2p_conn @@ -185,46 +195,73 @@ class TestNode(): def disconnect_p2ps(self): """Close all p2p connections to the node.""" for p in self.p2ps: - # Connection could have already been closed by other end. - if p.connection is not None: - p.connection.disconnect_node() - self.p2ps = [] + p.peer_disconnect() + del self.p2ps[:] + +class TestNodeCLIAttr: + def __init__(self, cli, command): + self.cli = cli + self.command = command + + def __call__(self, *args, **kwargs): + return self.cli.send_cli(self.command, *args, **kwargs) + def get_request(self, *args, **kwargs): + return lambda: self(*args, **kwargs) class TestNodeCLI(): """Interface to bitcoin-cli for an individual node""" def __init__(self, binary, datadir): - self.args = [] + self.options = [] self.binary = binary self.datadir = datadir self.input = None + self.log = logging.getLogger('TestFramework.bitcoincli') - def __call__(self, *args, input=None): - # TestNodeCLI is callable with bitcoin-cli command-line args - self.args = [str(arg) for arg in args] - self.input = input - return self + def __call__(self, *options, input=None): + # TestNodeCLI is callable with bitcoin-cli command-line options + cli = TestNodeCLI(self.binary, self.datadir) + cli.options = [str(o) for o in options] + cli.input = input + return cli def __getattr__(self, command): - def dispatcher(*args, **kwargs): - return self.send_cli(command, *args, **kwargs) - return dispatcher - - def send_cli(self, command, *args, **kwargs): + return TestNodeCLIAttr(self, command) + + def batch(self, requests): + results = [] + for request in requests: + try: + results.append(dict(result=request())) + except JSONRPCException as e: + results.append(dict(error=e)) + return results + + def send_cli(self, command=None, *args, **kwargs): """Run bitcoin-cli command. Deserializes returned string as python object.""" pos_args = [str(arg) for arg in args] named_args = [str(key) + "=" + str(value) for (key, value) in kwargs.items()] assert not (pos_args and named_args), "Cannot use positional arguments and named arguments in the same bitcoin-cli call" - p_args = [self.binary, "-datadir=" + self.datadir] + self.args + p_args = [self.binary, "-datadir=" + self.datadir] + self.options if named_args: p_args += ["-named"] - p_args += [command] + pos_args + named_args + if command is not None: + p_args += [command] + p_args += pos_args + named_args + self.log.debug("Running bitcoin-cli command: %s" % command) process = subprocess.Popen(p_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) cli_stdout, cli_stderr = process.communicate(input=self.input) returncode = process.poll() if returncode: + match = re.match(r'error code: ([-0-9]+)\nerror message:\n(.*)', cli_stderr) + if match: + code, message = match.groups() + raise JSONRPCException(dict(code=int(code), message=message)) # Ignore cli_stdout, raise with cli_stderr raise subprocess.CalledProcessError(returncode, self.binary, output=cli_stderr) - return json.loads(cli_stdout, parse_float=decimal.Decimal) + try: + return json.loads(cli_stdout, parse_float=decimal.Decimal) + except JSONDecodeError: + return cli_stdout.rstrip("\n") diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 102c903018..7fdc171332 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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. """Helpful routines for regression testing.""" @@ -390,7 +390,7 @@ def sync_chain(rpc_connections, *, wait=1, timeout=60): timeout -= wait raise AssertionError("Chain sync failed: Best block hashes don't match") -def sync_mempools(rpc_connections, *, wait=1, timeout=60): +def sync_mempools(rpc_connections, *, wait=1, timeout=60, flush_scheduler=True): """ Wait until everybody has the same transactions in their memory pools @@ -402,6 +402,9 @@ def sync_mempools(rpc_connections, *, wait=1, timeout=60): if set(rpc_connections[i].getrawmempool()) == pool: num_match = num_match + 1 if num_match == len(rpc_connections): + if flush_scheduler: + for r in rpc_connections: + r.syncwithvalidationinterfacequeue() return time.sleep(wait) timeout -= wait diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index ca36426a0a..0cf3424c71 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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. """Run regression test suite. @@ -15,6 +15,7 @@ For a description of arguments recognized by test scripts, see """ import argparse +from collections import deque import configparser import datetime import os @@ -54,106 +55,114 @@ TEST_EXIT_SKIPPED = 77 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', + 'wallet_hd.py', + 'wallet_backup.py', # vv Tests less than 5m vv - 'p2p-fullblocktest.py', - 'fundrawtransaction.py', - 'p2p-compactblocks.py', - 'segwit.py', + 'feature_block.py', + 'rpc_fundrawtransaction.py', + 'p2p_compactblocks.py', + 'feature_segwit.py', # vv Tests less than 2m vv - 'wallet.py', - 'wallet-accounts.py', - 'p2p-segwit.py', - 'wallet-dump.py', - 'listtransactions.py', + 'wallet_basic.py', + 'wallet_accounts.py', + 'p2p_segwit.py', + 'wallet_dump.py', + 'rpc_listtransactions.py', # vv Tests less than 60s vv - 'sendheaders.py', - 'zapwallettxes.py', - 'importmulti.py', + 'p2p_sendheaders.py', + 'wallet_zapwallettxes.py', + 'wallet_importmulti.py', 'mempool_limit.py', - 'merkle_blocks.py', - 'receivedby.py', - 'abandonconflict.py', - 'bip68-112-113-p2p.py', - 'rawtransactions.py', - 'reindex.py', + 'rpc_txoutproof.py', + 'wallet_listreceivedby.py', + 'wallet_abandonconflict.py', + 'feature_csv_activation.py', + 'rpc_rawtransaction.py', + 'wallet_address_types.py', + 'feature_reindex.py', # vv Tests less than 30s vv - 'keypool-topup.py', - 'zmq_test.py', - 'bitcoin_cli.py', - 'mempool_resurrect_test.py', - 'txn_doublespend.py --mineblock', - 'txn_clone.py', - 'getchaintips.py', - 'rest.py', - 'mempool_spendcoinbase.py', + 'wallet_keypool_topup.py', + 'interface_zmq.py', + 'interface_bitcoin_cli.py', + 'mempool_resurrect.py', + 'wallet_txn_doublespend.py --mineblock', + 'wallet_txn_clone.py', + 'wallet_txn_clone.py --segwit', + 'rpc_getchaintips.py', + 'interface_rest.py', + 'mempool_spend_coinbase.py', 'mempool_reorg.py', 'mempool_persist.py', - 'multiwallet.py', - 'httpbasics.py', - 'multi_rpc.py', - 'proxy_test.py', - 'signrawtransactions.py', - 'disconnect_ban.py', - 'decodescript.py', - 'blockchain.py', - 'deprecated_rpc.py', - 'disablewallet.py', - 'net.py', - 'keypool.py', - 'p2p-mempool.py', - 'prioritise_transaction.py', - 'invalidblockrequest.py', - 'invalidtxrequest.py', - 'p2p-versionbits-warning.py', - 'preciousblock.py', - 'importprunedfunds.py', - 'signmessages.py', - 'nulldummy.py', - 'import-rescan.py', - 'mining.py', - 'bumpfee.py', - 'rpcnamedargs.py', - 'listsinceblock.py', - 'p2p-leaktests.py', - 'wallet-encryption.py', - 'bipdersig-p2p.py', - 'bip65-cltv-p2p.py', - 'uptime.py', - 'resendwallettransactions.py', - 'minchainwork.py', - 'p2p-fingerprint.py', - 'uacomment.py', - 'p2p-acceptblock.py', + 'wallet_multiwallet.py', + 'wallet_multiwallet.py --usecli', + 'interface_http.py', + 'rpc_users.py', + 'feature_proxy.py', + 'rpc_signrawtransaction.py', + 'p2p_disconnect_ban.py', + 'rpc_decodescript.py', + 'rpc_blockchain.py', + 'rpc_deprecated.py', + 'wallet_disable.py', + 'rpc_net.py', + 'wallet_keypool.py', + 'p2p_mempool.py', + 'mining_prioritisetransaction.py', + 'p2p_invalid_block.py', + 'p2p_invalid_tx.py', + 'feature_versionbits_warning.py', + 'rpc_preciousblock.py', + 'wallet_importprunedfunds.py', + 'rpc_signmessage.py', + 'feature_nulldummy.py', + 'wallet_import_rescan.py', + 'mining_basic.py', + 'wallet_bumpfee.py', + 'rpc_named_arguments.py', + 'wallet_listsinceblock.py', + 'p2p_leak.py', + 'wallet_encryption.py', + 'feature_dersig.py', + 'feature_cltv.py', + 'rpc_uptime.py', + 'wallet_resendwallettransactions.py', + 'feature_minchainwork.py', + 'p2p_fingerprint.py', + 'feature_uacomment.py', + 'p2p_unrequested_blocks.py', + 'feature_logging.py', + 'p2p_node_network_limited.py', + 'feature_config_args.py', + # Don't append tests at the end to avoid merge conflicts + # Put them in a random line within the section that fits their approximate run-time ] 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', + 'feature_pruning.py', # vv Tests less than 20m vv - 'smartfees.py', + 'feature_fee_estimation.py', # vv Tests less than 5m vv - 'maxuploadtarget.py', + 'feature_maxuploadtarget.py', 'mempool_packages.py', - 'dbcrash.py', + 'feature_dbcrash.py', # vv Tests less than 2m vv - 'bip68-sequence.py', - 'getblocktemplate_longpoll.py', - 'p2p-timeouts.py', + 'feature_bip68_sequence.py', + 'mining_getblocktemplate_longpoll.py', + 'p2p_timeouts.py', # vv Tests less than 60s vv - 'bip9-softforks.py', - 'p2p-feefilter.py', - 'rpcbind_test.py', + 'feature_bip9_softforks.py', + 'p2p_feefilter.py', + 'rpc_bind.py', # vv Tests less than 30s vv - 'assumevalid.py', + 'feature_assumevalid.py', 'example_test.py', - 'txn_doublespend.py', - 'txn_clone.py --mineblock', - 'notifications.py', - 'invalidateblock.py', - 'replace-by-fee.py', + 'wallet_txn_doublespend.py', + 'wallet_txn_clone.py --mineblock', + 'feature_notifications.py', + 'rpc_invalidateblock.py', + 'feature_rbf.py', ] # Place EXTENDED_SCRIPTS first since it has the 3 longest running tests @@ -174,6 +183,7 @@ def main(): epilog=''' Help text and arguments for individual test script:''', formatter_class=argparse.RawTextHelpFormatter) + parser.add_argument('--combinedlogslen', '-c', type=int, default=0, help='print a combined log (of length n lines) from all test nodes and test framework to the console on failure.') 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-separated-list of scripts to exclude.') parser.add_argument('--extended', action='store_true', help='run the extended test suite in addition to the basic tests') @@ -258,17 +268,18 @@ def main(): if args.help: # Print help for test_runner.py, then print help of the first script (with args removed) and exit. parser.print_help() - subprocess.check_call([(config["environment"]["SRCDIR"] + '/test/functional/' + test_list[0].split()[0])] + ['-h']) + subprocess.check_call([sys.executable, os.path.join(config["environment"]["SRCDIR"], 'test', 'functional', test_list[0].split()[0]), '-h']) sys.exit(0) check_script_list(config["environment"]["SRCDIR"]) + check_script_prefixes() if not args.keepcache: shutil.rmtree("%s/test/cache" % config["environment"]["BUILDDIR"], ignore_errors=True) - run_tests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], config["environment"]["EXEEXT"], tmpdir, args.jobs, args.coverage, passon_args) + run_tests(test_list, config["environment"]["SRCDIR"], config["environment"]["BUILDDIR"], config["environment"]["EXEEXT"], tmpdir, args.jobs, args.coverage, passon_args, args.combinedlogslen) -def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_coverage=False, args=[]): +def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_coverage=False, args=[], combined_logs_len=0): # Warn if bitcoind is already running (unix only) try: if subprocess.check_output(["pidof", "bitcoind"]) is not None: @@ -300,7 +311,11 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove if len(test_list) > 1 and jobs > 1: # Populate cache - subprocess.check_output([tests_dir + 'create_cache.py'] + flags + ["--tmpdir=%s/cache" % tmpdir]) + try: + subprocess.check_output([sys.executable, tests_dir + 'create_cache.py'] + flags + ["--tmpdir=%s/cache" % tmpdir]) + except subprocess.CalledProcessError as e: + sys.stdout.buffer.write(e.output) + raise #Run Tests job_queue = TestHandler(jobs, tests_dir, tmpdir, test_list, flags) @@ -310,7 +325,7 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove max_len_name = len(max(test_list, key=len)) for _ in range(len(test_list)): - test_result, stdout, stderr = job_queue.get_next() + test_result, testdir, stdout, stderr = job_queue.get_next() test_results.append(test_result) if test_result.status == "Passed": @@ -321,6 +336,14 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove print("\n%s%s%s failed, Duration: %s s\n" % (BOLD[1], test_result.name, BOLD[0], test_result.time)) print(BOLD[1] + 'stdout:\n' + BOLD[0] + stdout + '\n') print(BOLD[1] + 'stderr:\n' + BOLD[0] + stderr + '\n') + if combined_logs_len and os.path.isdir(testdir): + # Print the final `combinedlogslen` lines of the combined logs + print('{}Combine the logs and print the last {} lines ...{}'.format(BOLD[1], combined_logs_len, BOLD[0])) + print('\n============') + print('{}Combined log for {}:{}'.format(BOLD[1], testdir, BOLD[0])) + print('============\n') + combined_logs, _ = subprocess.Popen([sys.executble, os.path.join(tests_dir, 'combine_logs.py'), '-c', testdir], universal_newlines=True, stdout=subprocess.PIPE).communicate() + print("\n".join(deque(combined_logs.splitlines(), combined_logs_len))) print_results(test_results, max_len_name, (int(time.time() - time0))) @@ -385,13 +408,15 @@ class TestHandler: log_stdout = tempfile.SpooledTemporaryFile(max_size=2**16) log_stderr = tempfile.SpooledTemporaryFile(max_size=2**16) test_argv = t.split() - tmpdir = ["--tmpdir=%s/%s_%s" % (self.tmpdir, re.sub(".py$", "", test_argv[0]), portseed)] + testdir = "{}/{}_{}".format(self.tmpdir, re.sub(".py$", "", test_argv[0]), portseed) + tmpdir_arg = ["--tmpdir={}".format(testdir)] self.jobs.append((t, time.time(), - subprocess.Popen([self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + portseed_arg + tmpdir, + subprocess.Popen([sys.executable, self.tests_dir + test_argv[0]] + test_argv[1:] + self.flags + portseed_arg + tmpdir_arg, universal_newlines=True, stdout=log_stdout, stderr=log_stderr), + testdir, log_stdout, log_stderr)) if not self.jobs: @@ -400,7 +425,7 @@ class TestHandler: # Return first proc that finishes time.sleep(.5) for j in self.jobs: - (name, time0, proc, log_out, log_err) = j + (name, time0, proc, testdir, log_out, log_err) = j if os.getenv('TRAVIS') == 'true' and int(time.time() - time0) > 20 * 60: # In travis, timeout individual tests after 20 minutes (to stop tests hanging and not # providing useful output. @@ -418,7 +443,7 @@ class TestHandler: self.num_running -= 1 self.jobs.remove(j) - return TestResult(name, status, int(time.time() - time0)), stdout, stderr + return TestResult(name, status, int(time.time() - time0)), testdir, stdout, stderr print('.', end='', flush=True) class TestResult(): @@ -446,6 +471,18 @@ class TestResult(): return self.status != "Failed" +def check_script_prefixes(): + """Check that test scripts start with one of the allowed name prefixes.""" + + good_prefixes_re = re.compile("(example|feature|interface|mempool|mining|p2p|rpc|wallet)_") + bad_script_names = [script for script in ALL_SCRIPTS if good_prefixes_re.match(script) is None] + + if bad_script_names: + print("%sERROR:%s %d tests not meeting naming conventions:" % (BOLD[1], BOLD[0], len(bad_script_names))) + print(" %s" % ("\n ".join(sorted(bad_script_names)))) + raise AssertionError("Some tests are not following naming convention!") + + def check_script_list(src_dir): """Check scripts directory. diff --git a/test/functional/abandonconflict.py b/test/functional/wallet_abandonconflict.py index e8dbc86469..8fb860cd7e 100755 --- a/test/functional/abandonconflict.py +++ b/test/functional/wallet_abandonconflict.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 abandontransaction RPC. @@ -8,11 +8,12 @@ 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. + no effect on transactions which are already abandoned. """ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * + class AbandonConflictTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 @@ -28,6 +29,11 @@ class AbandonConflictTest(BitcoinTestFramework): sync_mempools(self.nodes) self.nodes[1].generate(1) + # Can not abandon non-wallet transaction + assert_raises_rpc_error(-5, 'Invalid or non-wallet transaction id', lambda: self.nodes[0].abandontransaction(txid='ff' * 32)) + # Can not abandon confirmed transaction + assert_raises_rpc_error(-5, 'Transaction not eligible for abandonment', lambda: self.nodes[0].abandontransaction(txid=txA)) + sync_blocks(self.nodes) newbalance = self.nodes[0].getbalance() assert(balance - newbalance < Decimal("0.001")) #no more than fees lost diff --git a/test/functional/wallet-accounts.py b/test/functional/wallet_accounts.py index bc1efaee15..ecd1cfc82b 100755 --- a/test/functional/wallet-accounts.py +++ b/test/functional/wallet_accounts.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016 The Bitcoin Core developers +# Copyright (c) 2016-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 account RPCs. @@ -126,7 +126,7 @@ class WalletAccountsTest(BitcoinTestFramework): addresses = [] for x in range(10): addresses.append(node.getnewaddress()) - multisig_address = node.addmultisigaddress(5, addresses, account.name) + multisig_address = node.addmultisigaddress(5, addresses, account.name)['address'] account.add_address(multisig_address) account.verify(node) node.sendfrom("", multisig_address, 50) diff --git a/test/functional/wallet_address_types.py b/test/functional/wallet_address_types.py new file mode 100755 index 0000000000..38a3425214 --- /dev/null +++ b/test/functional/wallet_address_types.py @@ -0,0 +1,294 @@ +#!/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 that the wallet can send and receive using all combinations of address types. + +There are 5 nodes-under-test: + - node0 uses legacy addresses + - node1 uses p2sh/segwit addresses + - node2 uses p2sh/segwit addresses and bech32 addresses for change + - node3 uses bech32 addresses + - node4 uses a p2sh/segwit addresses for change + +node5 exists to generate new blocks. + +## Multisig address test + +Test that adding a multisig address with: + - an uncompressed pubkey always gives a legacy address + - only compressed pubkeys gives the an `-addresstype` address + +## Sending to address types test + +A series of tests, iterating over node0-node4. In each iteration of the test, one node sends: + - 10/101th of its balance to itself (using getrawchangeaddress for single key addresses) + - 20/101th to the next node + - 30/101th to the node after that + - 40/101th to the remaining node + - 1/101th remains as fee+change + +Iterate over each node for single key addresses, and then over each node for +multisig addresses. + +Repeat test, but with explicit address_type parameters passed to getnewaddress +and getrawchangeaddress: + - node0 and node3 send to p2sh. + - node1 sends to bech32. + - node2 sends to legacy. + +As every node sends coins after receiving, this also +verifies that spending coins sent to all these address types works. + +## Change type test + +Test that the nodes generate the correct change address type: + - node0 always uses a legacy change address. + - node1 uses a bech32 addresses for change if any destination address is bech32. + - node2 always uses a bech32 address for change + - node3 always uses a bech32 address for change + - node4 always uses p2sh/segwit output for change. +""" + +from decimal import Decimal +import itertools + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, + assert_greater_than, + assert_raises_rpc_error, + connect_nodes_bi, + sync_blocks, + sync_mempools, +) + +class AddressTypeTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 6 + self.extra_args = [ + ["-addresstype=legacy"], + ["-addresstype=p2sh-segwit"], + ["-addresstype=p2sh-segwit", "-changetype=bech32"], + ["-addresstype=bech32"], + ["-changetype=p2sh-segwit"], + [] + ] + + def setup_network(self): + self.setup_nodes() + + # Fully mesh-connect nodes for faster mempool sync + for i, j in itertools.product(range(self.num_nodes), repeat=2): + if i > j: + connect_nodes_bi(self.nodes, i, j) + self.sync_all() + + def get_balances(self, confirmed=True): + """Return a list of confirmed or unconfirmed balances.""" + if confirmed: + return [self.nodes[i].getbalance() for i in range(4)] + else: + return [self.nodes[i].getunconfirmedbalance() for i in range(4)] + + def test_address(self, node, address, multisig, typ): + """Run sanity checks on an address.""" + info = self.nodes[node].validateaddress(address) + assert(info['isvalid']) + if not multisig and typ == 'legacy': + # P2PKH + assert(not info['isscript']) + assert(not info['iswitness']) + assert('pubkey' in info) + elif not multisig and typ == 'p2sh-segwit': + # P2SH-P2WPKH + assert(info['isscript']) + assert(not info['iswitness']) + assert_equal(info['script'], 'witness_v0_keyhash') + assert('pubkey' in info) + elif not multisig and typ == 'bech32': + # P2WPKH + assert(not info['isscript']) + assert(info['iswitness']) + assert_equal(info['witness_version'], 0) + assert_equal(len(info['witness_program']), 40) + assert('pubkey' in info) + elif typ == 'legacy': + # P2SH-multisig + assert(info['isscript']) + assert_equal(info['script'], 'multisig') + assert(not info['iswitness']) + assert('pubkeys' in info) + elif typ == 'p2sh-segwit': + # P2SH-P2WSH-multisig + assert(info['isscript']) + assert_equal(info['script'], 'witness_v0_scripthash') + assert(not info['iswitness']) + assert(info['embedded']['isscript']) + assert_equal(info['embedded']['script'], 'multisig') + assert(info['embedded']['iswitness']) + assert_equal(info['embedded']['witness_version'], 0) + assert_equal(len(info['embedded']['witness_program']), 64) + assert('pubkeys' in info['embedded']) + elif typ == 'bech32': + # P2WSH-multisig + assert(info['isscript']) + assert_equal(info['script'], 'multisig') + assert(info['iswitness']) + assert_equal(info['witness_version'], 0) + assert_equal(len(info['witness_program']), 64) + assert('pubkeys' in info) + else: + # Unknown type + assert(False) + + def test_change_output_type(self, node_sender, destinations, expected_type): + txid = self.nodes[node_sender].sendmany(fromaccount="", amounts=dict.fromkeys(destinations, 0.001)) + raw_tx = self.nodes[node_sender].getrawtransaction(txid) + tx = self.nodes[node_sender].decoderawtransaction(raw_tx) + + # Make sure the transaction has change: + assert_equal(len(tx["vout"]), len(destinations) + 1) + + # Make sure the destinations are included, and remove them: + output_addresses = [vout['scriptPubKey']['addresses'][0] for vout in tx["vout"]] + change_addresses = [d for d in output_addresses if d not in destinations] + assert_equal(len(change_addresses), 1) + + self.log.debug("Check if change address " + change_addresses[0] + " is " + expected_type) + self.test_address(node_sender, change_addresses[0], multisig=False, typ=expected_type) + + def run_test(self): + # Mine 101 blocks on node5 to bring nodes out of IBD and make sure that + # no coinbases are maturing for the nodes-under-test during the test + self.nodes[5].generate(101) + sync_blocks(self.nodes) + + uncompressed_1 = "0496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858ee" + uncompressed_2 = "047211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073dee6c89064984f03385237d92167c13e236446b417ab79a0fcae412ae3316b77" + compressed_1 = "0296b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52" + compressed_2 = "037211a824f55b505228e4c3d5194c1fcfaa15a456abdf37f9b9d97a4040afc073" + + # addmultisigaddress with at least 1 uncompressed key should return a legacy address. + for node in range(4): + self.test_address(node, self.nodes[node].addmultisigaddress(2, [uncompressed_1, uncompressed_2])['address'], True, 'legacy') + self.test_address(node, self.nodes[node].addmultisigaddress(2, [compressed_1, uncompressed_2])['address'], True, 'legacy') + self.test_address(node, self.nodes[node].addmultisigaddress(2, [uncompressed_1, compressed_2])['address'], True, 'legacy') + # addmultisigaddress with all compressed keys should return the appropriate address type (even when the keys are not ours). + self.test_address(0, self.nodes[0].addmultisigaddress(2, [compressed_1, compressed_2])['address'], True, 'legacy') + self.test_address(1, self.nodes[1].addmultisigaddress(2, [compressed_1, compressed_2])['address'], True, 'p2sh-segwit') + self.test_address(2, self.nodes[2].addmultisigaddress(2, [compressed_1, compressed_2])['address'], True, 'p2sh-segwit') + self.test_address(3, self.nodes[3].addmultisigaddress(2, [compressed_1, compressed_2])['address'], True, 'bech32') + + for explicit_type, multisig, from_node in itertools.product([False, True], [False, True], range(4)): + address_type = None + if explicit_type and not multisig: + if from_node == 1: + address_type = 'bech32' + elif from_node == 0 or from_node == 3: + address_type = 'p2sh-segwit' + else: + address_type = 'legacy' + self.log.info("Sending from node {} ({}) with{} multisig using {}".format(from_node, self.extra_args[from_node], "" if multisig else "out", "default" if address_type is None else address_type)) + old_balances = self.get_balances() + self.log.debug("Old balances are {}".format(old_balances)) + to_send = (old_balances[from_node] / 101).quantize(Decimal("0.00000001")) + sends = {} + + self.log.debug("Prepare sends") + for n, to_node in enumerate(range(from_node, from_node + 4)): + to_node %= 4 + change = False + if not multisig: + if from_node == to_node: + # When sending non-multisig to self, use getrawchangeaddress + address = self.nodes[to_node].getrawchangeaddress(address_type=address_type) + change = True + else: + address = self.nodes[to_node].getnewaddress(address_type=address_type) + else: + addr1 = self.nodes[to_node].getnewaddress() + addr2 = self.nodes[to_node].getnewaddress() + address = self.nodes[to_node].addmultisigaddress(2, [addr1, addr2])['address'] + + # Do some sanity checking on the created address + if address_type is not None: + typ = address_type + elif to_node == 0: + typ = 'legacy' + elif to_node == 1 or (to_node == 2 and not change): + typ = 'p2sh-segwit' + else: + typ = 'bech32' + self.test_address(to_node, address, multisig, typ) + + # Output entry + sends[address] = to_send * 10 * (1 + n) + + self.log.debug("Sending: {}".format(sends)) + self.nodes[from_node].sendmany("", sends) + sync_mempools(self.nodes) + + unconf_balances = self.get_balances(False) + self.log.debug("Check unconfirmed balances: {}".format(unconf_balances)) + assert_equal(unconf_balances[from_node], 0) + for n, to_node in enumerate(range(from_node + 1, from_node + 4)): + to_node %= 4 + assert_equal(unconf_balances[to_node], to_send * 10 * (2 + n)) + + # node5 collects fee and block subsidy to keep accounting simple + self.nodes[5].generate(1) + sync_blocks(self.nodes) + + new_balances = self.get_balances() + self.log.debug("Check new balances: {}".format(new_balances)) + # We don't know what fee was set, so we can only check bounds on the balance of the sending node + assert_greater_than(new_balances[from_node], to_send * 10) + assert_greater_than(to_send * 11, new_balances[from_node]) + for n, to_node in enumerate(range(from_node + 1, from_node + 4)): + to_node %= 4 + assert_equal(new_balances[to_node], old_balances[to_node] + to_send * 10 * (2 + n)) + + # Get one p2sh/segwit address from node2 and two bech32 addresses from node3: + to_address_p2sh = self.nodes[2].getnewaddress() + to_address_bech32_1 = self.nodes[3].getnewaddress() + to_address_bech32_2 = self.nodes[3].getnewaddress() + + # Fund node 4: + self.nodes[5].sendtoaddress(self.nodes[4].getnewaddress(), Decimal("1")) + self.nodes[5].generate(1) + sync_blocks(self.nodes) + assert_equal(self.nodes[4].getbalance(), 1) + + self.log.info("Nodes with addresstype=legacy never use a P2WPKH change output") + self.test_change_output_type(0, [to_address_bech32_1], 'legacy') + + self.log.info("Nodes with addresstype=p2sh-segwit only use a P2WPKH change output if any destination address is bech32:") + self.test_change_output_type(1, [to_address_p2sh], 'p2sh-segwit') + self.test_change_output_type(1, [to_address_bech32_1], 'bech32') + self.test_change_output_type(1, [to_address_p2sh, to_address_bech32_1], 'bech32') + self.test_change_output_type(1, [to_address_bech32_1, to_address_bech32_2], 'bech32') + + self.log.info("Nodes with change_type=bech32 always use a P2WPKH change output:") + self.test_change_output_type(2, [to_address_bech32_1], 'bech32') + self.test_change_output_type(2, [to_address_p2sh], 'bech32') + + self.log.info("Nodes with addresstype=bech32 always use a P2WPKH change output (unless changetype is set otherwise):") + self.test_change_output_type(3, [to_address_bech32_1], 'bech32') + self.test_change_output_type(3, [to_address_p2sh], 'bech32') + + self.log.info('getrawchangeaddress defaults to addresstype if -changetype is not set and argument is absent') + self.test_address(3, self.nodes[3].getrawchangeaddress(), multisig=False, typ='bech32') + + self.log.info('getrawchangeaddress fails with invalid changetype argument') + assert_raises_rpc_error(-5, "Unknown address type 'bech23'", self.nodes[3].getrawchangeaddress, 'bech23') + + self.log.info("Nodes with changetype=p2sh-segwit never use a P2WPKH change output") + self.test_change_output_type(4, [to_address_bech32_1], 'p2sh-segwit') + self.test_address(4, self.nodes[4].getrawchangeaddress(), multisig=False, typ='p2sh-segwit') + self.log.info("Except for getrawchangeaddress if specified:") + self.test_address(4, self.nodes[4].getrawchangeaddress(), multisig=False, typ='p2sh-segwit') + self.test_address(4, self.nodes[4].getrawchangeaddress('bech32'), multisig=False, typ='bech32') + +if __name__ == '__main__': + AddressTypeTest().main() diff --git a/test/functional/walletbackup.py b/test/functional/wallet_backup.py index 85a149793e..b4be7debb5 100755 --- a/test/functional/walletbackup.py +++ b/test/functional/wallet_backup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 wallet backup features. @@ -90,9 +90,9 @@ class WalletBackupTest(BitcoinTestFramework): self.stop_node(2) def erase_three(self): - os.remove(self.options.tmpdir + "/node0/regtest/wallet.dat") - os.remove(self.options.tmpdir + "/node1/regtest/wallet.dat") - os.remove(self.options.tmpdir + "/node2/regtest/wallet.dat") + os.remove(self.options.tmpdir + "/node0/regtest/wallets/wallet.dat") + os.remove(self.options.tmpdir + "/node1/regtest/wallets/wallet.dat") + os.remove(self.options.tmpdir + "/node2/regtest/wallets/wallet.dat") def run_test(self): self.log.info("Generating initial blockchain") @@ -154,9 +154,9 @@ class WalletBackupTest(BitcoinTestFramework): shutil.rmtree(self.options.tmpdir + "/node2/regtest/chainstate") # Restore wallets from backup - shutil.copyfile(tmpdir + "/node0/wallet.bak", tmpdir + "/node0/regtest/wallet.dat") - shutil.copyfile(tmpdir + "/node1/wallet.bak", tmpdir + "/node1/regtest/wallet.dat") - shutil.copyfile(tmpdir + "/node2/wallet.bak", tmpdir + "/node2/regtest/wallet.dat") + shutil.copyfile(tmpdir + "/node0/wallet.bak", tmpdir + "/node0/regtest/wallets/wallet.dat") + shutil.copyfile(tmpdir + "/node1/wallet.bak", tmpdir + "/node1/regtest/wallets/wallet.dat") + shutil.copyfile(tmpdir + "/node2/wallet.bak", tmpdir + "/node2/regtest/wallets/wallet.dat") self.log.info("Re-starting nodes") self.start_three() @@ -192,10 +192,10 @@ class WalletBackupTest(BitcoinTestFramework): # Backup to source wallet file must fail sourcePaths = [ - tmpdir + "/node0/regtest/wallet.dat", - tmpdir + "/node0/./regtest/wallet.dat", - tmpdir + "/node0/regtest/", - tmpdir + "/node0/regtest"] + tmpdir + "/node0/regtest/wallets/wallet.dat", + tmpdir + "/node0/./regtest/wallets/wallet.dat", + tmpdir + "/node0/regtest/wallets/", + tmpdir + "/node0/regtest/wallets"] for sourcePath in sourcePaths: assert_raises_rpc_error(-4, "backup failed", self.nodes[0].backupwallet, sourcePath) diff --git a/test/functional/wallet.py b/test/functional/wallet_basic.py index db60df18ed..a90dbc8adf 100755 --- a/test/functional/wallet.py +++ b/test/functional/wallet_basic.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 wallet.""" @@ -27,6 +27,9 @@ class WalletTest(BitcoinTestFramework): assert_fee_amount(fee, tx_size, fee_per_byte * 1000) return curr_balance + def get_vsize(self, txn): + return self.nodes[0].decoderawtransaction(txn)['vsize'] + def run_test(self): # Check that there's no UTXO on none of the nodes assert_equal(len(self.nodes[0].listunspent()), 0) @@ -162,7 +165,7 @@ class WalletTest(BitcoinTestFramework): txid = self.nodes[2].sendtoaddress(address, 10, "", "", False) self.nodes[2].generate(1) self.sync_all([self.nodes[0:3]]) - node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), Decimal('84'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) + node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), Decimal('84'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid))) assert_equal(self.nodes[0].getbalance(), Decimal('10')) # Send 10 BTC with subtract fee from amount @@ -171,14 +174,14 @@ class WalletTest(BitcoinTestFramework): self.sync_all([self.nodes[0:3]]) node_2_bal -= Decimal('10') assert_equal(self.nodes[2].getbalance(), node_2_bal) - node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) + node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid))) # Sendmany 10 BTC txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", []) self.nodes[2].generate(1) self.sync_all([self.nodes[0:3]]) node_0_bal += Decimal('10') - node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) + node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid))) assert_equal(self.nodes[0].getbalance(), node_0_bal) # Sendmany 10 BTC with subtract fee from amount @@ -187,7 +190,7 @@ class WalletTest(BitcoinTestFramework): self.sync_all([self.nodes[0:3]]) node_2_bal -= Decimal('10') assert_equal(self.nodes[2].getbalance(), node_2_bal) - node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) + node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, self.get_vsize(self.nodes[2].getrawtransaction(txid))) # Test ResendWalletTransactions: # Create a couple of transactions, then start up a fourth diff --git a/test/functional/bumpfee.py b/test/functional/wallet_bumpfee.py index 008e83d5b2..f621d41b4e 100755 --- a/test/functional/bumpfee.py +++ b/test/functional/wallet_bumpfee.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016 The Bitcoin Core developers +# Copyright (c) 2016-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 bumpfee RPC. @@ -14,7 +14,7 @@ added in the future, they should try to follow the same convention and not make assumptions about execution order. """ -from segwit import send_to_witness +from test_framework.blocktools import send_to_witness from test_framework.test_framework import BitcoinTestFramework from test_framework import blocktools from test_framework.mininode import CTransaction @@ -33,7 +33,7 @@ class BumpFeeTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True - self.extra_args = [["-prematurewitness", "-walletprematurewitness", "-walletrbf={}".format(i)] + self.extra_args = [["-prematurewitness", "-walletprematurewitness", "-deprecatedrpc=addwitnessaddress", "-walletrbf={}".format(i)] for i in range(self.num_nodes)] def run_test(self): @@ -194,7 +194,7 @@ def test_settxfee(rbf_node, dest_address): requested_feerate = Decimal("0.00025000") rbf_node.settxfee(requested_feerate) bumped_tx = rbf_node.bumpfee(rbfid) - actual_feerate = bumped_tx["fee"] * 1000 / rbf_node.getrawtransaction(bumped_tx["txid"], True)["size"] + actual_feerate = bumped_tx["fee"] * 1000 / rbf_node.getrawtransaction(bumped_tx["txid"], True)["vsize"] # Assert that the difference between the requested feerate and the actual # feerate of the bumped transaction is small. assert_greater_than(Decimal("0.00001000"), abs(requested_feerate - actual_feerate)) @@ -231,13 +231,16 @@ def test_unconfirmed_not_spendable(rbf_node, rbf_node_address): 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 + # 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) + # Can not abandon conflicted tx + assert_raises_rpc_error(-5, 'Transaction not eligible for abandonment', lambda: rbf_node.abandontransaction(txid=bumpid)) rbf_node.invalidateblock(block.hash) + # Call abandon to make sure the wallet doesn't attempt to resubmit + # the bump tx and hope the wallet does not rebroadcast before we call. + rbf_node.abandontransaction(bumpid) assert bumpid not in rbf_node.getrawmempool() assert rbfid in rbf_node.getrawmempool() @@ -290,6 +293,7 @@ def submit_block_with_tx(node, tx): block.vtx.append(ctx) block.rehash() block.hashMerkleRoot = block.calc_merkle_root() + blocktools.add_witness_commitment(block) block.solve() node.submitblock(bytes_to_hex_str(block.serialize(True))) return block diff --git a/test/functional/disablewallet.py b/test/functional/wallet_disable.py index c75ef9b9f1..b0627d88ac 100755 --- a/test/functional/disablewallet.py +++ b/test/functional/wallet_disable.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2015-2016 The Bitcoin Core developers +# Copyright (c) 2015-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 a node with the -disablewallet option. diff --git a/test/functional/wallet-dump.py b/test/functional/wallet_dump.py index 47de8777a6..5e943d048d 100755 --- a/test/functional/wallet-dump.py +++ b/test/functional/wallet_dump.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016 The Bitcoin Core developers +# Copyright (c) 2016-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 dumpwallet RPC.""" @@ -10,15 +10,17 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import (assert_equal, assert_raises_rpc_error) -def read_dump(file_name, addrs, hd_master_addr_old): +def read_dump(file_name, addrs, script_addrs, hd_master_addr_old): """ Read the given dump, count the addrs that match, count change and reserve. Also check that the old hd_master is inactive """ with open(file_name, encoding='utf8') as inputfile: found_addr = 0 + found_script_addr = 0 found_addr_chg = 0 found_addr_rsv = 0 + witness_addr_ret = None hd_master_addr_ret = None for line in inputfile: # only read non comment lines @@ -38,12 +40,22 @@ def read_dump(file_name, addrs, hd_master_addr_old): # ensure we have generated a new hd master key assert(hd_master_addr_old != addr) hd_master_addr_ret = addr + elif keytype == "script=1": + # scripts don't have keypaths + keypath = None else: keypath = addr_keypath.rstrip().split("hdkeypath=")[1] # count key types for addrObj in addrs: - if addrObj['address'] == addr and addrObj['hdkeypath'] == keypath and keytype == "label=": + if addrObj['address'] == addr.split(",")[0] and addrObj['hdkeypath'] == keypath and keytype == "label=": + # a labled entry in the wallet should contain both a native address + # and the p2sh-p2wpkh address that was added at wallet setup + if len(addr.split(",")) == 2: + addr_list = addr.split(",") + # the entry should be of the first key in the wallet + assert_equal(addrs[0]['address'], addr_list[0]) + witness_addr_ret = addr_list[1] found_addr += 1 break elif keytype == "change=1": @@ -52,13 +64,20 @@ def read_dump(file_name, addrs, hd_master_addr_old): elif keytype == "reserve=1": found_addr_rsv += 1 break - return found_addr, found_addr_chg, found_addr_rsv, hd_master_addr_ret + + # count scripts + for script_addr in script_addrs: + if script_addr == addr.rstrip() and keytype == "script=1": + found_script_addr += 1 + break + + return found_addr, found_script_addr, found_addr_chg, found_addr_rsv, hd_master_addr_ret, witness_addr_ret class WalletDumpTest(BitcoinTestFramework): def set_test_params(self): self.num_nodes = 1 - self.extra_args = [["-keypool=90"]] + self.extra_args = [["-keypool=90", "-addresstype=legacy", "-deprecatedrpc=addwitnessaddress"]] def setup_network(self, split=False): # Use 1 minute timeout because the initial getnewaddress RPC can take @@ -72,6 +91,8 @@ class WalletDumpTest(BitcoinTestFramework): tmpdir = self.options.tmpdir # generate 20 addresses to compare against the dump + # but since we add a p2sh-p2wpkh address for the first pubkey in the + # wallet, we will expect 21 addresses in the dump test_addr_count = 20 addrs = [] for i in range(0,test_addr_count): @@ -81,15 +102,22 @@ class WalletDumpTest(BitcoinTestFramework): # Should be a no-op: self.nodes[0].keypoolrefill() + # Test scripts dump by adding a P2SH witness and a 1-of-1 multisig address + witness_addr = self.nodes[0].addwitnessaddress(addrs[0]["address"], True) + multisig_addr = self.nodes[0].addmultisigaddress(1, [addrs[1]["address"]])["address"] + script_addrs = [witness_addr, multisig_addr] + # dump unencrypted wallet result = self.nodes[0].dumpwallet(tmpdir + "/node0/wallet.unencrypted.dump") assert_equal(result['filename'], os.path.abspath(tmpdir + "/node0/wallet.unencrypted.dump")) - found_addr, found_addr_chg, found_addr_rsv, hd_master_addr_unenc = \ - read_dump(tmpdir + "/node0/wallet.unencrypted.dump", addrs, None) + found_addr, found_script_addr, found_addr_chg, found_addr_rsv, hd_master_addr_unenc, witness_addr_ret = \ + read_dump(tmpdir + "/node0/wallet.unencrypted.dump", addrs, script_addrs, None) assert_equal(found_addr, test_addr_count) # all keys must be in the dump + assert_equal(found_script_addr, 2) # all scripts must be in the dump assert_equal(found_addr_chg, 50) # 50 blocks where mined assert_equal(found_addr_rsv, 90*2) # 90 keys plus 100% internal keys + assert_equal(witness_addr_ret, witness_addr) # p2sh-p2wsh address added to the first key #encrypt wallet, restart, unlock and dump self.nodes[0].node_encrypt_wallet('test') @@ -99,14 +127,30 @@ class WalletDumpTest(BitcoinTestFramework): self.nodes[0].keypoolrefill() self.nodes[0].dumpwallet(tmpdir + "/node0/wallet.encrypted.dump") - found_addr, found_addr_chg, found_addr_rsv, _ = \ - read_dump(tmpdir + "/node0/wallet.encrypted.dump", addrs, hd_master_addr_unenc) + found_addr, found_script_addr, found_addr_chg, found_addr_rsv, _, witness_addr_ret = \ + read_dump(tmpdir + "/node0/wallet.encrypted.dump", addrs, script_addrs, hd_master_addr_unenc) assert_equal(found_addr, test_addr_count) + assert_equal(found_script_addr, 2) assert_equal(found_addr_chg, 90*2 + 50) # old reserve keys are marked as change now assert_equal(found_addr_rsv, 90*2) + assert_equal(witness_addr_ret, witness_addr) # Overwriting should fail assert_raises_rpc_error(-8, "already exists", self.nodes[0].dumpwallet, tmpdir + "/node0/wallet.unencrypted.dump") + # Restart node with new wallet, and test importwallet + self.stop_node(0) + self.start_node(0, ['-wallet=w2']) + + # Make sure the address is not IsMine before import + result = self.nodes[0].validateaddress(multisig_addr) + assert(result['ismine'] == False) + + self.nodes[0].importwallet(os.path.abspath(tmpdir + "/node0/wallet.unencrypted.dump")) + + # Now check IsMine is true + result = self.nodes[0].validateaddress(multisig_addr) + assert(result['ismine'] == True) + if __name__ == '__main__': WalletDumpTest().main () diff --git a/test/functional/wallet-encryption.py b/test/functional/wallet_encryption.py index db62e1e30f..3c927ee484 100755 --- a/test/functional/wallet-encryption.py +++ b/test/functional/wallet_encryption.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016 The Bitcoin Core developers +# Copyright (c) 2016-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 Wallet encryption""" @@ -10,6 +10,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_raises_rpc_error, + assert_greater_than, + assert_greater_than_or_equal, ) class WalletEncryptionTest(BitcoinTestFramework): @@ -56,6 +58,23 @@ class WalletEncryptionTest(BitcoinTestFramework): assert_raises_rpc_error(-14, "wallet passphrase entered was incorrect", self.nodes[0].walletpassphrase, passphrase, 10) self.nodes[0].walletpassphrase(passphrase2, 10) assert_equal(privkey, self.nodes[0].dumpprivkey(address)) + self.nodes[0].walletlock() + + # Test timeout bounds + assert_raises_rpc_error(-8, "Timeout cannot be negative.", self.nodes[0].walletpassphrase, passphrase2, -10) + # Check the timeout + # Check a time less than the limit + expected_time = int(time.time()) + (1 << 30) - 600 + self.nodes[0].walletpassphrase(passphrase2, (1 << 30) - 600) + actual_time = self.nodes[0].getwalletinfo()['unlocked_until'] + assert_greater_than_or_equal(actual_time, expected_time) + assert_greater_than(expected_time + 5, actual_time) # 5 second buffer + # Check a time greater than the limit + expected_time = int(time.time()) + (1 << 30) - 1 + self.nodes[0].walletpassphrase(passphrase2, (1 << 33)) + actual_time = self.nodes[0].getwalletinfo()['unlocked_until'] + assert_greater_than_or_equal(actual_time, expected_time) + assert_greater_than(expected_time + 5, actual_time) # 5 second buffer if __name__ == '__main__': WalletEncryptionTest().main() diff --git a/test/functional/wallet-hd.py b/test/functional/wallet_hd.py index 9b6ce68609..9f0e9acb47 100755 --- a/test/functional/wallet-hd.py +++ b/test/functional/wallet_hd.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2016 The Bitcoin Core developers +# Copyright (c) 2016-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 Hierarchical Deterministic wallet function.""" @@ -73,7 +73,7 @@ class WalletHDTest(BitcoinTestFramework): # otherwise node1 would auto-recover all funds in flag the keypool keys as used shutil.rmtree(os.path.join(tmpdir, "node1/regtest/blocks")) shutil.rmtree(os.path.join(tmpdir, "node1/regtest/chainstate")) - shutil.copyfile(os.path.join(tmpdir, "hd.bak"), os.path.join(tmpdir, "node1/regtest/wallet.dat")) + shutil.copyfile(os.path.join(tmpdir, "hd.bak"), os.path.join(tmpdir, "node1/regtest/wallets/wallet.dat")) self.start_node(1) # Assert that derivation is deterministic diff --git a/test/functional/import-rescan.py b/test/functional/wallet_import_rescan.py index 6807fa6696..d193a99d5b 100755 --- a/test/functional/import-rescan.py +++ b/test/functional/wallet_import_rescan.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 wallet import RPCs. @@ -119,7 +119,7 @@ class ImportRescanTest(BitcoinTestFramework): self.num_nodes = 2 + len(IMPORT_NODES) def setup_network(self): - extra_args = [[] for _ in range(self.num_nodes)] + extra_args = [["-addresstype=legacy"] for _ in range(self.num_nodes)] for i, import_node in enumerate(IMPORT_NODES, 2): if import_node.prune: extra_args[i] += ["-prune=1"] diff --git a/test/functional/importmulti.py b/test/functional/wallet_importmulti.py index a691595f15..be9be83839 100755 --- a/test/functional/importmulti.py +++ b/test/functional/wallet_importmulti.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 importmulti RPC.""" @@ -9,6 +9,7 @@ from test_framework.util import * class ImportMultiTest (BitcoinTestFramework): def set_test_params(self): self.num_nodes = 2 + self.extra_args = [["-addresstype=legacy"], ["-addresstype=legacy"]] self.setup_clean_chain = True def setup_network(self): @@ -227,7 +228,7 @@ class ImportMultiTest (BitcoinTestFramework): sig_address_1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) sig_address_2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) sig_address_3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['address'], sig_address_2['address'], sig_address_3['pubkey']]) + multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']]) self.nodes[1].generate(100) transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) self.nodes[1].generate(1) @@ -254,7 +255,7 @@ class ImportMultiTest (BitcoinTestFramework): sig_address_1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) sig_address_2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) sig_address_3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['address'], sig_address_2['address'], sig_address_3['pubkey']]) + multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']]) self.nodes[1].generate(100) transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) self.nodes[1].generate(1) @@ -281,7 +282,7 @@ class ImportMultiTest (BitcoinTestFramework): sig_address_1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) sig_address_2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) sig_address_3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['address'], sig_address_2['address'], sig_address_3['pubkey']]) + multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']]) self.nodes[1].generate(100) transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) self.nodes[1].generate(1) @@ -308,7 +309,7 @@ class ImportMultiTest (BitcoinTestFramework): sig_address_1 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) sig_address_2 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) sig_address_3 = self.nodes[0].validateaddress(self.nodes[0].getnewaddress()) - multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['address'], sig_address_2['address'], sig_address_3['pubkey']]) + multi_sig_script = self.nodes[0].createmultisig(2, [sig_address_1['pubkey'], sig_address_2['pubkey'], sig_address_3['pubkey']]) self.nodes[1].generate(100) transactionid = self.nodes[1].sendtoaddress(multi_sig_script['address'], 10.00) self.nodes[1].generate(1) diff --git a/test/functional/importprunedfunds.py b/test/functional/wallet_importprunedfunds.py index 068052409a..6b2919b5ae 100755 --- a/test/functional/importprunedfunds.py +++ b/test/functional/wallet_importprunedfunds.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 importprunedfunds and removeprunedfunds RPCs.""" diff --git a/test/functional/keypool.py b/test/functional/wallet_keypool.py index f2701c36bd..45a5eed8ec 100755 --- a/test/functional/keypool.py +++ b/test/functional/wallet_keypool.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 wallet keypool and interaction with wallet encryption/locking.""" diff --git a/test/functional/keypool-topup.py b/test/functional/wallet_keypool_topup.py index 160a0f7ae5..e7af3c3987 100755 --- a/test/functional/keypool-topup.py +++ b/test/functional/wallet_keypool_topup.py @@ -33,7 +33,7 @@ class KeypoolRestoreTest(BitcoinTestFramework): self.stop_node(1) - shutil.copyfile(self.tmpdir + "/node1/regtest/wallet.dat", self.tmpdir + "/wallet.bak") + shutil.copyfile(self.tmpdir + "/node1/regtest/wallets/wallet.dat", self.tmpdir + "/wallet.bak") self.start_node(1, self.extra_args[1]) connect_nodes_bi(self.nodes, 0, 1) @@ -56,7 +56,7 @@ class KeypoolRestoreTest(BitcoinTestFramework): self.stop_node(1) - shutil.copyfile(self.tmpdir + "/wallet.bak", self.tmpdir + "/node1/regtest/wallet.dat") + shutil.copyfile(self.tmpdir + "/wallet.bak", self.tmpdir + "/node1/regtest/wallets/wallet.dat") self.log.info("Verify keypool is restored and balance is correct") diff --git a/test/functional/receivedby.py b/test/functional/wallet_listreceivedby.py index 97da19546f..1f2b3c8aa7 100755 --- a/test/functional/receivedby.py +++ b/test/functional/wallet_listreceivedby.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 listreceivedbyaddress RPC.""" diff --git a/test/functional/listsinceblock.py b/test/functional/wallet_listsinceblock.py index 67e7744bf8..67e7744bf8 100755 --- a/test/functional/listsinceblock.py +++ b/test/functional/wallet_listsinceblock.py diff --git a/test/functional/wallet_multiwallet.py b/test/functional/wallet_multiwallet.py new file mode 100755 index 0000000000..b07e451667 --- /dev/null +++ b/test/functional/wallet_multiwallet.py @@ -0,0 +1,133 @@ +#!/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 multiwallet. + +Verify that a bitcoind node can load multiple wallet files +""" +import os +import shutil + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal, assert_raises_rpc_error + +class MultiWalletTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 2 + self.extra_args = [['-wallet=w1', '-wallet=w2', '-wallet=w3', '-wallet=w'], []] + self.supports_cli = True + + def run_test(self): + node = self.nodes[0] + + data_dir = lambda *p: os.path.join(node.datadir, 'regtest', *p) + wallet_dir = lambda *p: data_dir('wallets', *p) + wallet = lambda name: node.get_wallet_rpc(name) + + assert_equal(set(node.listwallets()), {"w1", "w2", "w3", "w"}) + + self.stop_nodes() + + self.assert_start_raises_init_error(0, ['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" does not exist') + self.assert_start_raises_init_error(0, ['-walletdir=wallets'], 'Error: Specified -walletdir "wallets" is a relative path', cwd=data_dir()) + self.assert_start_raises_init_error(0, ['-walletdir=debug.log'], 'Error: Specified -walletdir "debug.log" is not a directory', cwd=data_dir()) + + # should not initialize if there are duplicate wallets + self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.') + + # should not initialize if wallet file is a directory + os.mkdir(wallet_dir('w11')) + self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.') + + # should not initialize if one wallet is a copy of another + shutil.copyfile(wallet_dir('w2'), wallet_dir('w22')) + self.assert_start_raises_init_error(0, ['-wallet=w2', '-wallet=w22'], 'duplicates fileid') + + # should not initialize if wallet file is a symlink + os.symlink(wallet_dir('w1'), wallet_dir('w12')) + self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.') + + # should not initialize if the specified walletdir does not exist + self.assert_start_raises_init_error(0, ['-walletdir=bad'], 'Error: Specified -walletdir "bad" does not exist') + # should not initialize if the specified walletdir is not a directory + not_a_dir = wallet_dir('notadir') + open(not_a_dir, 'a').close() + self.assert_start_raises_init_error(0, ['-walletdir=' + not_a_dir], 'Error: Specified -walletdir "' + not_a_dir + '" is not a directory') + + # if wallets/ doesn't exist, datadir should be the default wallet dir + wallet_dir2 = data_dir('walletdir') + os.rename(wallet_dir(), wallet_dir2) + self.start_node(0, ['-wallet=w4', '-wallet=w5']) + assert_equal(set(node.listwallets()), {"w4", "w5"}) + w5 = wallet("w5") + w5.generate(1) + + # now if wallets/ exists again, but the rootdir is specified as the walletdir, w4 and w5 should still be loaded + os.rename(wallet_dir2, wallet_dir()) + self.restart_node(0, ['-wallet=w4', '-wallet=w5', '-walletdir=' + data_dir()]) + assert_equal(set(node.listwallets()), {"w4", "w5"}) + w5 = wallet("w5") + w5_info = w5.getwalletinfo() + assert_equal(w5_info['immature_balance'], 50) + + competing_wallet_dir = os.path.join(self.options.tmpdir, 'competing_walletdir') + os.mkdir(competing_wallet_dir) + self.restart_node(0, ['-walletdir='+competing_wallet_dir]) + self.assert_start_raises_init_error(1, ['-walletdir='+competing_wallet_dir], 'Error initializing wallet database environment') + + self.restart_node(0, self.extra_args[0]) + + w1 = wallet("w1") + w2 = wallet("w2") + w3 = wallet("w3") + w4 = wallet("w") + wallet_bad = wallet("bad") + + w1.generate(1) + + # accessing invalid wallet fails + assert_raises_rpc_error(-18, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo) + + # accessing wallet RPC without using wallet endpoint fails + assert_raises_rpc_error(-19, "Wallet file not specified", node.getwalletinfo) + + # check w1 wallet balance + w1_info = w1.getwalletinfo() + assert_equal(w1_info['immature_balance'], 50) + w1_name = w1_info['walletname'] + assert_equal(w1_name, "w1") + + # check w2 wallet balance + w2_info = w2.getwalletinfo() + assert_equal(w2_info['immature_balance'], 0) + w2_name = w2_info['walletname'] + assert_equal(w2_name, "w2") + + w3_name = w3.getwalletinfo()['walletname'] + assert_equal(w3_name, "w3") + + w4_name = w4.getwalletinfo()['walletname'] + assert_equal(w4_name, "w") + + w1.generate(101) + assert_equal(w1.getbalance(), 100) + assert_equal(w2.getbalance(), 0) + assert_equal(w3.getbalance(), 0) + assert_equal(w4.getbalance(), 0) + + w1.sendtoaddress(w2.getnewaddress(), 1) + w1.sendtoaddress(w3.getnewaddress(), 2) + w1.sendtoaddress(w4.getnewaddress(), 3) + w1.generate(1) + assert_equal(w2.getbalance(), 1) + assert_equal(w3.getbalance(), 2) + assert_equal(w4.getbalance(), 3) + + batch = w1.batch([w1.getblockchaininfo.get_request(), w1.getwalletinfo.get_request()]) + assert_equal(batch[0]["result"]["chain"], "regtest") + assert_equal(batch[1]["result"]["walletname"], "w1") + +if __name__ == '__main__': + MultiWalletTest().main() diff --git a/test/functional/resendwallettransactions.py b/test/functional/wallet_resendwallettransactions.py index d959bb4c38..d959bb4c38 100755 --- a/test/functional/resendwallettransactions.py +++ b/test/functional/wallet_resendwallettransactions.py diff --git a/test/functional/txn_clone.py b/test/functional/wallet_txn_clone.py index 740bb2d4c5..ce26d6e0ee 100755 --- a/test/functional/txn_clone.py +++ b/test/functional/wallet_txn_clone.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 wallet accounts properly when there are cloned transactions with malleated scriptsigs.""" @@ -14,6 +14,8 @@ class TxnMallTest(BitcoinTestFramework): def add_options(self, parser): parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true", help="Test double-spend of 1-confirmed transaction") + parser.add_option("--segwit", dest="segwit", default=False, action="store_true", + help="Test behaviour with SegWit txn (which should fail") def setup_network(self): # Start with split network: @@ -22,6 +24,11 @@ class TxnMallTest(BitcoinTestFramework): disconnect_nodes(self.nodes[2], 1) def run_test(self): + if self.options.segwit: + output_type="p2sh-segwit" + else: + output_type="legacy" + # All nodes should start with 1,250 BTC: starting_balance = 1250 for i in range(4): @@ -31,11 +38,11 @@ class TxnMallTest(BitcoinTestFramework): # Assign coins to foo and bar accounts: self.nodes[0].settxfee(.001) - node0_address_foo = self.nodes[0].getnewaddress("foo") + node0_address_foo = self.nodes[0].getnewaddress("foo", output_type) fund_foo_txid = self.nodes[0].sendfrom("", node0_address_foo, 1219) fund_foo_tx = self.nodes[0].gettransaction(fund_foo_txid) - node0_address_bar = self.nodes[0].getnewaddress("bar") + node0_address_bar = self.nodes[0].getnewaddress("bar", output_type) fund_bar_txid = self.nodes[0].sendfrom("", node0_address_bar, 29) fund_bar_tx = self.nodes[0].gettransaction(fund_bar_txid) @@ -106,6 +113,10 @@ class TxnMallTest(BitcoinTestFramework): # Send clone and its parent to miner self.nodes[2].sendrawtransaction(fund_foo_tx["hex"]) txid1_clone = self.nodes[2].sendrawtransaction(tx1_clone["hex"]) + if self.options.segwit: + assert_equal(txid1, txid1_clone) + return + # ... mine a block... self.nodes[2].generate(1) diff --git a/test/functional/txn_doublespend.py b/test/functional/wallet_txn_doublespend.py index 69629ef951..01129f3817 100755 --- a/test/functional/txn_doublespend.py +++ b/test/functional/wallet_txn_doublespend.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 wallet accounts properly when there is a double-spend conflict.""" diff --git a/test/functional/zapwallettxes.py b/test/functional/wallet_zapwallettxes.py index 8cd622dc8e..87f44b2737 100755 --- a/test/functional/zapwallettxes.py +++ b/test/functional/wallet_zapwallettxes.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (c) 2014-2016 The Bitcoin Core developers +# Copyright (c) 2014-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 zapwallettxes functionality. @@ -10,7 +10,7 @@ transactions are still available. - restart node 0 with zapwallettxes and persistmempool, and verify that both the confirmed and the unconfirmed transactions are still available. -- restart node 0 with just zapwallettxed and verify that the confirmed +- restart node 0 with just zapwallettxes and verify that the confirmed transactions are still available, but that the unconfirmed transaction has been zapped. """ @@ -59,6 +59,7 @@ class ZapWalletTXesTest (BitcoinTestFramework): self.start_node(0, ["-persistmempool=1", "-zapwallettxes=2"]) wait_until(lambda: self.nodes[0].getmempoolinfo()['size'] == 1, timeout=3) + self.nodes[0].syncwithvalidationinterfacequeue() # Flush mempool to wallet assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1) assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2) diff --git a/test/util/bitcoin-util-test.py b/test/util/bitcoin-util-test.py index ef34955d90..30bd13d0dc 100755 --- a/test/util/bitcoin-util-test.py +++ b/test/util/bitcoin-util-test.py @@ -44,11 +44,11 @@ def main(): # Add the format/level to the logger logging.basicConfig(format=formatter, level=level) - bctester(os.path.join(env_conf["SRCDIR"], "test/util/data"), "bitcoin-util-test.json", env_conf) + bctester(os.path.join(env_conf["SRCDIR"], "test", "util", "data"), "bitcoin-util-test.json", env_conf) def bctester(testDir, input_basename, buildenv): """ Loads and parses the input file, runs all tests and reports results""" - input_filename = testDir + "/" + input_basename + input_filename = os.path.join(testDir, input_basename) raw_data = open(input_filename).read() input_data = json.loads(raw_data) @@ -77,7 +77,7 @@ def bctest(testDir, testObj, buildenv): are not as expected. Error is caught by bctester() and reported. """ # Get the exec names and arguments - execprog = buildenv["BUILDDIR"] + "/src/" + testObj['exec'] + buildenv["EXEEXT"] + execprog = os.path.join(buildenv["BUILDDIR"], "src", testObj["exec"] + buildenv["EXEEXT"]) execargs = testObj['args'] execrun = [execprog] + execargs @@ -85,24 +85,28 @@ def bctest(testDir, testObj, buildenv): stdinCfg = None inputData = None if "input" in testObj: - filename = testDir + "/" + testObj['input'] + filename = os.path.join(testDir, testObj["input"]) inputData = open(filename).read() stdinCfg = subprocess.PIPE # Read the expected output data (if there is any) outputFn = None outputData = None + outputType = None if "output_cmp" in testObj: outputFn = testObj['output_cmp'] outputType = os.path.splitext(outputFn)[1][1:] # output type from file extension (determines how to compare) try: - outputData = open(testDir + "/" + outputFn).read() + outputData = open(os.path.join(testDir, outputFn)).read() except: logging.error("Output file " + outputFn + " can not be opened") raise if not outputData: logging.error("Output data missing for " + outputFn) raise Exception + if not outputType: + logging.error("Output file %s does not have a file extension" % outputFn) + raise Exception # Run the test proc = subprocess.Popen(execrun, stdin=stdinCfg, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) |