p2p_block_times.py raw

   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