You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

p2p-leaktests.py 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. #!/usr/bin/env python3
  2. # Copyright (c) 2017 The Bitcoin Core developers
  3. # Distributed under the MIT software license, see the accompanying
  4. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
  5. """Test message sending before handshake completion.
  6. A node should never send anything other than VERSION/VERACK/REJECT until it's
  7. received a VERACK.
  8. This test connects to a node and sends it a few messages, trying to intice it
  9. into sending us something it shouldn't.
  10. """
  11. from test_framework.mininode import *
  12. from test_framework.test_framework import BitcoinTestFramework
  13. from test_framework.util import *
  14. banscore = 10
  15. class CLazyNode(NodeConnCB):
  16. def __init__(self):
  17. self.connection = None
  18. self.unexpected_msg = False
  19. self.connected = False
  20. super().__init__()
  21. def add_connection(self, conn):
  22. self.connection = conn
  23. def send_message(self, message):
  24. self.connection.send_message(message)
  25. def bad_message(self, message):
  26. self.unexpected_msg = True
  27. self.log.info("should not have received message: %s" % message.command)
  28. def on_open(self, conn):
  29. self.connected = True
  30. def on_version(self, conn, message): self.bad_message(message)
  31. def on_verack(self, conn, message): self.bad_message(message)
  32. def on_reject(self, conn, message): self.bad_message(message)
  33. def on_inv(self, conn, message): self.bad_message(message)
  34. def on_addr(self, conn, message): self.bad_message(message)
  35. def on_alert(self, conn, message): self.bad_message(message)
  36. def on_getdata(self, conn, message): self.bad_message(message)
  37. def on_getblocks(self, conn, message): self.bad_message(message)
  38. def on_tx(self, conn, message): self.bad_message(message)
  39. def on_block(self, conn, message): self.bad_message(message)
  40. def on_getaddr(self, conn, message): self.bad_message(message)
  41. def on_headers(self, conn, message): self.bad_message(message)
  42. def on_getheaders(self, conn, message): self.bad_message(message)
  43. def on_ping(self, conn, message): self.bad_message(message)
  44. def on_mempool(self, conn): self.bad_message(message)
  45. def on_pong(self, conn, message): self.bad_message(message)
  46. def on_feefilter(self, conn, message): self.bad_message(message)
  47. def on_sendheaders(self, conn, message): self.bad_message(message)
  48. def on_sendcmpct(self, conn, message): self.bad_message(message)
  49. def on_cmpctblock(self, conn, message): self.bad_message(message)
  50. def on_getblocktxn(self, conn, message): self.bad_message(message)
  51. def on_blocktxn(self, conn, message): self.bad_message(message)
  52. # Node that never sends a version. We'll use this to send a bunch of messages
  53. # anyway, and eventually get disconnected.
  54. class CNodeNoVersionBan(CLazyNode):
  55. def __init__(self):
  56. super().__init__()
  57. # send a bunch of veracks without sending a message. This should get us disconnected.
  58. # NOTE: implementation-specific check here. Remove if bitcoind ban behavior changes
  59. def on_open(self, conn):
  60. super().on_open(conn)
  61. for i in range(banscore):
  62. self.send_message(msg_verack())
  63. def on_reject(self, conn, message): pass
  64. # Node that never sends a version. This one just sits idle and hopes to receive
  65. # any message (it shouldn't!)
  66. class CNodeNoVersionIdle(CLazyNode):
  67. def __init__(self):
  68. super().__init__()
  69. # Node that sends a version but not a verack.
  70. class CNodeNoVerackIdle(CLazyNode):
  71. def __init__(self):
  72. self.version_received = False
  73. super().__init__()
  74. def on_reject(self, conn, message): pass
  75. def on_verack(self, conn, message): pass
  76. # When version is received, don't reply with a verack. Instead, see if the
  77. # node will give us a message that it shouldn't. This is not an exhaustive
  78. # list!
  79. def on_version(self, conn, message):
  80. self.version_received = True
  81. conn.send_message(msg_ping())
  82. conn.send_message(msg_getaddr())
  83. class P2PLeakTest(BitcoinTestFramework):
  84. def __init__(self):
  85. super().__init__()
  86. self.num_nodes = 1
  87. def setup_network(self):
  88. extra_args = [['-banscore='+str(banscore)]
  89. for i in range(self.num_nodes)]
  90. self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, extra_args)
  91. def run_test(self):
  92. no_version_bannode = CNodeNoVersionBan()
  93. no_version_idlenode = CNodeNoVersionIdle()
  94. no_verack_idlenode = CNodeNoVerackIdle()
  95. connections = []
  96. connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_version_bannode, send_version=False))
  97. connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_version_idlenode, send_version=False))
  98. connections.append(NodeConn('127.0.0.1', p2p_port(0), self.nodes[0], no_verack_idlenode))
  99. no_version_bannode.add_connection(connections[0])
  100. no_version_idlenode.add_connection(connections[1])
  101. no_verack_idlenode.add_connection(connections[2])
  102. NetworkThread().start() # Start up network handling in another thread
  103. assert(wait_until(lambda: no_version_bannode.connected and no_version_idlenode.connected and no_verack_idlenode.version_received, timeout=10))
  104. # Mine a block and make sure that it's not sent to the connected nodes
  105. self.nodes[0].generate(1)
  106. #Give the node enough time to possibly leak out a message
  107. time.sleep(5)
  108. #This node should have been banned
  109. assert(no_version_bannode.connection.state == "closed")
  110. [conn.disconnect_node() for conn in connections]
  111. # Make sure no unexpected messages came in
  112. assert(no_version_bannode.unexpected_msg == False)
  113. assert(no_version_idlenode.unexpected_msg == False)
  114. assert(no_verack_idlenode.unexpected_msg == False)
  115. if __name__ == '__main__':
  116. P2PLeakTest().main()