mirror of
https://github.com/kvazar-network/kevacoin.git
synced 2025-01-12 08:08:25 +00:00
fixup! Add pruneblockchain RPC to enable manual block file pruning.
Extend pruneblockchain RPC to accept block timestamps as well as block indices.
This commit is contained in:
parent
1fc4ec7bf2
commit
afffeea7d9
@ -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 = 5
|
self.num_nodes = 6
|
||||||
|
|
||||||
# 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,12 +43,12 @@ 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)
|
# Create nodes 3 and 4 to test manual pruning (they 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.nodes.append(start_node(3, self.options.tmpdir, ["-debug=0","-maxreceivebuffer=20000","-blockmaxsize=999000"], timewait=900))
|
||||||
self.manualdir = self.options.tmpdir+"/node3/regtest/blocks/"
|
self.nodes.append(start_node(4, self.options.tmpdir, ["-debug=0","-maxreceivebuffer=20000","-blockmaxsize=999000"], timewait=900))
|
||||||
|
|
||||||
# Create node 4 to test wallet in prune mode, but do not connect
|
# Create nodes 5 to test wallet in prune mode, but do not connect
|
||||||
self.nodes.append(start_node(4, self.options.tmpdir, ["-debug=0", "-prune=550"]))
|
self.nodes.append(start_node(5, self.options.tmpdir, ["-debug=0", "-prune=550"]))
|
||||||
|
|
||||||
# Determine default relay fee
|
# Determine default relay fee
|
||||||
self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"]
|
self.relayfee = self.nodes[0].getnetworkinfo()["relayfee"]
|
||||||
@ -57,7 +57,8 @@ class PruneTest(BitcoinTestFramework):
|
|||||||
connect_nodes(self.nodes[1], 2)
|
connect_nodes(self.nodes[1], 2)
|
||||||
connect_nodes(self.nodes[2], 0)
|
connect_nodes(self.nodes[2], 0)
|
||||||
connect_nodes(self.nodes[0], 3)
|
connect_nodes(self.nodes[0], 3)
|
||||||
sync_blocks(self.nodes[0:4])
|
connect_nodes(self.nodes[0], 4)
|
||||||
|
sync_blocks(self.nodes[0:5])
|
||||||
|
|
||||||
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
|
||||||
@ -68,7 +69,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:4])
|
sync_blocks(self.nodes[0:5])
|
||||||
|
|
||||||
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"):
|
||||||
@ -223,68 +224,78 @@ 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):
|
def manual_test(self, node_number, use_timestamp):
|
||||||
# at this point, node3 has 995 blocks and has not yet run in prune mode
|
# at this point, node has 995 blocks and has not yet run in prune mode
|
||||||
self.nodes[3] = start_node(3, self.options.tmpdir, ["-debug=0"], timewait=900)
|
node = self.nodes[node_number] = start_node(node_number, self.options.tmpdir, ["-debug=0"], timewait=900)
|
||||||
assert_raises_message(JSONRPCException, "not in prune mode", self.nodes[3].pruneblockchain, 500)
|
assert_equal(node.getblockcount(), 995)
|
||||||
stop_node(self.nodes[3],3)
|
assert_raises_message(JSONRPCException, "not in prune mode", node.pruneblockchain, 500)
|
||||||
|
stop_node(node, node_number)
|
||||||
|
|
||||||
# now re-start in manual pruning mode
|
# now re-start in manual pruning mode
|
||||||
self.nodes[3] = start_node(3, self.options.tmpdir, ["-debug=0","-prune=1"], timewait=900)
|
node = self.nodes[node_number] = start_node(node_number, self.options.tmpdir, ["-debug=0","-prune=1"], timewait=900)
|
||||||
assert_equal(self.nodes[3].getblockcount(), 995)
|
assert_equal(node.getblockcount(), 995)
|
||||||
|
|
||||||
|
def height(index):
|
||||||
|
if use_timestamp:
|
||||||
|
return node.getblockheader(node.getblockhash(index))["time"]
|
||||||
|
else:
|
||||||
|
return index
|
||||||
|
|
||||||
|
def has_block(index):
|
||||||
|
return os.path.isfile(self.options.tmpdir + "/node{}/regtest/blocks/blk{:05}.dat".format(node_number, index))
|
||||||
|
|
||||||
# should not prune because chain tip of node 3 (995) < PruneAfterHeight (1000)
|
# 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)
|
assert_raises_message(JSONRPCException, "Blockchain is too short for pruning", node.pruneblockchain, height(500))
|
||||||
|
|
||||||
# mine 6 blocks so we are at height 1001 (i.e., above PruneAfterHeight)
|
# mine 6 blocks so we are at height 1001 (i.e., above PruneAfterHeight)
|
||||||
self.nodes[3].generate(6)
|
node.generate(6)
|
||||||
|
|
||||||
# negative and zero inputs should raise an exception
|
# negative and zero inputs should raise an exception
|
||||||
try:
|
try:
|
||||||
self.nodes[3].pruneblockchain(-10)
|
node.pruneblockchain(-10)
|
||||||
raise AssertionError("pruneblockchain(-10) should have failed.")
|
raise AssertionError("pruneblockchain(-10) should have failed.")
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.nodes[3].pruneblockchain(0)
|
node.pruneblockchain(0)
|
||||||
raise AssertionError("pruneblockchain(0) should have failed.")
|
raise AssertionError("pruneblockchain(0) should have failed.")
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# height=100 too low to prune first block file so this is a no-op
|
# height=100 too low to prune first block file so this is a no-op
|
||||||
self.nodes[3].pruneblockchain(100)
|
node.pruneblockchain(height(100))
|
||||||
if not os.path.isfile(self.manualdir+"blk00000.dat"):
|
if not has_block(0):
|
||||||
raise AssertionError("blk00000.dat is missing when should still be there")
|
raise AssertionError("blk00000.dat is missing when should still be there")
|
||||||
|
|
||||||
# height=500 should prune first file
|
# height=500 should prune first file
|
||||||
self.nodes[3].pruneblockchain(500)
|
node.pruneblockchain(height(500))
|
||||||
if os.path.isfile(self.manualdir+"blk00000.dat"):
|
if has_block(0):
|
||||||
raise AssertionError("blk00000.dat is still there, should be pruned by now")
|
raise AssertionError("blk00000.dat is still there, should be pruned by now")
|
||||||
if not os.path.isfile(self.manualdir+"blk00001.dat"):
|
if not has_block(1):
|
||||||
raise AssertionError("blk00001.dat is missing when should still be there")
|
raise AssertionError("blk00001.dat is missing when should still be there")
|
||||||
|
|
||||||
# height=650 should prune second file
|
# height=650 should prune second file
|
||||||
self.nodes[3].pruneblockchain(650)
|
node.pruneblockchain(height(650))
|
||||||
if os.path.isfile(self.manualdir+"blk00001.dat"):
|
if has_block(1):
|
||||||
raise AssertionError("blk00001.dat is still there, should be pruned by now")
|
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.
|
# height=1000 should not prune anything more, because tip-288 is in blk00002.dat.
|
||||||
self.nodes[3].pruneblockchain(1000)
|
node.pruneblockchain(height(1000))
|
||||||
if not os.path.isfile(self.manualdir+"blk00002.dat"):
|
if not has_block(2):
|
||||||
raise AssertionError("blk00002.dat is still there, should be pruned by now")
|
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)
|
# 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)
|
node.generate(288)
|
||||||
self.nodes[3].pruneblockchain(1000)
|
node.pruneblockchain(height(1000))
|
||||||
if os.path.isfile(self.manualdir+"blk00002.dat"):
|
if has_block(2):
|
||||||
raise AssertionError("blk00002.dat is still there, should be pruned by now")
|
raise AssertionError("blk00002.dat is still there, should be pruned by now")
|
||||||
if os.path.isfile(self.manualdir+"blk00003.dat"):
|
if has_block(3):
|
||||||
raise AssertionError("blk00003.dat is still there, should be pruned by now")
|
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, start back up with auto-prune at 550MB, make sure still runs
|
||||||
stop_node(self.nodes[3],3)
|
stop_node(node, node_number)
|
||||||
self.nodes[3] = start_node(3, self.options.tmpdir, ["-debug=0","-prune=550"], timewait=900)
|
self.nodes[node_number] = start_node(node_number, self.options.tmpdir, ["-debug=0","-prune=550"], timewait=900)
|
||||||
|
|
||||||
print("Success")
|
print("Success")
|
||||||
|
|
||||||
@ -300,16 +311,16 @@ class PruneTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
# check that wallet loads loads successfully when restarting a pruned node after IBD.
|
# check that wallet loads loads successfully when restarting a pruned node after IBD.
|
||||||
# this was reported to fail in #7494.
|
# this was reported to fail in #7494.
|
||||||
print ("Syncing node 4 to test wallet")
|
print ("Syncing node 5 to test wallet")
|
||||||
connect_nodes(self.nodes[0], 4)
|
connect_nodes(self.nodes[0], 5)
|
||||||
nds = [self.nodes[0], self.nodes[4]]
|
nds = [self.nodes[0], self.nodes[5]]
|
||||||
sync_blocks(nds)
|
sync_blocks(nds)
|
||||||
try:
|
try:
|
||||||
stop_node(self.nodes[4],4) #stop and start to trigger rescan
|
stop_node(self.nodes[5],5) #stop and start to trigger rescan
|
||||||
start_node(4, self.options.tmpdir, ["-debug=1","-prune=550"])
|
start_node(5, self.options.tmpdir, ["-debug=1","-prune=550"])
|
||||||
print ("Success")
|
print ("Success")
|
||||||
except Exception as detail:
|
except Exception as detail:
|
||||||
raise AssertionError("Wallet test: unable to re-start node4")
|
raise AssertionError("Wallet test: unable to re-start node5")
|
||||||
|
|
||||||
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)")
|
||||||
@ -326,6 +337,7 @@ class PruneTest(BitcoinTestFramework):
|
|||||||
|
|
||||||
# stop manual-pruning node with 995 blocks
|
# stop manual-pruning node with 995 blocks
|
||||||
stop_node(self.nodes[3],3)
|
stop_node(self.nodes[3],3)
|
||||||
|
stop_node(self.nodes[4],4)
|
||||||
|
|
||||||
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()
|
||||||
@ -409,8 +421,11 @@ 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")
|
print("Test manual pruning with block indices")
|
||||||
self.manual_test()
|
self.manual_test(3, use_timestamp=False)
|
||||||
|
|
||||||
|
print("Test manual pruning with timestamps")
|
||||||
|
self.manual_test(4, use_timestamp=True)
|
||||||
|
|
||||||
print("Test wallet re-scan")
|
print("Test wallet re-scan")
|
||||||
self.wallet_test()
|
self.wallet_test()
|
||||||
|
@ -820,7 +820,7 @@ UniValue pruneblockchain(const JSONRPCRequest& request)
|
|||||||
throw runtime_error(
|
throw runtime_error(
|
||||||
"pruneblockchain\n"
|
"pruneblockchain\n"
|
||||||
"\nArguments:\n"
|
"\nArguments:\n"
|
||||||
"1. \"height\" (int, required) The block height to prune up to.\n");
|
"1. \"height\" (numeric, required) The block height to prune up to. May be set to a discrete height, or to a unix timestamp to prune based on block time.\n");
|
||||||
|
|
||||||
if (!fPruneMode)
|
if (!fPruneMode)
|
||||||
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Cannot prune blocks because node is not in prune mode.");
|
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Cannot prune blocks because node is not in prune mode.");
|
||||||
@ -831,6 +831,16 @@ UniValue pruneblockchain(const JSONRPCRequest& request)
|
|||||||
if (heightParam < 0)
|
if (heightParam < 0)
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height.");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height.");
|
||||||
|
|
||||||
|
// Height value more than a billion is too high to be a block height, and
|
||||||
|
// too low to be a block time (corresponds to timestamp from Sep 2001).
|
||||||
|
if (heightParam > 1000000000) {
|
||||||
|
CBlockIndex* pindex = chainActive.FindLatestBefore(heightParam);
|
||||||
|
if (!pindex) {
|
||||||
|
throw JSONRPCError(RPC_INTERNAL_ERROR, "Could not find block before specified timestamp.");
|
||||||
|
}
|
||||||
|
heightParam = pindex->nHeight;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int height = (unsigned int) heightParam;
|
unsigned int height = (unsigned int) heightParam;
|
||||||
unsigned int chainHeight = (unsigned int) chainActive.Height();
|
unsigned int chainHeight = (unsigned int) chainActive.Height();
|
||||||
if (chainHeight < Params().PruneAfterHeight())
|
if (chainHeight < Params().PruneAfterHeight())
|
||||||
|
Loading…
Reference in New Issue
Block a user