@ -25,7 +25,7 @@ class PruneTest(BitcoinTestFramework):
def __init__ ( self ) :
def __init__ ( self ) :
super ( ) . __init__ ( )
super ( ) . __init__ ( )
self . setup_clean_chain = True
self . setup_clean_chain = True
self . num_nodes = 3
self . num_nodes = 5
# Cache for utxos, as the listunspent may take a long time later in the test
# Cache for utxos, as the listunspent may take a long time later in the test
self . utxo_cache_0 = [ ]
self . utxo_cache_0 = [ ]
@ -43,10 +43,21 @@ class PruneTest(BitcoinTestFramework):
self . nodes . append ( start_node ( 2 , self . options . tmpdir , [ " -debug " , " -maxreceivebuffer=20000 " , " -prune=550 " ] , timewait = 900 ) )
self . nodes . append ( start_node ( 2 , self . options . tmpdir , [ " -debug " , " -maxreceivebuffer=20000 " , " -prune=550 " ] , timewait = 900 ) )
self . prunedir = self . options . tmpdir + " /node2/regtest/blocks/ "
self . prunedir = self . options . tmpdir + " /node2/regtest/blocks/ "
# Create node 3 to test manual pruning (it will be re-started with manual pruning later)
self . nodes . append ( start_node ( 3 , self . options . tmpdir , [ " -debug=0 " , " -maxreceivebuffer=20000 " , " -blockmaxsize=999000 " ] , timewait = 900 ) )
self . manualdir = self . options . tmpdir + " /node3/regtest/blocks/ "
# Create node 4 to test wallet in prune mode, but do not connect
self . nodes . append ( start_node ( 4 , self . options . tmpdir , [ " -debug=0 " , " -prune=550 " ] ) )
# Determine default relay fee
self . relayfee = self . nodes [ 0 ] . getnetworkinfo ( ) [ " relayfee " ]
connect_nodes ( self . nodes [ 0 ] , 1 )
connect_nodes ( self . nodes [ 0 ] , 1 )
connect_nodes ( self . nodes [ 1 ] , 2 )
connect_nodes ( self . nodes [ 1 ] , 2 )
connect_nodes ( self . nodes [ 2 ] , 0 )
connect_nodes ( self . nodes [ 2 ] , 0 )
sync_blocks ( self . nodes [ 0 : 3 ] )
connect_nodes ( self . nodes [ 0 ] , 3 )
sync_blocks ( self . nodes [ 0 : 4 ] )
def create_big_chain ( self ) :
def create_big_chain ( self ) :
# Start by creating some coinbases we can spend later
# Start by creating some coinbases we can spend later
@ -57,7 +68,7 @@ class PruneTest(BitcoinTestFramework):
for i in range ( 645 ) :
for i in range ( 645 ) :
mine_large_block ( self . nodes [ 0 ] , self . utxo_cache_0 )
mine_large_block ( self . nodes [ 0 ] , self . utxo_cache_0 )
sync_blocks ( self . nodes [ 0 : 3 ] )
sync_blocks ( self . nodes [ 0 : 4 ] )
def test_height_min ( self ) :
def test_height_min ( self ) :
if not os . path . isfile ( self . prunedir + " blk00000.dat " ) :
if not os . path . isfile ( self . prunedir + " blk00000.dat " ) :
@ -212,6 +223,93 @@ class PruneTest(BitcoinTestFramework):
# Verify we can now have the data for a block previously pruned
# Verify we can now have the data for a block previously pruned
assert ( self . nodes [ 2 ] . getblock ( self . forkhash ) [ " height " ] == self . forkheight )
assert ( self . nodes [ 2 ] . getblock ( self . forkhash ) [ " height " ] == self . forkheight )
def manual_test ( self ) :
# at this point, node3 has 995 blocks and has not yet run in prune mode
self . nodes [ 3 ] = start_node ( 3 , self . options . tmpdir , [ " -debug=0 " ] , timewait = 900 )
assert_raises_message ( JSONRPCException , " not in prune mode " , self . nodes [ 3 ] . pruneblockchain , 500 )
stop_node ( self . nodes [ 3 ] , 3 )
# now re-start in manual pruning mode
self . nodes [ 3 ] = start_node ( 3 , self . options . tmpdir , [ " -debug=0 " , " -prune=1 " ] , timewait = 900 )
assert_equal ( self . nodes [ 3 ] . getblockcount ( ) , 995 )
# should not prune because chain tip of node 3 (995) < PruneAfterHeight (1000)
assert_raises_message ( JSONRPCException , " Blockchain is too short for pruning " , self . nodes [ 3 ] . pruneblockchain , 500 )
# mine 6 blocks so we are at height 1001 (i.e., above PruneAfterHeight)
self . nodes [ 3 ] . generate ( 6 )
# negative and zero inputs should raise an exception
try :
self . nodes [ 3 ] . pruneblockchain ( - 10 )
raise AssertionError ( " pruneblockchain(-10) should have failed. " )
except :
pass
try :
self . nodes [ 3 ] . pruneblockchain ( 0 )
raise AssertionError ( " pruneblockchain(0) should have failed. " )
except :
pass
# height=100 too low to prune first block file so this is a no-op
self . nodes [ 3 ] . pruneblockchain ( 100 )
if not os . path . isfile ( self . manualdir + " blk00000.dat " ) :
raise AssertionError ( " blk00000.dat is missing when should still be there " )
# height=500 should prune first file
self . nodes [ 3 ] . pruneblockchain ( 500 )
if os . path . isfile ( self . manualdir + " blk00000.dat " ) :
raise AssertionError ( " blk00000.dat is still there, should be pruned by now " )
if not os . path . isfile ( self . manualdir + " blk00001.dat " ) :
raise AssertionError ( " blk00001.dat is missing when should still be there " )
# height=650 should prune second file
self . nodes [ 3 ] . pruneblockchain ( 650 )
if os . path . isfile ( self . manualdir + " blk00001.dat " ) :
raise AssertionError ( " blk00001.dat is still there, should be pruned by now " )
# height=1000 should not prune anything more, because tip-288 is in blk00002.dat.
self . nodes [ 3 ] . pruneblockchain ( 1000 )
if not os . path . isfile ( self . manualdir + " blk00002.dat " ) :
raise AssertionError ( " blk00002.dat is still there, should be pruned by now " )
# advance the tip so blk00002.dat and blk00003.dat can be pruned (the last 288 blocks should now be in blk00004.dat)
self . nodes [ 3 ] . generate ( 288 )
self . nodes [ 3 ] . pruneblockchain ( 1000 )
if os . path . isfile ( self . manualdir + " blk00002.dat " ) :
raise AssertionError ( " blk00002.dat is still there, should be pruned by now " )
if os . path . isfile ( self . manualdir + " blk00003.dat " ) :
raise AssertionError ( " blk00003.dat is still there, should be pruned by now " )
# stop node, start back up with auto-prune at 550MB, make sure still runs
stop_node ( self . nodes [ 3 ] , 3 )
self . nodes [ 3 ] = start_node ( 3 , self . options . tmpdir , [ " -debug=0 " , " -prune=550 " ] , timewait = 900 )
print ( " Success " )
def wallet_test ( self ) :
# check that the pruning node's wallet is still in good shape
print ( " Stop and start pruning node to trigger wallet rescan " )
try :
stop_node ( self . nodes [ 2 ] , 2 )
start_node ( 2 , self . options . tmpdir , [ " -debug=1 " , " -prune=550 " ] )
print ( " Success " )
except Exception as detail :
raise AssertionError ( " Wallet test: unable to re-start the pruning node " )
# check that wallet loads loads successfully when restarting a pruned node after IBD.
# this was reported to fail in #7494.
print ( " Syncing node 4 to test wallet " )
connect_nodes ( self . nodes [ 0 ] , 4 )
nds = [ self . nodes [ 0 ] , self . nodes [ 4 ] ]
sync_blocks ( nds )
try :
stop_node ( self . nodes [ 4 ] , 4 ) #stop and start to trigger rescan
start_node ( 4 , self . options . tmpdir , [ " -debug=1 " , " -prune=550 " ] )
print ( " Success " )
except Exception as detail :
raise AssertionError ( " Wallet test: unable to re-start node4 " )
def run_test ( self ) :
def run_test ( self ) :
print ( " Warning! This test requires 4GB of disk space and takes over 30 mins (up to 2 hours) " )
print ( " Warning! This test requires 4GB of disk space and takes over 30 mins (up to 2 hours) " )
@ -226,6 +324,9 @@ class PruneTest(BitcoinTestFramework):
# Start by mining a simple chain that all nodes have
# Start by mining a simple chain that all nodes have
# N0=N1=N2 **...*(995)
# N0=N1=N2 **...*(995)
# stop manual-pruning node with 995 blocks
stop_node ( self . nodes [ 3 ] , 3 )
print ( " Check that we haven ' t started pruning yet because we ' re below PruneAfterHeight " )
print ( " Check that we haven ' t started pruning yet because we ' re below PruneAfterHeight " )
self . test_height_min ( )
self . test_height_min ( )
# Extend this chain past the PruneAfterHeight
# Extend this chain past the PruneAfterHeight
@ -308,6 +409,12 @@ class PruneTest(BitcoinTestFramework):
#
#
# N1 doesn't change because 1033 on main chain (*) is invalid
# N1 doesn't change because 1033 on main chain (*) is invalid
print ( " Test manual pruning " )
self . manual_test ( )
print ( " Test wallet re-scan " )
self . wallet_test ( )
print ( " Done " )
print ( " Done " )
if __name__ == ' __main__ ' :
if __name__ == ' __main__ ' :