diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 534f460c02..ba112582dd 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -29,7 +29,6 @@ from .util import ( disconnect_nodes, get_datadir_path, initialize_datadir, - p2p_port, sync_blocks, sync_mempools, ) @@ -477,36 +476,24 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): def _initialize_chain(self): """Initialize a pre-mined blockchain for use by the test. - Create a cache of a 199-block-long chain (with wallet) for MAX_NODES + Create a cache of a 199-block-long chain Afterward, create num_nodes copies from the cache.""" + CACHE_NODE_ID = 0 # Use node 0 to create the cache for all other nodes + cache_node_dir = get_datadir_path(self.options.cachedir, CACHE_NODE_ID) assert self.num_nodes <= MAX_NODES - create_cache = False - for i in range(MAX_NODES): - if not os.path.isdir(get_datadir_path(self.options.cachedir, i)): - create_cache = True - break - if create_cache: - self.log.debug("Creating data directories from cached datadir") + if not os.path.isdir(cache_node_dir): + self.log.debug("Creating cache directory {}".format(cache_node_dir)) - # find and delete old cache directories if any exist - for i in range(MAX_NODES): - if os.path.isdir(get_datadir_path(self.options.cachedir, i)): - shutil.rmtree(get_datadir_path(self.options.cachedir, i)) - - # Create cache directories, run bitcoinds: - for i in range(MAX_NODES): - datadir = initialize_datadir(self.options.cachedir, i, self.chain) - args = [self.options.bitcoind, "-datadir=" + datadir, '-disablewallet'] - if i > 0: - args.append("-connect=127.0.0.1:" + str(p2p_port(0))) - self.nodes.append(TestNode( - i, - get_datadir_path(self.options.cachedir, i), + initialize_datadir(self.options.cachedir, CACHE_NODE_ID, self.chain) + self.nodes.append( + TestNode( + CACHE_NODE_ID, + cache_node_dir, self.chain, extra_conf=["bind=127.0.0.1"], - extra_args=[], + extra_args=['-disablewallet'], rpchost=None, timewait=self.rpc_timeout, bitcoind=self.options.bitcoind, @@ -514,12 +501,10 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): coverage_dir=None, cwd=self.options.tmpdir, )) - self.nodes[i].args = args - self.start_node(i) + self.start_node(CACHE_NODE_ID) # Wait for RPC connections to be ready - for node in self.nodes: - node.wait_for_rpc_connection() + self.nodes[CACHE_NODE_ID].wait_for_rpc_connection() # Create a 199-block-long chain; each of the 4 first nodes # gets 25 mature blocks and 25 immature. @@ -528,29 +513,29 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass): # This is needed so that we are out of IBD when the test starts, # see the tip age check in IsInitialBlockDownload(). for i in range(8): - self.nodes[0].generatetoaddress(25 if i != 7 else 24, self.nodes[i % 4].get_deterministic_priv_key().address) - self.sync_blocks() + self.nodes[CACHE_NODE_ID].generatetoaddress( + nblocks=25 if i != 7 else 24, + address=TestNode.PRIV_KEYS[i % 4].address, + ) - for n in self.nodes: - assert_equal(n.getblockchaininfo()["blocks"], 199) + assert_equal(self.nodes[CACHE_NODE_ID].getblockchaininfo()["blocks"], 199) - # Shut them down, and clean up cache directories: + # Shut it down, and clean up cache directories: self.stop_nodes() self.nodes = [] - def cache_path(n, *paths): - return os.path.join(get_datadir_path(self.options.cachedir, n), self.chain, *paths) + def cache_path(*paths): + return os.path.join(cache_node_dir, self.chain, *paths) - for i in range(MAX_NODES): - os.rmdir(cache_path(i, 'wallets')) # Remove empty wallets dir - for entry in os.listdir(cache_path(i)): - if entry not in ['chainstate', 'blocks']: - os.remove(cache_path(i, entry)) + os.rmdir(cache_path('wallets')) # Remove empty wallets dir + for entry in os.listdir(cache_path()): + if entry not in ['chainstate', 'blocks']: # Only keep chainstate and blocks folder + os.remove(cache_path(entry)) for i in range(self.num_nodes): - from_dir = get_datadir_path(self.options.cachedir, i) + self.log.debug("Copy cache directory {} to node {}".format(cache_node_dir, i)) to_dir = get_datadir_path(self.options.tmpdir, i) - shutil.copytree(from_dir, to_dir) + shutil.copytree(cache_node_dir, to_dir) initialize_datadir(self.options.tmpdir, i, self.chain) # Overwrite port/rpcport in bitcoin.conf def _initialize_chain_clean(self): diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index f0fb76bb13..47cd610b9a 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -23,6 +23,7 @@ import sys from .authproxy import JSONRPCException from .util import ( + MAX_NODES, append_config, delete_cookie_file, get_rpc_proxy, @@ -120,11 +121,8 @@ class TestNode(): AddressKeyPair = collections.namedtuple('AddressKeyPair', ['address', 'key']) self.deterministic_priv_key = AddressKeyPair(address, privkey) - def get_deterministic_priv_key(self): - """Return a deterministic priv key in base58, that only depends on the node's index""" - - AddressKeyPair = collections.namedtuple('AddressKeyPair', ['address', 'key']) - PRIV_KEYS = [ + AddressKeyPair = collections.namedtuple('AddressKeyPair', ['address', 'key']) + PRIV_KEYS = [ # address , privkey AddressKeyPair('2doncj41FX6LahE2aspuLuQcnmgrLtfvvma', 'cQ1PxVTn5J3qCzUqXzpNeHiPVxZcwwaJkFmZcokescXwmHWAdBoe'), AddressKeyPair('2dv4oTKjmi3TF6dRq2TmasMdNtTeuqHNcPb', 'cRuGzyZjb5zQQg1TbAiGK1UJBuK1UQHaFf4DXBUcPZNZ3WomNoxW'), @@ -135,14 +133,21 @@ class TestNode(): AddressKeyPair('2dxjmBn21SQjhffXzzi1hz5nKA4quM7jtsT', 'cVpzWr59KE7DsWaSJkySSPSvkrr6huUsjYBF3wcwCMhW4cEPNmU1'), AddressKeyPair('2dpFhgNeWqm7LVbvZo29bvXPEzwWTVfzVSY', 'cRPF2Kfm21BWf3GPHMKGGMQN1a6sNJPGsyYSz8VWQhsoVUr42q4r'), AddressKeyPair('2df8FzAJJtPcHsvXYRh4BTmnhSUUVE5zNhE', 'cU9yBKyGyRNBpzSmVavoh9szgaFUjKbPG9P3CPtycXAxmdwnKxiL'), - ] + AddressKeyPair('2N7XEbmyLeviPkfiTMEcnEFrdcwfrW2nypj', 'cSpjeQMdDAD6Kr6g4T4HwFX1jMoiMKw5iBjHUSNgVTEdiZbrSa8o'), + AddressKeyPair('2N5epYpqDw55w9HBpU97tNRUdwHXtFQ1kHs', 'cNEfBggwDGdf27hitveT75RicFtjeZX7pN4fsV3KjKEamScPzh63'), + AddressKeyPair('2N1hYBmCYcYNjUwiSAxricmwEgqpxhXeBHo', 'cMrjzHT5XvEYob52zEHLgpC2jbSTiRYRAJgPvFDdeQTMy7AM5L3N'), + ] + + def get_deterministic_priv_key(self): + """Return a deterministic priv key in base58, that only depends on the node's index""" + assert len(self.PRIV_KEYS) == MAX_NODES # ELEMENTS: this allows overriding the default for parent nodes in fedpeg test if self.deterministic_priv_key is not None: self.log.debug("Custom deterministic_priv_key: {}".format(self.deterministic_priv_key)) return self.deterministic_priv_key - return PRIV_KEYS[self.index] + return self.PRIV_KEYS[self.index] def get_mem_rss_kilobytes(self): """Get the memory usage (RSS) per `ps`. diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 77547018f3..945ddf91b6 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -241,7 +241,7 @@ def wait_until(predicate, *, attempts=float('inf'), timeout=float('inf'), lock=N ############################################ # The maximum number of nodes a single test can spawn -MAX_NODES = 8 +MAX_NODES = 12 # Don't assign rpc or p2p ports lower than this PORT_MIN = 11000 # The number of ports to "reserve" for p2p and rpc, each