5
5
"""Base class for RPC testing."""
6
6
7
7
from collections import deque
8
- import errno
9
8
from enum import Enum
10
- import http .client
11
9
import logging
12
10
import optparse
13
11
import os
14
12
import shutil
15
- import subprocess
16
13
import sys
17
14
import tempfile
18
15
import time
19
16
import traceback
20
17
21
18
from .authproxy import JSONRPCException
22
19
from . import coverage
20
+ from .test_node import TestNode
23
21
from .util import (
24
22
MAX_NODES ,
25
23
PortSeed ,
26
24
assert_equal ,
27
25
check_json_precision ,
28
26
connect_nodes_bi ,
29
27
disconnect_nodes ,
30
- get_rpc_proxy ,
31
28
initialize_datadir ,
32
- get_datadir_path ,
33
29
log_filename ,
34
30
p2p_port ,
35
- rpc_url ,
36
31
set_node_times ,
37
32
sync_blocks ,
38
33
sync_mempools ,
@@ -69,7 +64,6 @@ def __init__(self):
69
64
self .num_nodes = 4
70
65
self .setup_clean_chain = False
71
66
self .nodes = []
72
- self .bitcoind_processes = {}
73
67
self .mocktime = 0
74
68
75
69
def add_options (self , parser ):
@@ -206,64 +200,62 @@ def main(self):
206
200
def start_node (self , i , dirname , extra_args = None , rpchost = None , timewait = None , binary = None , stderr = None ):
207
201
"""Start a bitcoind and return RPC connection to it"""
208
202
209
- datadir = os .path .join (dirname , "node" + str (i ))
203
+ if extra_args is None :
204
+ extra_args = []
210
205
if binary is None :
211
206
binary = os .getenv ("BITCOIND" , "bitcoind" )
212
- args = [binary , "-datadir=" + datadir , "-server" , "-keypool=1" , "-discover=0" , "-rest" , "-logtimemicros" , "-debug" , "-debugexclude=libevent" , "-debugexclude=leveldb" , "-mocktime=" + str (self .mocktime ), "-uacomment=testnode%d" % i ]
213
- if extra_args is not None :
214
- args .extend (extra_args )
215
- self .bitcoind_processes [i ] = subprocess .Popen (args , stderr = stderr )
216
- self .log .debug ("initialize_chain: bitcoind started, waiting for RPC to come up" )
217
- self ._wait_for_bitcoind_start (self .bitcoind_processes [i ], datadir , i , rpchost )
218
- self .log .debug ("initialize_chain: RPC successfully started" )
219
- proxy = get_rpc_proxy (rpc_url (datadir , i , rpchost ), i , timeout = timewait )
207
+ node = TestNode (i , dirname , extra_args , rpchost , timewait , binary , stderr , self .mocktime , coverage_dir = self .options .coveragedir )
208
+ node .start ()
209
+ node .wait_for_rpc_connection ()
220
210
221
- if self .options .coveragedir :
222
- coverage .write_all_rpc_commands (self .options .coveragedir , proxy )
211
+ if self .options .coveragedir is not None :
212
+ coverage .write_all_rpc_commands (self .options .coveragedir , node . rpc )
223
213
224
- return proxy
214
+ return node
225
215
226
216
def start_nodes (self , num_nodes , dirname , extra_args = None , rpchost = None , timewait = None , binary = None ):
227
217
"""Start multiple bitcoinds, return RPC connections to them"""
228
218
229
219
if extra_args is None :
230
- extra_args = [None ] * num_nodes
220
+ extra_args = [[] ] * num_nodes
231
221
if binary is None :
232
222
binary = [None ] * num_nodes
233
223
assert_equal (len (extra_args ), num_nodes )
234
224
assert_equal (len (binary ), num_nodes )
235
- rpcs = []
225
+ nodes = []
236
226
try :
237
227
for i in range (num_nodes ):
238
- rpcs .append (self .start_node (i , dirname , extra_args [i ], rpchost , timewait = timewait , binary = binary [i ]))
228
+ nodes .append (TestNode (i , dirname , extra_args [i ], rpchost , timewait = timewait , binary = binary [i ], stderr = None , mocktime = self .mocktime , coverage_dir = self .options .coveragedir ))
229
+ nodes [i ].start ()
230
+ for node in nodes :
231
+ node .wait_for_rpc_connection ()
239
232
except :
240
233
# If one node failed to start, stop the others
241
- # TODO: abusing self.nodes in this way is a little hacky.
242
- # Eventually we should do a better job of tracking nodes
243
- self .nodes .extend (rpcs )
244
234
self .stop_nodes ()
245
- self .nodes = []
246
235
raise
247
- return rpcs
236
+
237
+ if self .options .coveragedir is not None :
238
+ for node in nodes :
239
+ coverage .write_all_rpc_commands (self .options .coveragedir , node .rpc )
240
+
241
+ return nodes
248
242
249
243
def stop_node (self , i ):
250
244
"""Stop a bitcoind test node"""
251
-
252
- self .log .debug ("Stopping node %d" % i )
253
- try :
254
- self .nodes [i ].stop ()
255
- except http .client .CannotSendRequest as e :
256
- self .log .exception ("Unable to stop node" )
257
- return_code = self .bitcoind_processes [i ].wait (timeout = BITCOIND_PROC_WAIT_TIMEOUT )
258
- del self .bitcoind_processes [i ]
259
- assert_equal (return_code , 0 )
245
+ self .nodes [i ].stop_node ()
246
+ while not self .nodes [i ].is_node_stopped ():
247
+ time .sleep (0.1 )
260
248
261
249
def stop_nodes (self ):
262
250
"""Stop multiple bitcoind test nodes"""
251
+ for node in self .nodes :
252
+ # Issue RPC to stop nodes
253
+ node .stop_node ()
263
254
264
- for i in range (len (self .nodes )):
265
- self .stop_node (i )
266
- assert not self .bitcoind_processes .values () # All connections must be gone now
255
+ for node in self .nodes :
256
+ # Wait for nodes to stop
257
+ while not node .is_node_stopped ():
258
+ time .sleep (0.1 )
267
259
268
260
def assert_start_raises_init_error (self , i , dirname , extra_args = None , expected_msg = None ):
269
261
with tempfile .SpooledTemporaryFile (max_size = 2 ** 16 ) as log_stderr :
@@ -272,6 +264,8 @@ def assert_start_raises_init_error(self, i, dirname, extra_args=None, expected_m
272
264
self .stop_node (i )
273
265
except Exception as e :
274
266
assert 'bitcoind exited' in str (e ) # node must have shutdown
267
+ self .nodes [i ].running = False
268
+ self .nodes [i ].process = None
275
269
if expected_msg is not None :
276
270
log_stderr .seek (0 )
277
271
stderr = log_stderr .read ().decode ('utf-8' )
@@ -285,7 +279,7 @@ def assert_start_raises_init_error(self, i, dirname, extra_args=None, expected_m
285
279
raise AssertionError (assert_msg )
286
280
287
281
def wait_for_node_exit (self , i , timeout ):
288
- self .bitcoind_processes [i ].wait (timeout )
282
+ self .nodes [i ]. process .wait (timeout )
289
283
290
284
def split_network (self ):
291
285
"""
@@ -382,18 +376,13 @@ def _initialize_chain(self, test_dir, num_nodes, cachedir):
382
376
args = [os .getenv ("BITCOIND" , "bitcoind" ), "-server" , "-keypool=1" , "-datadir=" + datadir , "-discover=0" ]
383
377
if i > 0 :
384
378
args .append ("-connect=127.0.0.1:" + str (p2p_port (0 )))
385
- self .bitcoind_processes [i ] = subprocess .Popen (args )
386
- self .log .debug ("initialize_chain: bitcoind started, waiting for RPC to come up" )
387
- self ._wait_for_bitcoind_start (self .bitcoind_processes [i ], datadir , i )
388
- self .log .debug ("initialize_chain: RPC successfully started" )
379
+ self .nodes .append (TestNode (i , cachedir , extra_args = [], rpchost = None , timewait = None , binary = None , stderr = None , mocktime = self .mocktime , coverage_dir = None ))
380
+ self .nodes [i ].args = args
381
+ self .nodes [i ].start ()
389
382
390
- self .nodes = []
391
- for i in range (MAX_NODES ):
392
- try :
393
- self .nodes .append (get_rpc_proxy (rpc_url (get_datadir_path (cachedir , i ), i ), i ))
394
- except :
395
- self .log .exception ("Error connecting to node %d" % i )
396
- sys .exit (1 )
383
+ # Wait for RPC connections to be ready
384
+ for node in self .nodes :
385
+ node .wait_for_rpc_connection ()
397
386
398
387
# Create a 200-block-long chain; each of the 4 first nodes
399
388
# gets 25 mature blocks and 25 immature.
@@ -437,30 +426,6 @@ def _initialize_chain_clean(self, test_dir, num_nodes):
437
426
for i in range (num_nodes ):
438
427
initialize_datadir (test_dir , i )
439
428
440
- def _wait_for_bitcoind_start (self , process , datadir , i , rpchost = None ):
441
- """Wait for bitcoind to start.
442
-
443
- This means that RPC is accessible and fully initialized.
444
- Raise an exception if bitcoind exits during initialization."""
445
- while True :
446
- if process .poll () is not None :
447
- raise Exception ('bitcoind exited with status %i during initialization' % process .returncode )
448
- try :
449
- # Check if .cookie file to be created
450
- rpc = get_rpc_proxy (rpc_url (datadir , i , rpchost ), i , coveragedir = self .options .coveragedir )
451
- rpc .getblockcount ()
452
- break # break out of loop on success
453
- except IOError as e :
454
- if e .errno != errno .ECONNREFUSED : # Port not yet open?
455
- raise # unknown IO error
456
- except JSONRPCException as e : # Initialization phase
457
- if e .error ['code' ] != - 28 : # RPC in warmup?
458
- raise # unknown JSON RPC exception
459
- except ValueError as e : # cookie file not found and no rpcuser or rpcassword. bitcoind still starting
460
- if "No RPC credentials" not in str (e ):
461
- raise
462
- time .sleep (0.25 )
463
-
464
429
class ComparisonTestFramework (BitcoinTestFramework ):
465
430
"""Test framework for doing p2p comparison testing
466
431
0 commit comments