Skip to content

Commit 018b60c

Browse files
committed
test_framework: detect failure of bitcoind startup
Replace the `bitcoin-cli -rpcwait` after spawning bitcoind with our own loop that detects when bitcoind exits prematurely. And if one node fails to start, stop the others. This prevents a hang in such a case (see bitcoin#7463).
1 parent 0b98dd7 commit 018b60c

File tree

1 file changed

+40
-20
lines changed

1 file changed

+40
-20
lines changed

qa/rpc-tests/test_framework/util.py

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import subprocess
1717
import time
1818
import re
19+
import errno
1920

2021
from . import coverage
2122
from .authproxy import AuthServiceProxy, JSONRPCException
@@ -130,11 +131,33 @@ def initialize_datadir(dirname, n):
130131
f.write("listenonion=0\n")
131132
return datadir
132133

134+
def rpc_url(i, rpchost=None):
135+
return "http://rt:rt@%s:%d" % (rpchost or '127.0.0.1', rpc_port(i))
136+
137+
def wait_for_bitcoind_start(process, url, i):
138+
'''
139+
Wait for bitcoind to start. This means that RPC is accessible and fully initialized.
140+
Raise an exception if bitcoind exits during initialization.
141+
'''
142+
while True:
143+
if process.poll() is not None:
144+
raise Exception('bitcoind exited with status %i during initialization' % process.returncode)
145+
try:
146+
rpc = get_rpc_proxy(url, i)
147+
blocks = rpc.getblockcount()
148+
break # break out of loop on success
149+
except IOError as e:
150+
if e.errno != errno.ECONNREFUSED: # Port not yet open?
151+
raise # unknown IO error
152+
except JSONRPCException as e: # Initialization phase
153+
if e.error['code'] != -28: # RPC in warmup?
154+
raise # unkown JSON RPC exception
155+
time.sleep(0.25)
156+
133157
def initialize_chain(test_dir):
134158
"""
135159
Create (or copy from cache) a 200-block-long chain and
136160
4 wallets.
137-
bitcoind and bitcoin-cli must be in search path.
138161
"""
139162

140163
if (not os.path.isdir(os.path.join("cache","node0"))
@@ -147,7 +170,6 @@ def initialize_chain(test_dir):
147170
if os.path.isdir(os.path.join("cache","node"+str(i))):
148171
shutil.rmtree(os.path.join("cache","node"+str(i)))
149172

150-
devnull = open(os.devnull, "w")
151173
# Create cache directories, run bitcoinds:
152174
for i in range(4):
153175
datadir=initialize_datadir("cache", i)
@@ -156,19 +178,15 @@ def initialize_chain(test_dir):
156178
args.append("-connect=127.0.0.1:"+str(p2p_port(0)))
157179
bitcoind_processes[i] = subprocess.Popen(args)
158180
if os.getenv("PYTHON_DEBUG", ""):
159-
print "initialize_chain: bitcoind started, calling bitcoin-cli -rpcwait getblockcount"
160-
subprocess.check_call([ os.getenv("BITCOINCLI", "bitcoin-cli"), "-datadir="+datadir,
161-
"-rpcwait", "getblockcount"], stdout=devnull)
181+
print "initialize_chain: bitcoind started, waiting for RPC to come up"
182+
wait_for_bitcoind_start(bitcoind_processes[i], rpc_url(i), i)
162183
if os.getenv("PYTHON_DEBUG", ""):
163-
print "initialize_chain: bitcoin-cli -rpcwait getblockcount completed"
164-
devnull.close()
184+
print "initialize_chain: RPC succesfully started"
165185

166186
rpcs = []
167-
168187
for i in range(4):
169188
try:
170-
url = "http://rt:[email protected]:%d" % (rpc_port(i),)
171-
rpcs.append(get_rpc_proxy(url, i))
189+
rpcs.append(get_rpc_proxy(rpc_url(i), i))
172190
except:
173191
sys.stderr.write("Error connecting to "+url+"\n")
174192
sys.exit(1)
@@ -243,17 +261,12 @@ def start_node(i, dirname, extra_args=None, rpchost=None, timewait=None, binary=
243261
args = [ binary, "-datadir="+datadir, "-server", "-keypool=1", "-discover=0", "-rest", "-mocktime="+str(get_mocktime()) ]
244262
if extra_args is not None: args.extend(extra_args)
245263
bitcoind_processes[i] = subprocess.Popen(args)
246-
devnull = open(os.devnull, "w")
247264
if os.getenv("PYTHON_DEBUG", ""):
248-
print "start_node: bitcoind started, calling bitcoin-cli -rpcwait getblockcount"
249-
subprocess.check_call([ os.getenv("BITCOINCLI", "bitcoin-cli"), "-datadir="+datadir] +
250-
_rpchost_to_args(rpchost) +
251-
["-rpcwait", "getblockcount"], stdout=devnull)
265+
print "start_node: bitcoind started, waiting for RPC to come up"
266+
url = rpc_url(i, rpchost)
267+
wait_for_bitcoind_start(bitcoind_processes[i], url, i)
252268
if os.getenv("PYTHON_DEBUG", ""):
253-
print "start_node: calling bitcoin-cli -rpcwait getblockcount returned"
254-
devnull.close()
255-
url = "http://rt:rt@%s:%d" % (rpchost or '127.0.0.1', rpc_port(i))
256-
269+
print "start_node: RPC succesfully started"
257270
proxy = get_rpc_proxy(url, i, timeout=timewait)
258271

259272
if COVERAGE_DIR:
@@ -267,7 +280,14 @@ def start_nodes(num_nodes, dirname, extra_args=None, rpchost=None, binary=None):
267280
"""
268281
if extra_args is None: extra_args = [ None for i in range(num_nodes) ]
269282
if binary is None: binary = [ None for i in range(num_nodes) ]
270-
return [ start_node(i, dirname, extra_args[i], rpchost, binary=binary[i]) for i in range(num_nodes) ]
283+
rpcs = []
284+
try:
285+
for i in range(num_nodes):
286+
rpcs.append(start_node(i, dirname, extra_args[i], rpchost, binary=binary[i]))
287+
except: # If one node failed to start, stop the others
288+
stop_nodes(rpcs)
289+
raise
290+
return rpcs
271291

272292
def log_filename(dirname, n_node, logname):
273293
return os.path.join(dirname, "node"+str(n_node), "regtest", logname)

0 commit comments

Comments
 (0)