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 pdb
15
13
import shutil
16
- import subprocess
17
14
import sys
18
15
import tempfile
19
16
import time
20
17
import traceback
21
18
22
19
from .authproxy import JSONRPCException
23
20
from . import coverage
21
+ from .test_node import TestNode
24
22
from .util import (
25
23
MAX_NODES ,
26
24
PortSeed ,
27
25
assert_equal ,
28
26
check_json_precision ,
29
27
connect_nodes_bi ,
30
28
disconnect_nodes ,
31
- get_rpc_proxy ,
32
29
initialize_datadir ,
33
- get_datadir_path ,
34
30
log_filename ,
35
31
p2p_port ,
36
- rpc_url ,
37
32
set_node_times ,
38
33
sync_blocks ,
39
34
sync_mempools ,
@@ -70,7 +65,6 @@ def __init__(self):
70
65
self .num_nodes = 4
71
66
self .setup_clean_chain = False
72
67
self .nodes = []
73
- self .bitcoind_processes = {}
74
68
self .mocktime = 0
75
69
76
70
def add_options (self , parser ):
@@ -213,64 +207,62 @@ def main(self):
213
207
def start_node (self , i , dirname , extra_args = None , rpchost = None , timewait = None , binary = None , stderr = None ):
214
208
"""Start a bitcoind and return RPC connection to it"""
215
209
216
- datadir = os .path .join (dirname , "node" + str (i ))
210
+ if extra_args is None :
211
+ extra_args = []
217
212
if binary is None :
218
213
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 ()
227
217
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 )
230
220
231
- return proxy
221
+ return node
232
222
233
223
def start_nodes (self , num_nodes , dirname , extra_args = None , rpchost = None , timewait = None , binary = None ):
234
224
"""Start multiple bitcoinds, return RPC connections to them"""
235
225
236
226
if extra_args is None :
237
- extra_args = [None ] * num_nodes
227
+ extra_args = [[] ] * num_nodes
238
228
if binary is None :
239
229
binary = [None ] * num_nodes
240
230
assert_equal (len (extra_args ), num_nodes )
241
231
assert_equal (len (binary ), num_nodes )
242
- rpcs = []
232
+ nodes = []
243
233
try :
244
234
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 ()
246
239
except :
247
240
# 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 )
251
241
self .stop_nodes ()
252
- self .nodes = []
253
242
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
255
249
256
250
def stop_node (self , i ):
257
251
"""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 )
267
255
268
256
def stop_nodes (self ):
269
257
"""Stop multiple bitcoind test nodes"""
258
+ for node in self .nodes :
259
+ # Issue RPC to stop nodes
260
+ node .stop_node ()
270
261
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 )
274
266
275
267
def assert_start_raises_init_error (self , i , dirname , extra_args = None , expected_msg = None ):
276
268
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
279
271
self .stop_node (i )
280
272
except Exception as e :
281
273
assert 'bitcoind exited' in str (e ) # node must have shutdown
274
+ self .nodes [i ].running = False
275
+ self .nodes [i ].process = None
282
276
if expected_msg is not None :
283
277
log_stderr .seek (0 )
284
278
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
292
286
raise AssertionError (assert_msg )
293
287
294
288
def wait_for_node_exit (self , i , timeout ):
295
- self .bitcoind_processes [i ].wait (timeout )
289
+ self .nodes [i ]. process .wait (timeout )
296
290
297
291
def split_network (self ):
298
292
"""
@@ -389,18 +383,13 @@ def _initialize_chain(self, test_dir, num_nodes, cachedir):
389
383
args = [os .getenv ("BITCOIND" , "bitcoind" ), "-server" , "-keypool=1" , "-datadir=" + datadir , "-discover=0" ]
390
384
if i > 0 :
391
385
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 ()
396
389
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 ()
404
393
405
394
# Create a 200-block-long chain; each of the 4 first nodes
406
395
# gets 25 mature blocks and 25 immature.
@@ -444,30 +433,6 @@ def _initialize_chain_clean(self, test_dir, num_nodes):
444
433
for i in range (num_nodes ):
445
434
initialize_datadir (test_dir , i )
446
435
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
-
471
436
class ComparisonTestFramework (BitcoinTestFramework ):
472
437
"""Test framework for doing p2p comparison testing
473
438
0 commit comments