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

Code before:
        node.setmocktime(cur_mock_time)
        self.log.info("Test that the peer gets evicted")
        test_node.wait_for_disconnect()

        self.log.info("Create an outbound connection and keep lagging behind, but not too much")
        # Test that if the peer never catches up with our current tip, but it does with the
        # expected work that we set when setting the timer (that is, our tip at the time)
        # we do not disconnect the peer
        test_node = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=0, connection_type="outbound-full-relay")
        self.log.info("Mine a block so our peer starts lagging")
        best_block_hash = self.generateblock(node, output="raw(42)", transactions=[])["hash"]
        tip_header = from_hex(CBlockHeader(), node.getblockheader(best_block_hash, False))
        test_node.sync_with_ping()
        self.log.info("Keep catching up with the old tip and check that we are not evicted")
        for _ in range(10):
            # Advance time but not enough to evict the peer
            best_block_hash = self.generateblock(node, output="raw(42)", transactions=[])["hash"]
            cur_mock_time += (CHAIN_SYNC_TIMEOUT + 1)
            node.setmocktime(cur_mock_time)

            # Send a header with the old tip
            test_node.send_and_ping(msg_headers([tip_header]))
            tip_header = from_hex(CBlockHeader(), node.getblockheader(best_block_hash, False))

            # Check that we are not evicted
            test_node.wait_for_getheaders()
            cur_mock_time += (HEADERS_RESPONSE_TIME + 1)
            node.setmocktime(cur_mock_time)
            test_node.sync_with_ping()

        self.log.info("Create an outbound connection and take some time to catch up, but do it in time")
        # Check that if the peer manages to catch up within time, the timeouts are removed (and the peer is not disconnected)
        # We are reusing the peer from the previous case which already sent us a valid (but old) block and whose timer is ticking

        # Send an updated headers message matching our tip
        test_node.send_and_ping(msg_headers([from_hex(CBlockHeader(), node.getblockheader(node.getbestblockhash(), False))]))

        # Wait for long enough for the timeouts to have triggered and check that we are still connected
        cur_mock_time += (CHAIN_SYNC_TIMEOUT + 1)
        node.setmocktime(cur_mock_time)
        test_node.sync_with_ping()
        test_node.wait_for_getheaders()
        cur_mock_time += (HEADERS_RESPONSE_TIME + 1)
        node.setmocktime(cur_mock_time)
        self.log.info("Test that the peer does not get evicted")
        test_node.sync_with_ping()

        node.disconnect_p2ps()

    def test_outbound_eviction_protected(self):
        # This tests the eviction logic for **protected** outbound peers (that is, PeerManagerImpl::ConsiderEviction)

Review comment (suggestion):
8fa272dc: `last_message` needs to be explicitly cleared:
```
with p2p_lock:
    test_node.last_message.pop("getheaders", None)
```
before calling `wait_for_getheaders()`. otherwise, it will always pass.

Code after:

        self.log.info("Create an outbound connection and keep lagging behind, but not too much")
        # Test that if the peer never catches up with our current tip, but it does with the
        # expected work that we set when setting the timer (that is, our tip at the time)
        # we do not disconnect the peer
        peer = node.add_outbound_p2p_connection(P2PInterface(), p2p_idx=0, connection_type="outbound-full-relay")

        self.log.info("Mine a block so our peer starts lagging")
        prev_prev_hash = tip_header.hashPrevBlock
        best_block_hash = self.generateblock(node, output="raw(42)", transactions=[])["hash"]
        peer.sync_with_ping()

        self.log.info("Keep catching up with the old tip and check that we are not evicted")
        for i in range(10):
            # Generate an additional block so the peers is 2 blocks behind
            prev_header = from_hex(CBlockHeader(), node.getblockheader(best_block_hash, False))
            best_block_hash = self.generateblock(node, output="raw(42)", transactions=[])["hash"]
            peer.sync_with_ping()

            # Advance time but not enough to evict the peer
            cur_mock_time += (CHAIN_SYNC_TIMEOUT + 1)
            node.setmocktime(cur_mock_time)
            peer.sync_with_ping()

            # Wait until we get out last call (by receiving a getheaders)
            peer.wait_for_getheaders(block_hash=prev_prev_hash)

            # Send a header with the previous tip (so we go back to 1 block behind)
            peer.send_and_ping(msg_headers([prev_header]))
            prev_prev_hash = tip_header.hash

        self.log.info("Create an outbound connection and take some time to catch up, but do it in time")
        # Check that if the peer manages to catch up within time, the timeouts are removed (and the peer is not disconnected)
        # We are reusing the peer from the previous case which already sent us a valid (but old) block and whose timer is ticking

        # Send an updated headers message matching our tip
        peer.send_and_ping(msg_headers([from_hex(CBlockHeader(), node.getblockheader(best_block_hash, False))]))

        # Wait for long enough for the timeouts to have triggered and check that we are still connected
        cur_mock_time += (CHAIN_SYNC_TIMEOUT + 1)
        node.setmocktime(cur_mock_time)
        peer.sync_with_ping()
        cur_mock_time += (HEADERS_RESPONSE_TIME + 1)
        node.setmocktime(cur_mock_time)
        self.log.info("Test that the peer does not get evicted")
        peer.sync_with_ping()

        node.disconnect_p2ps()

    def test_outbound_eviction_protected(self):
        # This tests the eviction logic for **protected** outbound peers (that is, PeerManagerImpl::ConsiderEviction)