I observed that block propagation didn’t work as I expected in testing with InvalidateBlock.
Let’s say we have two nodes connected and synced. Then we use one to mine 20 blocks and sync the nodes. Then we invalidate the first of those blocks via rpc call to each node.
If we then mine 19 blocks on that same node that generated the initial 20, those blocks won’t all propagate to the other node (the first 16 will make it, because of the call to getdata on processing each inv, which occurs until we’ve maxed out the number of blocks in flight to that peer). A specific test that fails is pasted below.
I believe what is happening is that the CNodeState variable pindexBestKnownBlock is stuck pointing at an invalid but more-work chain, and consequently the block download logic believes it doesn’t have a peer from which to download the blocks on the new headers chain.
I’m not sure what the best way to fix this is, maybe we could clear the pindexBestKnownBlock variables if they’re pointing to now-invalid blocks before returning from InvalidateBlock, or perhaps we could check the validity of pindexBestKnownBlock when we are comparing it in UpdateBlockAvailability? Both those approaches do appear to fix this test, but I’m not quite familiar enough with this logic to know whether either of those potential solutions makes sense.
0#!/usr/bin/env python2
1# Copyright (c) 2014 The Bitcoin Core developers
2# Distributed under the MIT software license, see the accompanying
3# file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#
6# Test invalidateblock
7#
8
9from test_framework import BitcoinTestFramework
10from bitcoinrpc.authproxy import AuthServiceProxy, JSONRPCException
11from util import *
12import os
13import shutil
14
15class InvalidateBlockTest(BitcoinTestFramework):
16
17 def setup_network(self):
18 self.nodes = []
19 self.nodes.append(start_node(0, self.options.tmpdir, ['-debug', '-whitelist=127.0.0.1']))
20 self.nodes.append(start_node(1, self.options.tmpdir, ['-debug', '-whitelist=127.0.0.1']))
21 connect_nodes(self.nodes[1], 0)
22 self.is_network_split = False
23 self.sync_all()
24
25 def run_test(self):
26 self.nodes[0].setgenerate(True, 1) # Leave IBD
27 self.sync_all()
28
29 cnt = self.nodes[0].getblockcount()
30
31 node1blocks = self.nodes[1].setgenerate(True, 18)
32
33 self.sync_all()
34 if (self.nodes[0].getblockcount() != cnt + 18):
35 raise AssertionError("Failed to sync initial blocks")
36
37 self.nodes[0].invalidateblock(node1blocks[0])
38 self.nodes[1].invalidateblock(node1blocks[0])
39
40 if (self.nodes[0].getblockcount() != cnt):
41 raise AssertionError("Failed to invalidate initial blocks")
42
43 self.nodes[1].setgenerate(True, 17)
44
45 print "All blocks generated, trying to sync"
46 self.sync_all()
47 if (self.nodes[0].getblockcount() != cnt + 17):
48 raise AssertionError("Failed to sync shorter but valid chain")
49
50if __name__ == '__main__':
51 InvalidateBlockTest().main()