1#!/usr/bin/env python3
2# Copyright (c) 2014-2018 The Bitcoin Core developers
3# Distributed under the MIT software license, see the accompanying
4# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5"""Test the invalidateblock RPC."""
6
7from test_framework.test_framework import BitcoinTestFramework
8from test_framework.address import ADDRESS_BCRT1_UNSPENDABLE
9from test_framework.util import (
10    assert_equal,
11    connect_nodes_bi,
12    sync_blocks,
13    wait_until,
14)
15
16
17class InvalidateTest(BitcoinTestFramework):
18    def set_test_params(self):
19        self.setup_clean_chain = True
20        self.num_nodes = 3
21
22    def setup_network(self):
23        self.setup_nodes()
24
25    def run_test(self):
26        self.log.info("Make sure we repopulate setBlockIndexCandidates after InvalidateBlock:")
27        self.log.info("Mine 4 blocks on Node 0")
28        self.nodes[0].generatetoaddress(4, self.nodes[0].get_deterministic_priv_key().address)
29        assert_equal(self.nodes[0].getblockcount(), 4)
30        besthash_n0 = self.nodes[0].getbestblockhash()
31
32        self.log.info("Mine competing 6 blocks on Node 1")
33        self.nodes[1].generatetoaddress(6, self.nodes[1].get_deterministic_priv_key().address)
34        assert_equal(self.nodes[1].getblockcount(), 6)
35
36        self.log.info("Connect nodes to force a reorg")
37        connect_nodes_bi(self.nodes, 0, 1)
38        sync_blocks(self.nodes[0:2])
39        assert_equal(self.nodes[0].getblockcount(), 6)
40        badhash = self.nodes[1].getblockhash(2)
41
42        self.log.info("Invalidate block 2 on node 0 and verify we reorg to node 0's original chain")
43        self.nodes[0].invalidateblock(badhash)
44        assert_equal(self.nodes[0].getblockcount(), 4)
45        assert_equal(self.nodes[0].getbestblockhash(), besthash_n0)
46
47        self.log.info("Make sure we won't reorg to a lower work chain:")
48        connect_nodes_bi(self.nodes, 1, 2)
49        self.log.info("Sync node 2 to node 1 so both have 6 blocks")
50        sync_blocks(self.nodes[1:3])
51        assert_equal(self.nodes[2].getblockcount(), 6)
52        self.log.info("Invalidate block 5 on node 1 so its tip is now at 4")
53        self.nodes[1].invalidateblock(self.nodes[1].getblockhash(5))
54        assert_equal(self.nodes[1].getblockcount(), 4)
55        self.log.info("Invalidate block 3 on node 2, so its tip is now 2")
56        self.nodes[2].invalidateblock(self.nodes[2].getblockhash(3))
57        assert_equal(self.nodes[2].getblockcount(), 2)
58        self.log.info("..and then mine a block")
59        self.nodes[2].generatetoaddress(1, self.nodes[2].get_deterministic_priv_key().address)
60        self.log.info("Verify all nodes are at the right height")
61        wait_until(lambda: self.nodes[2].getblockcount() == 3, timeout=5)
62        wait_until(lambda: self.nodes[0].getblockcount() == 4, timeout=5)
63        wait_until(lambda: self.nodes[1].getblockcount() == 4, timeout=5)
64
65        self.log.info("Verify that we reconsider all ancestors as well")
66        blocks = self.nodes[1].generatetoaddress(10, ADDRESS_BCRT1_UNSPENDABLE)
67        assert_equal(self.nodes[1].getbestblockhash(), blocks[-1])
68        # Invalidate the two blocks at the tip
69        self.nodes[1].invalidateblock(blocks[-1])
70        self.nodes[1].invalidateblock(blocks[-2])
71        assert_equal(self.nodes[1].getbestblockhash(), blocks[-3])
72        # Reconsider only the previous tip
73        self.nodes[1].reconsiderblock(blocks[-1])
74        # Should be back at the tip by now
75        assert_equal(self.nodes[1].getbestblockhash(), blocks[-1])
76
77        self.log.info("Verify that we reconsider all descendants")
78        blocks = self.nodes[1].generatetoaddress(10, ADDRESS_BCRT1_UNSPENDABLE)
79        assert_equal(self.nodes[1].getbestblockhash(), blocks[-1])
80        # Invalidate the two blocks at the tip
81        self.nodes[1].invalidateblock(blocks[-2])
82        self.nodes[1].invalidateblock(blocks[-4])
83        assert_equal(self.nodes[1].getbestblockhash(), blocks[-5])
84        # Reconsider only the previous tip
85        self.nodes[1].reconsiderblock(blocks[-4])
86        # Should be back at the tip by now
87        assert_equal(self.nodes[1].getbestblockhash(), blocks[-1])
88
89
90if __name__ == '__main__':
91    InvalidateTest().main()
92