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 pdb
1513import shutil
16- import subprocess
1714import sys
1815import tempfile
1916import time
2017import traceback
2118
2219from .authproxy import JSONRPCException
2320from . import coverage
21+ from .test_node import TestNode
2422from .util import (
2523 MAX_NODES ,
2624 PortSeed ,
2725 assert_equal ,
2826 check_json_precision ,
2927 connect_nodes_bi ,
3028 disconnect_nodes ,
31- get_rpc_proxy ,
3229 initialize_datadir ,
33- get_datadir_path ,
3430 log_filename ,
3531 p2p_port ,
36- rpc_url ,
3732 set_node_times ,
3833 sync_blocks ,
3934 sync_mempools ,
@@ -70,7 +65,6 @@ def __init__(self):
7065 self .num_nodes = 4
7166 self .setup_clean_chain = False
7267 self .nodes = []
73- self .bitcoind_processes = {}
7468 self .mocktime = 0
7569
7670 def add_options (self , parser ):
@@ -213,64 +207,62 @@ def main(self):
213207 def start_node (self , i , dirname , extra_args = None , rpchost = None , timewait = None , binary = None , stderr = None ):
214208 """Start a bitcoind and return RPC connection to it"""
215209
216- datadir = os .path .join (dirname , "node" + str (i ))
210+ if extra_args is None :
211+ extra_args = []
217212 if binary is None :
218213 binary = os .getenv ("BITCOIND" , "bitcoind" )
219- args = [binary , "-datadir=" + datadir , "-server" , "-keypool=1" , "-discover=0" , "-rest" , "-logtimemicros" , "-debug" , "-debugexclude=libevent" , "-debugexclude=leveldb" , "-mocktime=" + str (self .mocktime ), "-uacomment=testnode%d" % i ]
220- if extra_args is not None :
221- args .extend (extra_args )
222- self .bitcoind_processes [i ] = subprocess .Popen (args , stderr = stderr )
223- self .log .debug ("initialize_chain: bitcoind started, waiting for RPC to come up" )
224- self ._wait_for_bitcoind_start (self .bitcoind_processes [i ], datadir , i , rpchost )
225- self .log .debug ("initialize_chain: RPC successfully started" )
226- proxy = get_rpc_proxy (rpc_url (datadir , i , rpchost ), i , timeout = timewait )
214+ node = TestNode (i , dirname , extra_args , rpchost , timewait , binary , stderr , self .mocktime , coverage_dir = self .options .coveragedir )
215+ node .start ()
216+ node .wait_for_rpc_connection ()
227217
228- if self .options .coveragedir :
229- coverage .write_all_rpc_commands (self .options .coveragedir , proxy )
218+ if self .options .coveragedir is not None :
219+ coverage .write_all_rpc_commands (self .options .coveragedir , node . rpc )
230220
231- return proxy
221+ return node
232222
233223 def start_nodes (self , num_nodes , dirname , extra_args = None , rpchost = None , timewait = None , binary = None ):
234224 """Start multiple bitcoinds, return RPC connections to them"""
235225
236226 if extra_args is None :
237- extra_args = [None ] * num_nodes
227+ extra_args = [[] ] * num_nodes
238228 if binary is None :
239229 binary = [None ] * num_nodes
240230 assert_equal (len (extra_args ), num_nodes )
241231 assert_equal (len (binary ), num_nodes )
242- rpcs = []
232+ nodes = []
243233 try :
244234 for i in range (num_nodes ):
245- rpcs .append (self .start_node (i , dirname , extra_args [i ], rpchost , timewait = timewait , binary = binary [i ]))
235+ nodes .append (TestNode (i , dirname , extra_args [i ], rpchost , timewait = timewait , binary = binary [i ], stderr = None , mocktime = self .mocktime , coverage_dir = self .options .coveragedir ))
236+ nodes [i ].start ()
237+ for node in nodes :
238+ node .wait_for_rpc_connection ()
246239 except :
247240 # If one node failed to start, stop the others
248- # TODO: abusing self.nodes in this way is a little hacky.
249- # Eventually we should do a better job of tracking nodes
250- self .nodes .extend (rpcs )
251241 self .stop_nodes ()
252- self .nodes = []
253242 raise
254- return rpcs
243+
244+ if self .options .coveragedir is not None :
245+ for node in nodes :
246+ coverage .write_all_rpc_commands (self .options .coveragedir , node .rpc )
247+
248+ return nodes
255249
256250 def stop_node (self , i ):
257251 """Stop a bitcoind test node"""
258-
259- self .log .debug ("Stopping node %d" % i )
260- try :
261- self .nodes [i ].stop ()
262- except http .client .CannotSendRequest as e :
263- self .log .exception ("Unable to stop node" )
264- return_code = self .bitcoind_processes [i ].wait (timeout = BITCOIND_PROC_WAIT_TIMEOUT )
265- del self .bitcoind_processes [i ]
266- assert_equal (return_code , 0 )
252+ self .nodes [i ].stop_node ()
253+ while not self .nodes [i ].is_node_stopped ():
254+ time .sleep (0.1 )
267255
268256 def stop_nodes (self ):
269257 """Stop multiple bitcoind test nodes"""
258+ for node in self .nodes :
259+ # Issue RPC to stop nodes
260+ node .stop_node ()
270261
271- for i in range (len (self .nodes )):
272- self .stop_node (i )
273- assert not self .bitcoind_processes .values () # All connections must be gone now
262+ for node in self .nodes :
263+ # Wait for nodes to stop
264+ while not node .is_node_stopped ():
265+ time .sleep (0.1 )
274266
275267 def assert_start_raises_init_error (self , i , dirname , extra_args = None , expected_msg = None ):
276268 with tempfile .SpooledTemporaryFile (max_size = 2 ** 16 ) as log_stderr :
@@ -279,6 +271,8 @@ def assert_start_raises_init_error(self, i, dirname, extra_args=None, expected_m
279271 self .stop_node (i )
280272 except Exception as e :
281273 assert 'bitcoind exited' in str (e ) # node must have shutdown
274+ self .nodes [i ].running = False
275+ self .nodes [i ].process = None
282276 if expected_msg is not None :
283277 log_stderr .seek (0 )
284278 stderr = log_stderr .read ().decode ('utf-8' )
@@ -292,7 +286,7 @@ def assert_start_raises_init_error(self, i, dirname, extra_args=None, expected_m
292286 raise AssertionError (assert_msg )
293287
294288 def wait_for_node_exit (self , i , timeout ):
295- self .bitcoind_processes [i ].wait (timeout )
289+ self .nodes [i ]. process .wait (timeout )
296290
297291 def split_network (self ):
298292 """
@@ -389,18 +383,13 @@ def _initialize_chain(self, test_dir, num_nodes, cachedir):
389383 args = [os .getenv ("BITCOIND" , "bitcoind" ), "-server" , "-keypool=1" , "-datadir=" + datadir , "-discover=0" ]
390384 if i > 0 :
391385 args .append ("-connect=127.0.0.1:" + str (p2p_port (0 )))
392- self .bitcoind_processes [i ] = subprocess .Popen (args )
393- self .log .debug ("initialize_chain: bitcoind started, waiting for RPC to come up" )
394- self ._wait_for_bitcoind_start (self .bitcoind_processes [i ], datadir , i )
395- self .log .debug ("initialize_chain: RPC successfully started" )
386+ self .nodes .append (TestNode (i , cachedir , extra_args = [], rpchost = None , timewait = None , binary = None , stderr = None , mocktime = self .mocktime , coverage_dir = None ))
387+ self .nodes [i ].args = args
388+ self .nodes [i ].start ()
396389
397- self .nodes = []
398- for i in range (MAX_NODES ):
399- try :
400- self .nodes .append (get_rpc_proxy (rpc_url (get_datadir_path (cachedir , i ), i ), i ))
401- except :
402- self .log .exception ("Error connecting to node %d" % i )
403- sys .exit (1 )
390+ # Wait for RPC connections to be ready
391+ for node in self .nodes :
392+ node .wait_for_rpc_connection ()
404393
405394 # Create a 200-block-long chain; each of the 4 first nodes
406395 # gets 25 mature blocks and 25 immature.
@@ -444,30 +433,6 @@ def _initialize_chain_clean(self, test_dir, num_nodes):
444433 for i in range (num_nodes ):
445434 initialize_datadir (test_dir , i )
446435
447- def _wait_for_bitcoind_start (self , process , datadir , i , rpchost = None ):
448- """Wait for bitcoind to start.
449-
450- This means that RPC is accessible and fully initialized.
451- Raise an exception if bitcoind exits during initialization."""
452- while True :
453- if process .poll () is not None :
454- raise Exception ('bitcoind exited with status %i during initialization' % process .returncode )
455- try :
456- # Check if .cookie file to be created
457- rpc = get_rpc_proxy (rpc_url (datadir , i , rpchost ), i , coveragedir = self .options .coveragedir )
458- rpc .getblockcount ()
459- break # break out of loop on success
460- except IOError as e :
461- if e .errno != errno .ECONNREFUSED : # Port not yet open?
462- raise # unknown IO error
463- except JSONRPCException as e : # Initialization phase
464- if e .error ['code' ] != - 28 : # RPC in warmup?
465- raise # unknown JSON RPC exception
466- except ValueError as e : # cookie file not found and no rpcuser or rpcassword. bitcoind still starting
467- if "No RPC credentials" not in str (e ):
468- raise
469- time .sleep (0.25 )
470-
471436class ComparisonTestFramework (BitcoinTestFramework ):
472437 """Test framework for doing p2p comparison testing
473438
0 commit comments