Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc-parkedchain.py
Show All 13 Lines | def set_test_params(self): | ||||
self.extra_args = [ | self.extra_args = [ | ||||
[ | [ | ||||
"-noparkdeepreorg", | "-noparkdeepreorg", | ||||
"-noautomaticunparking", | "-noautomaticunparking", | ||||
"-whitelist=noban@127.0.0.1", | "-whitelist=noban@127.0.0.1", | ||||
], | ], | ||||
[ | [ | ||||
"-automaticunparking=1", | "-automaticunparking=1", | ||||
] | ], | ||||
] | ] | ||||
def skip_test_if_missing_module(self): | def skip_test_if_missing_module(self): | ||||
self.skip_if_no_wallet() | self.skip_if_no_wallet() | ||||
# There should only be one chaintip, which is expected_tip | # There should only be one chaintip, which is expected_tip | ||||
def only_valid_tip(self, expected_tip, other_tip_status=None): | def only_valid_tip(self, expected_tip, other_tip_status=None): | ||||
node = self.nodes[0] | node = self.nodes[0] | ||||
assert_equal(node.getbestblockhash(), expected_tip) | assert_equal(node.getbestblockhash(), expected_tip) | ||||
for tip in node.getchaintips(): | for tip in node.getchaintips(): | ||||
if tip["hash"] == expected_tip: | if tip["hash"] == expected_tip: | ||||
assert_equal(tip["status"], "active") | assert_equal(tip["status"], "active") | ||||
else: | else: | ||||
assert_equal(tip["status"], other_tip_status) | assert_equal(tip["status"], other_tip_status) | ||||
def run_test(self): | def run_test(self): | ||||
def wait_for_tip(node, tip): | def wait_for_tip(node, tip): | ||||
def check_tip(): | def check_tip(): | ||||
return node.getbestblockhash() == tip | return node.getbestblockhash() == tip | ||||
self.wait_until(check_tip) | self.wait_until(check_tip) | ||||
node = self.nodes[0] | node = self.nodes[0] | ||||
parking_node = self.nodes[1] | parking_node = self.nodes[1] | ||||
self.log.info("Test chain parking...") | self.log.info("Test chain parking...") | ||||
self.generate(node, 10, sync_fun=self.no_op) | self.generate(node, 10, sync_fun=self.no_op) | ||||
tip = node.getbestblockhash() | tip = node.getbestblockhash() | ||||
▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
# Wait for node 1 to park the chain. | # Wait for node 1 to park the chain. | ||||
def wait_for_parked_block(block): | def wait_for_parked_block(block): | ||||
def check_block(): | def check_block(): | ||||
for tip in parking_node.getchaintips(): | for tip in parking_node.getchaintips(): | ||||
if tip["hash"] == block: | if tip["hash"] == block: | ||||
assert tip["status"] != "active" | assert tip["status"] != "active" | ||||
return tip["status"] == "parked" | return tip["status"] == "parked" | ||||
return False | return False | ||||
self.wait_until(check_block) | self.wait_until(check_block) | ||||
def check_reorg_protection(depth, extra_blocks): | def check_reorg_protection(depth, extra_blocks): | ||||
self.log.info( | self.log.info(f"Test deep reorg parking, {depth} block deep") | ||||
f"Test deep reorg parking, {depth} block deep") | |||||
# Invalidate the tip on node 0, so it doesn't follow node 1. | # Invalidate the tip on node 0, so it doesn't follow node 1. | ||||
node.invalidateblock(node.getbestblockhash()) | node.invalidateblock(node.getbestblockhash()) | ||||
# Mine block to create a fork of proper depth | # Mine block to create a fork of proper depth | ||||
self.generatetoaddress(parking_node, | self.generatetoaddress( | ||||
parking_node, | |||||
nblocks=depth - 1, | nblocks=depth - 1, | ||||
address=parking_node.getnewaddress( | address=parking_node.getnewaddress(label="coinbase"), | ||||
label='coinbase'), | |||||
sync_fun=self.no_op, | sync_fun=self.no_op, | ||||
) | ) | ||||
self.generatetoaddress(node, | self.generatetoaddress( | ||||
node, | |||||
nblocks=depth, | nblocks=depth, | ||||
address=node.getnewaddress( | address=node.getnewaddress(label="coinbase"), | ||||
label='coinbase'), | |||||
sync_fun=self.no_op, | sync_fun=self.no_op, | ||||
) | ) | ||||
# extra block should now find themselves parked | # extra block should now find themselves parked | ||||
for _ in range(extra_blocks): | for _ in range(extra_blocks): | ||||
self.generate(node, 1, sync_fun=self.no_op) | self.generate(node, 1, sync_fun=self.no_op) | ||||
wait_for_parked_block(node.getbestblockhash()) | wait_for_parked_block(node.getbestblockhash()) | ||||
# If we mine one more block, the node reorgs (generate also waits | # If we mine one more block, the node reorgs (generate also waits | ||||
# for chain sync). | # for chain sync). | ||||
self.generate(node, 1) | self.generate(node, 1) | ||||
check_reorg_protection(1, 0) | check_reorg_protection(1, 0) | ||||
check_reorg_protection(2, 0) | check_reorg_protection(2, 0) | ||||
check_reorg_protection(3, 1) | check_reorg_protection(3, 1) | ||||
check_reorg_protection(4, 4) | check_reorg_protection(4, 4) | ||||
check_reorg_protection(5, 5) | check_reorg_protection(5, 5) | ||||
check_reorg_protection(6, 6) | check_reorg_protection(6, 6) | ||||
check_reorg_protection(100, 100) | check_reorg_protection(100, 100) | ||||
# try deep reorg with a log check. | # try deep reorg with a log check. | ||||
with parking_node.assert_debug_log(["Park block"]): | with parking_node.assert_debug_log(["Park block"]): | ||||
check_reorg_protection(3, 1) | check_reorg_protection(3, 1) | ||||
self.log.info( | self.log.info( | ||||
"Accepting many blocks at once (possibly out of order) should not park if there is no reorg.") | "Accepting many blocks at once (possibly out of order) should not park if" | ||||
" there is no reorg." | |||||
) | |||||
# rewind one block to make a reorg that is shallow. | # rewind one block to make a reorg that is shallow. | ||||
node.invalidateblock(parking_node.getbestblockhash()) | node.invalidateblock(parking_node.getbestblockhash()) | ||||
# generate a ton of blocks at once. | # generate a ton of blocks at once. | ||||
try: | try: | ||||
with parking_node.assert_debug_log(["Park block"]): | with parking_node.assert_debug_log(["Park block"]): | ||||
# Also waits for chain sync | # Also waits for chain sync | ||||
self.generatetoaddress(node, | self.generatetoaddress( | ||||
nblocks=20, | node, nblocks=20, address=node.getnewaddress(label="coinbase") | ||||
address=node.getnewaddress(label='coinbase')) | ) | ||||
except AssertionError as exc: | except AssertionError as exc: | ||||
# good, we want an absence of "Park block" messages | # good, we want an absence of "Park block" messages | ||||
assert "does not partially match log" in exc.args[0] | assert "does not partially match log" in exc.args[0] | ||||
else: | else: | ||||
raise AssertionError("Parked block when there was no deep reorg") | raise AssertionError("Parked block when there was no deep reorg") | ||||
self.log.info("Test that unparking works when -parkdeepreorg=0") | self.log.info("Test that unparking works when -parkdeepreorg=0") | ||||
# Set up parking node height = fork + 4, node height = fork + 5 | # Set up parking node height = fork + 4, node height = fork + 5 | ||||
node.invalidateblock(node.getbestblockhash()) | node.invalidateblock(node.getbestblockhash()) | ||||
self.generate(parking_node, 3, sync_fun=self.no_op) | self.generate(parking_node, 3, sync_fun=self.no_op) | ||||
self.generatetoaddress(node, | self.generatetoaddress( | ||||
node, | |||||
nblocks=5, | nblocks=5, | ||||
address=node.getnewaddress(label='coinbase'), | address=node.getnewaddress(label="coinbase"), | ||||
sync_fun=self.no_op, | sync_fun=self.no_op, | ||||
) | ) | ||||
wait_for_parked_block(node.getbestblockhash()) | wait_for_parked_block(node.getbestblockhash()) | ||||
# Restart the parking node without parkdeepreorg. | # Restart the parking node without parkdeepreorg. | ||||
self.restart_node(1, self.extra_args[1] + ["-parkdeepreorg=0"]) | self.restart_node(1, self.extra_args[1] + ["-parkdeepreorg=0"]) | ||||
parking_node = self.nodes[1] | parking_node = self.nodes[1] | ||||
self.connect_nodes(node.index, parking_node.index) | self.connect_nodes(node.index, parking_node.index) | ||||
# The other chain should still be marked 'parked'. | # The other chain should still be marked 'parked'. | ||||
wait_for_parked_block(node.getbestblockhash()) | wait_for_parked_block(node.getbestblockhash()) | ||||
# Three more blocks is not enough to unpark. Even though its PoW is | # Three more blocks is not enough to unpark. Even though its PoW is | ||||
# larger, we are still following the delayed-unparking rules. | # larger, we are still following the delayed-unparking rules. | ||||
self.generate(node, 3, sync_fun=self.no_op) | self.generate(node, 3, sync_fun=self.no_op) | ||||
wait_for_parked_block(node.getbestblockhash()) | wait_for_parked_block(node.getbestblockhash()) | ||||
# Final block pushes over the edge, and should unpark (generate also | # Final block pushes over the edge, and should unpark (generate also | ||||
# waits for chain sync). | # waits for chain sync). | ||||
self.generate(node, 1) | self.generate(node, 1) | ||||
# Do not append tests after this point without restarting node again. | # Do not append tests after this point without restarting node again. | ||||
# Parking node is no longer parking. | # Parking node is no longer parking. | ||||
if __name__ == '__main__': | if __name__ == "__main__": | ||||
ParkedChainTest().main() | ParkedChainTest().main() |