Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc-parkedchain.py
Show All 18 Lines | def only_valid_tip(self, expected_tip, other_tip_status=None): | ||||
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 check_tip(): | |||||
return node.getbestblockhash() == tip | |||||
wait_until(check_tip) | |||||
node = self.nodes[0] | node = self.nodes[0] | ||||
parking_node = self.nodes[1] | |||||
self.log.info("Test chain parking...") | self.log.info("Test chain parking...") | ||||
node.generate(10) | node.generate(10) | ||||
tip = node.getbestblockhash() | tip = node.getbestblockhash() | ||||
node.generate(1) | node.generate(1) | ||||
block_to_park = node.getbestblockhash() | block_to_park = node.getbestblockhash() | ||||
node.generate(10) | node.generate(10) | ||||
parked_tip = node.getbestblockhash() | parked_tip = node.getbestblockhash() | ||||
# get parking_node caught up. | |||||
# (probably not needed, but just in case parking can have race | |||||
# condition like invalidateblock below) | |||||
wait_for_tip(parking_node, parked_tip) | |||||
# Let's park the chain. | # Let's park the chain. | ||||
assert(parked_tip != tip) | assert(parked_tip != tip) | ||||
assert(block_to_park != tip) | assert(block_to_park != tip) | ||||
assert(block_to_park != parked_tip) | assert(block_to_park != parked_tip) | ||||
node.parkblock(block_to_park) | node.parkblock(block_to_park) | ||||
assert_equal(node.getbestblockhash(), tip) | assert_equal(node.getbestblockhash(), tip) | ||||
# When the chain is unparked, the node reorg into its original chain. | # When the chain is unparked, the node reorg into its original chain. | ||||
node.unparkblock(parked_tip) | node.unparkblock(parked_tip) | ||||
assert_equal(node.getbestblockhash(), parked_tip) | assert_equal(node.getbestblockhash(), parked_tip) | ||||
# Parking and then unparking a block should not change its validity, | # Parking and then unparking a block should not change its validity, | ||||
# and invaliding and reconsidering a block should not change its | # and invaliding and reconsidering a block should not change its | ||||
# parked state. See the following test cases: | # parked state. See the following test cases: | ||||
self.log.info("Test invalidate, park, unpark, reconsider...") | self.log.info("Test invalidate, park, unpark, reconsider...") | ||||
node.generate(1) | node.generate(1) | ||||
tip = node.getbestblockhash() | tip = node.getbestblockhash() | ||||
node.generate(1) | node.generate(1) | ||||
bad_tip = node.getbestblockhash() | bad_tip = node.getbestblockhash() | ||||
# Generate an extra block to check that children are invalidated as | # Generate an extra block to check that children are invalidated as | ||||
# expected and do not produce dangling chaintips | # expected and do not produce dangling chaintips | ||||
node.generate(1) | node.generate(1) | ||||
good_tip = node.getbestblockhash() | good_tip = node.getbestblockhash() | ||||
# avoid race condition from parking_node requesting block when invalid | |||||
wait_for_tip(parking_node, good_tip) | |||||
node.invalidateblock(bad_tip) | node.invalidateblock(bad_tip) | ||||
self.only_valid_tip(tip, other_tip_status="invalid") | self.only_valid_tip(tip, other_tip_status="invalid") | ||||
node.parkblock(bad_tip) | node.parkblock(bad_tip) | ||||
self.only_valid_tip(tip, other_tip_status="invalid") | self.only_valid_tip(tip, other_tip_status="invalid") | ||||
node.unparkblock(bad_tip) | node.unparkblock(bad_tip) | ||||
self.only_valid_tip(tip, other_tip_status="invalid") | self.only_valid_tip(tip, other_tip_status="invalid") | ||||
node.reconsiderblock(bad_tip) | node.reconsiderblock(bad_tip) | ||||
self.only_valid_tip(good_tip) | self.only_valid_tip(good_tip) | ||||
self.log.info("Test park, invalidate, reconsider, unpark") | self.log.info("Test park, invalidate, reconsider, unpark") | ||||
node.generate(1) | node.generate(1) | ||||
tip = node.getbestblockhash() | tip = node.getbestblockhash() | ||||
node.generate(1) | node.generate(1) | ||||
bad_tip = node.getbestblockhash() | bad_tip = node.getbestblockhash() | ||||
node.generate(1) | node.generate(1) | ||||
good_tip = node.getbestblockhash() | good_tip = node.getbestblockhash() | ||||
# avoid race condition from parking_node requesting block when invalid | |||||
wait_for_tip(parking_node, good_tip) | |||||
node.parkblock(bad_tip) | node.parkblock(bad_tip) | ||||
self.only_valid_tip(tip, other_tip_status="parked") | self.only_valid_tip(tip, other_tip_status="parked") | ||||
node.invalidateblock(bad_tip) | node.invalidateblock(bad_tip) | ||||
# NOTE: Intuitively, other_tip_status would be "invalid", but because | # NOTE: Intuitively, other_tip_status would be "invalid", but because | ||||
# only valid (unparked) chains are walked, child blocks' statuses are | # only valid (unparked) chains are walked, child blocks' statuses are | ||||
# not updated, so the "parked" state remains. | # not updated, so the "parked" state remains. | ||||
self.only_valid_tip(tip, other_tip_status="parked") | self.only_valid_tip(tip, other_tip_status="parked") | ||||
node.reconsiderblock(bad_tip) | node.reconsiderblock(bad_tip) | ||||
self.only_valid_tip(tip, other_tip_status="parked") | self.only_valid_tip(tip, other_tip_status="parked") | ||||
node.unparkblock(bad_tip) | node.unparkblock(bad_tip) | ||||
self.only_valid_tip(good_tip) | self.only_valid_tip(good_tip) | ||||
self.log.info("Test invalidate, park, reconsider, unpark...") | self.log.info("Test invalidate, park, reconsider, unpark...") | ||||
node.generate(1) | node.generate(1) | ||||
tip = node.getbestblockhash() | tip = node.getbestblockhash() | ||||
node.generate(1) | node.generate(1) | ||||
bad_tip = node.getbestblockhash() | bad_tip = node.getbestblockhash() | ||||
node.generate(1) | node.generate(1) | ||||
good_tip = node.getbestblockhash() | good_tip = node.getbestblockhash() | ||||
# avoid race condition from parking_node requesting block when invalid | |||||
wait_for_tip(parking_node, good_tip) | |||||
node.invalidateblock(bad_tip) | node.invalidateblock(bad_tip) | ||||
self.only_valid_tip(tip, other_tip_status="invalid") | self.only_valid_tip(tip, other_tip_status="invalid") | ||||
node.parkblock(bad_tip) | node.parkblock(bad_tip) | ||||
self.only_valid_tip(tip, other_tip_status="invalid") | self.only_valid_tip(tip, other_tip_status="invalid") | ||||
node.reconsiderblock(bad_tip) | node.reconsiderblock(bad_tip) | ||||
self.only_valid_tip(tip, other_tip_status="parked") | self.only_valid_tip(tip, other_tip_status="parked") | ||||
node.unparkblock(bad_tip) | node.unparkblock(bad_tip) | ||||
self.only_valid_tip(good_tip) | self.only_valid_tip(good_tip) | ||||
self.log.info("Test park, invalidate, unpark, reconsider") | self.log.info("Test park, invalidate, unpark, reconsider") | ||||
node.generate(1) | node.generate(1) | ||||
tip = node.getbestblockhash() | tip = node.getbestblockhash() | ||||
node.generate(1) | node.generate(1) | ||||
bad_tip = node.getbestblockhash() | bad_tip = node.getbestblockhash() | ||||
node.generate(1) | node.generate(1) | ||||
good_tip = node.getbestblockhash() | good_tip = node.getbestblockhash() | ||||
# avoid race condition from parking_node requesting block when invalid | |||||
wait_for_tip(parking_node, good_tip) | |||||
node.parkblock(bad_tip) | node.parkblock(bad_tip) | ||||
self.only_valid_tip(tip, other_tip_status="parked") | self.only_valid_tip(tip, other_tip_status="parked") | ||||
node.invalidateblock(bad_tip) | node.invalidateblock(bad_tip) | ||||
# NOTE: Intuitively, other_tip_status would be "invalid", but because | # NOTE: Intuitively, other_tip_status would be "invalid", but because | ||||
# only valid (unparked) chains are walked, child blocks' statuses are | # only valid (unparked) chains are walked, child blocks' statuses are | ||||
# not updated, so the "parked" state remains. | # not updated, so the "parked" state remains. | ||||
self.only_valid_tip(tip, other_tip_status="parked") | self.only_valid_tip(tip, other_tip_status="parked") | ||||
node.unparkblock(bad_tip) | node.unparkblock(bad_tip) | ||||
self.only_valid_tip(tip, other_tip_status="invalid") | self.only_valid_tip(tip, other_tip_status="invalid") | ||||
node.reconsiderblock(bad_tip) | node.reconsiderblock(bad_tip) | ||||
self.only_valid_tip(good_tip) | self.only_valid_tip(good_tip) | ||||
# First, make sure both nodes are in sync. | # To get ready for next testset, make sure both nodes are in sync. | ||||
def wait_for_tip(node, tip): | |||||
def check_tip(): | |||||
return node.getbestblockhash() == tip | |||||
wait_until(check_tip) | |||||
parking_node = self.nodes[1] | |||||
wait_for_tip(parking_node, good_tip) | wait_for_tip(parking_node, good_tip) | ||||
assert_equal(node.getbestblockhash(), parking_node.getbestblockhash()) | assert_equal(node.getbestblockhash(), parking_node.getbestblockhash()) | ||||
# 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: | ||||
Show All 34 Lines |