dht_simulator.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. from dht_common import compute_document_ID, MAX_ID
  2. from base_node import Base_Node
  3. import math
  4. class DHT_Simulator(object):
  5. def __init__(self, nodeType, numGroups, documentSize, numNodes):
  6. if nodeType == Base_Node:
  7. self.nodes = [nodeType(self.index_to_owner_node(i, numGroups), documentSize) for i in range(numGroups)]
  8. else:
  9. self.nodes = [nodeType(self.index_to_owner_node(i, numGroups), documentSize, numNodes) for i in range(numGroups)]
  10. self.__init_finger_tables()
  11. # Nobody else needs to call this, but when we put together all the nodes they need their finger table values updated
  12. def __init_finger_tables(self):
  13. numNodes = len(self.nodes)
  14. numEntries = math.ceil(math.log(numNodes, 2))
  15. [[self.nodes[i].insert_relation(self.index_to_owner_node(x), x) for x in [(2**j + i) % numNodes for j in range(numEntries)]] for i in range(numNodes)]
  16. # convert an index (of its internal data structure; consider this analogous to routing information)
  17. # to which node it should belong to (note that IDs are SHA3 outputs)
  18. def index_to_owner_node(self, which, numNodes=None):
  19. if not numNodes:
  20. numNodes = len(self.nodes)
  21. fullID = math.floor(which * MAX_ID / numNodes)
  22. return (fullID).to_bytes(32, byteorder="big")
  23. # convert a node ID (SHA3 output) to which index in the internal data structure it should be
  24. def owner_node_to_index(self, nodeID):
  25. numNodes = len(self.nodes)
  26. v = int.from_bytes(nodeID, byteorder='big')
  27. return math.floor(v * numNodes / MAX_ID)
  28. # let a client "route" to the node in question (accepts indices, which are analogous to routing information)
  29. def access_node(self, nodeID):
  30. if nodeID >= 0 and nodeID < len(self.nodes):
  31. return self.nodes[nodeID]
  32. else:
  33. return None
  34. def get_num_rounds_base(self, nodeID):
  35. if nodeID >= 0 and nodeID < len(self.nodes):
  36. return self.nodes[nodeID].get_num_rounds()
  37. else:
  38. return None
  39. def get_recent_num_rounds_base(self, nodeID):
  40. if nodeID >= 0 and nodeID < len(self.nodes):
  41. return self.nodes[nodeID].get_recent_num_rounds()
  42. else:
  43. return None
  44. def get_num_rounds(self, quorumID, nodeID):
  45. if quorumID >= 0 and quorumID < len(self.nodes):
  46. return self.nodes[quorumID].get_num_rounds(nodeID)
  47. else:
  48. return None
  49. def get_recent_num_rounds(self, quorumID, nodeID):
  50. if quorumID >= 0 and quorumID < len(self.nodes):
  51. return self.nodes[quorumID].get_recent_num_rounds(nodeID)
  52. else:
  53. return None
  54. def get_num_messages_sent_base(self, nodeID):
  55. if nodeID >= 0 and nodeID < len(self.nodes):
  56. return self.nodes[nodeID].get_num_messages_sent()
  57. else:
  58. return None
  59. def get_recent_num_messages_sent_base(self, nodeID):
  60. if nodeID >= 0 and nodeID < len(self.nodes):
  61. return self.nodes[nodeID].get_recent_num_messages_sent()
  62. else:
  63. return None
  64. def get_num_messages_sent(self, quorumID, nodeID):
  65. if quorumID >= 0 and quorumID < len(self.nodes):
  66. return self.nodes[quorumID].get_num_messages_sent(nodeID)
  67. else:
  68. return None
  69. def get_recent_num_messages_sent(self, quorumID, nodeID):
  70. if quorumID >= 0 and quorumID < len(self.nodes):
  71. return self.nodes[quorumID].get_recent_num_messages_sent(nodeID)
  72. else:
  73. return None
  74. def get_num_messages_recv_base(self, nodeID):
  75. if nodeID >= 0 and nodeID < len(self.nodes):
  76. return self.nodes[nodeID].get_num_messages_recv()
  77. else:
  78. return None
  79. def get_recent_num_messages_recv_base(self, nodeID):
  80. if nodeID >= 0 and nodeID < len(self.nodes):
  81. return self.nodes[nodeID].get_recent_num_messages_recv()
  82. else:
  83. return None
  84. def get_num_messages_recv(self, quorumID, nodeID):
  85. if quorumID >= 0 and quorumID < len(self.nodes):
  86. return self.nodes[quorumID].get_num_messages_recv(nodeID)
  87. else:
  88. return None
  89. def get_recent_num_messages_recv(self, quorumID, nodeID):
  90. if quorumID >= 0 and quorumID < len(self.nodes):
  91. return self.nodes[quorumID].get_recent_num_messages_recv(nodeID)
  92. else:
  93. return None
  94. def get_num_bytes_sent_base(self, nodeID):
  95. if nodeID >= 0 and nodeID < len(self.nodes):
  96. return self.nodes[nodeID].get_num_bytes_sent()
  97. else:
  98. return None
  99. def get_recent_num_bytes_sent_base(self, nodeID):
  100. if nodeID >= 0 and nodeID < len(self.nodes):
  101. return self.nodes[nodeID].get_recent_num_bytes_sent()
  102. else:
  103. return None
  104. def get_num_bytes_sent(self, quorumID, nodeID):
  105. if quorumID >= 0 and quorumID < len(self.nodes):
  106. return self.nodes[quorumID].get_num_bytes_sent(nodeID)
  107. else:
  108. return None
  109. def get_recent_num_bytes_sent(self, quorumID, nodeID):
  110. if quorumID >= 0 and quorumID < len(self.nodes):
  111. return self.nodes[quorumID].get_recent_num_bytes_sent(nodeID)
  112. else:
  113. return None
  114. def get_num_bytes_recv_base(self, nodeID):
  115. if nodeID >= 0 and nodeID < len(self.nodes):
  116. return self.nodes[nodeID].get_num_bytes_recv()
  117. else:
  118. return None
  119. def get_recent_num_bytes_recv_base(self, nodeID):
  120. if nodeID >= 0 and nodeID < len(self.nodes):
  121. return self.nodes[nodeID].get_recent_num_bytes_recv()
  122. else:
  123. return None
  124. def get_num_bytes_recv(self, quorumID, nodeID):
  125. if quorumID >= 0 and quorumID < len(self.nodes):
  126. return self.nodes[quorumID].get_num_bytes_recv(nodeID)
  127. else:
  128. return None
  129. def get_recent_num_bytes_recv(self, quorumID, nodeID):
  130. if quorumID >= 0 and quorumID < len(self.nodes):
  131. return self.nodes[quorumID].get_recent_num_bytes_recv(nodeID)
  132. else:
  133. return None
  134. # ONLY QP Nodes and higher will have these
  135. def get_finger_table_range_accesses(self, quorumID, nodeID):
  136. if quorumID >= 0 and quorumID < len(self.nodes):
  137. return self.nodes[quorumID].get_finger_table_range_accesses(nodeID)
  138. else:
  139. return None
  140. def get_finger_table_accesses(self, quorumID, nodeID):
  141. if quorumID >= 0 and quorumID < len(self.nodes):
  142. return self.nodes[quorumID].get_finger_table_accesses(nodeID)
  143. else:
  144. return None
  145. # ONLY QPLastHop Nodes will have these
  146. def get_database_accesses(self, quorumID, nodeID):
  147. if quorumID >= 0 and quorumID < len(self.nodes):
  148. return self.nodes[quorumID].get_database_accesses(nodeID)
  149. else:
  150. return None
  151. # ONLY DHTPIR Nodes will have these
  152. def get_PHF_generations(self, quorumID, nodeID):
  153. if quorumID >= 0 and quorumID < len(self.nodes):
  154. return self.nodes[quorumID].get_PHF_generations(nodeID)
  155. else:
  156. return None
  157. def get_PIR_retrievals(self, quorumID, nodeID):
  158. if quorumID >= 0 and quorumID < len(self.nodes):
  159. return self.nodes[quorumID].get_PIR_retrievals(nodeID)
  160. else:
  161. return None
  162. # End insert for calcuations
  163. def __access_node_tables(self, nodeID):
  164. if nodeID >= 0 and nodeID < len(self.nodes):
  165. return self.nodes[nodeID].table
  166. else:
  167. return None
  168. def get_num_nodes(self):
  169. return len(self.nodes)
  170. def test_tables(self):
  171. if self.get_num_nodes() == 10:
  172. retval = True
  173. table = self.__access_node_tables(5)
  174. retval = retval and table[b'\x99\x99\x99\x99\x99\x99\x98\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'] == 6
  175. retval = retval and table[b'\xb3333330\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'] == 7
  176. retval = retval and table[b'\xe6fffffh\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'] == 9
  177. retval = retval and table[b'L\xcc\xcc\xcc\xcc\xcc\xcc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'] == 3
  178. table = self.__access_node_tables(9)
  179. retval = retval and table[b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'] == 0
  180. retval = retval and table[b'\xb3333330\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'] == 7
  181. retval = retval and table[b'\x19\x99\x99\x99\x99\x99\x9a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'] == 1
  182. retval = retval and table[b'L\xcc\xcc\xcc\xcc\xcc\xcc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'] == 3
  183. return retval
  184. else:
  185. print("Assumed testing conditions not met; no tests run.")
  186. return False
  187. # Normally this file is a class to be used elsewhere,
  188. # but if you run it directly it performs some rudimentary unit tests
  189. if __name__ == "__main__":
  190. from base_node import Base_Node
  191. SIZE_OF_DOCUMENTS_IN_TEST = 1024
  192. NUM_NODES_IN_TEST = 10
  193. # The 1 here isn't used at all within the simulator,
  194. # but it is customary to invoke DHT_Simulator with that value there for Base_Node
  195. test = DHT_Simulator(Base_Node, NUM_NODES_IN_TEST, SIZE_OF_DOCUMENTS_IN_TEST, 1)
  196. assert test.index_to_owner_node(8) == b'\xcc\xcc\xcc\xcc\xcc\xcc\xd0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
  197. assert test.index_to_owner_node(4) == b'ffffffh\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
  198. assert test.owner_node_to_index(b'3333334\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') == 2
  199. assert test.owner_node_to_index(b'\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') == 5
  200. print("Index to nodeID conversion functioning correctly.")
  201. assert test.test_tables()
  202. print("Finger tables correctly generated.")