55"""Base class for RPC testing."""
66
77from collections import deque
8- import errno
98from enum import Enum
10- import http .client
119import logging
1210import optparse
1311import os
1412import shutil
15- import subprocess
1613import sys
1714import tempfile
1815import time
1916import traceback
2017
2118from .authproxy import JSONRPCException
2219from . import coverage
20+ from .test_node import TestNode
2321from .util import (
2422 MAX_NODES ,
2523 PortSeed ,
2624 assert_equal ,
2725 check_json_precision ,
2826 connect_nodes_bi ,
2927 disconnect_nodes ,
30- get_rpc_proxy ,
3128 initialize_datadir ,
32- get_datadir_path ,
3329 log_filename ,
3430 p2p_port ,
35- rpc_url ,
3631 set_node_times ,
3732 sync_blocks ,
3833 sync_mempools ,
@@ -69,7 +64,6 @@ def __init__(self):
6964 self .num_nodes = 4
7065 self .setup_clean_chain = False
7166 self .nodes = []
72- self .bitcoind_processes = {}
7367 self .mocktime = 0
7468
7569 def add_options (self , parser ):
@@ -206,64 +200,62 @@ def main(self):
206200 def start_node (self , i , dirname , extra_args = None , rpchost = None , timewait = None , binary = None , stderr = None ):
207201 """Start a bitcoind and return RPC connection to it"""
208202
209- datadir = os .path .join (dirname , "node" + str (i ))
203+ if extra_args is None :
204+ extra_args = []
210205 if binary is None :
211206 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 ()
220210
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 )
223213
224- return proxy
214+ return node
225215
226216 def start_nodes (self , num_nodes , dirname , extra_args = None , rpchost = None , timewait = None , binary = None ):
227217 """Start multiple bitcoinds, return RPC connections to them"""
228218
229219 if extra_args is None :
230- extra_args = [None ] * num_nodes
220+ extra_args = [[] ] * num_nodes
231221 if binary is None :
232222 binary = [None ] * num_nodes
233223 assert_equal (len (extra_args ), num_nodes )
234224 assert_equal (len (binary ), num_nodes )
235- rpcs = []
225+ nodes = []
236226 try :
237227 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 ()
239232 except :
240233 # 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 )
244234 self .stop_nodes ()
245- self .nodes = []
246235 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
248242
249243 def stop_node (self , i ):
250244 """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 )
260248
261249 def stop_nodes (self ):
262250 """Stop multiple bitcoind test nodes"""
251+ for node in self .nodes :
252+ # Issue RPC to stop nodes
253+ node .stop_node ()
263254
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 )
267259
268260 def assert_start_raises_init_error (self , i , dirname , extra_args = None , expected_msg = None ):
269261 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
272264 self .stop_node (i )
273265 except Exception as e :
274266 assert 'bitcoind exited' in str (e ) # node must have shutdown
267+ self .nodes [i ].running = False
268+ self .nodes [i ].process = None
275269 if expected_msg is not None :
276270 log_stderr .seek (0 )
277271 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
285279 raise AssertionError (assert_msg )
286280
287281 def wait_for_node_exit (self , i , timeout ):
288- self .bitcoind_processes [i ].wait (timeout )
282+ self .nodes [i ]. process .wait (timeout )
289283
290284 def split_network (self ):
291285 """
@@ -382,18 +376,13 @@ def _initialize_chain(self, test_dir, num_nodes, cachedir):
382376 args = [os .getenv ("BITCOIND" , "bitcoind" ), "-server" , "-keypool=1" , "-datadir=" + datadir , "-discover=0" ]
383377 if i > 0 :
384378 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 ()
389382
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 ()
397386
398387 # Create a 200-block-long chain; each of the 4 first nodes
399388 # gets 25 mature blocks and 25 immature.
@@ -437,30 +426,6 @@ def _initialize_chain_clean(self, test_dir, num_nodes):
437426 for i in range (num_nodes ):
438427 initialize_datadir (test_dir , i )
439428
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-
464429class ComparisonTestFramework (BitcoinTestFramework ):
465430 """Test framework for doing p2p comparison testing
466431
0 commit comments