8
8
import logging
9
9
import optparse
10
10
import os
11
- import sys
12
11
import shutil
12
+ import subprocess
13
+ import sys
13
14
import tempfile
14
15
import time
15
16
16
17
from .util import (
17
- initialize_chain ,
18
- start_nodes ,
18
+ PortSeed ,
19
+ MAX_NODES ,
20
+ bitcoind_processes ,
21
+ check_json_precision ,
19
22
connect_nodes_bi ,
23
+ disable_mocktime ,
20
24
disconnect_nodes ,
25
+ enable_coverage ,
26
+ enable_mocktime ,
27
+ get_mocktime ,
28
+ get_rpc_proxy ,
29
+ initialize_datadir ,
30
+ log_filename ,
31
+ p2p_port ,
32
+ rpc_url ,
33
+ set_node_times ,
34
+ start_node ,
35
+ start_nodes ,
36
+ stop_node ,
37
+ stop_nodes ,
21
38
sync_blocks ,
22
39
sync_mempools ,
23
- stop_nodes ,
24
- stop_node ,
25
- enable_coverage ,
26
- check_json_precision ,
27
- initialize_chain_clean ,
28
- PortSeed ,
40
+ wait_for_bitcoind_start ,
29
41
)
30
42
from .authproxy import JSONRPCException
31
43
32
44
class BitcoinTestFramework (object ):
45
+ """Base class for a bitcoin test script.
46
+
47
+ Individual bitcoin test scripts should subclass this class and override the following methods:
48
+
49
+ - __init__()
50
+ - add_options()
51
+ - setup_chain()
52
+ - setup_network()
53
+ - run_test()
54
+
55
+ The main() method should not be overridden.
56
+
57
+ This class also contains various public and private helper methods."""
58
+
59
+ # Methods to override in subclass test scripts.
33
60
34
61
TEST_EXIT_PASSED = 0
35
62
TEST_EXIT_FAILED = 1
@@ -40,27 +67,15 @@ def __init__(self):
40
67
self .setup_clean_chain = False
41
68
self .nodes = None
42
69
43
- def run_test (self ):
44
- raise NotImplementedError
45
-
46
70
def add_options (self , parser ):
47
71
pass
48
72
49
73
def setup_chain (self ):
50
74
self .log .info ("Initializing test directory " + self .options .tmpdir )
51
75
if self .setup_clean_chain :
52
- initialize_chain_clean (self .options .tmpdir , self .num_nodes )
76
+ self . _initialize_chain_clean (self .options .tmpdir , self .num_nodes )
53
77
else :
54
- initialize_chain (self .options .tmpdir , self .num_nodes , self .options .cachedir )
55
-
56
- def stop_node (self , num_node ):
57
- stop_node (self .nodes [num_node ], num_node )
58
-
59
- def setup_nodes (self ):
60
- extra_args = None
61
- if hasattr (self , "extra_args" ):
62
- extra_args = self .extra_args
63
- self .nodes = start_nodes (self .num_nodes , self .options .tmpdir , extra_args )
78
+ self ._initialize_chain (self .options .tmpdir , self .num_nodes , self .options .cachedir )
64
79
65
80
def setup_network (self ):
66
81
self .setup_nodes ()
@@ -72,27 +87,16 @@ def setup_network(self):
72
87
connect_nodes_bi (self .nodes , i , i + 1 )
73
88
self .sync_all ()
74
89
75
- def split_network (self ):
76
- """
77
- Split the network of four nodes into nodes 0/1 and 2/3.
78
- """
79
- disconnect_nodes (self .nodes [1 ], 2 )
80
- disconnect_nodes (self .nodes [2 ], 1 )
81
- self .sync_all ([self .nodes [:2 ], self .nodes [2 :]])
82
-
83
- def sync_all (self , node_groups = None ):
84
- if not node_groups :
85
- node_groups = [self .nodes ]
90
+ def setup_nodes (self ):
91
+ extra_args = None
92
+ if hasattr (self , "extra_args" ):
93
+ extra_args = self .extra_args
94
+ self .nodes = start_nodes (self .num_nodes , self .options .tmpdir , extra_args )
86
95
87
- [ sync_blocks ( group ) for group in node_groups ]
88
- [ sync_mempools ( group ) for group in node_groups ]
96
+ def run_test ( self ):
97
+ raise NotImplementedError
89
98
90
- def join_network (self ):
91
- """
92
- Join the (previously split) network halves together.
93
- """
94
- connect_nodes_bi (self .nodes , 1 , 2 )
95
- self .sync_all ()
99
+ # Main function. This should not be overridden by the subclass test scripts.
96
100
97
101
def main (self ):
98
102
@@ -156,7 +160,7 @@ def main(self):
156
160
157
161
if not self .options .noshutdown :
158
162
self .log .info ("Stopping nodes" )
159
- stop_nodes ( self .nodes )
163
+ self .stop_nodes ( )
160
164
else :
161
165
self .log .info ("Note: bitcoinds were not stopped and may still be running" )
162
166
@@ -190,6 +194,45 @@ def main(self):
190
194
logging .shutdown ()
191
195
sys .exit (self .TEST_EXIT_FAILED )
192
196
197
+ # Public helper methods. These can be accessed by the subclass test scripts.
198
+
199
+ def start_node (self , i , dirname , extra_args = None , rpchost = None , timewait = None , binary = None , stderr = None ):
200
+ return start_node (i , dirname , extra_args , rpchost , timewait , binary , stderr )
201
+
202
+ def start_nodes (self , num_nodes , dirname , extra_args = None , rpchost = None , timewait = None , binary = None ):
203
+ return start_nodes (num_nodes , dirname , extra_args , rpchost , timewait , binary )
204
+
205
+ def stop_node (self , num_node ):
206
+ stop_node (self .nodes [num_node ], num_node )
207
+
208
+ def stop_nodes (self ):
209
+ stop_nodes (self .nodes )
210
+
211
+ def split_network (self ):
212
+ """
213
+ Split the network of four nodes into nodes 0/1 and 2/3.
214
+ """
215
+ disconnect_nodes (self .nodes [1 ], 2 )
216
+ disconnect_nodes (self .nodes [2 ], 1 )
217
+ self .sync_all ([self .nodes [:2 ], self .nodes [2 :]])
218
+
219
+ def join_network (self ):
220
+ """
221
+ Join the (previously split) network halves together.
222
+ """
223
+ connect_nodes_bi (self .nodes , 1 , 2 )
224
+ self .sync_all ()
225
+
226
+ def sync_all (self , node_groups = None ):
227
+ if not node_groups :
228
+ node_groups = [self .nodes ]
229
+
230
+ for group in node_groups :
231
+ sync_blocks (group )
232
+ sync_mempools (group )
233
+
234
+ # Private helper methods. These should not be accessed by the subclass test scripts.
235
+
193
236
def _start_logging (self ):
194
237
# Add logger and logging handlers
195
238
self .log = logging .getLogger ('TestFramework' )
@@ -218,6 +261,88 @@ def _start_logging(self):
218
261
rpc_handler .setLevel (logging .DEBUG )
219
262
rpc_logger .addHandler (rpc_handler )
220
263
264
+ def _initialize_chain (self , test_dir , num_nodes , cachedir ):
265
+ """Initialize a pre-mined blockchain for use by the test.
266
+
267
+ Create a cache of a 200-block-long chain (with wallet) for MAX_NODES
268
+ Afterward, create num_nodes copies from the cache."""
269
+
270
+ assert num_nodes <= MAX_NODES
271
+ create_cache = False
272
+ for i in range (MAX_NODES ):
273
+ if not os .path .isdir (os .path .join (cachedir , 'node' + str (i ))):
274
+ create_cache = True
275
+ break
276
+
277
+ if create_cache :
278
+ self .log .debug ("Creating data directories from cached datadir" )
279
+
280
+ # find and delete old cache directories if any exist
281
+ for i in range (MAX_NODES ):
282
+ if os .path .isdir (os .path .join (cachedir , "node" + str (i ))):
283
+ shutil .rmtree (os .path .join (cachedir , "node" + str (i )))
284
+
285
+ # Create cache directories, run bitcoinds:
286
+ for i in range (MAX_NODES ):
287
+ datadir = initialize_datadir (cachedir , i )
288
+ args = [os .getenv ("BITCOIND" , "bitcoind" ), "-server" , "-keypool=1" , "-datadir=" + datadir , "-discover=0" ]
289
+ if i > 0 :
290
+ args .append ("-connect=127.0.0.1:" + str (p2p_port (0 )))
291
+ bitcoind_processes [i ] = subprocess .Popen (args )
292
+ self .log .debug ("initialize_chain: bitcoind started, waiting for RPC to come up" )
293
+ wait_for_bitcoind_start (bitcoind_processes [i ], rpc_url (i ), i )
294
+ self .log .debug ("initialize_chain: RPC successfully started" )
295
+
296
+ self .nodes = []
297
+ for i in range (MAX_NODES ):
298
+ try :
299
+ self .nodes .append (get_rpc_proxy (rpc_url (i ), i ))
300
+ except :
301
+ self .log .exception ("Error connecting to node %d" % i )
302
+ sys .exit (1 )
303
+
304
+ # Create a 200-block-long chain; each of the 4 first nodes
305
+ # gets 25 mature blocks and 25 immature.
306
+ # Note: To preserve compatibility with older versions of
307
+ # initialize_chain, only 4 nodes will generate coins.
308
+ #
309
+ # blocks are created with timestamps 10 minutes apart
310
+ # starting from 2010 minutes in the past
311
+ enable_mocktime ()
312
+ block_time = get_mocktime () - (201 * 10 * 60 )
313
+ for i in range (2 ):
314
+ for peer in range (4 ):
315
+ for j in range (25 ):
316
+ set_node_times (self .nodes , block_time )
317
+ self .nodes [peer ].generate (1 )
318
+ block_time += 10 * 60
319
+ # Must sync before next peer starts generating blocks
320
+ sync_blocks (self .nodes )
321
+
322
+ # Shut them down, and clean up cache directories:
323
+ self .stop_nodes ()
324
+ self .nodes = []
325
+ disable_mocktime ()
326
+ for i in range (MAX_NODES ):
327
+ os .remove (log_filename (cachedir , i , "debug.log" ))
328
+ os .remove (log_filename (cachedir , i , "db.log" ))
329
+ os .remove (log_filename (cachedir , i , "peers.dat" ))
330
+ os .remove (log_filename (cachedir , i , "fee_estimates.dat" ))
331
+
332
+ for i in range (num_nodes ):
333
+ from_dir = os .path .join (cachedir , "node" + str (i ))
334
+ to_dir = os .path .join (test_dir , "node" + str (i ))
335
+ shutil .copytree (from_dir , to_dir )
336
+ initialize_datadir (test_dir , i ) # Overwrite port/rpcport in bitcoin.conf
337
+
338
+ def _initialize_chain_clean (self , test_dir , num_nodes ):
339
+ """Initialize empty blockchain for use by the test.
340
+
341
+ Create an empty blockchain and num_nodes wallets.
342
+ Useful if a test case wants complete control over initialization."""
343
+ for i in range (num_nodes ):
344
+ initialize_datadir (test_dir , i )
345
+
221
346
# Test framework for doing p2p comparison testing, which sets up some bitcoind
222
347
# binaries:
223
348
# 1 binary: test binary
@@ -240,7 +365,7 @@ def add_options(self, parser):
240
365
help = "bitcoind binary to use for reference nodes (if any)" )
241
366
242
367
def setup_network (self ):
243
- self .nodes = start_nodes (
368
+ self .nodes = self . start_nodes (
244
369
self .num_nodes , self .options .tmpdir ,
245
370
extra_args = [['-whitelist=127.0.0.1' ]] * self .num_nodes ,
246
371
binary = [self .options .testbinary ] +
0 commit comments