diff options
author | Wladimir J. van der Laan <laanwj@gmail.com> | 2017-10-11 18:28:41 +0200 |
---|---|---|
committer | Wladimir J. van der Laan <laanwj@gmail.com> | 2017-10-11 18:29:55 +0200 |
commit | 51bad9195eb435200626beccbaf6b60774a4a894 (patch) | |
tree | 7fa3066c94e421b80cadcd2fd39117b62ad6910a /test/functional | |
parent | fb7b5293844ea6adc5dcf5ad0a0c5890b4495939 (diff) | |
parent | 019c492490077a7581ab526b4208439200cbff24 (diff) | |
download | bitcoin-51bad9195eb435200626beccbaf6b60774a4a894.tar.xz |
Merge #11445: [qa] 0.15.1 Backports
019c492 qa: Fix lcov for out-of-tree builds (MarcoFalke)
e169349 qa: Restore bitcoin-util-test py2 compatibility (MarcoFalke)
806c78f add functional test for mempoolreplacement command line arg (Gregory Sanders)
a825d4a Fix bip68-sequence rpc test (Johnson Lau)
a36f332 Verify DBWrapper iterators are taking snapshots (Matt Corallo)
8d2e51d qa: Fix bug introduced in p2p-segwit.py (Suhas Daftuar)
2f0b30a qa: Treat mininode p2p exceptions as fatal (Suhas Daftuar)
e4605d9 Tests for zmqpubrawtx and zmqpubrawblock (Andrew Chow)
2c4ff35 [script] Unit tests for IsMine (Jim Posen)
794a80e [script] Unit tests for script/standard functions (Jim Posen)
f9cf7b5 [tests] Check connectivity before sending in assumevalid.py (John Newbery)
f1ced0d [tests] Make p2p-leaktests.py more robust (John Newbery)
2e1ac70 [qa] zapwallettxes: Wait up to 3s for mempool reload (MarcoFalke)
b6468d3 Add listwallets RPC test to multiwallet.py (Cristian Mircea Messel)
d8dd8e7 [tests] fixup dbcrash interaction with add_nodes() (John Newbery)
2b97b36 [test] Replace check_output with low level version (João Barbosa)
e38211f [test] Add assert_raises_process_error to assert process errors (João Barbosa)
e0bfd28 [test] Add support for custom arguments to TestNodeCLI (João Barbosa)
812c870 [test] Improve assert_raises_jsonrpc docstring (João Barbosa)
eeb24a3 [qa] TestNode: Add wait_until_stopped helper method (MarcoFalke)
f3f7891 Stop test_bitcoin-qt touching ~/.bitcoin (MeshCollider)
f0b6795 Remove redundant testutil files (MeshCollider)
4424176 Improve signmessages functional test (Cristian Mircea Messel)
cef0319 [tests] fixups from set_test_params() (John Newbery)
82bf6fc [tests] Functional tests must explicitly set num_nodes (John Newbery)
801d2ae [tests] don't override __init__() in individual tests (John Newbery)
bb5e7cb [tests] Avoid passing around member variables in test_framework (John Newbery)
4d3ba18 [tests] TestNode: separate add_node from start_node (John Newbery)
11a5992 [tests] fix - use rpc_timeout as rpc timeout (John Newbery)
847c75e Add getmininginfo functional test (Cristian Mircea Messel)
2a5d099 RPC: gettxout: Slightly improve doc and tests (Jorge Timón)
716066d [tests] Add bitcoin_cli.py test script (John Newbery)
016b9ad [tests] add TestNodeCLI class for calling bitcoin-cli for a node (John Newbery)
5398f20 qa: Move wait_until to util (MarcoFalke)
1d80d1e [tests] fix timeout issues from TestNode (John Newbery)
c276c1e test: Increase initial RPC timeout to 60 seconds (Wladimir J. van der Laan)
fc2aa09 [tests] Introduce TestNode (John Newbery)
Pull request description:
This includes test related backports for 0.15.1. The motivation is twofold:
* Make backporting new tests written for current master easier
* Fix the most common test issues that happen(ed) frequently on travis
Even though this includes the new TestNode class, which comes with a lot
of refactoring, I believe that the issues caused by refactoring are found
and fixed by now.
Tree-SHA512: 6a0c4e5246da83ff0b3f7d2cb8df358d105ed548fb3857e5d882f26cc336553aa07b39e38c281879bf82f95078298b775334f9a60c0b23140f77c50174bd8347
Diffstat (limited to 'test/functional')
88 files changed, 876 insertions, 733 deletions
diff --git a/test/functional/README.md b/test/functional/README.md index 44efda33d3..2558bd017d 100644 --- a/test/functional/README.md +++ b/test/functional/README.md @@ -24,8 +24,8 @@ don't have test cases for. - Use a module-level docstring to describe what the test is testing, and how it is testing it. - When subclassing the BitcoinTestFramwork, place overrides for the - `__init__()`, and `setup_xxxx()` methods at the top of the subclass, then - locally-defined helper methods, then the `run_test()` method. + `set_test_params()`, `add_options()` and `setup_xxxx()` methods at the top of + the subclass, then locally-defined helper methods, then the `run_test()` method. #### General test-writing advice @@ -36,7 +36,7 @@ don't have test cases for. - Avoid stop-starting the nodes multiple times during the test if possible. A stop-start takes several seconds, so doing it several times blows up the runtime of the test. -- Set the `self.setup_clean_chain` variable in `__init__()` to control whether +- Set the `self.setup_clean_chain` variable in `set_test_params()` to control whether or not to use the cached data directories. The cached data directories contain a 200-block pre-mined blockchain and wallets for four nodes. Each node has 25 mature blocks (25x50=1250 BTC) in its wallet. diff --git a/test/functional/abandonconflict.py b/test/functional/abandonconflict.py index c87c02492d..e8dbc86469 100755 --- a/test/functional/abandonconflict.py +++ b/test/functional/abandonconflict.py @@ -14,10 +14,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class AbandonConflictTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False self.extra_args = [["-minrelaytxfee=0.00001"], []] def run_test(self): @@ -74,7 +72,7 @@ class AbandonConflictTest(BitcoinTestFramework): # Restart the node with a higher min relay fee so the parent tx is no longer in mempool # TODO: redo with eviction self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-minrelaytxfee=0.0001"]) + self.start_node(0, extra_args=["-minrelaytxfee=0.0001"]) # Verify txs no longer in either node's mempool assert_equal(len(self.nodes[0].getrawmempool()), 0) @@ -101,7 +99,7 @@ class AbandonConflictTest(BitcoinTestFramework): # Verify that even with a low min relay fee, the tx is not reaccepted from wallet on startup once abandoned self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-minrelaytxfee=0.00001"]) + self.start_node(0, extra_args=["-minrelaytxfee=0.00001"]) assert_equal(len(self.nodes[0].getrawmempool()), 0) assert_equal(self.nodes[0].getbalance(), balance) @@ -121,7 +119,7 @@ class AbandonConflictTest(BitcoinTestFramework): # Remove using high relay fee again self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-minrelaytxfee=0.0001"]) + self.start_node(0, extra_args=["-minrelaytxfee=0.0001"]) assert_equal(len(self.nodes[0].getrawmempool()), 0) newbalance = self.nodes[0].getbalance() assert_equal(newbalance, balance - Decimal("24.9996")) diff --git a/test/functional/assumevalid.py b/test/functional/assumevalid.py index 9d17faac51..65685c48b7 100755 --- a/test/functional/assumevalid.py +++ b/test/functional/assumevalid.py @@ -54,20 +54,22 @@ class BaseNode(NodeConnCB): self.send_message(headers_message) class AssumeValidTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 def setup_network(self): + self.add_nodes(3) # Start node0. We don't start the other nodes yet since # we need to pre-mine a block with an invalid transaction # signature so we can pass in the block hash as assumevalid. - self.nodes = [self.start_node(0, self.options.tmpdir)] + self.start_node(0) def send_blocks_until_disconnected(self, node): """Keep sending blocks to the node until we're disconnected.""" for i in range(len(self.blocks)): + if not node.connection: + break try: node.send_message(msg_block(self.blocks[i])) except IOError as e: @@ -162,15 +164,13 @@ class AssumeValidTest(BitcoinTestFramework): height += 1 # Start node1 and node2 with assumevalid so they accept a block with a bad signature. - self.nodes.append(self.start_node(1, self.options.tmpdir, - ["-assumevalid=" + hex(block102.sha256)])) + self.start_node(1, extra_args=["-assumevalid=" + hex(block102.sha256)]) node1 = BaseNode() # connects to node1 connections.append(NodeConn('127.0.0.1', p2p_port(1), self.nodes[1], node1)) node1.add_connection(connections[1]) node1.wait_for_verack() - self.nodes.append(self.start_node(2, self.options.tmpdir, - ["-assumevalid=" + hex(block102.sha256)])) + self.start_node(2, extra_args=["-assumevalid=" + hex(block102.sha256)]) node2 = BaseNode() # connects to node2 connections.append(NodeConn('127.0.0.1', p2p_port(2), self.nodes[2], node2)) node2.add_connection(connections[2]) diff --git a/test/functional/bip65-cltv-p2p.py b/test/functional/bip65-cltv-p2p.py index 7e5e4cf682..2cd6df6e37 100755 --- a/test/functional/bip65-cltv-p2p.py +++ b/test/functional/bip65-cltv-p2p.py @@ -60,9 +60,7 @@ def create_transaction(node, coinbase, to_address, amount): return tx class BIP65Test(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 self.extra_args = [['-promiscuousmempoolflags=1', '-whitelist=127.0.0.1']] self.setup_clean_chain = True @@ -109,7 +107,7 @@ class BIP65Test(BitcoinTestFramework): node0.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) - assert wait_until(lambda: "reject" in node0.last_message.keys()) + wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock) with mininode_lock: assert_equal(node0.last_message["reject"].code, REJECT_OBSOLETE) assert_equal(node0.last_message["reject"].reason, b'bad-version(0x00000003)') @@ -138,7 +136,7 @@ class BIP65Test(BitcoinTestFramework): node0.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) - assert wait_until (lambda: "reject" in node0.last_message.keys()) + wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock) with mininode_lock: assert node0.last_message["reject"].code in [REJECT_INVALID, REJECT_NONSTANDARD] assert_equal(node0.last_message["reject"].data, block.sha256) diff --git a/test/functional/bip68-112-113-p2p.py b/test/functional/bip68-112-113-p2p.py index 5a322e8c0e..7e6a4f4408 100755 --- a/test/functional/bip68-112-113-p2p.py +++ b/test/functional/bip68-112-113-p2p.py @@ -92,9 +92,9 @@ def all_rlt_txs(txarray): return txs class BIP68_112_113Test(ComparisonTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 + self.setup_clean_chain = True self.extra_args = [['-whitelist=127.0.0.1', '-blockversion=4']] def run_test(self): diff --git a/test/functional/bip68-sequence.py b/test/functional/bip68-sequence.py index 87a50692f6..1391b9ed46 100755 --- a/test/functional/bip68-sequence.py +++ b/test/functional/bip68-sequence.py @@ -17,10 +17,8 @@ SEQUENCE_LOCKTIME_MASK = 0x0000ffff NOT_FINAL_ERROR = "64: non-BIP68-final" class BIP68Test(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False self.extra_args = [[], ["-acceptnonstdtxn=0"]] def run_test(self): @@ -371,11 +369,14 @@ class BIP68Test(BitcoinTestFramework): def activateCSV(self): # activation should happen at block height 432 (3 periods) + # getblockchaininfo will show CSV as active at block 431 (144 * 3 -1) since it's returning whether CSV is active for the next block. min_activation_height = 432 height = self.nodes[0].getblockcount() - assert(height < min_activation_height) - self.nodes[0].generate(min_activation_height-height) - assert(get_bip9_status(self.nodes[0], 'csv')['status'] == 'active') + assert_greater_than(min_activation_height - height, 2) + self.nodes[0].generate(min_activation_height - height - 2) + assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], "locked_in") + self.nodes[0].generate(1) + assert_equal(get_bip9_status(self.nodes[0], 'csv')['status'], "active") sync_blocks(self.nodes) # Use self.nodes[1] to test that version 2 transactions are standard. diff --git a/test/functional/bip9-softforks.py b/test/functional/bip9-softforks.py index f00232c9ff..904789301a 100755 --- a/test/functional/bip9-softforks.py +++ b/test/functional/bip9-softforks.py @@ -28,11 +28,10 @@ from test_framework.comptool import TestInstance, TestManager from test_framework.script import CScript, OP_1NEGATE, OP_CHECKSEQUENCEVERIFY, OP_DROP class BIP9SoftForksTest(ComparisonTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 self.extra_args = [['-whitelist=127.0.0.1']] + self.setup_clean_chain = True def run_test(self): self.test = TestManager(self, self.options.tmpdir) @@ -241,6 +240,7 @@ class BIP9SoftForksTest(ComparisonTestFramework): # Restart all self.test.clear_all_connections() self.stop_nodes() + self.nodes = [] shutil.rmtree(self.options.tmpdir + "/node0") self.setup_chain() self.setup_network() diff --git a/test/functional/bipdersig-p2p.py b/test/functional/bipdersig-p2p.py index 38a9009544..c620d3e155 100755 --- a/test/functional/bipdersig-p2p.py +++ b/test/functional/bipdersig-p2p.py @@ -48,9 +48,7 @@ def create_transaction(node, coinbase, to_address, amount): return tx class BIP66Test(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 self.extra_args = [['-promiscuousmempoolflags=1', '-whitelist=127.0.0.1']] self.setup_clean_chain = True @@ -98,7 +96,7 @@ class BIP66Test(BitcoinTestFramework): node0.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) - assert wait_until(lambda: "reject" in node0.last_message.keys()) + wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock) with mininode_lock: assert_equal(node0.last_message["reject"].code, REJECT_OBSOLETE) assert_equal(node0.last_message["reject"].reason, b'bad-version(0x00000002)') @@ -128,7 +126,7 @@ class BIP66Test(BitcoinTestFramework): node0.send_and_ping(msg_block(block)) assert_equal(int(self.nodes[0].getbestblockhash(), 16), tip) - assert wait_until (lambda: "reject" in node0.last_message.keys()) + wait_until(lambda: "reject" in node0.last_message.keys(), lock=mininode_lock) with mininode_lock: # We can receive different reject messages depending on whether # bitcoind is running with multiple script check threads. If script diff --git a/test/functional/bitcoin_cli.py b/test/functional/bitcoin_cli.py new file mode 100755 index 0000000000..7acfede3f7 --- /dev/null +++ b/test/functional/bitcoin_cli.py @@ -0,0 +1,25 @@ +#!/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 bitcoin-cli""" +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal + +class TestBitcoinCli(BitcoinTestFramework): + + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 1 + + def run_test(self): + """Main test logic""" + + self.log.info("Compare responses from getinfo RPC and `bitcoin-cli getinfo`") + cli_get_info = self.nodes[0].cli.getinfo() + rpc_get_info = self.nodes[0].getinfo() + + assert_equal(cli_get_info, rpc_get_info) + +if __name__ == '__main__': + TestBitcoinCli().main() diff --git a/test/functional/blockchain.py b/test/functional/blockchain.py index a7034e6bcd..50be9262e4 100755 --- a/test/functional/blockchain.py +++ b/test/functional/blockchain.py @@ -21,7 +21,7 @@ from decimal import Decimal import http.client import subprocess -from test_framework.test_framework import (BitcoinTestFramework, BITCOIND_PROC_WAIT_TIMEOUT) +from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_raises, @@ -30,12 +30,8 @@ from test_framework.util import ( assert_is_hash_string, ) - class BlockchainTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = False + def set_test_params(self): self.num_nodes = 1 self.extra_args = [['-stopatheight=207']] @@ -139,14 +135,14 @@ class BlockchainTest(BitcoinTestFramework): self.nodes[0].generate(6) assert_equal(self.nodes[0].getblockcount(), 206) self.log.debug('Node should not stop at this height') - assert_raises(subprocess.TimeoutExpired, lambda: self.bitcoind_processes[0].wait(timeout=3)) + assert_raises(subprocess.TimeoutExpired, lambda: self.nodes[0].process.wait(timeout=3)) try: self.nodes[0].generate(1) except (ConnectionError, http.client.BadStatusLine): pass # The node already shut down before response self.log.debug('Node should stop at this height...') - self.bitcoind_processes[0].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) - self.nodes[0] = self.start_node(0, self.options.tmpdir) + self.nodes[0].wait_until_stopped() + self.start_node(0) assert_equal(self.nodes[0].getblockcount(), 207) diff --git a/test/functional/bumpfee.py b/test/functional/bumpfee.py index 9237f09240..ede0df29d8 100755 --- a/test/functional/bumpfee.py +++ b/test/functional/bumpfee.py @@ -30,26 +30,21 @@ WALLET_PASSPHRASE_TIMEOUT = 3600 class BumpFeeTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True + self.extra_args = [["-prematurewitness", "-walletprematurewitness", "-walletrbf={}".format(i)] + for i in range(self.num_nodes)] - def setup_network(self, split=False): - extra_args = [["-prematurewitness", "-walletprematurewitness", "-walletrbf={}".format(i)] - for i in range(self.num_nodes)] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args) - + def run_test(self): # Encrypt wallet for test_locked_wallet_fails test - self.nodes[1].encryptwallet(WALLET_PASSPHRASE) - self.bitcoind_processes[1].wait() - self.nodes[1] = self.start_node(1, self.options.tmpdir, extra_args[1]) + self.nodes[1].node_encrypt_wallet(WALLET_PASSPHRASE) + self.start_node(1) self.nodes[1].walletpassphrase(WALLET_PASSPHRASE, WALLET_PASSPHRASE_TIMEOUT) connect_nodes_bi(self.nodes, 0, 1) self.sync_all() - def run_test(self): peer_node, rbf_node = self.nodes rbf_node_address = rbf_node.getnewaddress() diff --git a/test/functional/create_cache.py b/test/functional/create_cache.py index 39c4c0f47e..7d4d1a529b 100755 --- a/test/functional/create_cache.py +++ b/test/functional/create_cache.py @@ -12,13 +12,10 @@ tests are being run in parallel. from test_framework.test_framework import BitcoinTestFramework class CreateCache(BitcoinTestFramework): + # Test network and test nodes are not required: - def __init__(self): - super().__init__() - - # Test network and test nodes are not required: + def set_test_params(self): self.num_nodes = 0 - self.nodes = [] def setup_network(self): pass diff --git a/test/functional/dbcrash.py b/test/functional/dbcrash.py index 8339305f5e..24b9765b4e 100755 --- a/test/functional/dbcrash.py +++ b/test/functional/dbcrash.py @@ -43,8 +43,7 @@ except AttributeError: pass class ChainstateWriteCrashTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 4 self.setup_clean_chain = False @@ -64,7 +63,9 @@ class ChainstateWriteCrashTest(BitcoinTestFramework): self.extra_args = [self.node0_args, self.node1_args, self.node2_args, self.node3_args] def setup_network(self): - self.setup_nodes() + # Need a bit of extra time for the nodes to start up for this test + self.add_nodes(self.num_nodes, extra_args=self.extra_args, timewait=90) + self.start_nodes() # Leave them unconnected, we'll use submitblock directly in this test def restart_node(self, node_index, expected_tip): @@ -74,10 +75,10 @@ class ChainstateWriteCrashTest(BitcoinTestFramework): after 60 seconds. Returns the utxo hash of the given node.""" time_start = time.time() - while time.time() - time_start < 60: + while time.time() - time_start < 120: try: # Any of these RPC calls could throw due to node crash - self.nodes[node_index] = self.start_node(node_index, self.options.tmpdir, self.extra_args[node_index]) + self.start_node(node_index) self.nodes[node_index].waitforblock(expected_tip) utxo_hash = self.nodes[node_index].gettxoutsetinfo()['hash_serialized_2'] return utxo_hash diff --git a/test/functional/decodescript.py b/test/functional/decodescript.py index 21a9f1223f..6611da8831 100755 --- a/test/functional/decodescript.py +++ b/test/functional/decodescript.py @@ -10,9 +10,7 @@ from test_framework.mininode import * from io import BytesIO class DecodeScriptTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/disablewallet.py b/test/functional/disablewallet.py index d344513414..c1d37963bc 100755 --- a/test/functional/disablewallet.py +++ b/test/functional/disablewallet.py @@ -11,11 +11,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * - class DisableWalletTest (BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [["-disablewallet"]] diff --git a/test/functional/disconnect_ban.py b/test/functional/disconnect_ban.py index 89b68aeb25..a6445b9b35 100755 --- a/test/functional/disconnect_ban.py +++ b/test/functional/disconnect_ban.py @@ -5,18 +5,17 @@ """Test node disconnect and ban behavior""" import time -from test_framework.mininode import wait_until from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import (assert_equal, - assert_raises_jsonrpc, - connect_nodes_bi) +from test_framework.util import ( + assert_equal, + assert_raises_jsonrpc, + connect_nodes_bi, + wait_until, +) class DisconnectBanTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False def run_test(self): self.log.info("Test setban and listbanned RPCs") @@ -24,7 +23,7 @@ class DisconnectBanTest(BitcoinTestFramework): self.log.info("setban: successfully ban single IP address") assert_equal(len(self.nodes[1].getpeerinfo()), 2) # node1 should have 2 connections to node0 at this point self.nodes[1].setban("127.0.0.1", "add") - assert wait_until(lambda: len(self.nodes[1].getpeerinfo()) == 0, timeout=10) + wait_until(lambda: len(self.nodes[1].getpeerinfo()) == 0, timeout=10) assert_equal(len(self.nodes[1].getpeerinfo()), 0) # all nodes must be disconnected at this point assert_equal(len(self.nodes[1].listbanned()), 1) @@ -66,8 +65,8 @@ class DisconnectBanTest(BitcoinTestFramework): assert_equal(len(self.nodes[1].listbanned()), 3) self.stop_node(1) + self.start_node(1) - self.nodes[1] = self.start_node(1, self.options.tmpdir) listAfterShutdown = self.nodes[1].listbanned() assert_equal("127.0.0.0/24", listAfterShutdown[0]['address']) assert_equal("127.0.0.0/32", listAfterShutdown[1]['address']) @@ -90,7 +89,7 @@ class DisconnectBanTest(BitcoinTestFramework): self.log.info("disconnectnode: successfully disconnect node by address") address1 = self.nodes[0].getpeerinfo()[0]['addr'] self.nodes[0].disconnectnode(address=address1) - assert wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10) + wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10) assert not [node for node in self.nodes[0].getpeerinfo() if node['addr'] == address1] self.log.info("disconnectnode: successfully reconnect node") @@ -101,7 +100,7 @@ class DisconnectBanTest(BitcoinTestFramework): self.log.info("disconnectnode: successfully disconnect node by node id") id1 = self.nodes[0].getpeerinfo()[0]['id'] self.nodes[0].disconnectnode(nodeid=id1) - assert wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10) + wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 1, timeout=10) assert not [node for node in self.nodes[0].getpeerinfo() if node['id'] == id1] if __name__ == '__main__': diff --git a/test/functional/example_test.py b/test/functional/example_test.py index 7709524f23..228e150598 100755 --- a/test/functional/example_test.py +++ b/test/functional/example_test.py @@ -23,13 +23,13 @@ from test_framework.mininode import ( mininode_lock, msg_block, msg_getdata, - wait_until, ) from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, connect_nodes, p2p_port, + wait_until, ) # NodeConnCB is a class containing callbacks to be executed when a P2P @@ -73,21 +73,19 @@ def custom_function(): class ExampleTest(BitcoinTestFramework): # Each functional test is a subclass of the BitcoinTestFramework class. - # Override the __init__(), add_options(), setup_chain(), setup_network() + # Override the set_test_params(), add_options(), setup_chain(), setup_network() # and setup_nodes() methods to customize the test setup as required. - def __init__(self): - """Initialize the test + def set_test_params(self): + """Override test parameters for your individual test. - Call super().__init__() first, and then override any test parameters - for your individual test.""" - super().__init__() + This method must be overridden and num_nodes must be exlicitly set.""" self.setup_clean_chain = True self.num_nodes = 3 # Use self.extra_args to change command-line arguments for the nodes self.extra_args = [[], ["-logips"], []] - # self.log.info("I've finished __init__") # Oops! Can't run self.log before run_test() + # self.log.info("I've finished set_test_params") # Oops! Can't run self.log before run_test() # Use add_options() to add specific command-line options for your test. # In practice this is not used very much, since the tests are mostly written @@ -209,7 +207,7 @@ class ExampleTest(BitcoinTestFramework): # wait_until() will loop until a predicate condition is met. Use it to test properties of the # NodeConnCB objects. - assert wait_until(lambda: sorted(blocks) == sorted(list(node2.block_receive_map.keys())), timeout=5) + wait_until(lambda: sorted(blocks) == sorted(list(node2.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 diff --git a/test/functional/forknotify.py b/test/functional/forknotify.py index 3bcf0a6795..afcad1f9cc 100755 --- a/test/functional/forknotify.py +++ b/test/functional/forknotify.py @@ -7,28 +7,18 @@ import os import time from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import * class ForkNotifyTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False def setup_network(self): - self.nodes = [] self.alert_filename = os.path.join(self.options.tmpdir, "alert.txt") with open(self.alert_filename, 'w', encoding='utf8'): pass # Just open then close to create zero-length file - self.nodes.append(self.start_node(0, self.options.tmpdir, - ["-blockversion=2", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""])) - # Node1 mines block.version=211 blocks - self.nodes.append(self.start_node(1, self.options.tmpdir, - ["-blockversion=211"])) - connect_nodes(self.nodes[1], 0) - - self.sync_all() + self.extra_args = [["-blockversion=2", "-alertnotify=echo %s >> \"" + self.alert_filename + "\""], + ["-blockversion=211"]] + super().setup_network() def run_test(self): # Mine 51 up-version blocks diff --git a/test/functional/fundrawtransaction.py b/test/functional/fundrawtransaction.py index e52e773918..42d95dd520 100755 --- a/test/functional/fundrawtransaction.py +++ b/test/functional/fundrawtransaction.py @@ -4,7 +4,7 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test the fundrawtransaction RPC.""" -from test_framework.test_framework import BitcoinTestFramework, BITCOIND_PROC_WAIT_TIMEOUT +from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * @@ -14,13 +14,10 @@ def get_unspent(listunspent, amount): return utx raise AssertionError('Could not find unspent with amount={}'.format(amount)) - class RawTransactionsTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = True + def set_test_params(self): self.num_nodes = 4 + self.setup_clean_chain = True def setup_network(self, split=False): self.setup_nodes() @@ -449,12 +446,11 @@ class RawTransactionsTest(BitcoinTestFramework): ############################################################ # locked wallet test self.stop_node(0) + self.nodes[1].node_encrypt_wallet("test") self.stop_node(2) self.stop_node(3) - self.nodes[1].encryptwallet("test") - self.bitcoind_processes[1].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir) + self.start_nodes() # This test is not meant to test fee estimation and we'd like # to be sure all txs are sent at a consistent desired feerate for node in self.nodes: diff --git a/test/functional/getblocktemplate_longpoll.py b/test/functional/getblocktemplate_longpoll.py index bbe1dda5f7..89768bd2fb 100755 --- a/test/functional/getblocktemplate_longpoll.py +++ b/test/functional/getblocktemplate_longpoll.py @@ -17,16 +17,14 @@ class LongpollThread(threading.Thread): self.longpollid = templat['longpollid'] # create a new connection to the node, we can't use the same # connection from two threads - self.node = get_rpc_proxy(node.url, 1, timeout=600) + self.node = get_rpc_proxy(node.url, 1, timeout=600, coveragedir=node.coverage_dir) def run(self): self.node.getblocktemplate({'longpollid':self.longpollid}) class GetBlockTemplateLPTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - self.num_nodes = 4 - self.setup_clean_chain = False + def set_test_params(self): + self.num_nodes = 2 def run_test(self): self.log.info("Warning: this test will take about 70 seconds in the best case. Be patient.") diff --git a/test/functional/getchaintips.py b/test/functional/getchaintips.py index 15f96c565f..21b67bfc64 100755 --- a/test/functional/getchaintips.py +++ b/test/functional/getchaintips.py @@ -14,13 +14,10 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal class GetChainTipsTest (BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 4 - self.setup_clean_chain = False def run_test (self): - tips = self.nodes[0].getchaintips () assert_equal (len (tips), 1) assert_equal (tips[0]['branchlen'], 0) diff --git a/test/functional/httpbasics.py b/test/functional/httpbasics.py index 4b32e8d9ca..c7682cb49d 100755 --- a/test/functional/httpbasics.py +++ b/test/functional/httpbasics.py @@ -11,10 +11,8 @@ import http.client import urllib.parse class HTTPBasicsTest (BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 3 - self.setup_clean_chain = False def setup_network(self): self.setup_nodes() diff --git a/test/functional/import-rescan.py b/test/functional/import-rescan.py index 4fc5078217..02d7428120 100755 --- a/test/functional/import-rescan.py +++ b/test/functional/import-rescan.py @@ -111,8 +111,7 @@ TIMESTAMP_WINDOW = 2 * 60 * 60 class ImportRescanTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 + len(IMPORT_NODES) def setup_network(self): @@ -121,7 +120,8 @@ class ImportRescanTest(BitcoinTestFramework): if import_node.prune: extra_args[i] += ["-prune=1"] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args) + self.add_nodes(self.num_nodes, extra_args) + self.start_nodes() for i in range(1, self.num_nodes): connect_nodes(self.nodes[i], 0) diff --git a/test/functional/importmulti.py b/test/functional/importmulti.py index e83e85de13..4c519f40d5 100755 --- a/test/functional/importmulti.py +++ b/test/functional/importmulti.py @@ -7,8 +7,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class ImportMultiTest (BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True @@ -429,7 +428,7 @@ class ImportMultiTest (BitcoinTestFramework): # restart nodes to check for proper serialization/deserialization of watch only address self.stop_nodes() - self.nodes = self.start_nodes(2, self.options.tmpdir) + self.start_nodes() address_assert = self.nodes[1].validateaddress(watchonly_address) assert_equal(address_assert['iswatchonly'], True) assert_equal(address_assert['ismine'], False) diff --git a/test/functional/importprunedfunds.py b/test/functional/importprunedfunds.py index 94753fe431..df90e9ec17 100755 --- a/test/functional/importprunedfunds.py +++ b/test/functional/importprunedfunds.py @@ -6,11 +6,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * - class ImportPrunedFundsTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 diff --git a/test/functional/invalidateblock.py b/test/functional/invalidateblock.py index c499d57b90..dd3daf1e07 100755 --- a/test/functional/invalidateblock.py +++ b/test/functional/invalidateblock.py @@ -8,9 +8,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class InvalidateTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 diff --git a/test/functional/invalidblockrequest.py b/test/functional/invalidblockrequest.py index eabc0db8df..9f44b44927 100755 --- a/test/functional/invalidblockrequest.py +++ b/test/functional/invalidblockrequest.py @@ -23,9 +23,9 @@ class InvalidBlockRequestTest(ComparisonTestFramework): ''' Can either run this test as 1 node with expected answers, or two and compare them. Change the "outcome" variable from each TestInstance object to only do the comparison. ''' - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 + self.setup_clean_chain = True def run_test(self): test = TestManager(self, self.options.tmpdir) diff --git a/test/functional/invalidtxrequest.py b/test/functional/invalidtxrequest.py index a9ac231f09..a22bd8f8cd 100755 --- a/test/functional/invalidtxrequest.py +++ b/test/functional/invalidtxrequest.py @@ -19,9 +19,9 @@ class InvalidTxRequestTest(ComparisonTestFramework): ''' Can either run this test as 1 node with expected answers, or two and compare them. Change the "outcome" variable from each TestInstance object to only do the comparison. ''' - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 + self.setup_clean_chain = True def run_test(self): test = TestManager(self, self.options.tmpdir) diff --git a/test/functional/keypool-topup.py b/test/functional/keypool-topup.py index 0e0c0ea74b..8302e3ef60 100755 --- a/test/functional/keypool-topup.py +++ b/test/functional/keypool-topup.py @@ -20,8 +20,7 @@ from test_framework.util import ( ) class KeypoolRestoreTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 self.extra_args = [['-usehd=0'], ['-usehd=1', '-keypool=100', '-keypoolmin=20']] @@ -35,7 +34,7 @@ class KeypoolRestoreTest(BitcoinTestFramework): self.stop_node(1) shutil.copyfile(self.tmpdir + "/node1/regtest/wallet.dat", self.tmpdir + "/wallet.bak") - self.nodes[1] = self.start_node(1, self.tmpdir, self.extra_args[1]) + self.start_node(1, self.extra_args[1]) connect_nodes_bi(self.nodes, 0, 1) self.log.info("Generate keys for wallet") @@ -61,7 +60,7 @@ class KeypoolRestoreTest(BitcoinTestFramework): self.log.info("Verify keypool is restored and balance is correct") - self.nodes[1] = self.start_node(1, self.tmpdir, self.extra_args[1]) + self.start_node(1, self.extra_args[1]) connect_nodes_bi(self.nodes, 0, 1) self.sync_all() diff --git a/test/functional/keypool.py b/test/functional/keypool.py index e8be559918..b823ca63bb 100755 --- a/test/functional/keypool.py +++ b/test/functional/keypool.py @@ -8,6 +8,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class KeyPoolTest(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 def run_test(self): nodes = self.nodes @@ -17,10 +19,9 @@ class KeyPoolTest(BitcoinTestFramework): assert(addr_before_encrypting_data['hdmasterkeyid'] == wallet_info_old['hdmasterkeyid']) # Encrypt wallet and wait to terminate - nodes[0].encryptwallet('test') - self.bitcoind_processes[0].wait() + nodes[0].node_encrypt_wallet('test') # Restart node 0 - nodes[0] = self.start_node(0, self.options.tmpdir) + self.start_node(0) # Keep creating keys addr = nodes[0].getnewaddress() addr_data = nodes[0].validateaddress(addr) @@ -79,10 +80,5 @@ class KeyPoolTest(BitcoinTestFramework): assert_equal(wi['keypoolsize_hd_internal'], 100) assert_equal(wi['keypoolsize'], 100) - def __init__(self): - super().__init__() - self.setup_clean_chain = False - self.num_nodes = 1 - if __name__ == '__main__': KeyPoolTest().main() diff --git a/test/functional/listsinceblock.py b/test/functional/listsinceblock.py index ce2d556ef0..6f428388ec 100755 --- a/test/functional/listsinceblock.py +++ b/test/functional/listsinceblock.py @@ -8,11 +8,9 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal class ListSinceBlockTest (BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = True + def set_test_params(self): self.num_nodes = 4 + self.setup_clean_chain = True def run_test(self): self.nodes[2].generate(101) diff --git a/test/functional/listtransactions.py b/test/functional/listtransactions.py index f75a8e29cc..e4522cc3b5 100755 --- a/test/functional/listtransactions.py +++ b/test/functional/listtransactions.py @@ -16,15 +16,9 @@ def txFromHex(hexstring): return tx class ListTransactionsTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - self.num_nodes = 4 - self.setup_clean_chain = False - - def setup_nodes(self): - #This test requires mocktime + def set_test_params(self): + self.num_nodes = 2 self.enable_mocktime() - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir) def run_test(self): # Simple send, 0 to 1: diff --git a/test/functional/maxuploadtarget.py b/test/functional/maxuploadtarget.py index 66e5bd29e6..1f402798e7 100755 --- a/test/functional/maxuploadtarget.py +++ b/test/functional/maxuploadtarget.py @@ -31,8 +31,7 @@ class TestNode(NodeConnCB): class MaxUploadTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [["-maxuploadtarget=800", "-blockmaxsize=999000"]] @@ -147,7 +146,7 @@ class MaxUploadTest(BitcoinTestFramework): #stop and start node 0 with 1MB maxuploadtarget, whitelist 127.0.0.1 self.log.info("Restarting nodes with -whitelist=127.0.0.1") self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-whitelist=127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000"]) + self.start_node(0, ["-whitelist=127.0.0.1", "-maxuploadtarget=1", "-blockmaxsize=999000"]) #recreate/reconnect a test node test_nodes = [TestNode()] diff --git a/test/functional/mempool_limit.py b/test/functional/mempool_limit.py index 2777291dd0..e24dc5a464 100755 --- a/test/functional/mempool_limit.py +++ b/test/functional/mempool_limit.py @@ -8,9 +8,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class MempoolLimitTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [["-maxmempool=5", "-spendzeroconfchange=0"]] diff --git a/test/functional/mempool_packages.py b/test/functional/mempool_packages.py index e225493816..2dedadf8ce 100755 --- a/test/functional/mempool_packages.py +++ b/test/functional/mempool_packages.py @@ -12,10 +12,8 @@ MAX_ANCESTORS = 25 MAX_DESCENDANTS = 25 class MempoolPackagesTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False self.extra_args = [["-maxorphantx=1000"], ["-maxorphantx=1000", "-limitancestorcount=5"]] # Build a transaction that spends parent_txid:vout diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py index e0889fd5e9..01f65b1373 100755 --- a/test/functional/mempool_persist.py +++ b/test/functional/mempool_persist.py @@ -32,17 +32,12 @@ Test is as follows: """ import time -from test_framework.mininode import wait_until from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class MempoolPersistTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - # We need 3 nodes for this test. Node1 does not have a persistent mempool. + def set_test_params(self): self.num_nodes = 3 - self.setup_clean_chain = False self.extra_args = [[], ["-persistmempool=0"], []] def run_test(self): @@ -64,27 +59,24 @@ class MempoolPersistTest(BitcoinTestFramework): self.log.debug("Stop-start node0 and node1. Verify that node0 has the transactions in its mempool and node1 does not.") self.stop_nodes() - self.nodes = [] - self.nodes.append(self.start_node(0, self.options.tmpdir)) - self.nodes.append(self.start_node(1, self.options.tmpdir)) + self.start_node(0) + self.start_node(1) # Give bitcoind a second to reload the mempool time.sleep(1) - assert wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5) + wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5) assert_equal(len(self.nodes[1].getrawmempool()), 0) self.log.debug("Stop-start node0 with -persistmempool=0. Verify that it doesn't load its mempool.dat file.") self.stop_nodes() - self.nodes = [] - self.nodes.append(self.start_node(0, self.options.tmpdir, ["-persistmempool=0"])) + self.start_node(0, extra_args=["-persistmempool=0"]) # Give bitcoind a second to reload the mempool time.sleep(1) assert_equal(len(self.nodes[0].getrawmempool()), 0) self.log.debug("Stop-start node0. Verify that it has the transactions in its mempool.") self.stop_nodes() - self.nodes = [] - self.nodes.append(self.start_node(0, self.options.tmpdir)) - assert wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5) + self.start_node(0) + wait_until(lambda: len(self.nodes[0].getrawmempool()) == 5) if __name__ == '__main__': MempoolPersistTest().main() diff --git a/test/functional/mempool_reorg.py b/test/functional/mempool_reorg.py index 937bf4bab5..7dfddd3230 100755 --- a/test/functional/mempool_reorg.py +++ b/test/functional/mempool_reorg.py @@ -13,10 +13,8 @@ from test_framework.util import * # Create one-input, one-output, no-fee transaction: class MempoolCoinbaseTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False self.extra_args = [["-checkmempool"]] * 2 alert_filename = None # Set by setup_network diff --git a/test/functional/mempool_resurrect_test.py b/test/functional/mempool_resurrect_test.py index a2f6228df9..1263c9306b 100755 --- a/test/functional/mempool_resurrect_test.py +++ b/test/functional/mempool_resurrect_test.py @@ -9,12 +9,8 @@ from test_framework.util import * # Create one-input, one-output, no-fee transaction: class MempoolCoinbaseTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 - self.setup_clean_chain = False - # Just need one node for this test self.extra_args = [["-checkmempool"]] def run_test(self): diff --git a/test/functional/mempool_spendcoinbase.py b/test/functional/mempool_spendcoinbase.py index 277ea45ad5..58ccd3e373 100755 --- a/test/functional/mempool_spendcoinbase.py +++ b/test/functional/mempool_spendcoinbase.py @@ -17,11 +17,8 @@ from test_framework.util import * # Create one-input, one-output, no-fee transaction: class MempoolSpendCoinbaseTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 - self.setup_clean_chain = False self.extra_args = [["-checkmempool"]] def run_test(self): diff --git a/test/functional/merkle_blocks.py b/test/functional/merkle_blocks.py index bcc65c8408..a58334b2a5 100755 --- a/test/functional/merkle_blocks.py +++ b/test/functional/merkle_blocks.py @@ -8,11 +8,9 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class MerkleBlockTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = True + def set_test_params(self): self.num_nodes = 4 + self.setup_clean_chain = True # Nodes 0/1 are "wallet" nodes, Nodes 2/3 are used for testing self.extra_args = [[], [], [], ["-txindex"]] diff --git a/test/functional/mining.py b/test/functional/mining.py index dbd4e29eca..93f9838896 100755 --- a/test/functional/mining.py +++ b/test/functional/mining.py @@ -4,16 +4,18 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Test mining RPCs +- getmininginfo - getblocktemplate proposal mode - submitblock""" -from binascii import b2a_hex import copy +from binascii import b2a_hex +from decimal import Decimal from test_framework.blocktools import create_coinbase -from test_framework.test_framework import BitcoinTestFramework from test_framework.mininode import CBlock -from test_framework.util import * +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal, assert_raises_jsonrpc def b2x(b): return b2a_hex(b).decode('ascii') @@ -25,14 +27,24 @@ def assert_template(node, block, expect, rehash=True): assert_equal(rsp, expect) class MiningTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = False def run_test(self): node = self.nodes[0] + + self.log.info('getmininginfo') + mining_info = node.getmininginfo() + assert_equal(mining_info['blocks'], 200) + assert_equal(mining_info['chain'], 'regtest') + assert_equal(mining_info['currentblocksize'], 0) + assert_equal(mining_info['currentblocktx'], 0) + assert_equal(mining_info['currentblockweight'], 0) + assert_equal(mining_info['difficulty'], Decimal('4.656542373906925E-10')) + assert_equal(mining_info['networkhashps'], Decimal('0.003333333333333334')) + assert_equal(mining_info['pooledtx'], 0) + # Mine a block to leave initial block download node.generate(1) tmpl = node.getblocktemplate() diff --git a/test/functional/multi_rpc.py b/test/functional/multi_rpc.py index a30e15ace9..a2b346f274 100755 --- a/test/functional/multi_rpc.py +++ b/test/functional/multi_rpc.py @@ -12,10 +12,7 @@ import http.client import urllib.parse class HTTPBasicsTest (BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = False + def set_test_params(self): self.num_nodes = 2 def setup_chain(self): diff --git a/test/functional/multiwallet.py b/test/functional/multiwallet.py index 5679f40503..b4e15a3322 100755 --- a/test/functional/multiwallet.py +++ b/test/functional/multiwallet.py @@ -12,34 +12,38 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_jsonrpc class MultiWalletTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [['-wallet=w1', '-wallet=w2', '-wallet=w3']] def run_test(self): + assert_equal(set(self.nodes[0].listwallets()), {"w1", "w2", "w3"}) + self.stop_node(0) # should not initialize if there are duplicate wallets - self.assert_start_raises_init_error(0, self.options.tmpdir, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.') + self.assert_start_raises_init_error(0, ['-wallet=w1', '-wallet=w1'], 'Error loading wallet w1. Duplicate -wallet filename specified.') # should not initialize if wallet file is a directory os.mkdir(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w11')) - self.assert_start_raises_init_error(0, self.options.tmpdir, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.') + self.assert_start_raises_init_error(0, ['-wallet=w11'], 'Error loading wallet w11. -wallet filename must be a regular file.') # should not initialize if wallet file is a symlink os.symlink(os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w1'), os.path.join(self.options.tmpdir, 'node0', 'regtest', 'w12')) - self.assert_start_raises_init_error(0, self.options.tmpdir, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.') + self.assert_start_raises_init_error(0, ['-wallet=w12'], 'Error loading wallet w12. -wallet filename must be a regular file.') - self.nodes[0] = self.start_node(0, self.options.tmpdir, self.extra_args[0]) + self.start_node(0, self.extra_args[0]) + + w1 = self.nodes[0].get_wallet_rpc("w1") + w2 = self.nodes[0].get_wallet_rpc("w2") + w3 = self.nodes[0].get_wallet_rpc("w3") + wallet_bad = self.nodes[0].get_wallet_rpc("bad") - w1 = self.nodes[0] / "wallet/w1" w1.generate(1) # accessing invalid wallet fails - assert_raises_jsonrpc(-18, "Requested wallet does not exist or is not loaded", (self.nodes[0] / "wallet/bad").getwalletinfo) + assert_raises_jsonrpc(-18, "Requested wallet does not exist or is not loaded", wallet_bad.getwalletinfo) # accessing wallet RPC without using wallet endpoint fails assert_raises_jsonrpc(-19, "Wallet file not specified", self.nodes[0].getwalletinfo) @@ -50,14 +54,12 @@ class MultiWalletTest(BitcoinTestFramework): w1_name = w1_info['walletname'] assert_equal(w1_name, "w1") - # check w1 wallet balance - w2 = self.nodes[0] / "wallet/w2" + # 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 = self.nodes[0] / "wallet/w3" w3_name = w3.getwalletinfo()['walletname'] assert_equal(w3_name, "w3") diff --git a/test/functional/net.py b/test/functional/net.py index 3ba3764cf9..37ea6824d5 100755 --- a/test/functional/net.py +++ b/test/functional/net.py @@ -17,10 +17,8 @@ from test_framework.util import ( p2p_port, ) - class NetTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 diff --git a/test/functional/nulldummy.py b/test/functional/nulldummy.py index 9717add272..60d0d876df 100755 --- a/test/functional/nulldummy.py +++ b/test/functional/nulldummy.py @@ -37,8 +37,7 @@ def trueDummy(tx): class NULLDUMMYTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True self.extra_args = [['-whitelist=127.0.0.1', '-walletprematurewitness']] diff --git a/test/functional/p2p-acceptblock.py b/test/functional/p2p-acceptblock.py index 322cb767db..293bc05539 100755 --- a/test/functional/p2p-acceptblock.py +++ b/test/functional/p2p-acceptblock.py @@ -60,8 +60,7 @@ class AcceptBlockTest(BitcoinTestFramework): default=os.getenv("BITCOIND", "bitcoind"), help="bitcoind binary to test") - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 self.extra_args = [[], ["-whitelist=127.0.0.1"]] diff --git a/test/functional/p2p-compactblocks.py b/test/functional/p2p-compactblocks.py index ff76e49fba..9c91c00453 100755 --- a/test/functional/p2p-compactblocks.py +++ b/test/functional/p2p-compactblocks.py @@ -70,7 +70,7 @@ class TestNode(NodeConnCB): def request_headers_and_sync(self, locator, hashstop=0): self.clear_block_announcement() self.get_headers(locator, hashstop) - assert wait_until(self.received_block_announcement, timeout=30) + wait_until(self.received_block_announcement, timeout=30, lock=mininode_lock) self.clear_block_announcement() # Block until a block announcement for a particular block hash is @@ -78,7 +78,7 @@ class TestNode(NodeConnCB): def wait_for_block_announcement(self, block_hash, timeout=30): def received_hash(): return (block_hash in self.announced_blockhashes) - return wait_until(received_hash, timeout=timeout) + wait_until(received_hash, timeout=timeout, lock=mininode_lock) def send_await_disconnect(self, message, timeout=30): """Sends a message to the node and wait for disconnect. @@ -86,15 +86,10 @@ 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) - success = wait_until(lambda: not self.connected, timeout=timeout) - if not success: - logger.error("send_await_disconnect failed!") - raise AssertionError("send_await_disconnect failed!") - return success + wait_until(lambda: not self.connected, timeout=timeout, lock=mininode_lock) class CompactBlocksTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True # Node0 = pre-segwit, node1 = segwit-aware self.num_nodes = 2 @@ -150,9 +145,7 @@ class CompactBlocksTest(BitcoinTestFramework): # Make sure we get a SENDCMPCT message from our peer def received_sendcmpct(): return (len(test_node.last_sendcmpct) > 0) - got_message = wait_until(received_sendcmpct, timeout=30) - assert(received_sendcmpct()) - assert(got_message) + wait_until(received_sendcmpct, timeout=30, lock=mininode_lock) with mininode_lock: # Check that the first version received is the preferred one assert_equal(test_node.last_sendcmpct[0].version, preferred_version) @@ -167,7 +160,6 @@ class CompactBlocksTest(BitcoinTestFramework): block_hash = int(node.generate(1)[0], 16) peer.wait_for_block_announcement(block_hash, timeout=30) assert(peer.block_announced) - assert(got_message) with mininode_lock: assert predicate(peer), ( @@ -282,7 +274,7 @@ class CompactBlocksTest(BitcoinTestFramework): # Wait until we've seen the block announcement for the resulting tip tip = int(node.getbestblockhash(), 16) - assert(test_node.wait_for_block_announcement(tip)) + test_node.wait_for_block_announcement(tip) # Make sure we will receive a fast-announce compact block self.request_cb_announcements(test_node, node, version) @@ -297,8 +289,7 @@ class CompactBlocksTest(BitcoinTestFramework): block.rehash() # Wait until the block was announced (via compact blocks) - wait_until(test_node.received_block_announcement, timeout=30) - assert(test_node.received_block_announcement()) + wait_until(test_node.received_block_announcement, timeout=30, lock=mininode_lock) # Now fetch and check the compact block header_and_shortids = None @@ -314,8 +305,7 @@ class CompactBlocksTest(BitcoinTestFramework): inv = CInv(4, block_hash) # 4 == "CompactBlock" test_node.send_message(msg_getdata([inv])) - wait_until(test_node.received_block_announcement, timeout=30) - assert(test_node.received_block_announcement()) + wait_until(test_node.received_block_announcement, timeout=30, lock=mininode_lock) # Now fetch and check the compact block header_and_shortids = None @@ -386,13 +376,11 @@ class CompactBlocksTest(BitcoinTestFramework): if announce == "inv": test_node.send_message(msg_inv([CInv(2, block.sha256)])) - success = wait_until(lambda: "getheaders" in test_node.last_message, timeout=30) - assert(success) + wait_until(lambda: "getheaders" in test_node.last_message, timeout=30, lock=mininode_lock) test_node.send_header_for_blocks([block]) else: test_node.send_header_for_blocks([block]) - success = wait_until(lambda: "getdata" in test_node.last_message, timeout=30) - assert(success) + wait_until(lambda: "getdata" in test_node.last_message, timeout=30, lock=mininode_lock) assert_equal(len(test_node.last_message["getdata"].inv), 1) assert_equal(test_node.last_message["getdata"].inv[0].type, 4) assert_equal(test_node.last_message["getdata"].inv[0].hash, block.sha256) @@ -571,8 +559,7 @@ class CompactBlocksTest(BitcoinTestFramework): assert_equal(int(node.getbestblockhash(), 16), block.hashPrevBlock) # We should receive a getdata request - success = wait_until(lambda: "getdata" in test_node.last_message, timeout=10) - assert(success) + wait_until(lambda: "getdata" in test_node.last_message, timeout=10, lock=mininode_lock) assert_equal(len(test_node.last_message["getdata"].inv), 1) assert(test_node.last_message["getdata"].inv[0].type == 2 or test_node.last_message["getdata"].inv[0].type == 2|MSG_WITNESS_FLAG) assert_equal(test_node.last_message["getdata"].inv[0].hash, block.sha256) @@ -599,8 +586,7 @@ class CompactBlocksTest(BitcoinTestFramework): num_to_request = random.randint(1, len(block.vtx)) msg.block_txn_request.from_absolute(sorted(random.sample(range(len(block.vtx)), num_to_request))) test_node.send_message(msg) - success = wait_until(lambda: "blocktxn" in test_node.last_message, timeout=10) - assert(success) + wait_until(lambda: "blocktxn" in test_node.last_message, timeout=10, lock=mininode_lock) [tx.calc_sha256() for tx in block.vtx] with mininode_lock: @@ -639,22 +625,20 @@ class CompactBlocksTest(BitcoinTestFramework): for i in range(MAX_CMPCTBLOCK_DEPTH + 1): test_node.clear_block_announcement() new_blocks.append(node.generate(1)[0]) - wait_until(test_node.received_block_announcement, timeout=30) + wait_until(test_node.received_block_announcement, timeout=30, lock=mininode_lock) test_node.clear_block_announcement() test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))])) - success = wait_until(lambda: "cmpctblock" in test_node.last_message, timeout=30) - assert(success) + wait_until(lambda: "cmpctblock" in test_node.last_message, timeout=30, lock=mininode_lock) test_node.clear_block_announcement() node.generate(1) - wait_until(test_node.received_block_announcement, timeout=30) + wait_until(test_node.received_block_announcement, timeout=30, lock=mininode_lock) test_node.clear_block_announcement() with mininode_lock: test_node.last_message.pop("block", None) test_node.send_message(msg_getdata([CInv(4, int(new_blocks[0], 16))])) - success = wait_until(lambda: "block" in test_node.last_message, timeout=30) - assert(success) + wait_until(lambda: "block" in test_node.last_message, timeout=30, lock=mininode_lock) with mininode_lock: test_node.last_message["block"].block.calc_sha256() assert_equal(test_node.last_message["block"].block.sha256, int(new_blocks[0], 16)) @@ -705,7 +689,7 @@ class CompactBlocksTest(BitcoinTestFramework): node.submitblock(ToHex(block)) for l in listeners: - wait_until(lambda: l.received_block_announcement(), timeout=30) + wait_until(lambda: l.received_block_announcement(), timeout=30, lock=mininode_lock) with mininode_lock: for l in listeners: assert "cmpctblock" in l.last_message diff --git a/test/functional/p2p-feefilter.py b/test/functional/p2p-feefilter.py index dbccb633a5..8c92365ced 100755 --- a/test/functional/p2p-feefilter.py +++ b/test/functional/p2p-feefilter.py @@ -37,11 +37,8 @@ class TestNode(NodeConnCB): self.txinvs = [] class FeeFilterTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 - self.setup_clean_chain = False def run_test(self): node1 = self.nodes[1] diff --git a/test/functional/p2p-fullblocktest.py b/test/functional/p2p-fullblocktest.py index e7fe7372c8..cb7bbaf7e5 100755 --- a/test/functional/p2p-fullblocktest.py +++ b/test/functional/p2p-fullblocktest.py @@ -49,12 +49,11 @@ class CBrokenBlock(CBlock): return r class FullBlockTest(ComparisonTestFramework): - # Can either run this test as 1 node with expected answers, or two and compare them. # Change the "outcome" variable from each TestInstance object to only do the comparison. - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 + self.setup_clean_chain = True self.block_heights = {} self.coinbase_key = CECKey() self.coinbase_key.set_secretbytes(b"horsebattery") diff --git a/test/functional/p2p-leaktests.py b/test/functional/p2p-leaktests.py index 5611c876ae..1dc8f72cd6 100755 --- a/test/functional/p2p-leaktests.py +++ b/test/functional/p2p-leaktests.py @@ -92,8 +92,7 @@ class CNodeNoVerackIdle(CLazyNode): conn.send_message(msg_getaddr()) class P2PLeakTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 1 self.extra_args = [['-banscore='+str(banscore)]] @@ -119,11 +118,11 @@ class P2PLeakTest(BitcoinTestFramework): NetworkThread().start() # Start up network handling in another thread - assert wait_until(lambda: no_version_bannode.ever_connected, timeout=10) - assert wait_until(lambda: no_version_idlenode.ever_connected, timeout=10) - assert wait_until(lambda: no_verack_idlenode.version_received, timeout=10) - assert wait_until(lambda: unsupported_service_bit5_node.ever_connected, timeout=10) - assert wait_until(lambda: unsupported_service_bit7_node.ever_connected, timeout=10) + 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) + wait_until(lambda: no_verack_idlenode.version_received, timeout=10, lock=mininode_lock) + wait_until(lambda: unsupported_service_bit5_node.ever_connected, timeout=10, lock=mininode_lock) + wait_until(lambda: unsupported_service_bit7_node.ever_connected, timeout=10, lock=mininode_lock) # Mine a block and make sure that it's not sent to the connected nodes self.nodes[0].generate(1) @@ -140,6 +139,9 @@ class P2PLeakTest(BitcoinTestFramework): [conn.disconnect_node() for conn in connections] + # Wait until all connections are closed + wait_until(lambda: len(self.nodes[0].getpeerinfo()) == 0) + # Make sure no unexpected messages came in assert(no_version_bannode.unexpected_msg == False) assert(no_version_idlenode.unexpected_msg == False) @@ -158,8 +160,10 @@ class P2PLeakTest(BitcoinTestFramework): allowed_service_bit5_node.add_connection(connections[5]) allowed_service_bit7_node.add_connection(connections[6]) - assert wait_until(lambda: allowed_service_bit5_node.message_count["verack"], timeout=10) - assert wait_until(lambda: allowed_service_bit7_node.message_count["verack"], timeout=10) + NetworkThread().start() # Network thread stopped when all previous NodeConnCBs disconnected. Restart it + + wait_until(lambda: allowed_service_bit5_node.message_count["verack"], lock=mininode_lock) + wait_until(lambda: allowed_service_bit7_node.message_count["verack"], lock=mininode_lock) if __name__ == '__main__': P2PLeakTest().main() diff --git a/test/functional/p2p-mempool.py b/test/functional/p2p-mempool.py index 34ef249eea..40fcde2605 100755 --- a/test/functional/p2p-mempool.py +++ b/test/functional/p2p-mempool.py @@ -13,9 +13,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class P2PMempoolTests(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [["-peerbloomfilters=0"]] diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py index 63dfbb8ae6..9105f0ee06 100755 --- a/test/functional/p2p-segwit.py +++ b/test/functional/p2p-segwit.py @@ -109,9 +109,7 @@ def sign_P2PK_witness_input(script, txTo, inIdx, hashtype, value, key): class SegWitTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 self.extra_args = [["-whitelist=127.0.0.1"], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0"], ["-whitelist=127.0.0.1", "-vbparams=segwit:0:0"]] @@ -1496,7 +1494,7 @@ class SegWitTest(BitcoinTestFramework): # Restart with the new binary self.stop_node(node_id) - self.nodes[node_id] = self.start_node(node_id, self.options.tmpdir) + self.start_node(node_id, extra_args=[]) connect_nodes(self.nodes[0], node_id) sync_blocks(self.nodes) diff --git a/test/functional/p2p-timeouts.py b/test/functional/p2p-timeouts.py index c3b29c215b..51d4769efc 100755 --- a/test/functional/p2p-timeouts.py +++ b/test/functional/p2p-timeouts.py @@ -33,8 +33,7 @@ class TestNode(NodeConnCB): pass class TimeoutsTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/p2p-versionbits-warning.py b/test/functional/p2p-versionbits-warning.py index df7e8ce5c1..5dfac6dd10 100755 --- a/test/functional/p2p-versionbits-warning.py +++ b/test/functional/p2p-versionbits-warning.py @@ -28,8 +28,7 @@ class TestNode(NodeConnCB): pass class VersionBitsWarningTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 @@ -112,7 +111,7 @@ class VersionBitsWarningTest(BitcoinTestFramework): # Empty out the alert file with open(self.alert_filename, 'w', encoding='utf8') as _: pass - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args) + self.start_nodes() # Connecting one block should be enough to generate an error. self.nodes[0].generate(1) @@ -123,7 +122,7 @@ class VersionBitsWarningTest(BitcoinTestFramework): self.test_versionbits_in_alert_file() # Test framework expects the node to still be running... - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args) + self.start_nodes() if __name__ == '__main__': VersionBitsWarningTest().main() diff --git a/test/functional/preciousblock.py b/test/functional/preciousblock.py index 04b41e76ba..40d7bb14ed 100755 --- a/test/functional/preciousblock.py +++ b/test/functional/preciousblock.py @@ -35,8 +35,7 @@ def node_sync_via_rpc(nodes): unidirectional_node_sync_via_rpc(node_src, node_dest) class PreciousTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 diff --git a/test/functional/prioritise_transaction.py b/test/functional/prioritise_transaction.py index 4fc03d2547..7ad368acd4 100755 --- a/test/functional/prioritise_transaction.py +++ b/test/functional/prioritise_transaction.py @@ -9,9 +9,7 @@ from test_framework.util import * from test_framework.mininode import COIN, MAX_BLOCK_BASE_SIZE class PrioritiseTransactionTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 self.extra_args = [["-printpriority=1"], ["-printpriority=1"]] diff --git a/test/functional/proxy_test.py b/test/functional/proxy_test.py index ae6f843ddc..81b99d1bf4 100755 --- a/test/functional/proxy_test.py +++ b/test/functional/proxy_test.py @@ -41,12 +41,9 @@ from test_framework.netutil import test_ipv6_local RANGE_BEGIN = PORT_MIN + 2 * PORT_RANGE # Start after p2p and rpc ports - class ProxyTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 4 - self.setup_clean_chain = False def setup_nodes(self): self.have_ipv6 = test_ipv6_local() @@ -89,7 +86,8 @@ class ProxyTest(BitcoinTestFramework): ] if self.have_ipv6: args[3] = ['-listen', '-proxy=[%s]:%i' % (self.conf3.addr),'-proxyrandomize=0', '-noonion'] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args=args) + self.add_nodes(self.num_nodes, extra_args=args) + self.start_nodes() def node_test(self, node, proxies, auth, test_onion=True): rv = [] diff --git a/test/functional/pruning.py b/test/functional/pruning.py index 0af91e0658..a4afbd46d7 100755 --- a/test/functional/pruning.py +++ b/test/functional/pruning.py @@ -26,9 +26,7 @@ def calc_usage(blockdir): return sum(os.path.getsize(blockdir+f) for f in os.listdir(blockdir) if os.path.isfile(blockdir+f)) / (1024. * 1024.) class PruneTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 6 @@ -56,6 +54,10 @@ class PruneTest(BitcoinTestFramework): connect_nodes(self.nodes[0], 4) sync_blocks(self.nodes[0:5]) + def setup_nodes(self): + self.add_nodes(self.num_nodes, self.extra_args, timewait=900) + self.start_nodes() + def create_big_chain(self): # Start by creating some coinbases we can spend later self.nodes[1].generate(200) @@ -98,7 +100,7 @@ class PruneTest(BitcoinTestFramework): # Node 2 stays connected, so it hears about the stale blocks and then reorg's when node0 reconnects # Stopping node 0 also clears its mempool, so it doesn't have node1's transactions to accidentally mine self.stop_node(0) - self.nodes[0]=self.start_node(0, self.options.tmpdir, self.full_node_default_args, timewait=900) + self.start_node(0, extra_args=self.full_node_default_args) # Mine 24 blocks in node 1 for i in range(24): if j == 0: @@ -126,7 +128,7 @@ class PruneTest(BitcoinTestFramework): # Reboot node 1 to clear its mempool (hopefully make the invalidate faster) # Lower the block max size so we don't keep mining all our big mempool transactions (from disconnected blocks) self.stop_node(1) - self.nodes[1] = self.start_node(1, self.options.tmpdir, ["-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"], timewait=900) + self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"]) height = self.nodes[1].getblockcount() self.log.info("Current block height: %d" % height) @@ -149,7 +151,7 @@ class PruneTest(BitcoinTestFramework): # Reboot node1 to clear those giant tx's from mempool self.stop_node(1) - self.nodes[1] = self.start_node(1, self.options.tmpdir, ["-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"], timewait=900) + self.start_node(1, extra_args=["-maxreceivebuffer=20000","-blockmaxsize=5000", "-checkblocks=5", "-disablesafemode"]) self.log.info("Generating new longer chain of 300 more blocks") self.nodes[1].generate(300) @@ -227,13 +229,15 @@ class PruneTest(BitcoinTestFramework): def manual_test(self, node_number, use_timestamp): # at this point, node has 995 blocks and has not yet run in prune mode - node = self.nodes[node_number] = self.start_node(node_number, self.options.tmpdir, timewait=900) + self.start_node(node_number) + node = self.nodes[node_number] assert_equal(node.getblockcount(), 995) assert_raises_jsonrpc(-1, "not in prune mode", node.pruneblockchain, 500) - self.stop_node(node_number) # now re-start in manual pruning mode - node = self.nodes[node_number] = self.start_node(node_number, self.options.tmpdir, ["-prune=1"], timewait=900) + self.stop_node(node_number) + self.start_node(node_number, extra_args=["-prune=1"]) + node = self.nodes[node_number] assert_equal(node.getblockcount(), 995) def height(index): @@ -307,7 +311,7 @@ class PruneTest(BitcoinTestFramework): # stop node, start back up with auto-prune at 550MB, make sure still runs self.stop_node(node_number) - self.nodes[node_number] = self.start_node(node_number, self.options.tmpdir, ["-prune=550"], timewait=900) + self.start_node(node_number, extra_args=["-prune=550"]) self.log.info("Success") @@ -315,7 +319,7 @@ class PruneTest(BitcoinTestFramework): # check that the pruning node's wallet is still in good shape self.log.info("Stop and start pruning node to trigger wallet rescan") self.stop_node(2) - self.nodes[2] = self.start_node(2, self.options.tmpdir, ["-prune=550"]) + self.start_node(2, extra_args=["-prune=550"]) self.log.info("Success") # check that wallet loads successfully when restarting a pruned node after IBD. @@ -325,7 +329,7 @@ class PruneTest(BitcoinTestFramework): nds = [self.nodes[0], self.nodes[5]] sync_blocks(nds, wait=5, timeout=300) self.stop_node(5) #stop and start to trigger rescan - self.nodes[5] = self.start_node(5, self.options.tmpdir, ["-prune=550"]) + self.start_node(5, extra_args=["-prune=550"]) self.log.info("Success") def run_test(self): diff --git a/test/functional/rawtransactions.py b/test/functional/rawtransactions.py index 6272fc69b7..51dda09ac4 100755 --- a/test/functional/rawtransactions.py +++ b/test/functional/rawtransactions.py @@ -17,9 +17,7 @@ from test_framework.util import * # Create one-input, one-output, no-fee transaction: class RawTransactionsTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 diff --git a/test/functional/receivedby.py b/test/functional/receivedby.py index 19d99c9c9e..db6fc86b82 100755 --- a/test/functional/receivedby.py +++ b/test/functional/receivedby.py @@ -23,16 +23,9 @@ def get_sub_array_from_array(object_array, to_match): return [] class ReceivedByTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.num_nodes = 4 - self.setup_clean_chain = False - - def setup_nodes(self): - #This test requires mocktime + def set_test_params(self): + self.num_nodes = 2 self.enable_mocktime() - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir) def run_test(self): ''' diff --git a/test/functional/reindex.py b/test/functional/reindex.py index b446baa04d..1f684a1afe 100755 --- a/test/functional/reindex.py +++ b/test/functional/reindex.py @@ -15,8 +15,7 @@ import time class ReindexTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 @@ -25,7 +24,7 @@ class ReindexTest(BitcoinTestFramework): blockcount = self.nodes[0].getblockcount() self.stop_nodes() extra_args = [["-reindex-chainstate" if justchainstate else "-reindex", "-checkblockindex=1"]] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args) + self.start_nodes(extra_args) while self.nodes[0].getblockcount() < blockcount: time.sleep(0.1) assert_equal(self.nodes[0].getblockcount(), blockcount) diff --git a/test/functional/replace-by-fee.py b/test/functional/replace-by-fee.py index bc67654987..5e1f33991b 100755 --- a/test/functional/replace-by-fee.py +++ b/test/functional/replace-by-fee.py @@ -61,16 +61,15 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])): class ReplaceByFeeTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - self.num_nodes = 1 - self.setup_clean_chain = False + def set_test_params(self): + self.num_nodes = 2 self.extra_args= [["-maxorphantx=1000", "-whitelist=127.0.0.1", "-limitancestorcount=50", "-limitancestorsize=101", "-limitdescendantcount=200", - "-limitdescendantsize=101"]] + "-limitdescendantsize=101"], + ["-mempoolreplacement=0"]] def run_test(self): make_utxo(self.nodes[0], 1*COIN) @@ -117,6 +116,8 @@ class ReplaceByFeeTest(BitcoinTestFramework): tx1a_hex = txToHex(tx1a) tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True) + self.sync_all([self.nodes]) + # Should fail because we haven't changed the fee tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] @@ -125,12 +126,17 @@ class ReplaceByFeeTest(BitcoinTestFramework): # This will raise an exception due to insufficient fee assert_raises_jsonrpc(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True) + # This will raise an exception due to transaction replacement being disabled + assert_raises_jsonrpc(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True) # Extra 0.1 BTC fee tx1b = CTransaction() tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)] tx1b.vout = [CTxOut(int(0.9*COIN), CScript([b'b']))] tx1b_hex = txToHex(tx1b) + # Replacement still disabled even with "enough fee" + assert_raises_jsonrpc(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True) + # Works when enabled tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True) mempool = self.nodes[0].getrawmempool() @@ -140,6 +146,11 @@ class ReplaceByFeeTest(BitcoinTestFramework): assert_equal(tx1b_hex, self.nodes[0].getrawtransaction(tx1b_txid)) + # Second node is running mempoolreplacement=0, will not replace originally-seen txn + mempool = self.nodes[1].getrawmempool() + assert tx1a_txid in mempool + assert tx1b_txid not in mempool + def test_doublespend_chain(self): """Doublespend of a long chain""" diff --git a/test/functional/resendwallettransactions.py b/test/functional/resendwallettransactions.py index 5059aa106e..d6ba591391 100755 --- a/test/functional/resendwallettransactions.py +++ b/test/functional/resendwallettransactions.py @@ -8,11 +8,9 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal, assert_raises_jsonrpc class ResendWalletTransactionsTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.extra_args = [['--walletbroadcast=false']] + def set_test_params(self): self.num_nodes = 1 + self.extra_args = [['--walletbroadcast=false']] def run_test(self): # Should raise RPC_WALLET_ERROR (-4) if walletbroadcast is disabled. @@ -20,7 +18,7 @@ class ResendWalletTransactionsTest(BitcoinTestFramework): # Should return an empty array if there aren't unconfirmed wallet transactions. self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir) + self.start_node(0, extra_args=[]) assert_equal(self.nodes[0].resendwallettransactions(), []) # Should return an array with the unconfirmed wallet transaction. diff --git a/test/functional/rest.py b/test/functional/rest.py index a69dbb5013..437111a4d7 100755 --- a/test/functional/rest.py +++ b/test/functional/rest.py @@ -43,8 +43,7 @@ def http_post_call(host, port, path, requestdata = '', response_object = 0): class RESTTest (BitcoinTestFramework): FORMAT_SEPARATOR = "." - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 diff --git a/test/functional/rpcbind_test.py b/test/functional/rpcbind_test.py index 951685aa76..0cf64beebd 100755 --- a/test/functional/rpcbind_test.py +++ b/test/functional/rpcbind_test.py @@ -11,19 +11,13 @@ from test_framework.test_framework import BitcoinTestFramework, SkipTest from test_framework.util import * from test_framework.netutil import * - class RPCBindTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 def setup_network(self): - pass - - def setup_nodes(self): - pass + self.add_nodes(self.num_nodes, None) def run_bind_test(self, allow_ips, connect_to, addresses, expected): ''' @@ -31,13 +25,15 @@ class RPCBindTest(BitcoinTestFramework): then try to connect, and check if the set of bound addresses matches the expected set. ''' + self.log.info("Bind test for %s" % str(addresses)) expected = [(addr_to_hex(addr), port) for (addr, port) in expected] base_args = ['-disablewallet', '-nolisten'] if allow_ips: base_args += ['-rpcallowip=' + x for x in allow_ips] binds = ['-rpcbind='+addr for addr in addresses] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, [base_args + binds], connect_to) - pid = self.bitcoind_processes[0].pid + self.nodes[0].rpchost = connect_to + self.start_node(0, base_args + binds) + pid = self.nodes[0].process.pid assert_equal(set(get_bind_addrs(pid)), set(expected)) self.stop_nodes() @@ -46,10 +42,12 @@ class RPCBindTest(BitcoinTestFramework): Start a node with rpcallow IP, and request getnetworkinfo at a non-localhost IP. ''' + self.log.info("Allow IP test for %s:%d" % (rpchost, rpcport)) base_args = ['-disablewallet', '-nolisten'] + ['-rpcallowip='+x for x in allow_ips] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, [base_args]) + self.nodes[0].rpchost = None + self.start_nodes([base_args]) # connect to node through non-loopback interface - node = get_rpc_proxy(rpc_url(get_datadir_path(self.options.tmpdir, 0), 0, "%s:%d" % (rpchost, rpcport)), 0) + node = get_rpc_proxy(rpc_url(get_datadir_path(self.options.tmpdir, 0), 0, "%s:%d" % (rpchost, rpcport)), 0, coveragedir=self.options.coveragedir) node.getnetworkinfo() self.stop_nodes() diff --git a/test/functional/rpcnamedargs.py b/test/functional/rpcnamedargs.py index 3b286000a1..da61cc66e6 100755 --- a/test/functional/rpcnamedargs.py +++ b/test/functional/rpcnamedargs.py @@ -10,15 +10,8 @@ from test_framework.util import ( assert_raises_jsonrpc, ) - class NamedArgumentTest(BitcoinTestFramework): - """ - Test named arguments on RPC calls. - """ - - def __init__(self): - super().__init__() - self.setup_clean_chain = False + def set_test_params(self): self.num_nodes = 1 def run_test(self): diff --git a/test/functional/segwit.py b/test/functional/segwit.py index 51eaa34a54..c08fbd3e77 100755 --- a/test/functional/segwit.py +++ b/test/functional/segwit.py @@ -75,9 +75,7 @@ def find_unspent(node, min_value): return utxo class SegWitTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 3 self.extra_args = [["-walletprematurewitness", "-rpcserialversion=0"], diff --git a/test/functional/sendheaders.py b/test/functional/sendheaders.py index e47e07fb86..fe577dc20a 100755 --- a/test/functional/sendheaders.py +++ b/test/functional/sendheaders.py @@ -128,7 +128,7 @@ class TestNode(NodeConnCB): expect_headers = headers if headers != None else [] expect_inv = inv if inv != None else [] test_function = lambda: self.block_announced - assert(wait_until(test_function, timeout=60)) + wait_until(test_function, timeout=60, lock=mininode_lock) with mininode_lock: self.block_announced = False @@ -155,12 +155,12 @@ class TestNode(NodeConnCB): return test_function = lambda: "getdata" in self.last_message and [x.hash for x in self.last_message["getdata"].inv] == hash_list - assert(wait_until(test_function, timeout=timeout)) + 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 - assert(wait_until(test_function, timeout=timeout)) + wait_until(test_function, timeout=timeout, lock=mininode_lock) return def send_header_for_blocks(self, new_blocks): @@ -174,8 +174,7 @@ class TestNode(NodeConnCB): self.send_message(getblocks_message) class SendHeadersTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 diff --git a/test/functional/signmessages.py b/test/functional/signmessages.py index 42f6a9daaf..52ba6a5ad7 100755 --- a/test/functional/signmessages.py +++ b/test/functional/signmessages.py @@ -5,31 +5,34 @@ """Test RPC commands for signing and verifying messages.""" from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal class SignMessagesTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 def run_test(self): message = 'This is just a test message' - # Test the signing with a privkey - privKey = 'cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N' + self.log.info('test signing with priv_key') + priv_key = 'cUeKHd5orzT3mz8P9pxyREHfsWtVfgsfDjiZZBcjUBAaGk1BTj7N' address = 'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB' - signature = self.nodes[0].signmessagewithprivkey(privKey, message) - - # Verify the message + expected_signature = 'INbVnW4e6PeRmsv2Qgu8NuopvrVjkcxob+sX8OcZG0SALhWybUjzMLPdAsXI46YZGb0KQTRii+wWIQzRpG/U+S0=' + signature = self.nodes[0].signmessagewithprivkey(priv_key, message) + assert_equal(expected_signature, signature) assert(self.nodes[0].verifymessage(address, signature, message)) - # Test the signing with an address with wallet + self.log.info('test signing with an address with wallet') address = self.nodes[0].getnewaddress() signature = self.nodes[0].signmessage(address, message) - - # Verify the message assert(self.nodes[0].verifymessage(address, signature, message)) + self.log.info('test verifying with another address should not work') + other_address = self.nodes[0].getnewaddress() + other_signature = self.nodes[0].signmessage(other_address, message) + assert(not self.nodes[0].verifymessage(other_address, signature, message)) + assert(not self.nodes[0].verifymessage(address, other_signature, message)) + if __name__ == '__main__': SignMessagesTest().main() diff --git a/test/functional/signrawtransactions.py b/test/functional/signrawtransactions.py index 415727268a..b47ef93955 100755 --- a/test/functional/signrawtransactions.py +++ b/test/functional/signrawtransactions.py @@ -9,8 +9,7 @@ from test_framework.util import * class SignRawTransactionsTest(BitcoinTestFramework): - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 diff --git a/test/functional/smartfees.py b/test/functional/smartfees.py index bc42a319df..76632fc578 100755 --- a/test/functional/smartfees.py +++ b/test/functional/smartfees.py @@ -141,11 +141,8 @@ def check_estimates(node, fees_seen, max_invalid, print_estimates = True): class EstimateFeeTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 3 - self.setup_clean_chain = False def setup_network(self): """ @@ -153,57 +150,16 @@ class EstimateFeeTest(BitcoinTestFramework): But first we need to use one node to create a lot of outputs which we will use to generate our transactions. """ - self.nodes = [] + self.add_nodes(3, extra_args=[["-maxorphantx=1000", "-whitelist=127.0.0.1"], + ["-blockmaxsize=17000", "-maxorphantx=1000"], + ["-blockmaxsize=8000", "-maxorphantx=1000"]]) # Use node0 to mine blocks for input splitting - self.nodes.append(self.start_node(0, self.options.tmpdir, ["-maxorphantx=1000", - "-whitelist=127.0.0.1"])) - - self.log.info("This test is time consuming, please be patient") - self.log.info("Splitting inputs so we can generate tx's") - self.txouts = [] - self.txouts2 = [] - # Split a coinbase into two transaction puzzle outputs - split_inputs(self.nodes[0], self.nodes[0].listunspent(0), self.txouts, True) - - # Mine - while (len(self.nodes[0].getrawmempool()) > 0): - self.nodes[0].generate(1) - - # Repeatedly split those 2 outputs, doubling twice for each rep - # Use txouts to monitor the available utxo, since these won't be tracked in wallet - reps = 0 - while (reps < 5): - #Double txouts to txouts2 - while (len(self.txouts)>0): - split_inputs(self.nodes[0], self.txouts, self.txouts2) - while (len(self.nodes[0].getrawmempool()) > 0): - self.nodes[0].generate(1) - #Double txouts2 to txouts - while (len(self.txouts2)>0): - split_inputs(self.nodes[0], self.txouts2, self.txouts) - while (len(self.nodes[0].getrawmempool()) > 0): - self.nodes[0].generate(1) - reps += 1 - self.log.info("Finished splitting") - - # Now we can connect the other nodes, didn't want to connect them earlier - # so the estimates would not be affected by the splitting transactions # Node1 mines small blocks but that are bigger than the expected transaction rate. # NOTE: the CreateNewBlock code starts counting block size at 1,000 bytes, # (17k is room enough for 110 or so transactions) - self.nodes.append(self.start_node(1, self.options.tmpdir, - ["-blockmaxsize=17000", "-maxorphantx=1000"])) - connect_nodes(self.nodes[1], 0) - # Node2 is a stingy miner, that # produces too small blocks (room for only 55 or so transactions) - node2args = ["-blockmaxsize=8000", "-maxorphantx=1000"] - self.nodes.append(self.start_node(2, self.options.tmpdir, node2args)) - connect_nodes(self.nodes[0], 2) - connect_nodes(self.nodes[2], 1) - - self.sync_all() def transact_and_mine(self, numblocks, mining_node): min_fee = Decimal("0.00001") @@ -232,9 +188,51 @@ class EstimateFeeTest(BitcoinTestFramework): self.memutxo = newmem def run_test(self): + self.log.info("This test is time consuming, please be patient") + self.log.info("Splitting inputs so we can generate tx's") + # Make log handler available to helper functions global log log = self.log + + # Start node0 + self.start_node(0) + self.txouts = [] + self.txouts2 = [] + # Split a coinbase into two transaction puzzle outputs + split_inputs(self.nodes[0], self.nodes[0].listunspent(0), self.txouts, True) + + # Mine + while (len(self.nodes[0].getrawmempool()) > 0): + self.nodes[0].generate(1) + + # Repeatedly split those 2 outputs, doubling twice for each rep + # Use txouts to monitor the available utxo, since these won't be tracked in wallet + reps = 0 + while (reps < 5): + #Double txouts to txouts2 + while (len(self.txouts)>0): + split_inputs(self.nodes[0], self.txouts, self.txouts2) + while (len(self.nodes[0].getrawmempool()) > 0): + self.nodes[0].generate(1) + #Double txouts2 to txouts + while (len(self.txouts2)>0): + split_inputs(self.nodes[0], self.txouts2, self.txouts) + while (len(self.nodes[0].getrawmempool()) > 0): + self.nodes[0].generate(1) + reps += 1 + self.log.info("Finished splitting") + + # Now we can connect the other nodes, didn't want to connect them earlier + # so the estimates would not be affected by the splitting transactions + self.start_node(1) + self.start_node(2) + connect_nodes(self.nodes[1], 0) + connect_nodes(self.nodes[0], 2) + connect_nodes(self.nodes[2], 1) + + self.sync_all() + self.fees_per_kb = [] self.memutxo = [] self.confutxo = self.txouts # Start with the set of confirmed txouts after splitting diff --git a/test/functional/test_framework/comptool.py b/test/functional/test_framework/comptool.py index 9f062865a3..bfbc0c3b03 100755 --- a/test/functional/test_framework/comptool.py +++ b/test/functional/test_framework/comptool.py @@ -19,7 +19,7 @@ TestNode behaves as follows: from .mininode import * from .blockstore import BlockStore, TxStore -from .util import p2p_port +from .util import p2p_port, wait_until import logging @@ -189,7 +189,7 @@ class TestManager(object): def wait_for_disconnections(self): def disconnected(): return all(node.closed for node in self.test_nodes) - return wait_until(disconnected, timeout=10) + 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) @@ -197,7 +197,7 @@ class TestManager(object): def wait_for_pings(self, counter): def received_pongs(): return all(node.received_ping_response(counter) for node in self.test_nodes) - return wait_until(received_pongs) + wait_until(received_pongs, lock=mininode_lock) # sync_blocks: Wait for all connections to request the blockhash given # then send get_headers to find out the tip of each node, and synchronize @@ -210,8 +210,7 @@ class TestManager(object): ) # --> error if not requested - if not wait_until(blocks_requested, attempts=20*num_blocks): - raise AssertionError("Not all nodes requested block") + wait_until(blocks_requested, attempts=20*num_blocks, lock=mininode_lock) # Send getheaders message [ c.cb.send_getheaders() for c in self.connections ] @@ -231,8 +230,7 @@ class TestManager(object): ) # --> error if not requested - if not wait_until(transaction_requested, attempts=20*num_events): - raise AssertionError("Not all nodes requested transaction") + wait_until(transaction_requested, attempts=20*num_events, lock=mininode_lock) # Get the mempool [ c.cb.send_mempool() for c in self.connections ] diff --git a/test/functional/test_framework/mininode.py b/test/functional/test_framework/mininode.py index a4d85501e7..843b67cd5c 100755 --- a/test/functional/test_framework/mininode.py +++ b/test/functional/test_framework/mininode.py @@ -35,7 +35,7 @@ import time from threading import RLock, Thread from test_framework.siphash import siphash256 -from test_framework.util import hex_str_to_bytes, bytes_to_hex_str +from test_framework.util import hex_str_to_bytes, bytes_to_hex_str, wait_until BIP0031_VERSION = 60000 MY_VERSION = 70014 # past bip-31 for ping/pong @@ -1358,23 +1358,6 @@ class msg_reject(object): return "msg_reject: %s %d %s [%064x]" \ % (self.message, self.code, self.reason, self.data) -# Helper function -def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf')): - if attempts == float('inf') and timeout == float('inf'): - timeout = 60 - attempt = 0 - elapsed = 0 - - while attempt < attempts and elapsed < timeout: - with mininode_lock: - if predicate(): - return True - attempt += 1 - elapsed += 0.05 - time.sleep(0.05) - - return False - class msg_feefilter(object): command = b"feefilter" @@ -1522,6 +1505,7 @@ class NodeConnCB(object): except: print("ERROR delivering %s (%s)" % (repr(message), sys.exc_info()[0])) + raise def set_deliver_sleep_time(self, value): with mininode_lock: @@ -1591,21 +1575,21 @@ class NodeConnCB(object): def wait_for_disconnect(self, timeout=60): test_function = lambda: not self.connected - assert wait_until(test_function, timeout=timeout) + 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 - assert wait_until(test_function, timeout=timeout) + wait_until(test_function, timeout=timeout, lock=mininode_lock) def wait_for_getdata(self, timeout=60): test_function = lambda: self.last_message.get("getdata") - assert wait_until(test_function, timeout=timeout) + wait_until(test_function, timeout=timeout, lock=mininode_lock) def wait_for_getheaders(self, timeout=60): test_function = lambda: self.last_message.get("getheaders") - assert wait_until(test_function, timeout=timeout) + 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.""" @@ -1614,11 +1598,11 @@ class NodeConnCB(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 - assert wait_until(test_function, timeout=timeout) + wait_until(test_function, timeout=timeout, lock=mininode_lock) def wait_for_verack(self, timeout=60): test_function = lambda: self.message_count["verack"] - assert wait_until(test_function, timeout=timeout) + wait_until(test_function, timeout=timeout, lock=mininode_lock) # Message sending helper functions @@ -1636,7 +1620,7 @@ class NodeConnCB(object): 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 - assert wait_until(test_function, timeout=timeout) + wait_until(test_function, timeout=timeout, lock=mininode_lock) self.ping_counter += 1 return True @@ -1725,13 +1709,10 @@ class NodeConn(asyncore.dispatcher): self.cb.on_close(self) def handle_read(self): - try: - t = self.recv(8192) - if len(t) > 0: - self.recvbuf += t - self.got_data() - except: - pass + t = self.recv(8192) + if len(t) > 0: + self.recvbuf += t + self.got_data() def readable(self): return True @@ -1797,8 +1778,10 @@ class NodeConn(asyncore.dispatcher): self.got_message(t) else: logger.warning("Received unknown command from %s:%d: '%s' %s" % (self.dstaddr, self.dstport, command, repr(msg))) + raise ValueError("Unknown command: '%s'" % (command)) except Exception as e: logger.exception('got_data:', repr(e)) + raise def send_message(self, message, pushbuf=False): if self.state != "connected" and not pushbuf: @@ -1854,6 +1837,7 @@ class NetworkThread(Thread): disconnected.append(obj) [ obj.handle_close() for obj in disconnected ] asyncore.loop(0.1, use_poll=True, map=mininode_socket_map, count=1) + logger.debug("Network thread closing") # An exception we can raise if we detect a potential disconnect diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index e562d11938..a53eb51799 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -5,15 +5,12 @@ """Base class for RPC testing.""" from collections import deque -import errno from enum import Enum -import http.client import logging import optparse import os import pdb import shutil -import subprocess import sys import tempfile import time @@ -21,6 +18,7 @@ import traceback from .authproxy import JSONRPCException from . import coverage +from .test_node import TestNode from .util import ( MAX_NODES, PortSeed, @@ -28,12 +26,9 @@ from .util import ( check_json_precision, connect_nodes_bi, disconnect_nodes, - get_rpc_proxy, initialize_datadir, - get_datadir_path, log_filename, p2p_port, - rpc_url, set_node_times, sync_blocks, sync_mempools, @@ -48,63 +43,33 @@ TEST_EXIT_PASSED = 0 TEST_EXIT_FAILED = 1 TEST_EXIT_SKIPPED = 77 -BITCOIND_PROC_WAIT_TIMEOUT = 60 - class BitcoinTestFramework(object): """Base class for a bitcoin test script. - Individual bitcoin test scripts should subclass this class and override the following methods: + Individual bitcoin test scripts should subclass this class and override the set_test_params() and run_test() methods. + + Individual tests can also override the following methods to customize the test setup: - - __init__() - add_options() - setup_chain() - setup_network() - - run_test() + - setup_nodes() - The main() method should not be overridden. + The __init__() and main() methods should not be overridden. This class also contains various public and private helper methods.""" - # Methods to override in subclass test scripts. def __init__(self): - self.num_nodes = 4 + """Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method""" self.setup_clean_chain = False self.nodes = [] - self.bitcoind_processes = {} self.mocktime = 0 + self.set_test_params() - def add_options(self, parser): - pass - - def setup_chain(self): - self.log.info("Initializing test directory " + self.options.tmpdir) - if self.setup_clean_chain: - self._initialize_chain_clean(self.options.tmpdir, self.num_nodes) - else: - self._initialize_chain(self.options.tmpdir, self.num_nodes, self.options.cachedir) - - def setup_network(self): - self.setup_nodes() - - # Connect the nodes as a "chain". This allows us - # to split the network between nodes 1 and 2 to get - # two halves that can work on competing chains. - for i in range(self.num_nodes - 1): - connect_nodes_bi(self.nodes, i, i + 1) - self.sync_all() - - def setup_nodes(self): - extra_args = None - if hasattr(self, "extra_args"): - extra_args = self.extra_args - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args) - - def run_test(self): - raise NotImplementedError - - # Main function. This should not be overridden by the subclass test scripts. + assert hasattr(self, "num_nodes"), "Test must set self.num_nodes in set_test_params()" def main(self): + """Main function. This should not be overridden by the subclass test scripts.""" parser = optparse.OptionParser(usage="%prog [options]") parser.add_option("--nocleanup", dest="nocleanup", default=False, action="store_true", @@ -208,77 +173,115 @@ class BitcoinTestFramework(object): logging.shutdown() sys.exit(TEST_EXIT_FAILED) - # Public helper methods. These can be accessed by the subclass test scripts. + # Methods to override in subclass test scripts. + def set_test_params(self): + """Tests must this method to change default values for number of nodes, topology, etc""" + raise NotImplementedError - def start_node(self, i, dirname, extra_args=None, rpchost=None, timewait=None, binary=None, stderr=None): - """Start a bitcoind and return RPC connection to it""" + def add_options(self, parser): + """Override this method to add command-line options to the test""" + pass - datadir = os.path.join(dirname, "node" + str(i)) - if binary is None: - binary = os.getenv("BITCOIND", "bitcoind") - args = [binary, "-datadir=" + datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-logtimemicros", "-debug", "-debugexclude=libevent", "-debugexclude=leveldb", "-mocktime=" + str(self.mocktime), "-uacomment=testnode%d" % i] - if extra_args is not None: - args.extend(extra_args) - self.bitcoind_processes[i] = subprocess.Popen(args, stderr=stderr) - self.log.debug("initialize_chain: bitcoind started, waiting for RPC to come up") - self._wait_for_bitcoind_start(self.bitcoind_processes[i], datadir, i, rpchost) - self.log.debug("initialize_chain: RPC successfully started") - proxy = get_rpc_proxy(rpc_url(datadir, i, rpchost), i, timeout=timewait) + def setup_chain(self): + """Override this method to customize blockchain setup""" + self.log.info("Initializing test directory " + self.options.tmpdir) + if self.setup_clean_chain: + self._initialize_chain_clean() + else: + self._initialize_chain() - if self.options.coveragedir: - coverage.write_all_rpc_commands(self.options.coveragedir, proxy) + def setup_network(self): + """Override this method to customize test network topology""" + self.setup_nodes() + + # Connect the nodes as a "chain". This allows us + # to split the network between nodes 1 and 2 to get + # two halves that can work on competing chains. + for i in range(self.num_nodes - 1): + connect_nodes_bi(self.nodes, i, i + 1) + self.sync_all() + + def setup_nodes(self): + """Override this method to customize test node setup""" + extra_args = None + if hasattr(self, "extra_args"): + extra_args = self.extra_args + self.add_nodes(self.num_nodes, extra_args) + self.start_nodes() + + def run_test(self): + """Tests must override this method to define test logic""" + raise NotImplementedError - return proxy + # Public helper methods. These can be accessed by the subclass test scripts. - def start_nodes(self, num_nodes, dirname, extra_args=None, rpchost=None, timewait=None, binary=None): - """Start multiple bitcoinds, return RPC connections to them""" + def add_nodes(self, num_nodes, extra_args=None, rpchost=None, timewait=None, binary=None): + """Instantiate TestNode objects""" if extra_args is None: - extra_args = [None] * num_nodes + extra_args = [[]] * num_nodes if binary is None: binary = [None] * num_nodes assert_equal(len(extra_args), num_nodes) assert_equal(len(binary), num_nodes) - rpcs = [] + for i in range(num_nodes): + self.nodes.append(TestNode(i, self.options.tmpdir, extra_args[i], rpchost, timewait=timewait, binary=binary[i], stderr=None, mocktime=self.mocktime, coverage_dir=self.options.coveragedir)) + + def start_node(self, i, extra_args=None, stderr=None): + """Start a bitcoind""" + + node = self.nodes[i] + + node.start(extra_args, stderr) + node.wait_for_rpc_connection() + + if self.options.coveragedir is not None: + coverage.write_all_rpc_commands(self.options.coveragedir, node.rpc) + + def start_nodes(self, extra_args=None): + """Start multiple bitcoinds""" + + if extra_args is None: + extra_args = [None] * self.num_nodes + assert_equal(len(extra_args), self.num_nodes) try: - for i in range(num_nodes): - rpcs.append(self.start_node(i, dirname, extra_args[i], rpchost, timewait=timewait, binary=binary[i])) + for i, node in enumerate(self.nodes): + node.start(extra_args[i]) + for node in self.nodes: + node.wait_for_rpc_connection() except: # If one node failed to start, stop the others - # TODO: abusing self.nodes in this way is a little hacky. - # Eventually we should do a better job of tracking nodes - self.nodes.extend(rpcs) self.stop_nodes() - self.nodes = [] raise - return rpcs + + if self.options.coveragedir is not None: + for node in self.nodes: + coverage.write_all_rpc_commands(self.options.coveragedir, node.rpc) def stop_node(self, i): """Stop a bitcoind test node""" - - self.log.debug("Stopping node %d" % i) - try: - self.nodes[i].stop() - except http.client.CannotSendRequest as e: - self.log.exception("Unable to stop node") - return_code = self.bitcoind_processes[i].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) - del self.bitcoind_processes[i] - assert_equal(return_code, 0) + self.nodes[i].stop_node() + self.nodes[i].wait_until_stopped() def stop_nodes(self): """Stop multiple bitcoind test nodes""" + for node in self.nodes: + # Issue RPC to stop nodes + node.stop_node() - for i in range(len(self.nodes)): - self.stop_node(i) - assert not self.bitcoind_processes.values() # All connections must be gone now + for node in self.nodes: + # Wait for nodes to stop + node.wait_until_stopped() - def assert_start_raises_init_error(self, i, dirname, extra_args=None, expected_msg=None): + def assert_start_raises_init_error(self, i, extra_args=None, expected_msg=None): with tempfile.SpooledTemporaryFile(max_size=2**16) as log_stderr: try: - self.start_node(i, dirname, extra_args, stderr=log_stderr) + self.start_node(i, extra_args, stderr=log_stderr) self.stop_node(i) except Exception as e: assert 'bitcoind exited' in str(e) # node must have shutdown + self.nodes[i].running = False + self.nodes[i].process = None if expected_msg is not None: log_stderr.seek(0) stderr = log_stderr.read().decode('utf-8') @@ -292,7 +295,7 @@ class BitcoinTestFramework(object): raise AssertionError(assert_msg) def wait_for_node_exit(self, i, timeout): - self.bitcoind_processes[i].wait(timeout) + self.nodes[i].process.wait(timeout) def split_network(self): """ @@ -362,16 +365,16 @@ class BitcoinTestFramework(object): rpc_handler.setLevel(logging.DEBUG) rpc_logger.addHandler(rpc_handler) - def _initialize_chain(self, test_dir, num_nodes, cachedir): + def _initialize_chain(self): """Initialize a pre-mined blockchain for use by the test. Create a cache of a 200-block-long chain (with wallet) for MAX_NODES Afterward, create num_nodes copies from the cache.""" - assert num_nodes <= MAX_NODES + assert self.num_nodes <= MAX_NODES create_cache = False for i in range(MAX_NODES): - if not os.path.isdir(os.path.join(cachedir, 'node' + str(i))): + if not os.path.isdir(os.path.join(self.options.cachedir, 'node' + str(i))): create_cache = True break @@ -380,27 +383,22 @@ class BitcoinTestFramework(object): # find and delete old cache directories if any exist for i in range(MAX_NODES): - if os.path.isdir(os.path.join(cachedir, "node" + str(i))): - shutil.rmtree(os.path.join(cachedir, "node" + str(i))) + if os.path.isdir(os.path.join(self.options.cachedir, "node" + str(i))): + shutil.rmtree(os.path.join(self.options.cachedir, "node" + str(i))) # Create cache directories, run bitcoinds: for i in range(MAX_NODES): - datadir = initialize_datadir(cachedir, i) + datadir = initialize_datadir(self.options.cachedir, i) args = [os.getenv("BITCOIND", "bitcoind"), "-server", "-keypool=1", "-datadir=" + datadir, "-discover=0"] if i > 0: args.append("-connect=127.0.0.1:" + str(p2p_port(0))) - self.bitcoind_processes[i] = subprocess.Popen(args) - self.log.debug("initialize_chain: bitcoind started, waiting for RPC to come up") - self._wait_for_bitcoind_start(self.bitcoind_processes[i], datadir, i) - self.log.debug("initialize_chain: RPC successfully started") + self.nodes.append(TestNode(i, self.options.cachedir, extra_args=[], rpchost=None, timewait=None, binary=None, stderr=None, mocktime=self.mocktime, coverage_dir=None)) + self.nodes[i].args = args + self.start_node(i) - self.nodes = [] - for i in range(MAX_NODES): - try: - self.nodes.append(get_rpc_proxy(rpc_url(get_datadir_path(cachedir, i), i), i)) - except: - self.log.exception("Error connecting to node %d" % i) - sys.exit(1) + # Wait for RPC connections to be ready + for node in self.nodes: + node.wait_for_rpc_connection() # Create a 200-block-long chain; each of the 4 first nodes # gets 25 mature blocks and 25 immature. @@ -425,48 +423,24 @@ class BitcoinTestFramework(object): self.nodes = [] self.disable_mocktime() for i in range(MAX_NODES): - os.remove(log_filename(cachedir, i, "debug.log")) - os.remove(log_filename(cachedir, i, "db.log")) - os.remove(log_filename(cachedir, i, "peers.dat")) - os.remove(log_filename(cachedir, i, "fee_estimates.dat")) - - for i in range(num_nodes): - from_dir = os.path.join(cachedir, "node" + str(i)) - to_dir = os.path.join(test_dir, "node" + str(i)) + os.remove(log_filename(self.options.cachedir, i, "debug.log")) + os.remove(log_filename(self.options.cachedir, i, "db.log")) + os.remove(log_filename(self.options.cachedir, i, "peers.dat")) + os.remove(log_filename(self.options.cachedir, i, "fee_estimates.dat")) + + for i in range(self.num_nodes): + from_dir = os.path.join(self.options.cachedir, "node" + str(i)) + to_dir = os.path.join(self.options.tmpdir, "node" + str(i)) shutil.copytree(from_dir, to_dir) - initialize_datadir(test_dir, i) # Overwrite port/rpcport in bitcoin.conf + initialize_datadir(self.options.tmpdir, i) # Overwrite port/rpcport in bitcoin.conf - def _initialize_chain_clean(self, test_dir, num_nodes): + def _initialize_chain_clean(self): """Initialize empty blockchain for use by the test. Create an empty blockchain and num_nodes wallets. Useful if a test case wants complete control over initialization.""" - for i in range(num_nodes): - initialize_datadir(test_dir, i) - - def _wait_for_bitcoind_start(self, process, datadir, i, rpchost=None): - """Wait for bitcoind to start. - - This means that RPC is accessible and fully initialized. - Raise an exception if bitcoind exits during initialization.""" - while True: - if process.poll() is not None: - raise Exception('bitcoind exited with status %i during initialization' % process.returncode) - try: - # Check if .cookie file to be created - rpc = get_rpc_proxy(rpc_url(datadir, i, rpchost), i, coveragedir=self.options.coveragedir) - rpc.getblockcount() - break # break out of loop on success - except IOError as e: - if e.errno != errno.ECONNREFUSED: # Port not yet open? - raise # unknown IO error - except JSONRPCException as e: # Initialization phase - if e.error['code'] != -28: # RPC in warmup? - raise # unknown JSON RPC exception - except ValueError as e: # cookie file not found and no rpcuser or rpcassword. bitcoind still starting - if "No RPC credentials" not in str(e): - raise - time.sleep(0.25) + for i in range(self.num_nodes): + initialize_datadir(self.options.tmpdir, i) class ComparisonTestFramework(BitcoinTestFramework): """Test framework for doing p2p comparison testing @@ -476,8 +450,7 @@ class ComparisonTestFramework(BitcoinTestFramework): - 2 binaries: 1 test binary, 1 ref binary - n>2 binaries: 1 test binary, n-1 ref binaries""" - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True @@ -490,13 +463,13 @@ class ComparisonTestFramework(BitcoinTestFramework): help="bitcoind binary to use for reference nodes (if any)") def setup_network(self): - extra_args = [['-whitelist=127.0.0.1']]*self.num_nodes + extra_args = [['-whitelist=127.0.0.1']] * self.num_nodes if hasattr(self, "extra_args"): extra_args = self.extra_args - self.nodes = self.start_nodes( - self.num_nodes, self.options.tmpdir, extra_args, - binary=[self.options.testbinary] + - [self.options.refbinary] * (self.num_nodes - 1)) + self.add_nodes(self.num_nodes, extra_args, + binary=[self.options.testbinary] + + [self.options.refbinary] * (self.num_nodes - 1)) + self.start_nodes() class SkipTest(Exception): """This exception is raised to skip a test""" diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py new file mode 100755 index 0000000000..12dab57a02 --- /dev/null +++ b/test/functional/test_framework/test_node.py @@ -0,0 +1,190 @@ +#!/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. +"""Class for bitcoind node under test""" + +import decimal +import errno +import http.client +import json +import logging +import os +import subprocess +import time + +from .util import ( + assert_equal, + get_rpc_proxy, + rpc_url, + wait_until, +) +from .authproxy import JSONRPCException + +BITCOIND_PROC_WAIT_TIMEOUT = 60 + +class TestNode(): + """A class for representing a bitcoind node under test. + + This class contains: + + - state about the node (whether it's running, etc) + - a Python subprocess.Popen object representing the running process + - an RPC connection to the node + + To make things easier for the test writer, a bit of magic is happening under the covers. + Any unrecognised messages will be dispatched to the RPC connection.""" + + def __init__(self, i, dirname, extra_args, rpchost, timewait, binary, stderr, mocktime, coverage_dir): + self.index = i + self.datadir = os.path.join(dirname, "node" + str(i)) + self.rpchost = rpchost + if timewait: + self.rpc_timeout = timewait + else: + # Wait for up to 60 seconds for the RPC server to respond + self.rpc_timeout = 60 + if binary is None: + self.binary = os.getenv("BITCOIND", "bitcoind") + else: + self.binary = binary + self.stderr = stderr + self.coverage_dir = coverage_dir + # Most callers will just need to add extra args to the standard list below. For those callers that need more flexibity, they can just set the args property directly. + self.extra_args = extra_args + 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.running = False + self.process = None + self.rpc_connected = False + self.rpc = None + self.url = None + self.log = logging.getLogger('TestFramework.node%d' % i) + + def __getattr__(self, *args, **kwargs): + """Dispatches any unrecognised messages to the RPC connection.""" + assert self.rpc_connected and self.rpc is not None, "Error: no RPC connection" + return self.rpc.__getattr__(*args, **kwargs) + + def start(self, extra_args=None, stderr=None): + """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.running = True + self.log.debug("bitcoind started, waiting for RPC to come up") + + def wait_for_rpc_connection(self): + """Sets up an RPC connection to the bitcoind process. Returns False if unable to connect.""" + # Poll at a rate of four times per second + poll_per_s = 4 + for _ in range(poll_per_s * self.rpc_timeout): + assert self.process.poll() is None, "bitcoind exited with status %i during initialization" % self.process.returncode + try: + self.rpc = get_rpc_proxy(rpc_url(self.datadir, self.index, self.rpchost), self.index, timeout=self.rpc_timeout, coveragedir=self.coverage_dir) + self.rpc.getblockcount() + # If the call to getblockcount() succeeds then the RPC connection is up + self.rpc_connected = True + self.url = self.rpc.url + self.log.debug("RPC successfully started") + return + except IOError as e: + if e.errno != errno.ECONNREFUSED: # Port not yet open? + raise # unknown IO error + except JSONRPCException as e: # Initialization phase + if e.error['code'] != -28: # RPC in warmup? + raise # unknown JSON RPC exception + except ValueError as e: # cookie file not found and no rpcuser or rpcassword. bitcoind still starting + if "No RPC credentials" not in str(e): + raise + time.sleep(1.0 / poll_per_s) + 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 + + def stop_node(self): + """Stop the node.""" + if not self.running: + return + self.log.debug("Stopping node") + try: + self.stop() + except http.client.CannotSendRequest: + self.log.exception("Unable to stop node.") + + def is_node_stopped(self): + """Checks whether the node has stopped. + + Returns True if the node has stopped. False otherwise. + This method is responsible for freeing resources (self.process).""" + if not self.running: + return True + return_code = self.process.poll() + if return_code is None: + return False + + # process has stopped. Assert that it didn't return an error code. + assert_equal(return_code, 0) + self.running = False + self.process = None + self.rpc_connected = False + self.rpc = None + self.log.debug("Node stopped") + return True + + def wait_until_stopped(self, timeout=BITCOIND_PROC_WAIT_TIMEOUT): + wait_until(self.is_node_stopped, timeout=timeout) + + def node_encrypt_wallet(self, passphrase): + """"Encrypts the wallet. + + This causes bitcoind to shutdown, so this method takes + care of cleaning up resources.""" + self.encryptwallet(passphrase) + self.wait_until_stopped() + +class TestNodeCLI(): + """Interface to bitcoin-cli for an individual node""" + + def __init__(self, binary, datadir): + self.args = [] + self.binary = binary + self.datadir = datadir + self.input = None + + def __call__(self, *args, input=None): + # TestNodeCLI is callable with bitcoin-cli command-line args + self.args = [str(arg) for arg in args] + self.input = input + return self + + def __getattr__(self, command): + def dispatcher(*args, **kwargs): + return self.send_cli(command, *args, **kwargs) + return dispatcher + + def send_cli(self, command, *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 + if named_args: + p_args += ["-named"] + p_args += [command] + pos_args + named_args + process = subprocess.Popen(p_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True) + cli_stdout, cli_stderr = process.communicate(input=self.input) + returncode = process.poll() + if returncode: + # Ignore cli_stdout, raise with cli_stderr + raise subprocess.CalledProcessError(returncode, self.binary, output=cli_stderr) + return json.loads(cli_stdout, parse_float=decimal.Decimal) diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index acca72aa86..64966adb97 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -7,11 +7,13 @@ from base64 import b64encode from binascii import hexlify, unhexlify from decimal import Decimal, ROUND_DOWN +import hashlib import json import logging import os import random import re +from subprocess import CalledProcessError import time from . import coverage @@ -57,18 +59,42 @@ def assert_raises_message(exc, message, fun, *args, **kwds): else: raise AssertionError("No exception raised") +def assert_raises_process_error(returncode, output, fun, *args, **kwds): + """Execute a process and asserts the process return code and output. + + Calls function `fun` with arguments `args` and `kwds`. Catches a CalledProcessError + and verifies that the return code and output are as expected. Throws AssertionError if + no CalledProcessError was raised or if the return code and output are not as expected. + + Args: + returncode (int): the process return code. + output (string): [a substring of] the process output. + fun (function): the function to call. This should execute a process. + args*: positional arguments for the function. + kwds**: named arguments for the function. + """ + try: + fun(*args, **kwds) + except CalledProcessError as e: + if returncode != e.returncode: + raise AssertionError("Unexpected returncode %i" % e.returncode) + if output not in e.output: + raise AssertionError("Expected substring not found:" + e.output) + else: + raise AssertionError("No exception raised") + def assert_raises_jsonrpc(code, message, fun, *args, **kwds): """Run an RPC and verify that a specific JSONRPC exception code and message is raised. Calls function `fun` with arguments `args` and `kwds`. Catches a JSONRPCException and verifies that the error code and message are as expected. Throws AssertionError if - no JSONRPCException was returned or if the error code/message are not as expected. + no JSONRPCException was raised or if the error code/message are not as expected. Args: code (int), optional: the error code returned by the RPC call (defined in src/rpc/protocol.h). Set to None if checking the error code is not required. message (string), optional: [a substring of] the error string returned by the - RPC call. Set to None if checking the error string is not required + RPC call. Set to None if checking the error string is not required. fun (function): the function to call. This should be the name of an RPC. args*: positional arguments for the function. kwds**: named arguments for the function. @@ -148,6 +174,13 @@ def count_bytes(hex_string): def bytes_to_hex_str(byte_str): return hexlify(byte_str).decode('ascii') +def hash256(byte_str): + sha256 = hashlib.sha256() + sha256.update(byte_str) + sha256d = hashlib.sha256() + sha256d.update(sha256.digest()) + return sha256d.digest()[::-1] + def hex_str_to_bytes(hex_str): return unhexlify(hex_str.encode('ascii')) @@ -157,6 +190,28 @@ def str_to_b64str(string): def satoshi_round(amount): return Decimal(amount).quantize(Decimal('0.00000001'), rounding=ROUND_DOWN) +def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=None): + if attempts == float('inf') and timeout == float('inf'): + timeout = 60 + attempt = 0 + timeout += time.time() + + while attempt < attempts and time.time() < timeout: + if lock: + with lock: + if predicate(): + return + else: + if predicate(): + return + attempt += 1 + time.sleep(0.05) + + # Print the cause of the timeout + assert_greater_than(attempts, attempt) + assert_greater_than(timeout, time.time()) + raise RuntimeError('Unreachable') + # RPC/P2P connection constants and functions ############################################ @@ -204,7 +259,7 @@ def rpc_port(n): return PORT_MIN + PORT_RANGE + n + (MAX_NODES * PortSeed.n) % (PORT_RANGE - 1 - MAX_NODES) def rpc_url(datadir, i, rpchost=None): - rpc_u, rpc_p = get_auth_cookie(datadir, i) + rpc_u, rpc_p = get_auth_cookie(datadir) host = '127.0.0.1' port = rpc_port(i) if rpchost: @@ -232,7 +287,7 @@ def initialize_datadir(dirname, n): def get_datadir_path(dirname, n): return os.path.join(dirname, "node" + str(n)) -def get_auth_cookie(datadir, n): +def get_auth_cookie(datadir): user = None password = None if os.path.isfile(os.path.join(datadir, "bitcoin.conf")): diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 93f180555d..fae4f66d70 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -81,6 +81,7 @@ BASE_SCRIPTS= [ # 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', @@ -279,6 +280,7 @@ def run_tests(test_list, src_dir, build_dir, exeext, tmpdir, jobs=1, enable_cove #Set env vars if "BITCOIND" not in os.environ: os.environ["BITCOIND"] = build_dir + '/src/bitcoind' + exeext + os.environ["BITCOINCLI"] = build_dir + '/src/bitcoin-cli' + exeext tests_dir = src_dir + '/test/functional/' diff --git a/test/functional/txn_clone.py b/test/functional/txn_clone.py index 9b81af96cf..740bb2d4c5 100755 --- a/test/functional/txn_clone.py +++ b/test/functional/txn_clone.py @@ -8,11 +8,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class TxnMallTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 4 - self.setup_clean_chain = False def add_options(self, parser): parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true", diff --git a/test/functional/txn_doublespend.py b/test/functional/txn_doublespend.py index 1bd3b3271c..69629ef951 100755 --- a/test/functional/txn_doublespend.py +++ b/test/functional/txn_doublespend.py @@ -8,11 +8,8 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class TxnMallTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 4 - self.setup_clean_chain = False def add_options(self, parser): parser.add_option("--mineblock", dest="mine_block", default=False, action="store_true", diff --git a/test/functional/uptime.py b/test/functional/uptime.py index b20d6f5cb6..78236b2393 100755 --- a/test/functional/uptime.py +++ b/test/functional/uptime.py @@ -13,9 +13,7 @@ from test_framework.test_framework import BitcoinTestFramework class UptimeTest(BitcoinTestFramework): - def __init__(self): - super().__init__() - + def set_test_params(self): self.num_nodes = 1 self.setup_clean_chain = True diff --git a/test/functional/wallet-accounts.py b/test/functional/wallet-accounts.py index 158aa9ae89..40726d2a76 100755 --- a/test/functional/wallet-accounts.py +++ b/test/functional/wallet-accounts.py @@ -17,9 +17,7 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal class WalletAccountsTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 self.extra_args = [[]] diff --git a/test/functional/wallet-dump.py b/test/functional/wallet-dump.py index 569cc46e6c..39b7164d6e 100755 --- a/test/functional/wallet-dump.py +++ b/test/functional/wallet-dump.py @@ -56,10 +56,7 @@ def read_dump(file_name, addrs, hd_master_addr_old): class WalletDumpTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = False + def set_test_params(self): self.num_nodes = 1 self.extra_args = [["-keypool=90"]] @@ -68,7 +65,8 @@ class WalletDumpTest(BitcoinTestFramework): # longer than the default 30 seconds due to an expensive # CWallet::TopUpKeyPool call, and the encryptwallet RPC made later in # the test often takes even longer. - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, self.extra_args, timewait=60) + self.add_nodes(self.num_nodes, self.extra_args, timewait=60) + self.start_nodes() def run_test (self): tmpdir = self.options.tmpdir @@ -94,9 +92,8 @@ class WalletDumpTest(BitcoinTestFramework): assert_equal(found_addr_rsv, 90*2) # 90 keys plus 100% internal keys #encrypt wallet, restart, unlock and dump - self.nodes[0].encryptwallet('test') - self.bitcoind_processes[0].wait() - self.nodes[0] = self.start_node(0, self.options.tmpdir, self.extra_args[0]) + self.nodes[0].node_encrypt_wallet('test') + self.start_node(0) self.nodes[0].walletpassphrase('test', 10) # Should be a no-op: self.nodes[0].keypoolrefill() diff --git a/test/functional/wallet-encryption.py b/test/functional/wallet-encryption.py index ba72918fe1..ce1e7744e9 100755 --- a/test/functional/wallet-encryption.py +++ b/test/functional/wallet-encryption.py @@ -6,16 +6,14 @@ import time -from test_framework.test_framework import BitcoinTestFramework, BITCOIND_PROC_WAIT_TIMEOUT +from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, assert_raises_jsonrpc, ) class WalletEncryptionTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 1 @@ -30,9 +28,8 @@ class WalletEncryptionTest(BitcoinTestFramework): assert_equal(len(privkey), 52) # Encrypt the wallet - self.nodes[0].encryptwallet(passphrase) - self.bitcoind_processes[0].wait(timeout=BITCOIND_PROC_WAIT_TIMEOUT) - self.nodes[0] = self.start_node(0, self.options.tmpdir) + self.nodes[0].node_encrypt_wallet(passphrase) + self.start_node(0) # Test that the wallet is encrypted assert_raises_jsonrpc(-13, "Please enter the wallet passphrase with walletpassphrase first", self.nodes[0].dumpprivkey, address) diff --git a/test/functional/wallet-hd.py b/test/functional/wallet-hd.py index 821575ed19..68b5e5b8d4 100755 --- a/test/functional/wallet-hd.py +++ b/test/functional/wallet-hd.py @@ -11,11 +11,8 @@ from test_framework.util import ( ) import shutil - class WalletHDTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 self.extra_args = [['-usehd=0'], ['-usehd=1', '-keypool=0']] @@ -25,8 +22,8 @@ class WalletHDTest(BitcoinTestFramework): # Make sure can't switch off usehd after wallet creation self.stop_node(1) - self.assert_start_raises_init_error(1, self.options.tmpdir, ['-usehd=0'], 'already existing HD wallet') - self.nodes[1] = self.start_node(1, self.options.tmpdir, self.extra_args[1]) + self.assert_start_raises_init_error(1, ['-usehd=0'], 'already existing HD wallet') + self.start_node(1) connect_nodes_bi(self.nodes, 0, 1) # Make sure we use hd, keep masterkeyid @@ -76,7 +73,7 @@ class WalletHDTest(BitcoinTestFramework): shutil.rmtree(tmpdir + "/node1/regtest/blocks") shutil.rmtree(tmpdir + "/node1/regtest/chainstate") shutil.copyfile(tmpdir + "/hd.bak", tmpdir + "/node1/regtest/wallet.dat") - self.nodes[1] = self.start_node(1, self.options.tmpdir, self.extra_args[1]) + self.start_node(1) # Assert that derivation is deterministic hd_add_2 = None @@ -91,7 +88,7 @@ class WalletHDTest(BitcoinTestFramework): # Needs rescan self.stop_node(1) - self.nodes[1] = self.start_node(1, self.options.tmpdir, self.extra_args[1] + ['-rescan']) + self.start_node(1, extra_args=self.extra_args[1] + ['-rescan']) assert_equal(self.nodes[1].getbalance(), num_hd_adds + 1) # send a tx and make sure its using the internal chain for the changeoutput diff --git a/test/functional/wallet.py b/test/functional/wallet.py index 3e3e8fcddb..de3aa8a875 100755 --- a/test/functional/wallet.py +++ b/test/functional/wallet.py @@ -7,28 +7,28 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class WalletTest(BitcoinTestFramework): - - def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size): - """Return curr_balance after asserting the fee was in range""" - fee = balance_with_fee - curr_balance - assert_fee_amount(fee, tx_size, fee_per_byte * 1000) - return curr_balance - - def __init__(self): - super().__init__() - self.setup_clean_chain = True + def set_test_params(self): self.num_nodes = 4 + self.setup_clean_chain = True self.extra_args = [['-usehd={:d}'.format(i%2==0)] for i in range(4)] def setup_network(self): - self.nodes = self.start_nodes(3, self.options.tmpdir, self.extra_args[:3]) + self.add_nodes(4, self.extra_args) + self.start_node(0) + self.start_node(1) + self.start_node(2) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) - self.sync_all() + self.sync_all([self.nodes[0:3]]) - def run_test(self): + def check_fee_amount(self, curr_balance, balance_with_fee, fee_per_byte, tx_size): + """Return curr_balance after asserting the fee was in range""" + fee = balance_with_fee - curr_balance + assert_fee_amount(fee, tx_size, fee_per_byte * 1000) + return curr_balance + def run_test(self): # Check that there's no UTXO on none of the nodes assert_equal(len(self.nodes[0].listunspent()), 0) assert_equal(len(self.nodes[1].listunspent()), 0) @@ -42,9 +42,9 @@ class WalletTest(BitcoinTestFramework): assert_equal(walletinfo['immature_balance'], 50) assert_equal(walletinfo['balance'], 0) - self.sync_all() + self.sync_all([self.nodes[0:3]]) self.nodes[1].generate(101) - self.sync_all() + self.sync_all([self.nodes[0:3]]) assert_equal(self.nodes[0].getbalance(), 50) assert_equal(self.nodes[1].getbalance(), 50) @@ -56,6 +56,15 @@ class WalletTest(BitcoinTestFramework): assert_equal(len(self.nodes[1].listunspent()), 1) assert_equal(len(self.nodes[2].listunspent()), 0) + self.log.info("test gettxout") + confirmed_txid, confirmed_index = utxos[0]["txid"], utxos[0]["vout"] + # First, outputs that are unspent both in the chain and in the + # mempool should appear with or without include_mempool + txout = self.nodes[0].gettxout(txid=confirmed_txid, n=confirmed_index, include_mempool=False) + assert_equal(txout['value'], 50) + txout = self.nodes[0].gettxout(txid=confirmed_txid, n=confirmed_index, include_mempool=True) + assert_equal(txout['value'], 50) + # Send 21 BTC from 0 to 2 using sendtoaddress call. # Locked memory should use at least 32 bytes to sign each transaction self.log.info("test getmemoryinfo") @@ -65,10 +74,9 @@ class WalletTest(BitcoinTestFramework): memory_after = self.nodes[0].getmemoryinfo() assert(memory_before['locked']['used'] + 64 <= memory_after['locked']['used']) - self.log.info("test gettxout") + self.log.info("test gettxout (second part)") # utxo spent in mempool should be visible if you exclude mempool # but invisible if you include mempool - confirmed_txid, confirmed_index = utxos[0]["txid"], utxos[0]["vout"] txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, False) assert_equal(txout['value'], 50) txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, True) @@ -88,7 +96,7 @@ class WalletTest(BitcoinTestFramework): # Have node0 mine a block, thus it will collect its own fee. self.nodes[0].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) # Exercise locking of unspent outputs unspent_0 = self.nodes[2].listunspent()[0] @@ -101,7 +109,7 @@ class WalletTest(BitcoinTestFramework): # Have node1 generate 100 blocks (so node0 can recover the fee) self.nodes[1].generate(100) - self.sync_all() + self.sync_all([self.nodes[0:3]]) # node0 should end up with 100 btc in block rewards plus fees, but # minus the 21 plus fees sent to node2 @@ -130,7 +138,7 @@ class WalletTest(BitcoinTestFramework): # Have node1 mine a block to confirm transactions: self.nodes[1].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) assert_equal(self.nodes[0].getbalance(), 0) assert_equal(self.nodes[2].getbalance(), 94) @@ -142,14 +150,14 @@ class WalletTest(BitcoinTestFramework): self.nodes[2].settxfee(fee_per_byte * 1000) txid = self.nodes[2].sendtoaddress(address, 10, "", "", False) self.nodes[2].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), Decimal('84'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) assert_equal(self.nodes[0].getbalance(), Decimal('10')) # Send 10 BTC with subtract fee from amount txid = self.nodes[2].sendtoaddress(address, 10, "", "", True) self.nodes[2].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) node_2_bal -= Decimal('10') assert_equal(self.nodes[2].getbalance(), node_2_bal) node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), Decimal('20'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) @@ -157,7 +165,7 @@ class WalletTest(BitcoinTestFramework): # Sendmany 10 BTC txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", []) self.nodes[2].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) node_0_bal += Decimal('10') node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) assert_equal(self.nodes[0].getbalance(), node_0_bal) @@ -165,7 +173,7 @@ class WalletTest(BitcoinTestFramework): # Sendmany 10 BTC with subtract fee from amount txid = self.nodes[2].sendmany('from1', {address: 10}, 0, "", [address]) self.nodes[2].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) node_2_bal -= Decimal('10') assert_equal(self.nodes[2].getbalance(), node_2_bal) node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, count_bytes(self.nodes[2].getrawtransaction(txid))) @@ -176,9 +184,9 @@ class WalletTest(BitcoinTestFramework): # EXPECT: nodes[3] should have those transactions in its mempool. txid1 = self.nodes[0].sendtoaddress(self.nodes[1].getnewaddress(), 1) txid2 = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1) - sync_mempools(self.nodes) + sync_mempools(self.nodes[0:2]) - self.nodes.append(self.start_node(3, self.options.tmpdir, self.extra_args[3])) + self.start_node(3) connect_nodes_bi(self.nodes, 0, 3) sync_blocks(self.nodes) @@ -222,22 +230,24 @@ class WalletTest(BitcoinTestFramework): #do some -walletbroadcast tests self.stop_nodes() - self.nodes = self.start_nodes(3, self.options.tmpdir, [["-walletbroadcast=0"],["-walletbroadcast=0"],["-walletbroadcast=0"]]) + self.start_node(0, ["-walletbroadcast=0"]) + self.start_node(1, ["-walletbroadcast=0"]) + self.start_node(2, ["-walletbroadcast=0"]) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) - self.sync_all() + self.sync_all([self.nodes[0:3]]) txIdNotBroadcasted = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 2) txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted) self.nodes[1].generate(1) #mine a block, tx should not be in there - self.sync_all() + self.sync_all([self.nodes[0:3]]) assert_equal(self.nodes[2].getbalance(), node_2_bal) #should not be changed because tx was not broadcasted #now broadcast from another node, mine a block, sync, and check the balance self.nodes[1].sendrawtransaction(txObjNotBroadcasted['hex']) self.nodes[1].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) node_2_bal += 2 txObjNotBroadcasted = self.nodes[0].gettransaction(txIdNotBroadcasted) assert_equal(self.nodes[2].getbalance(), node_2_bal) @@ -247,14 +257,16 @@ class WalletTest(BitcoinTestFramework): #restart the nodes with -walletbroadcast=1 self.stop_nodes() - self.nodes = self.start_nodes(3, self.options.tmpdir) + self.start_node(0) + self.start_node(1) + self.start_node(2) connect_nodes_bi(self.nodes,0,1) connect_nodes_bi(self.nodes,1,2) connect_nodes_bi(self.nodes,0,2) - sync_blocks(self.nodes) + sync_blocks(self.nodes[0:3]) self.nodes[0].generate(1) - sync_blocks(self.nodes) + sync_blocks(self.nodes[0:3]) node_2_bal += 2 #tx should be added to balance because after restarting the nodes tx should be broadcastet @@ -285,7 +297,7 @@ class WalletTest(BitcoinTestFramework): address_to_import = self.nodes[2].getnewaddress() txid = self.nodes[0].sendtoaddress(address_to_import, 1) self.nodes[0].generate(1) - self.sync_all() + self.sync_all([self.nodes[0:3]]) # 2. Import address from node2 to node1 self.nodes[1].importaddress(address_to_import) @@ -311,15 +323,15 @@ class WalletTest(BitcoinTestFramework): cbAddr = self.nodes[1].getnewaddress() blkHash = self.nodes[0].generatetoaddress(1, cbAddr)[0] cbTxId = self.nodes[0].getblock(blkHash)['tx'][0] - self.sync_all() + self.sync_all([self.nodes[0:3]]) # Check that the txid and balance is found by node1 self.nodes[1].gettransaction(cbTxId) # check if wallet or blockchain maintenance changes the balance - self.sync_all() + self.sync_all([self.nodes[0:3]]) blocks = self.nodes[0].generate(2) - self.sync_all() + self.sync_all([self.nodes[0:3]]) balance_nodes = [self.nodes[i].getbalance() for i in range(3)] block_count = self.nodes[0].getblockcount() @@ -350,7 +362,9 @@ class WalletTest(BitcoinTestFramework): self.log.info("check " + m) self.stop_nodes() # set lower ancestor limit for later - self.nodes = self.start_nodes(3, self.options.tmpdir, [[m, "-limitancestorcount="+str(chainlimit)]] * 3) + self.start_node(0, [m, "-limitancestorcount="+str(chainlimit)]) + self.start_node(1, [m, "-limitancestorcount="+str(chainlimit)]) + self.start_node(2, [m, "-limitancestorcount="+str(chainlimit)]) while m == '-reindex' and [block_count] * 3 != [self.nodes[i].getblockcount() for i in range(3)]: # reindex will leave rpc warm up "early"; Wait for it to finish time.sleep(0.1) @@ -398,7 +412,7 @@ class WalletTest(BitcoinTestFramework): # Try with walletrejectlongchains # Double chain limit but require combining inputs, so we pass SelectCoinsMinConf self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-walletrejectlongchains", "-limitancestorcount="+str(2*chainlimit)]) + self.start_node(0, extra_args=["-walletrejectlongchains", "-limitancestorcount="+str(2*chainlimit)]) # wait for loadmempool timeout = 10 diff --git a/test/functional/walletbackup.py b/test/functional/walletbackup.py index ff51cba4b3..15ea26afa1 100755 --- a/test/functional/walletbackup.py +++ b/test/functional/walletbackup.py @@ -37,11 +37,9 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import * class WalletBackupTest(BitcoinTestFramework): - - def __init__(self): - super().__init__() - self.setup_clean_chain = True + def set_test_params(self): self.num_nodes = 4 + self.setup_clean_chain = True # nodes 1, 2,3 are spenders, let's give them a keypool=100 self.extra_args = [["-keypool=100"], ["-keypool=100"], ["-keypool=100"], []] @@ -78,9 +76,9 @@ class WalletBackupTest(BitcoinTestFramework): # As above, this mirrors the original bash test. def start_three(self): - self.nodes[0] = self.start_node(0, self.options.tmpdir) - self.nodes[1] = self.start_node(1, self.options.tmpdir) - self.nodes[2] = self.start_node(2, self.options.tmpdir) + self.start_node(0) + self.start_node(1) + self.start_node(2) connect_nodes(self.nodes[0], 3) connect_nodes(self.nodes[1], 3) connect_nodes(self.nodes[2], 3) diff --git a/test/functional/zapwallettxes.py b/test/functional/zapwallettxes.py index af867d7a52..83b11035c8 100755 --- a/test/functional/zapwallettxes.py +++ b/test/functional/zapwallettxes.py @@ -15,14 +15,14 @@ been zapped. """ from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import (assert_equal, - assert_raises_jsonrpc, - ) +from test_framework.util import ( + assert_equal, + assert_raises_jsonrpc, + wait_until, +) class ZapWalletTXesTest (BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 @@ -48,7 +48,7 @@ class ZapWalletTXesTest (BitcoinTestFramework): # Stop-start node0. Both confirmed and unconfirmed transactions remain in the wallet. self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir) + self.start_node(0) assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1) assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2) @@ -56,7 +56,9 @@ class ZapWalletTXesTest (BitcoinTestFramework): # Stop node0 and restart with zapwallettxes and persistmempool. The unconfirmed # transaction is zapped from the wallet, but is re-added when the mempool is reloaded. self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-persistmempool=1", "-zapwallettxes=2"]) + self.start_node(0, ["-persistmempool=1", "-zapwallettxes=2"]) + + wait_until(lambda: self.nodes[0].getmempoolinfo()['size'] == 1, timeout=3) assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1) assert_equal(self.nodes[0].gettransaction(txid2)['txid'], txid2) @@ -64,7 +66,7 @@ class ZapWalletTXesTest (BitcoinTestFramework): # Stop node0 and restart with zapwallettxes, but not persistmempool. # The unconfirmed transaction is zapped and is no longer in the wallet. self.stop_node(0) - self.nodes[0] = self.start_node(0, self.options.tmpdir, ["-zapwallettxes=2"]) + self.start_node(0, ["-zapwallettxes=2"]) # tx1 is still be available because it was confirmed assert_equal(self.nodes[0].gettransaction(txid1)['txid'], txid1) diff --git a/test/functional/zmq_test.py b/test/functional/zmq_test.py index 26c946d215..382ef5bae2 100755 --- a/test/functional/zmq_test.py +++ b/test/functional/zmq_test.py @@ -10,12 +10,11 @@ import struct from test_framework.test_framework import BitcoinTestFramework, SkipTest from test_framework.util import (assert_equal, bytes_to_hex_str, - ) + hash256, + ) class ZMQTest (BitcoinTestFramework): - - def __init__(self): - super().__init__() + def set_test_params(self): self.num_nodes = 2 def setup_nodes(self): @@ -39,10 +38,14 @@ class ZMQTest (BitcoinTestFramework): self.zmqSubSocket.set(zmq.RCVTIMEO, 60000) self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashblock") self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtx") + self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawblock") + self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"rawtx") ip_address = "tcp://127.0.0.1:28332" self.zmqSubSocket.connect(ip_address) - extra_args = [['-zmqpubhashtx=%s' % ip_address, '-zmqpubhashblock=%s' % ip_address], []] - self.nodes = self.start_nodes(self.num_nodes, self.options.tmpdir, extra_args) + self.extra_args = [['-zmqpubhashblock=%s' % ip_address, '-zmqpubhashtx=%s' % ip_address, + '-zmqpubrawblock=%s' % ip_address, '-zmqpubrawtx=%s' % ip_address], []] + self.add_nodes(self.num_nodes, self.extra_args) + self.start_nodes() def run_test(self): try: @@ -60,28 +63,51 @@ class ZMQTest (BitcoinTestFramework): msg = self.zmqSubSocket.recv_multipart() topic = msg[0] assert_equal(topic, b"hashtx") - body = msg[1] + txhash = msg[1] msgSequence = struct.unpack('<I', msg[-1])[-1] assert_equal(msgSequence, 0) # must be sequence 0 on hashtx + # rawtx + msg = self.zmqSubSocket.recv_multipart() + topic = msg[0] + assert_equal(topic, b"rawtx") + body = msg[1] + msgSequence = struct.unpack('<I', msg[-1])[-1] + assert_equal(msgSequence, 0) # must be sequence 0 on rawtx + + # Check that the rawtx hashes to the hashtx + assert_equal(hash256(body), txhash) + self.log.info("Wait for block") msg = self.zmqSubSocket.recv_multipart() topic = msg[0] + assert_equal(topic, b"hashblock") body = msg[1] msgSequence = struct.unpack('<I', msg[-1])[-1] assert_equal(msgSequence, 0) # must be sequence 0 on hashblock blkhash = bytes_to_hex_str(body) - assert_equal(genhashes[0], blkhash) # blockhash from generate must be equal to the hash received over zmq + # rawblock + msg = self.zmqSubSocket.recv_multipart() + topic = msg[0] + assert_equal(topic, b"rawblock") + body = msg[1] + msgSequence = struct.unpack('<I', msg[-1])[-1] + assert_equal(msgSequence, 0) #must be sequence 0 on rawblock + + # Check the hash of the rawblock's header matches generate + assert_equal(genhashes[0], bytes_to_hex_str(hash256(body[:80]))) + self.log.info("Generate 10 blocks (and 10 coinbase txes)") n = 10 genhashes = self.nodes[1].generate(n) self.sync_all() zmqHashes = [] + zmqRawHashed = [] blockcount = 0 - for x in range(n * 2): + for x in range(n * 4): msg = self.zmqSubSocket.recv_multipart() topic = msg[0] body = msg[1] @@ -90,9 +116,14 @@ class ZMQTest (BitcoinTestFramework): msgSequence = struct.unpack('<I', msg[-1])[-1] assert_equal(msgSequence, blockcount + 1) blockcount += 1 + if topic == b"rawblock": + zmqRawHashed.append(bytes_to_hex_str(hash256(body[:80]))) + msgSequence = struct.unpack('<I', msg[-1])[-1] + assert_equal(msgSequence, blockcount) for x in range(n): assert_equal(genhashes[x], zmqHashes[x]) # blockhash from generate must be equal to the hash received over zmq + assert_equal(genhashes[x], zmqRawHashed[x]) self.log.info("Wait for tx from second node") # test tx from a second node @@ -102,13 +133,21 @@ class ZMQTest (BitcoinTestFramework): # now we should receive a zmq msg because the tx was broadcast msg = self.zmqSubSocket.recv_multipart() topic = msg[0] - body = msg[1] assert_equal(topic, b"hashtx") + body = msg[1] hashZMQ = bytes_to_hex_str(body) msgSequence = struct.unpack('<I', msg[-1])[-1] assert_equal(msgSequence, blockcount + 1) + msg = self.zmqSubSocket.recv_multipart() + topic = msg[0] + assert_equal(topic, b"rawtx") + body = msg[1] + hashedZMQ = bytes_to_hex_str(hash256(body)) + msgSequence = struct.unpack('<I', msg[-1])[-1] + assert_equal(msgSequence, blockcount+1) assert_equal(hashRPC, hashZMQ) # txid from sendtoaddress must be equal to the hash received over zmq + assert_equal(hashRPC, hashedZMQ) if __name__ == '__main__': ZMQTest().main() |