Repository: bitcoin/bitcoin
File: test/functional/rpc_invalidateblock.py (Python)

Code before:
        self.nodes[2].invalidateblock(self.nodes[2].getblockhash(3))
        assert_equal(self.nodes[2].getblockcount(), 2)
        self.log.info("..and then mine a block")
        self.generate(self.nodes[2], 1, sync_fun=self.no_op)
        self.log.info("Verify all nodes are at the right height")
        self.wait_until(lambda: self.nodes[2].getblockcount() == 3, timeout=5)
        self.wait_until(lambda: self.nodes[0].getblockcount() == 4, timeout=5)
        self.wait_until(lambda: self.nodes[1].getblockcount() == 4, timeout=5)

        self.log.info("Verify that ancestors can become chain tip candidates when we reconsider blocks")
        # Invalidate node0's current chain (1' -> 2' -> 3' -> 4') so that we don't reorg back to it in this test
        badhash = self.nodes[0].getblockhash(1)
        self.nodes[0].invalidateblock(badhash)
        # Reconsider the tip so that node0's chain becomes this chain again : 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> header 7
        self.nodes[0].reconsiderblock(tip)
        blockhash_3 = self.nodes[0].getblockhash(3)
        blockhash_4 = self.nodes[0].getblockhash(4)
        blockhash_6 = self.nodes[0].getblockhash(6)
        assert_equal(self.nodes[0].getbestblockhash(), blockhash_6)

        # Invalidate block 4 so that chain becomes : 1 -> 2 -> 3
        self.nodes[0].invalidateblock(blockhash_4)
        assert_equal(self.nodes[0].getbestblockhash(), blockhash_3)
        assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 3)
        assert_equal(self.nodes[0].getblockchaininfo()['headers'], 3)

        self.log.info("Verify that we reconsider all ancestors as well")
        blocks = self.generatetodescriptor(self.nodes[1], 10, ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR, sync_fun=self.no_op)
        assert_equal(self.nodes[1].getbestblockhash(), blocks[-1])
        # Invalidate the two blocks at the tip
        self.nodes[1].invalidateblock(blocks[-1])
        self.nodes[1].invalidateblock(blocks[-2])
        assert_equal(self.nodes[1].getbestblockhash(), blocks[-3])
        # Reconsider only the previous tip
        self.nodes[1].reconsiderblock(blocks[-1])
        # Should be back at the tip by now
        assert_equal(self.nodes[1].getbestblockhash(), blocks[-1])

        self.log.info("Verify that we reconsider all descendants")
        blocks = self.generatetodescriptor(self.nodes[1], 10, ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR, sync_fun=self.no_op)
        assert_equal(self.nodes[1].getbestblockhash(), blocks[-1])
        # Invalidate the two blocks at the tip
        self.nodes[1].invalidateblock(blocks[-2])
        self.nodes[1].invalidateblock(blocks[-4])
        assert_equal(self.nodes[1].getbestblockhash(), blocks[-5])
        # Reconsider only the previous tip
        self.nodes[1].reconsiderblock(blocks[-4])
        # Should be back at the tip by now
        assert_equal(self.nodes[1].getbestblockhash(), blocks[-1])
        # Should report consistent blockchain info
        assert_equal(self.nodes[1].getblockchaininfo()["headers"], self.nodes[1].getblockchaininfo()["blocks"])

Review comment (suggestion):
This means in the remainder of the test, the state of the two nodes shouldn't affect each other. So the reconsiderblock+test

```
        assert_equal(self.nodes[0].getbestblockhash(), blockhash_6)
        assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 6)
        assert_equal(self.nodes[0].getblockchaininfo()['headers'], 7)
```

Can be moved after this line to disconnect the nodes? This would bundle the tests more nicely, but no strong opinion.

Code after:
        self.nodes[2].invalidateblock(self.nodes[2].getblockhash(3))
        assert_equal(self.nodes[2].getblockcount(), 2)
        self.log.info("..and then mine a block")
        self.generate(self.nodes[2], 1, sync_fun=self.no_op)
        self.log.info("Verify all nodes are at the right height")
        self.wait_until(lambda: self.nodes[2].getblockcount() == 3, timeout=5)
        self.wait_until(lambda: self.nodes[0].getblockcount() == 4, timeout=5)
        self.wait_until(lambda: self.nodes[1].getblockcount() == 4, timeout=5)

        self.log.info("Verify that ancestors can become chain tip candidates when we reconsider blocks")
        # Invalidate node0's current chain (1' -> 2' -> 3' -> 4') so that we don't reorg back to it in this test
        badhash = self.nodes[0].getblockhash(1)
        self.nodes[0].invalidateblock(badhash)
        # Reconsider the tip so that node0's chain becomes this chain again : 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> header 7
        self.nodes[0].reconsiderblock(tip)
        blockhash_3 = self.nodes[0].getblockhash(3)
        blockhash_4 = self.nodes[0].getblockhash(4)
        blockhash_6 = self.nodes[0].getblockhash(6)
        assert_equal(self.nodes[0].getbestblockhash(), blockhash_6)

        # Invalidate block 4 so that chain becomes : 1 -> 2 -> 3
        self.nodes[0].invalidateblock(blockhash_4)
        assert_equal(self.nodes[0].getbestblockhash(), blockhash_3)
        assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 3)
        assert_equal(self.nodes[0].getblockchaininfo()['headers'], 3)

        # Reconsider the header
        self.nodes[0].reconsiderblock(block.hash)
        # Since header doesn't have block data, it can't be chain tip
        # Check if it's possible for an ancestor (with block data) to be the chain tip
        assert_equal(self.nodes[0].getbestblockhash(), blockhash_6)
        assert_equal(self.nodes[0].getblockchaininfo()['blocks'], 6)
        assert_equal(self.nodes[0].getblockchaininfo()['headers'], 7)

        self.log.info("Verify that we reconsider all ancestors as well")
        blocks = self.generatetodescriptor(self.nodes[1], 10, ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR, sync_fun=self.no_op)
        assert_equal(self.nodes[1].getbestblockhash(), blocks[-1])
        # Invalidate the two blocks at the tip
        self.nodes[1].invalidateblock(blocks[-1])
        self.nodes[1].invalidateblock(blocks[-2])
        assert_equal(self.nodes[1].getbestblockhash(), blocks[-3])
        # Reconsider only the previous tip
        self.nodes[1].reconsiderblock(blocks[-1])
        # Should be back at the tip by now
        assert_equal(self.nodes[1].getbestblockhash(), blocks[-1])

        self.log.info("Verify that we reconsider all descendants")
        blocks = self.generatetodescriptor(self.nodes[1], 10, ADDRESS_BCRT1_UNSPENDABLE_DESCRIPTOR, sync_fun=self.no_op)
        assert_equal(self.nodes[1].getbestblockhash(), blocks[-1])
        # Invalidate the two blocks at the tip
        self.nodes[1].invalidateblock(blocks[-2])