2016-03-19 20:58:06 +01:00
#!/usr/bin/env python3
2019-02-21 02:03:13 +01:00
# Copyright (c) 2014-2019 The Bitcoin Core developers
2014-10-23 03:48:19 +02:00
# Distributed under the MIT software license, see the accompanying
2014-07-08 18:07:23 +02:00
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-01-18 00:34:40 +01:00
""" Base class for RPC testing. """
2014-07-08 18:07:23 +02:00
2018-04-19 14:38:59 +02:00
import configparser
2017-05-18 22:36:39 +02:00
from enum import Enum
2016-05-09 19:55:49 +02:00
import logging
2018-08-11 22:24:14 +02:00
import argparse
2014-07-08 18:07:23 +02:00
import os
2017-04-01 04:44:04 +02:00
import pdb
2019-05-06 19:17:04 +02:00
import random
2014-07-08 18:07:23 +02:00
import shutil
2017-03-23 21:49:02 +01:00
import sys
2014-07-08 18:07:23 +02:00
import tempfile
2017-03-21 15:05:59 +01:00
import time
2014-07-08 18:07:23 +02:00
2017-06-02 19:14:14 +02:00
from . authproxy import JSONRPCException
from . import coverage
2017-06-02 20:30:36 +02:00
from . test_node import TestNode
2018-06-18 23:28:37 +02:00
from . mininode import NetworkThread
2015-10-11 07:41:19 +02:00
from . util import (
2017-03-23 21:49:02 +01:00
MAX_NODES ,
2017-06-02 19:14:14 +02:00
PortSeed ,
assert_equal ,
2017-03-23 21:49:02 +01:00
check_json_precision ,
2019-08-27 21:13:56 +02:00
connect_nodes ,
2017-04-03 15:34:04 +02:00
disconnect_nodes ,
2018-03-07 19:49:44 +01:00
get_datadir_path ,
2017-03-23 21:49:02 +01:00
initialize_datadir ,
2015-10-11 07:41:19 +02:00
sync_blocks ,
sync_mempools ,
)
2014-07-08 18:07:23 +02:00
2019-02-11 17:18:36 +01:00
2017-05-18 22:36:39 +02:00
class TestStatus ( Enum ) :
PASSED = 1
FAILED = 2
SKIPPED = 3
TEST_EXIT_PASSED = 0
TEST_EXIT_FAILED = 1
TEST_EXIT_SKIPPED = 77
2018-11-07 18:07:25 +01:00
TMPDIR_PREFIX = " bitcoin_func_test_ "
2018-04-02 07:45:41 +02:00
2018-09-09 19:32:37 +02:00
class SkipTest ( Exception ) :
""" This exception is raised to skip a test """
def __init__ ( self , message ) :
self . message = message
2018-04-02 07:45:41 +02:00
class BitcoinTestMetaClass ( type ) :
""" Metaclass for BitcoinTestFramework.
Ensures that any attempt to register a subclass of ` BitcoinTestFramework `
adheres to a standard whereby the subclass overrides ` set_test_params ` and
` run_test ` but DOES NOT override either ` __init__ ` or ` main ` . If any of
those standards are violated , a ` ` TypeError ` ` is raised . """
def __new__ ( cls , clsname , bases , dct ) :
if not clsname == ' BitcoinTestFramework ' :
if not ( ' run_test ' in dct and ' set_test_params ' in dct ) :
raise TypeError ( " BitcoinTestFramework subclasses must override "
" ' run_test ' and ' set_test_params ' " )
if ' __init__ ' in dct or ' main ' in dct :
raise TypeError ( " BitcoinTestFramework subclasses may not override "
" ' __init__ ' or ' main ' " )
return super ( ) . __new__ ( cls , clsname , bases , dct )
class BitcoinTestFramework ( metaclass = BitcoinTestMetaClass ) :
2017-03-23 22:48:29 +01:00
""" Base class for a bitcoin test script.
2017-08-24 17:11:56 +02:00
Individual bitcoin test scripts should subclass this class and override the set_test_params ( ) and run_test ( ) methods .
2017-03-23 22:48:29 +01:00
2017-06-10 00:21:21 +02:00
Individual tests can also override the following methods to customize the test setup :
2017-03-23 22:48:29 +01:00
- add_options ( )
- setup_chain ( )
- setup_network ( )
2017-06-10 00:21:21 +02:00
- setup_nodes ( )
2017-03-23 22:48:29 +01:00
2017-06-10 00:21:21 +02:00
The __init__ ( ) and main ( ) methods should not be overridden .
2017-03-23 22:48:29 +01:00
This class also contains various public and private helper methods . """
2016-05-14 13:01:31 +02:00
def __init__ ( self ) :
2017-06-10 00:21:21 +02:00
""" Sets test framework defaults. Do not override this method. Instead, override the set_test_params() method """
2019-07-31 20:11:32 +02:00
self . chain = ' regtest '
2016-05-14 13:01:31 +02:00
self . setup_clean_chain = False
2017-06-02 19:14:14 +02:00
self . nodes = [ ]
2018-06-18 23:28:37 +02:00
self . network_thread = None
2018-12-22 17:22:55 +01:00
self . rpc_timeout = 60 # Wait for up to 60 seconds for the RPC server to respond
2017-07-11 19:14:18 +02:00
self . supports_cli = False
2018-03-06 22:48:15 +01:00
self . bind_to_localhost_only = True
2017-06-10 00:21:21 +02:00
self . set_test_params ( )
2019-10-31 12:01:58 +01:00
self . parse_args ( )
2014-07-08 18:07:23 +02:00
def main ( self ) :
2017-06-10 00:21:21 +02:00
""" Main function. This should not be overridden by the subclass test scripts. """
2014-07-08 18:07:23 +02:00
2019-10-26 16:00:02 +02:00
assert hasattr ( self , " num_nodes " ) , " Test must set self.num_nodes in set_test_params() "
2019-10-26 16:34:42 +02:00
try :
self . setup ( )
self . run_test ( )
except JSONRPCException :
self . log . exception ( " JSONRPC error " )
self . success = TestStatus . FAILED
except SkipTest as e :
self . log . warning ( " Test Skipped: %s " % e . message )
self . success = TestStatus . SKIPPED
except AssertionError :
self . log . exception ( " Assertion failed " )
self . success = TestStatus . FAILED
except KeyError :
self . log . exception ( " Key error " )
self . success = TestStatus . FAILED
except Exception :
self . log . exception ( " Unexpected exception caught during testing " )
self . success = TestStatus . FAILED
except KeyboardInterrupt :
self . log . warning ( " Exiting after keyboard interrupt " )
self . success = TestStatus . FAILED
finally :
exit_code = self . shutdown ( )
sys . exit ( exit_code )
def parse_args ( self ) :
2018-08-11 22:24:14 +02:00
parser = argparse . ArgumentParser ( usage = " %(prog)s [options] " )
parser . add_argument ( " --nocleanup " , dest = " nocleanup " , default = False , action = " store_true " ,
help = " Leave bitcoinds and test.* datadir on exit or error " )
parser . add_argument ( " --noshutdown " , dest = " noshutdown " , default = False , action = " store_true " ,
help = " Don ' t stop bitcoinds after the test execution " )
parser . add_argument ( " --cachedir " , dest = " cachedir " , default = os . path . abspath ( os . path . dirname ( os . path . realpath ( __file__ ) ) + " /../../cache " ) ,
help = " Directory for caching pregenerated datadirs (default: %(default)s ) " )
parser . add_argument ( " --tmpdir " , dest = " tmpdir " , help = " Root directory for datadirs " )
parser . add_argument ( " -l " , " --loglevel " , dest = " loglevel " , default = " INFO " ,
help = " log events at this level and higher to the console. Can be set to DEBUG, INFO, WARNING, ERROR or CRITICAL. Passing --loglevel DEBUG will output all logs to console. Note that logs at all levels are always written to the test_framework.log file in the temporary test directory. " )
parser . add_argument ( " --tracerpc " , dest = " trace_rpc " , default = False , action = " store_true " ,
help = " Print out all RPC calls as they are made " )
parser . add_argument ( " --portseed " , dest = " port_seed " , default = os . getpid ( ) , type = int ,
help = " The seed to use for assigning port numbers (default: current process id) " )
parser . add_argument ( " --coveragedir " , dest = " coveragedir " ,
help = " Write tested RPC commands into this directory " )
parser . add_argument ( " --configfile " , dest = " configfile " ,
default = os . path . abspath ( os . path . dirname ( os . path . realpath ( __file__ ) ) + " /../../config.ini " ) ,
help = " Location of the test framework config file (default: %(default)s ) " )
parser . add_argument ( " --pdbonfailure " , dest = " pdbonfailure " , default = False , action = " store_true " ,
help = " Attach a python debugger if test fails " )
parser . add_argument ( " --usecli " , dest = " usecli " , default = False , action = " store_true " ,
help = " use bitcoin-cli instead of RPC for all commands " )
2018-10-19 18:28:47 +02:00
parser . add_argument ( " --perf " , dest = " perf " , default = False , action = " store_true " ,
help = " profile running nodes with perf for the duration of the test " )
2019-05-06 19:17:04 +02:00
parser . add_argument ( " --randomseed " , type = int ,
help = " set a random seed for deterministically reproducing a previous test run " )
2014-07-08 18:07:23 +02:00
self . add_options ( parser )
2018-08-11 22:24:14 +02:00
self . options = parser . parse_args ( )
2014-07-08 18:07:23 +02:00
2019-10-26 16:34:42 +02:00
def setup ( self ) :
""" Call this method to start up the test framework object with options set. """
2016-05-09 19:55:49 +02:00
PortSeed . n = self . options . port_seed
2014-07-08 18:07:23 +02:00
check_json_precision ( )
2017-10-10 00:04:45 +02:00
self . options . cachedir = os . path . abspath ( self . options . cachedir )
2018-04-19 14:38:59 +02:00
config = configparser . ConfigParser ( )
config . read_file ( open ( self . options . configfile ) )
2018-09-21 02:23:42 +02:00
self . config = config
2018-04-19 14:38:59 +02:00
self . options . bitcoind = os . getenv ( " BITCOIND " , default = config [ " environment " ] [ " BUILDDIR " ] + ' /src/bitcoind ' + config [ " environment " ] [ " EXEEXT " ] )
self . options . bitcoincli = os . getenv ( " BITCOINCLI " , default = config [ " environment " ] [ " BUILDDIR " ] + ' /src/bitcoin-cli ' + config [ " environment " ] [ " EXEEXT " ] )
2018-05-30 23:09:59 +02:00
os . environ [ ' PATH ' ] = os . pathsep . join ( [
os . path . join ( config [ ' environment ' ] [ ' BUILDDIR ' ] , ' src ' ) ,
os . path . join ( config [ ' environment ' ] [ ' BUILDDIR ' ] , ' src ' , ' qt ' ) ,
os . environ [ ' PATH ' ]
] )
2018-05-08 18:56:13 +02:00
2017-02-15 17:36:46 +01:00
# Set up temp directory and start logging
2017-05-18 23:33:33 +02:00
if self . options . tmpdir :
2017-10-10 00:04:45 +02:00
self . options . tmpdir = os . path . abspath ( self . options . tmpdir )
2017-05-18 23:33:33 +02:00
os . makedirs ( self . options . tmpdir , exist_ok = False )
else :
2018-11-07 18:07:25 +01:00
self . options . tmpdir = tempfile . mkdtemp ( prefix = TMPDIR_PREFIX )
2017-02-15 17:36:46 +01:00
self . _start_logging ( )
2019-05-06 19:17:04 +02:00
# Seed the PRNG. Note that test runs are reproducible if and only if
# a single thread accesses the PRNG. For more information, see
# https://docs.python.org/3/library/random.html#notes-on-reproducibility.
# The network thread shouldn't access random. If we need to change the
# network thread to access randomness, it should instantiate its own
# random.Random object.
seed = self . options . randomseed
if seed is None :
seed = random . randrange ( sys . maxsize )
else :
self . log . debug ( " User supplied random seed {} " . format ( seed ) )
random . seed ( seed )
self . log . debug ( " PRNG seed is: {} " . format ( seed ) )
2018-06-18 23:28:37 +02:00
self . log . debug ( ' Setting up network thread ' )
self . network_thread = NetworkThread ( )
self . network_thread . start ( )
2019-10-26 16:34:42 +02:00
if self . options . usecli :
if not self . supports_cli :
raise SkipTest ( " --usecli specified but test does not support using CLI " )
self . skip_if_no_cli ( )
self . skip_test_if_missing_module ( )
self . setup_chain ( )
self . setup_network ( )
2017-02-15 17:36:46 +01:00
2019-10-26 16:34:42 +02:00
self . success = TestStatus . PASSED
def shutdown ( self ) :
""" Call this method to shut down the test framework object. """
2014-07-08 18:07:23 +02:00
2019-10-26 16:34:42 +02:00
if self . success == TestStatus . FAILED and self . options . pdbonfailure :
2017-04-01 04:44:04 +02:00
print ( " Testcase failed. Attaching python debugger. Enter ? for help " )
pdb . set_trace ( )
2018-06-18 23:28:37 +02:00
self . log . debug ( ' Closing down network thread ' )
self . network_thread . close ( )
2015-04-23 14:19:00 +02:00
if not self . options . noshutdown :
2017-02-15 17:36:46 +01:00
self . log . info ( " Stopping nodes " )
2017-05-18 22:36:39 +02:00
if self . nodes :
self . stop_nodes ( )
2015-04-23 14:19:00 +02:00
else :
2018-04-06 16:53:35 +02:00
for node in self . nodes :
node . cleanup_on_exit = False
2017-02-15 17:36:46 +01:00
self . log . info ( " Note: bitcoinds were not stopped and may still be running " )
2015-04-20 11:50:33 +02:00
2018-10-19 18:28:47 +02:00
should_clean_up = (
not self . options . nocleanup and
not self . options . noshutdown and
2019-10-26 16:34:42 +02:00
self . success != TestStatus . FAILED and
2018-10-19 18:28:47 +02:00
not self . options . perf
)
if should_clean_up :
2017-12-10 00:39:06 +01:00
self . log . info ( " Cleaning up {} on exit " . format ( self . options . tmpdir ) )
cleanup_tree_on_exit = True
2018-10-19 18:28:47 +02:00
elif self . options . perf :
self . log . warning ( " Not cleaning up dir {} due to perf data " . format ( self . options . tmpdir ) )
cleanup_tree_on_exit = False
2016-05-24 17:35:06 +02:00
else :
2018-10-19 18:28:47 +02:00
self . log . warning ( " Not cleaning up dir {} " . format ( self . options . tmpdir ) )
2017-12-10 00:39:06 +01:00
cleanup_tree_on_exit = False
2017-05-18 22:36:39 +02:00
2019-10-26 16:34:42 +02:00
if self . success == TestStatus . PASSED :
2017-02-15 17:36:46 +01:00
self . log . info ( " Tests successful " )
2017-11-29 19:21:51 +01:00
exit_code = TEST_EXIT_PASSED
2019-10-26 16:34:42 +02:00
elif self . success == TestStatus . SKIPPED :
2017-05-18 22:36:39 +02:00
self . log . info ( " Test skipped " )
2017-11-29 19:21:51 +01:00
exit_code = TEST_EXIT_SKIPPED
2014-07-08 18:07:23 +02:00
else :
2017-02-15 17:36:46 +01:00
self . log . error ( " Test failed. Test logging available at %s /test_framework.log " , self . options . tmpdir )
2017-11-29 19:21:51 +01:00
self . log . error ( " Hint: Call {} ' {} ' to consolidate all logs " . format ( os . path . normpath ( os . path . dirname ( os . path . realpath ( __file__ ) ) + " /../combine_logs.py " ) , self . options . tmpdir ) )
exit_code = TEST_EXIT_FAILED
2019-10-28 14:50:01 +01:00
# Logging.shutdown will not remove stream- and filehandlers, so we must
# do it explicitly. Handlers are removed so the next test run can apply
# different log handler settings.
# See: https://docs.python.org/3/library/logging.html#logging.shutdown
for h in list ( self . log . handlers ) :
h . flush ( )
h . close ( )
self . log . removeHandler ( h )
rpc_logger = logging . getLogger ( " BitcoinRPC " )
for h in list ( rpc_logger . handlers ) :
h . flush ( )
rpc_logger . removeHandler ( h )
2017-12-10 00:39:06 +01:00
if cleanup_tree_on_exit :
shutil . rmtree ( self . options . tmpdir )
2019-10-26 16:03:21 +02:00
self . nodes . clear ( )
2019-10-26 16:34:42 +02:00
return exit_code
2015-04-28 18:39:47 +02:00
2017-06-10 00:21:21 +02:00
# Methods to override in subclass test scripts.
def set_test_params ( self ) :
2017-08-24 17:11:56 +02:00
""" Tests must this method to change default values for number of nodes, topology, etc """
raise NotImplementedError
2017-06-10 00:21:21 +02:00
def add_options ( self , parser ) :
""" Override this method to add command-line options to the test """
pass
2018-09-09 19:32:37 +02:00
def skip_test_if_missing_module ( self ) :
""" Override this method to skip a test if a module is not compiled """
pass
2017-06-10 00:21:21 +02:00
def setup_chain ( self ) :
""" Override this method to customize blockchain setup """
self . log . info ( " Initializing test directory " + self . options . tmpdir )
if self . setup_clean_chain :
self . _initialize_chain_clean ( )
else :
self . _initialize_chain ( )
def setup_network ( self ) :
""" Override this method to customize test network topology """
self . setup_nodes ( )
# Connect the nodes as a "chain". This allows us
# to split the network between nodes 1 and 2 to get
# two halves that can work on competing chains.
2019-09-16 15:22:49 +02:00
#
# Topology looks like this:
# node0 <-- node1 <-- node2 <-- node3
#
# If all nodes are in IBD (clean chain from genesis), node0 is assumed to be the source of blocks (miner). To
# ensure block propagation, all nodes will establish outgoing connections toward node0.
# See fPreferredDownload in net_processing.
#
# If further outbound connections are needed, they can be added at the beginning of the test with e.g.
# connect_nodes(self.nodes[1], 2)
2017-06-10 00:21:21 +02:00
for i in range ( self . num_nodes - 1 ) :
2019-09-16 15:22:49 +02:00
connect_nodes ( self . nodes [ i + 1 ] , i )
2017-06-10 00:21:21 +02:00
self . sync_all ( )
def setup_nodes ( self ) :
""" Override this method to customize test node setup """
extra_args = None
if hasattr ( self , " extra_args " ) :
extra_args = self . extra_args
self . add_nodes ( self . num_nodes , extra_args )
self . start_nodes ( )
2018-10-16 17:21:07 +02:00
self . import_deterministic_coinbase_privkeys ( )
2019-02-11 19:56:19 +01:00
if not self . setup_clean_chain :
for n in self . nodes :
assert_equal ( n . getblockchaininfo ( ) [ " blocks " ] , 199 )
2019-02-23 18:56:54 +01:00
# To ensure that all nodes are out of IBD, the most recent block
# must have a timestamp not too old (see IsInitialBlockDownload()).
self . log . debug ( ' Generate a block with current time ' )
2019-02-11 19:56:19 +01:00
block_hash = self . nodes [ 0 ] . generate ( 1 ) [ 0 ]
block = self . nodes [ 0 ] . getblock ( blockhash = block_hash , verbosity = 0 )
for n in self . nodes :
n . submitblock ( block )
chain_info = n . getblockchaininfo ( )
assert_equal ( chain_info [ " blocks " ] , 200 )
assert_equal ( chain_info [ " initialblockdownload " ] , False )
2017-06-10 00:21:21 +02:00
2018-09-10 22:58:15 +02:00
def import_deterministic_coinbase_privkeys ( self ) :
for n in self . nodes :
try :
n . getwalletinfo ( )
except JSONRPCException as e :
assert str ( e ) . startswith ( ' Method not found ' )
continue
2018-10-16 17:21:07 +02:00
n . importprivkey ( privkey = n . get_deterministic_priv_key ( ) . key , label = ' coinbase ' )
2018-09-10 22:58:15 +02:00
2017-06-10 00:21:21 +02:00
def run_test ( self ) :
2017-08-24 17:11:56 +02:00
""" Tests must override this method to define test logic """
2017-06-10 00:21:21 +02:00
raise NotImplementedError
2017-03-23 22:48:29 +01:00
# Public helper methods. These can be accessed by the subclass test scripts.
2018-08-01 20:37:47 +02:00
def add_nodes ( self , num_nodes , extra_args = None , * , rpchost = None , binary = None ) :
2018-12-13 21:13:04 +01:00
""" Instantiate TestNode objects.
Should only be called once after the nodes have been specified in
set_test_params ( ) . """
2018-03-06 22:48:15 +01:00
if self . bind_to_localhost_only :
extra_confs = [ [ " bind=127.0.0.1 " ] ] * num_nodes
else :
extra_confs = [ [ ] ] * num_nodes
2017-06-02 20:30:36 +02:00
if extra_args is None :
2017-06-09 22:35:17 +02:00
extra_args = [ [ ] ] * num_nodes
2017-06-02 19:14:14 +02:00
if binary is None :
2018-04-19 14:38:59 +02:00
binary = [ self . options . bitcoind ] * num_nodes
2018-03-06 22:48:15 +01:00
assert_equal ( len ( extra_confs ) , num_nodes )
2017-06-09 22:35:17 +02:00
assert_equal ( len ( extra_args ) , num_nodes )
assert_equal ( len ( binary ) , num_nodes )
for i in range ( num_nodes ) :
2018-12-13 21:13:04 +01:00
self . nodes . append ( TestNode (
i ,
get_datadir_path ( self . options . tmpdir , i ) ,
2019-07-31 20:11:32 +02:00
chain = self . chain ,
2018-12-13 21:13:04 +01:00
rpchost = rpchost ,
2018-12-22 17:22:55 +01:00
timewait = self . rpc_timeout ,
2018-12-13 21:13:04 +01:00
bitcoind = binary [ i ] ,
bitcoin_cli = self . options . bitcoincli ,
coverage_dir = self . options . coveragedir ,
2019-02-15 12:54:29 +01:00
cwd = self . options . tmpdir ,
2018-12-13 21:13:04 +01:00
extra_conf = extra_confs [ i ] ,
extra_args = extra_args [ i ] ,
use_cli = self . options . usecli ,
2018-10-19 18:28:47 +02:00
start_perf = self . options . perf ,
2018-12-13 21:13:04 +01:00
) )
2017-06-09 22:35:17 +02:00
2018-01-18 19:15:00 +01:00
def start_node ( self , i , * args , * * kwargs ) :
2017-06-09 22:35:17 +02:00
""" Start a bitcoind """
node = self . nodes [ i ]
2018-01-18 19:15:00 +01:00
node . start ( * args , * * kwargs )
2017-06-02 20:30:36 +02:00
node . wait_for_rpc_connection ( )
2017-06-02 19:14:14 +02:00
2017-06-02 20:30:36 +02:00
if self . options . coveragedir is not None :
coverage . write_all_rpc_commands ( self . options . coveragedir , node . rpc )
2017-06-02 19:14:14 +02:00
2018-01-18 19:15:00 +01:00
def start_nodes ( self , extra_args = None , * args , * * kwargs ) :
2017-06-09 22:35:17 +02:00
""" Start multiple bitcoinds """
2017-06-02 19:14:14 +02:00
if extra_args is None :
2017-06-09 22:35:17 +02:00
extra_args = [ None ] * self . num_nodes
assert_equal ( len ( extra_args ) , self . num_nodes )
2017-06-02 19:14:14 +02:00
try :
2017-06-09 22:35:17 +02:00
for i , node in enumerate ( self . nodes ) :
2018-01-18 19:15:00 +01:00
node . start ( extra_args [ i ] , * args , * * kwargs )
2017-06-09 22:35:17 +02:00
for node in self . nodes :
2017-06-02 20:30:36 +02:00
node . wait_for_rpc_connection ( )
2017-06-02 19:14:14 +02:00
except :
# If one node failed to start, stop the others
self . stop_nodes ( )
raise
2017-06-02 20:30:36 +02:00
if self . options . coveragedir is not None :
2017-06-09 22:35:17 +02:00
for node in self . nodes :
2017-06-02 20:30:36 +02:00
coverage . write_all_rpc_commands ( self . options . coveragedir , node . rpc )
2018-11-20 18:59:07 +01:00
def stop_node ( self , i , expected_stderr = ' ' , wait = 0 ) :
2017-06-02 19:14:14 +02:00
""" Stop a bitcoind test node """
2018-11-20 18:59:07 +01:00
self . nodes [ i ] . stop_node ( expected_stderr , wait = wait )
2017-08-16 17:52:24 +02:00
self . nodes [ i ] . wait_until_stopped ( )
2017-03-23 22:48:29 +01:00
2018-11-20 18:59:07 +01:00
def stop_nodes ( self , wait = 0 ) :
2017-06-02 19:14:14 +02:00
""" Stop multiple bitcoind test nodes """
2017-06-02 20:30:36 +02:00
for node in self . nodes :
# Issue RPC to stop nodes
2018-11-20 18:59:07 +01:00
node . stop_node ( wait = wait )
2017-06-02 19:14:14 +02:00
2017-06-02 20:30:36 +02:00
for node in self . nodes :
# Wait for nodes to stop
2017-08-16 17:52:24 +02:00
node . wait_until_stopped ( )
2017-06-02 19:14:14 +02:00
2017-09-20 16:00:28 +02:00
def restart_node ( self , i , extra_args = None ) :
""" Stop and start a test node """
self . stop_node ( i )
self . start_node ( i , extra_args )
2017-06-02 19:14:14 +02:00
def wait_for_node_exit ( self , i , timeout ) :
2017-06-02 20:30:36 +02:00
self . nodes [ i ] . process . wait ( timeout )
2017-03-23 22:48:29 +01:00
def split_network ( self ) :
"""
Split the network of four nodes into nodes 0 / 1 and 2 / 3.
"""
disconnect_nodes ( self . nodes [ 1 ] , 2 )
disconnect_nodes ( self . nodes [ 2 ] , 1 )
2019-04-09 17:46:05 +02:00
self . sync_all ( self . nodes [ : 2 ] )
self . sync_all ( self . nodes [ 2 : ] )
2017-03-23 22:48:29 +01:00
def join_network ( self ) :
"""
Join the ( previously split ) network halves together .
"""
2019-08-27 21:13:56 +02:00
connect_nodes ( self . nodes [ 1 ] , 2 )
2017-03-23 22:48:29 +01:00
self . sync_all ( )
2019-04-09 17:57:10 +02:00
def sync_blocks ( self , nodes = None , * * kwargs ) :
sync_blocks ( nodes or self . nodes , * * kwargs )
def sync_mempools ( self , nodes = None , * * kwargs ) :
sync_mempools ( nodes or self . nodes , * * kwargs )
2019-04-09 17:46:05 +02:00
def sync_all ( self , nodes = None , * * kwargs ) :
2019-04-09 17:57:10 +02:00
self . sync_blocks ( nodes , * * kwargs )
self . sync_mempools ( nodes , * * kwargs )
2017-03-23 22:48:29 +01:00
# Private helper methods. These should not be accessed by the subclass test scripts.
2017-02-15 17:36:46 +01:00
def _start_logging ( self ) :
# Add logger and logging handlers
self . log = logging . getLogger ( ' TestFramework ' )
self . log . setLevel ( logging . DEBUG )
# Create file handler to log all messages
2018-06-11 22:25:44 +02:00
fh = logging . FileHandler ( self . options . tmpdir + ' /test_framework.log ' , encoding = ' utf-8 ' )
2017-02-15 17:36:46 +01:00
fh . setLevel ( logging . DEBUG )
# Create console handler to log messages to stderr. By default this logs only error messages, but can be configured with --loglevel.
ch = logging . StreamHandler ( sys . stdout )
# User can provide log level as a number or string (eg DEBUG). loglevel was caught as a string, so try to convert it to an int
ll = int ( self . options . loglevel ) if self . options . loglevel . isdigit ( ) else self . options . loglevel . upper ( )
ch . setLevel ( ll )
# Format logs the same as bitcoind's debug.log with microprecision (so log files can be concatenated and sorted)
2018-02-28 16:46:31 +01:00
formatter = logging . Formatter ( fmt = ' %(asctime)s . %(msecs)03d 000Z %(name)s ( %(levelname)s ): %(message)s ' , datefmt = ' % Y- % m- %d T % H: % M: % S ' )
2017-03-21 15:05:59 +01:00
formatter . converter = time . gmtime
2017-02-15 17:36:46 +01:00
fh . setFormatter ( formatter )
ch . setFormatter ( formatter )
# add the handlers to the logger
self . log . addHandler ( fh )
self . log . addHandler ( ch )
if self . options . trace_rpc :
rpc_logger = logging . getLogger ( " BitcoinRPC " )
rpc_logger . setLevel ( logging . DEBUG )
rpc_handler = logging . StreamHandler ( sys . stdout )
rpc_handler . setLevel ( logging . DEBUG )
rpc_logger . addHandler ( rpc_handler )
2015-04-28 18:39:47 +02:00
2017-08-24 17:37:25 +02:00
def _initialize_chain ( self ) :
2017-03-23 21:49:02 +01:00
""" Initialize a pre-mined blockchain for use by the test.
2019-05-17 19:07:07 +02:00
Create a cache of a 199 - block - long chain
2017-03-23 21:49:02 +01:00
Afterward , create num_nodes copies from the cache . """
2019-05-17 19:07:07 +02:00
CACHE_NODE_ID = 0 # Use node 0 to create the cache for all other nodes
cache_node_dir = get_datadir_path ( self . options . cachedir , CACHE_NODE_ID )
2017-08-24 17:37:25 +02:00
assert self . num_nodes < = MAX_NODES
2019-05-17 19:07:07 +02:00
if not os . path . isdir ( cache_node_dir ) :
self . log . debug ( " Creating cache directory {} " . format ( cache_node_dir ) )
2019-07-31 20:11:32 +02:00
initialize_datadir ( self . options . cachedir , CACHE_NODE_ID , self . chain )
2019-05-17 19:07:07 +02:00
self . nodes . append (
TestNode (
CACHE_NODE_ID ,
cache_node_dir ,
2019-07-31 20:11:32 +02:00
chain = self . chain ,
2018-12-22 17:33:48 +01:00
extra_conf = [ " bind=127.0.0.1 " ] ,
2019-05-17 19:07:07 +02:00
extra_args = [ ' -disablewallet ' ] ,
2018-12-22 17:33:48 +01:00
rpchost = None ,
timewait = self . rpc_timeout ,
bitcoind = self . options . bitcoind ,
bitcoin_cli = self . options . bitcoincli ,
coverage_dir = None ,
2019-02-15 12:54:29 +01:00
cwd = self . options . tmpdir ,
2018-12-22 17:33:48 +01:00
) )
2019-05-17 19:07:07 +02:00
self . start_node ( CACHE_NODE_ID )
2017-03-23 21:49:02 +01:00
2017-06-02 20:30:36 +02:00
# Wait for RPC connections to be ready
2019-05-17 19:07:07 +02:00
self . nodes [ CACHE_NODE_ID ] . wait_for_rpc_connection ( )
2017-03-23 21:49:02 +01:00
2019-02-11 19:56:19 +01:00
# Create a 199-block-long chain; each of the 4 first nodes
2017-03-23 21:49:02 +01:00
# gets 25 mature blocks and 25 immature.
2019-02-11 19:56:19 +01:00
# The 4th node gets only 24 immature blocks so that the very last
# block in the cache does not age too much (have an old tip age).
# This is needed so that we are out of IBD when the test starts,
# see the tip age check in IsInitialBlockDownload().
2019-02-23 18:56:54 +01:00
for i in range ( 8 ) :
2019-05-17 19:07:07 +02:00
self . nodes [ CACHE_NODE_ID ] . generatetoaddress (
nblocks = 25 if i != 7 else 24 ,
address = TestNode . PRIV_KEYS [ i % 4 ] . address ,
)
2017-03-23 21:49:02 +01:00
2019-05-17 19:07:07 +02:00
assert_equal ( self . nodes [ CACHE_NODE_ID ] . getblockchaininfo ( ) [ " blocks " ] , 199 )
2019-02-11 19:56:19 +01:00
2019-05-17 19:07:07 +02:00
# Shut it down, and clean up cache directories:
2017-03-23 21:56:45 +01:00
self . stop_nodes ( )
self . nodes = [ ]
2018-03-07 19:49:44 +01:00
2019-05-17 19:07:07 +02:00
def cache_path ( * paths ) :
2019-07-31 20:11:32 +02:00
return os . path . join ( cache_node_dir , self . chain , * paths )
2018-03-07 19:49:44 +01:00
2019-05-17 19:07:07 +02:00
os . rmdir ( cache_path ( ' wallets ' ) ) # Remove empty wallets dir
for entry in os . listdir ( cache_path ( ) ) :
if entry not in [ ' chainstate ' , ' blocks ' ] : # Only keep chainstate and blocks folder
os . remove ( cache_path ( entry ) )
2017-08-24 17:37:25 +02:00
for i in range ( self . num_nodes ) :
2019-05-17 19:07:07 +02:00
self . log . debug ( " Copy cache directory {} to node {} " . format ( cache_node_dir , i ) )
2018-03-07 19:49:44 +01:00
to_dir = get_datadir_path ( self . options . tmpdir , i )
2019-05-17 19:07:07 +02:00
shutil . copytree ( cache_node_dir , to_dir )
2019-07-31 20:11:32 +02:00
initialize_datadir ( self . options . tmpdir , i , self . chain ) # Overwrite port/rpcport in bitcoin.conf
2017-03-23 21:49:02 +01:00
2017-08-24 17:37:25 +02:00
def _initialize_chain_clean ( self ) :
2017-03-23 21:49:02 +01:00
""" Initialize empty blockchain for use by the test.
Create an empty blockchain and num_nodes wallets .
Useful if a test case wants complete control over initialization . """
2017-08-24 17:37:25 +02:00
for i in range ( self . num_nodes ) :
2019-07-31 20:11:32 +02:00
initialize_datadir ( self . options . tmpdir , i , self . chain )
2017-03-23 21:49:02 +01:00
2018-09-09 19:32:37 +02:00
def skip_if_no_py3_zmq ( self ) :
""" Attempt to import the zmq package and skip the test if the import fails. """
try :
import zmq # noqa
except ImportError :
raise SkipTest ( " python3-zmq module not available. " )
def skip_if_no_bitcoind_zmq ( self ) :
""" Skip the running test if bitcoind has not been compiled with zmq support. """
if not self . is_zmq_compiled ( ) :
raise SkipTest ( " bitcoind has not been built with zmq enabled. " )
def skip_if_no_wallet ( self ) :
""" Skip the running test if wallet has not been compiled. """
if not self . is_wallet_compiled ( ) :
raise SkipTest ( " wallet has not been compiled. " )
def skip_if_no_cli ( self ) :
""" Skip the running test if bitcoin-cli has not been compiled. """
if not self . is_cli_compiled ( ) :
raise SkipTest ( " bitcoin-cli has not been compiled. " )
def is_cli_compiled ( self ) :
""" Checks whether bitcoin-cli was compiled. """
2019-04-25 11:12:36 +02:00
return self . config [ " components " ] . getboolean ( " ENABLE_CLI " )
2018-07-12 17:52:02 +02:00
2018-09-09 19:32:37 +02:00
def is_wallet_compiled ( self ) :
""" Checks whether the wallet module was compiled. """
2019-04-25 11:12:36 +02:00
return self . config [ " components " ] . getboolean ( " ENABLE_WALLET " )
2018-08-31 22:30:26 +02:00
2018-09-09 19:32:37 +02:00
def is_zmq_compiled ( self ) :
""" Checks whether the zmq module was compiled. """
2019-04-25 11:12:36 +02:00
return self . config [ " components " ] . getboolean ( " ENABLE_ZMQ " )