Changeset View
Changeset View
Standalone View
Standalone View
test/functional/abc-parkedchain.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2018 The Bitcoin developers | # Copyright (c) 2018 The Bitcoin developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
"""Test the parckblock and unparkblock RPC calls.""" | """Test the parckblock and unparkblock RPC calls.""" | ||||
import os | import os | ||||
from test_framework.test_framework import BitcoinTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
from test_framework.util import assert_equal | from test_framework.util import assert_equal, connect_nodes_bi, sync_blocks, wait_until | ||||
class ParkedChainTest(BitcoinTestFramework): | class ParkedChainTest(BitcoinTestFramework): | ||||
def set_test_params(self): | def set_test_params(self): | ||||
self.num_nodes = 1 | self.num_nodes = 2 | ||||
self.extra_args = [["-noparkdeepreorg"], []] | |||||
# 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): | ||||
node = self.nodes[0] | node = self.nodes[0] | ||||
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() | ||||
# 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 != 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, | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
# 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. | |||||
parking_node = self.nodes[1] | |||||
connect_nodes_bi(self.nodes, 0, 1) | |||||
sync_blocks(self.nodes[0:2]) | |||||
assert_equal(node.getbestblockhash(), parking_node.getbestblockhash()) | |||||
# Wait for node 1 to park the chain. | |||||
def wait_for_parked_block(block): | |||||
def check_block(): | |||||
for tip in parking_node.getchaintips(): | |||||
if tip["hash"] == block: | |||||
assert(tip["status"] != "active") | |||||
return tip["status"] == "parked" | |||||
schancel: There is a behavior change here for `getbestblockhash()` that is not being tested. It… | |||||
return False | |||||
wait_until(check_block) | |||||
def check_reorg_protection(depth, extra_blocks): | |||||
self.log.info("Test deep reorg parking, %d block deep" % depth) | |||||
# Invalidate the tip on node 0, so it doesn't follow node 1. | |||||
node.invalidateblock(node.getbestblockhash()) | |||||
# Mine block to create a fork of proper depth | |||||
parking_node.generate(depth - 1) | |||||
node.generate(depth) | |||||
# extra block should now find themselves parked | |||||
for i in range(extra_blocks): | |||||
node.generate(1) | |||||
wait_for_parked_block(node.getbestblockhash()) | |||||
# If we mine one more block, the node reorgs. | |||||
node.generate(1) | |||||
wait_until(lambda: parking_node.getbestblockhash() | |||||
== node.getbestblockhash()) | |||||
check_reorg_protection(1, 1) | |||||
check_reorg_protection(2, 1) | |||||
check_reorg_protection(3, 1) | |||||
check_reorg_protection(4, 4) | |||||
check_reorg_protection(5, 5) | |||||
check_reorg_protection(6, 6) | |||||
check_reorg_protection(100, 100) | |||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
ParkedChainTest().main() | ParkedChainTest().main() |
There is a behavior change here for getbestblockhash() that is not being tested. It previously returned the most PoW tip as an invariant. Now it returns the non-parked best PoW chain. That's only implied by the fat that on line 160 you have the assertion that parking_node.getbestblockhash() == node.getbestblockhash()
There is no assertion that this is NOT true anywhere else.
I would strongly encourage an:
added on line 157