diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index b5bd7a1bd..a5be29fd6 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -20,9 +20,10 @@ import shutil import subprocess import time import re +import errno from . import coverage -from .authproxy import AuthServiceProxy +from .authproxy import AuthServiceProxy, JSONRPCException COVERAGE_DIR = None PRE_BLOSSOM_BLOCK_TARGET_SPACING = 150 @@ -143,11 +144,30 @@ def initialize_datadir(dirname, n): def rpc_url(i, rpchost=None): return "http://rt:rt@%s:%d" % (rpchost or '127.0.0.1', rpc_port(i)) +def wait_for_bitcoind_start(process, url, i): + ''' + 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: + rpc = get_rpc_proxy(url, i) + 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 # unkown JSON RPC exception + time.sleep(0.25) + def initialize_chain(test_dir): """ Create (or copy from cache) a 200-block-long chain and 4 wallets. - bitcoind and bitcoin-cli must be in search path. """ # Due to the consensus change fix for the timejacking attack, we need to @@ -181,7 +201,6 @@ def initialize_chain(test_dir): if os.path.isdir(os.path.join("cache","node"+str(i))): shutil.rmtree(os.path.join("cache","node"+str(i))) - devnull = open(os.devnull, "w") # Create cache directories, run bitcoinds: for i in range(4): datadir=initialize_datadir("cache", i) @@ -194,21 +213,17 @@ def initialize_chain(test_dir): args.append("-connect=127.0.0.1:"+str(p2p_port(0))) bitcoind_processes[i] = subprocess.Popen(args) if os.getenv("PYTHON_DEBUG", ""): - print("initialize_chain: bitcoind started, calling bitcoin-cli -rpcwait getblockcount") - subprocess.check_call([ os.getenv("BITCOINCLI", "bitcoin-cli"), "-datadir="+datadir, - "-rpcwait", "getblockcount"], stdout=devnull) + print("initialize_chain: bitcoind started, waiting for RPC to come up") + wait_for_bitcoind_start(bitcoind_processes[i], rpc_url(i), i) if os.getenv("PYTHON_DEBUG", ""): - print("initialize_chain: bitcoin-cli -rpcwait getblockcount completed") - devnull.close() + print("initialize_chain: RPC succesfully started") rpcs = [] - for i in range(4): try: - url = "http://rt:rt@127.0.0.1:%d" % (rpc_port(i),) - rpcs.append(get_rpc_proxy(url, i)) + rpcs.append(get_rpc_proxy(rpc_url(i), i)) except: - sys.stderr.write("Error connecting to "+url+"\n") + sys.stderr.write("Error connecting to "+rpc_url(i)+"\n") sys.exit(1) # Create a 200-block-long chain; each of the 4 nodes @@ -286,17 +301,12 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary= ]) if extra_args is not None: args.extend(extra_args) bitcoind_processes[i] = subprocess.Popen(args) - devnull = open(os.devnull, "w") if os.getenv("PYTHON_DEBUG", ""): - print("start_node: bitcoind started, calling bitcoin-cli -rpcwait getblockcount") - subprocess.check_call([ os.getenv("BITCOINCLI", "bitcoin-cli"), "-datadir="+datadir] + - _rpchost_to_args(rpchost) + - ["-rpcwait", "getblockcount"], stdout=devnull) + print("start_node: bitcoind started, waiting for RPC to come up") + url = rpc_url(i, rpchost) + wait_for_bitcoind_start(bitcoind_processes[i], url, i) if os.getenv("PYTHON_DEBUG", ""): - print("start_node: calling bitcoin-cli -rpcwait getblockcount returned") - devnull.close() - url = "http://rt:rt@%s:%d" % (rpchost or '127.0.0.1', rpc_port(i)) - + print("start_node: RPC succesfully started") proxy = get_rpc_proxy(url, i, timeout=timewait) if COVERAGE_DIR: @@ -310,7 +320,14 @@ def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None): """ if extra_args is None: extra_args = [ None for i in range(num_nodes) ] if binary is None: binary = [ None for i in range(num_nodes) ] - return [ start_node(i, dirname, extra_args[i], rpchost, binary=binary[i]) for i in range(num_nodes) ] + rpcs = [] + try: + for i in range(num_nodes): + rpcs.append(start_node(i, dirname, extra_args[i], rpchost, binary=binary[i])) + except: # If one node failed to start, stop the others + stop_nodes(rpcs) + raise + return rpcs def log_filename(dirname, n_node, logname): return os.path.join(dirname, "node"+str(n_node), "regtest", logname)