dhtpir_node.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. from dht_common import SIZE_OF_HASH, SIZE_OF_IP_ADDRESS, SIZE_OF_OT_VALUE, SIZE_OF_KEY, SIZE_OF_SIGNATURE, SIZE_OF_TIMESTAMP
  2. from qp_node import QP_Quorum
  3. from math import sqrt, ceil
  4. from collections import defaultdict
  5. class DHTPIR_Quorum(QP_Quorum):
  6. def __init__(self, quorumID, documentSize, numNodes, numItems=0, table=[]):
  7. QP_Quorum.__init__(self, quorumID, documentSize, numNodes, numItems, table)
  8. self.PHFGenerations = defaultdict(lambda: 0)
  9. self.PIRRetrievals = [defaultdict(lambda: 0) for i in range(self.numNodes)]
  10. def get_PHF_generations(self, whichNode):
  11. return self.PHFGenerations
  12. def get_PIR_retrievals(self, whichNode):
  13. return self.PIRRetrievals[whichNode]
  14. def insert(self, numKeys, numSignatures):
  15. self.numItems += 1
  16. # Asker's ID
  17. sizeOfRequest = SIZE_OF_HASH
  18. # timestamp
  19. sizeOfRequest += SIZE_OF_TIMESTAMP
  20. # keys in request
  21. sizeOfRequest += SIZE_OF_KEY * numKeys
  22. # signatures in request
  23. sizeOfRequest += SIZE_OF_SIGNATURE * numSignatures
  24. # actual document sent
  25. sizeOfRequest += self.documentSize
  26. # signature over whole thing
  27. sizeOfRequest += SIZE_OF_SIGNATURE
  28. sizeOfResponse = SIZE_OF_HASH + SIZE_OF_SIGNATURE
  29. map(lambda x: x+1, self.nodeNumRounds)
  30. map(lambda x: x+1, self.nodeNumMessagesSent)
  31. map(lambda x: x+1, self.nodeNumMessagesRecv)
  32. map(lambda x: x+sizeOfResponse, self.nodeNumBytesSent)
  33. map(lambda x: x+sizeOfRequest, self.nodeNumBytesRecv)
  34. # This should guarantee that this always gives an optimal request/response
  35. # based on whether it's appropriate to block responses
  36. blockingFactor = ceil(sqrt(self.numItems / self.documentSize))
  37. # NOTE: This works because Python 3 promotes ints to floats for "/"
  38. numRecords = ceil(self.numItems / blockingFactor)
  39. self.PHFGenerations[numRecords] += 1
  40. def get_hash_function(self, whichNode, numKeys, numSignatures):
  41. # Asker's ID
  42. sizeOfRequest = SIZE_OF_HASH
  43. # timestamp
  44. sizeOfRequest += SIZE_OF_TIMESTAMP
  45. # keys in request
  46. sizeOfRequest += SIZE_OF_KEY * numKeys
  47. # signatures in request
  48. sizeOfRequest += SIZE_OF_SIGNATURE * numSignatures
  49. # This should guarantee that this always gives an optimal request/response
  50. # based on whether it's appropriate to block responses
  51. blockingFactor = ceil(sqrt(self.numItems / self.documentSize))
  52. # NOTE: This works because Python 3 promotes ints to floats for "/"
  53. numRecords = ceil(self.numItems / blockingFactor)
  54. # From "Practical Perfect Hashing in Nearly Optimal Space" by Botelho et al.
  55. # A conservative but practical estimate of the size of the PHF is 2.7n *bits*,
  56. # for n = the number of valid keys in the PHF.
  57. # I round up for hopefully obvious reasons
  58. sizeOfResponse = ceil((2.7 * numRecords) / 8.0) + SIZE_OF_SIGNATURE
  59. self.nodeNumRounds[whichNode] += 1
  60. self.nodeNumMessagesSent[whichNode] += 1
  61. self.nodeNumMessagesRecv[whichNode] += 1
  62. self.nodeNumBytesSent[whichNode] += sizeOfResponse
  63. self.nodeNumBytesRecv[whichNode] += sizeOfRequest
  64. return sizeOfResponse
  65. # This shouldn't be used, just here to make sure you don't try the RCP_Quorum function it overrides
  66. def retrieve(self):
  67. return None
  68. def PIR_retrieve(self, whichNode, numKeys, numSignatures):
  69. # Asker's ID
  70. sizeOfRequest = SIZE_OF_HASH
  71. # timestamp
  72. sizeOfRequest += SIZE_OF_TIMESTAMP
  73. # keys in request
  74. sizeOfRequest += SIZE_OF_KEY * numKeys
  75. # signatures in request
  76. sizeOfRequest += SIZE_OF_SIGNATURE * numSignatures
  77. # This should guarantee that this always gives an optimal request/response
  78. # based on whether it's appropriate to block responses
  79. blockingFactor = ceil(sqrt(self.numItems / self.documentSize))
  80. # NOTE: This works because Python 3 promotes ints to floats for "/"
  81. numRecords = ceil(self.numItems / blockingFactor)
  82. recordSize = blockingFactor * self.documentSize
  83. self.nodeNumRounds[whichNode] += 1
  84. self.nodeNumMessagesSent[whichNode] += 1
  85. self.nodeNumMessagesRecv[whichNode] += 1
  86. self.nodeNumBytesSent[whichNode] += recordSize
  87. # This is specifically because in Goldberg PIR, you need 1B per record
  88. self.nodeNumBytesRecv[whichNode] += numRecords + sizeOfRequest
  89. self.PIRRetrievals[whichNode][numRecords] += 1
  90. return (numRecords, recordSize)
  91. # TODO: Add unit tests for size calculations
  92. if __name__ == "__main__":
  93. SIZE_OF_DOCUMENTS_IN_TEST = 1024
  94. NUM_NODES_PER_QUORUM_IN_TEST = 10
  95. test = DHTPIR_Quorum(0, SIZE_OF_DOCUMENTS_IN_TEST, NUM_NODES_PER_QUORUM_IN_TEST)
  96. [test.insert() for x in range(NUM_NODES_PER_QUORUM_IN_TEST)]
  97. [test.get_hash_function(x) for x in range(NUM_NODES_PER_QUORUM_IN_TEST)]
  98. print("Getting PHFs fires on all nodes correctly.")
  99. [test.PIR_retrieve(x) for x in range(NUM_NODES_PER_QUORUM_IN_TEST)]
  100. print("PIR retrieval fires on all nodes correctly.")