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

Code before:
                test_node.last_message.pop("getheaders", None)
            test_node.send_header_for_blocks([blocks[1]])
            test_node.wait_for_getheaders()
            test_node.send_header_for_blocks(blocks)
            test_node.wait_for_getdata([x.sha256 for x in blocks])
            [test_node.send_message(msg_block(x)) for x in blocks]
            test_node.sync_with_ping()
            assert_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256)

        blocks = []
        # Now we test that if we repeatedly don't send connecting headers, we
        # don't go into an infinite loop trying to get them to connect.
        MAX_NUM_UNCONNECTING_HEADERS_MSGS = 10
        for _ in range(MAX_NUM_UNCONNECTING_HEADERS_MSGS + 1):
            blocks.append(create_block(tip, create_coinbase(height), block_time))
            blocks[-1].solve()
            tip = blocks[-1].sha256
            block_time += 1
            height += 1

        for i in range(1, MAX_NUM_UNCONNECTING_HEADERS_MSGS):
            with p2p_lock:
                test_node.last_message.pop("getheaders", None)
            # Send an empty header as failed response to the receive getheaders, to
            # make marked responded to. New headers are not treated as announcements
            # otherwise.
            test_node.send_header_for_blocks([])
            # Send the actual unconnecting header, which should trigger a new getheaders.
            test_node.send_header_for_blocks([blocks[i]])
            test_node.wait_for_getheaders()

        # Next header will connect, should re-set our count:
        test_node.send_header_for_blocks([blocks[0]])

        # Remove the first two entries (blocks[1] would connect):
        blocks = blocks[2:]

        # Now try to see how many unconnecting headers we can send
        # before we get disconnected.  Should be 5*MAX_NUM_UNCONNECTING_HEADERS_MSGS
        for i in range(5 * MAX_NUM_UNCONNECTING_HEADERS_MSGS - 1):
            # Send a header that doesn't connect, check that we get a getheaders.
            with p2p_lock:
                test_node.last_message.pop("getheaders", None)
            test_node.send_header_for_blocks([])
            test_node.send_header_for_blocks([blocks[i % len(blocks)]])
            test_node.wait_for_getheaders()

        # Eventually this stops working.
        test_node.send_header_for_blocks([blocks[-1]])

        # Should get disconnected

Review comment (bug):
in: ce238dd79bd3a1190a2229fac4502fa5742fc6e3

nit: I found this wording a bit hard to follow, especially the "to make marked responded to" bit.

IIUC, this is sending an empty block as a response to the outstanding getheaders request from the previous loop iteration, otherwise sending `blocks[i]` would be treated as the reply instead. Is that it? If so, I'd suggest something along the lines of:

```suggestion
            # Send an empty header as a failed response to the received getheaders
            # (from the previous iteration). Otherwise, the new headers will be
            # treated as a response instead of as an announcement.
```

Code after:
            test_node.wait_for_getheaders(block_hash=expected_hash)
            test_node.send_header_for_blocks(blocks)
            test_node.wait_for_getdata([x.sha256 for x in blocks])
            [test_node.send_message(msg_block(x)) for x in blocks]
            test_node.sync_with_ping()
            assert_equal(int(self.nodes[0].getbestblockhash(), 16), blocks[1].sha256)
            expected_hash = blocks[1].sha256

        blocks = []
        # Now we test that if we repeatedly don't send connecting headers, we
        # don't go into an infinite loop trying to get them to connect.
        for _ in range(NUM_HEADERS + 1):
            blocks.append(create_block(tip, create_coinbase(height), block_time))
            blocks[-1].solve()
            tip = blocks[-1].sha256
            block_time += 1
            height += 1

        for i in range(1, NUM_HEADERS):
            with p2p_lock:
                test_node.last_message.pop("getheaders", None)
            # Send an empty header as a failed response to the received getheaders
            # (from the previous iteration). Otherwise, the new headers will be
            # treated as a response instead of as an announcement.
            test_node.send_header_for_blocks([])
            # Send the actual unconnecting header, which should trigger a new getheaders.
            test_node.send_header_for_blocks([blocks[i]])
            test_node.wait_for_getheaders(block_hash=expected_hash)

        # Finally, check that the inv node never received a getdata request,
        # throughout the test
        assert "getdata" not in inv_node.last_message

if __name__ == '__main__':
    SendHeadersTest().main()