1 #!/usr/bin/env python3
2 # Copyright (c) 2024-present The Limenka developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or https://www.opensource.org/licenses/mit-license.php.
5 6 """ Test block announcement time tracking
7 8 The limenkad client records, for each peer, the most recent time that
9 this peer announced a block that the client wasn't already aware of. This
10 timestamp, CNodeStateStats::m_last_block_announcement, is available in the
11 `last_block_announcement` field of each peer's `getpeerinfo` result. The
12 value zero means that this peer has never been the first to announce
13 a block to us. Blocks are announced using either the "headers" or
14 "cmpctblock" messages.
15 16 This timestamp is used when the "potential stale tip" condition occurs:
17 When a new block hasn't been seen for a longer-than-expected amount of
18 time (currently 30 minutes, see TipMayBeStale()), the client, suspecting
19 that there may be new blocks that its peers are not announcing, will
20 add an extra outbound peer and disconnect (evict) the peer that has
21 least recently been the first to announced a new block to us. (If there
22 is a tie, it will disconnect the most recently-added of those peers.)
23 24 This test verifies that this timestamp is being set correctly.
25 (This tests PR 26172.)
26 """
27 28 import time
29 from test_framework.blocktools import (
30 create_block,
31 create_coinbase,
32 )
33 from test_framework.messages import (
34 CBlockHeader,
35 msg_headers,
36 )
37 from test_framework.p2p import P2PDataStore
38 from test_framework.test_framework import LimenkaTestFramework
39 40 41 class P2PBlockTimes(LimenkaTestFramework):
42 def set_test_params(self):
43 self.setup_clean_chain = True
44 self.num_nodes = 1
45 self.disable_autoconnect = False
46 47 def run_test(self):
48 node = self.nodes[0]
49 cur_time = int(time.time())
50 node.setmocktime(cur_time)
51 52 # Generate one block to exit IBD
53 self.generate(node, 1)
54 55 self.log.info("Create a full-outbound test framework peer")
56 node.add_outbound_p2p_connection(P2PDataStore(), p2p_idx=0)
57 58 self.log.info("Test framework peer generates a new block")
59 tip = int(node.getbestblockhash(), 16)
60 block = create_block(tip, create_coinbase(2))
61 block.solve()
62 63 self.log.info("Check that last_block_announcement is initially zero")
64 peerinfo = node.getpeerinfo()[0]
65 assert peerinfo['last_block_announcement'] == 0
66 67 self.log.info("Test framework peer sends node the new block")
68 node.p2ps[0].send_blocks_and_test([block], node, success=True)
69 70 self.log.info("Verify peerinfo block timestamps")
71 peerinfo = node.getpeerinfo()[0]
72 assert peerinfo['last_block'] == cur_time
73 assert peerinfo['last_block_announcement'] == cur_time
74 75 self.log.info("Sending a block announcement with no new blocks")
76 node.setmocktime(cur_time+1)
77 headers_message = msg_headers()
78 headers_message.headers = [CBlockHeader(block)]
79 node.p2ps[0].send_and_ping(headers_message)
80 81 self.log.info("Verify that block announcement time isn't updated")
82 peerinfo = node.getpeerinfo()[0]
83 assert peerinfo['last_block_announcement'] == cur_time
84 85 if __name__ == '__main__':
86 P2PBlockTimes(__file__).main()
87