from dht_common import SIZE_OF_HASH, SIZE_OF_IP_ADDRESS, SIZE_OF_OT_VALUE, SIZE_OF_KEY, SIZE_OF_SIGNATURE, SIZE_OF_TIMESTAMP from qp_node import QP_Quorum from math import sqrt, ceil from collections import defaultdict class DHTPIR_Quorum(QP_Quorum): def __init__(self, quorumID, documentSize, numNodes, numItems=0, table=[]): QP_Quorum.__init__(self, quorumID, documentSize, numNodes, numItems, table) self.PHFGenerations = defaultdict(lambda: 0) self.PIRRetrievals = [defaultdict(lambda: 0) for i in range(self.numNodes)] def get_PHF_generations(self, whichNode): return self.PHFGenerations def get_PIR_retrievals(self, whichNode): return self.PIRRetrievals[whichNode] def insert(self, numKeys, numSignatures): self.numItems += 1 # Asker's ID sizeOfRequest = SIZE_OF_HASH # timestamp sizeOfRequest += SIZE_OF_TIMESTAMP # keys in request sizeOfRequest += SIZE_OF_KEY * numKeys # signatures in request sizeOfRequest += SIZE_OF_SIGNATURE * numSignatures # actual document sent sizeOfRequest += self.documentSize # signature over whole thing sizeOfRequest += SIZE_OF_SIGNATURE sizeOfResponse = SIZE_OF_HASH + SIZE_OF_SIGNATURE map(lambda x: x+1, self.nodeNumRounds) map(lambda x: x+1, self.nodeNumMessagesSent) map(lambda x: x+1, self.nodeNumMessagesRecv) map(lambda x: x+sizeOfResponse, self.nodeNumBytesSent) map(lambda x: x+sizeOfRequest, self.nodeNumBytesRecv) # This should guarantee that this always gives an optimal request/response # based on whether it's appropriate to block responses blockingFactor = ceil(sqrt(self.numItems / self.documentSize)) # NOTE: This works because Python 3 promotes ints to floats for "/" numRecords = ceil(self.numItems / blockingFactor) self.PHFGenerations[numRecords] += 1 def get_hash_function(self, whichNode, numKeys, numSignatures): # Asker's ID sizeOfRequest = SIZE_OF_HASH # timestamp sizeOfRequest += SIZE_OF_TIMESTAMP # keys in request sizeOfRequest += SIZE_OF_KEY * numKeys # signatures in request sizeOfRequest += SIZE_OF_SIGNATURE * numSignatures # This should guarantee that this always gives an optimal request/response # based on whether it's appropriate to block responses blockingFactor = ceil(sqrt(self.numItems / self.documentSize)) # NOTE: This works because Python 3 promotes ints to floats for "/" numRecords = ceil(self.numItems / blockingFactor) # From "Practical Perfect Hashing in Nearly Optimal Space" by Botelho et al. # A conservative but practical estimate of the size of the PHF is 2.7n *bits*, # for n = the number of valid keys in the PHF. # I round up for hopefully obvious reasons sizeOfResponse = ceil((2.7 * numRecords) / 8.0) + SIZE_OF_SIGNATURE self.nodeNumRounds[whichNode] += 1 self.nodeNumMessagesSent[whichNode] += 1 self.nodeNumMessagesRecv[whichNode] += 1 self.nodeNumBytesSent[whichNode] += sizeOfResponse self.nodeNumBytesRecv[whichNode] += sizeOfRequest return sizeOfResponse # This shouldn't be used, just here to make sure you don't try the RCP_Quorum function it overrides def retrieve(self): return None def PIR_retrieve(self, whichNode, numKeys, numSignatures): # Asker's ID sizeOfRequest = SIZE_OF_HASH # timestamp sizeOfRequest += SIZE_OF_TIMESTAMP # keys in request sizeOfRequest += SIZE_OF_KEY * numKeys # signatures in request sizeOfRequest += SIZE_OF_SIGNATURE * numSignatures # This should guarantee that this always gives an optimal request/response # based on whether it's appropriate to block responses blockingFactor = ceil(sqrt(self.numItems / self.documentSize)) # NOTE: This works because Python 3 promotes ints to floats for "/" numRecords = ceil(self.numItems / blockingFactor) recordSize = blockingFactor * self.documentSize self.nodeNumRounds[whichNode] += 1 self.nodeNumMessagesSent[whichNode] += 1 self.nodeNumMessagesRecv[whichNode] += 1 self.nodeNumBytesSent[whichNode] += recordSize # This is specifically because in Goldberg PIR, you need 1B per record self.nodeNumBytesRecv[whichNode] += numRecords + sizeOfRequest self.PIRRetrievals[whichNode][numRecords] += 1 return (numRecords, recordSize) # TODO: Add unit tests for size calculations if __name__ == "__main__": SIZE_OF_DOCUMENTS_IN_TEST = 1024 NUM_NODES_PER_QUORUM_IN_TEST = 10 test = DHTPIR_Quorum(0, SIZE_OF_DOCUMENTS_IN_TEST, NUM_NODES_PER_QUORUM_IN_TEST) [test.insert() for x in range(NUM_NODES_PER_QUORUM_IN_TEST)] [test.get_hash_function(x) for x in range(NUM_NODES_PER_QUORUM_IN_TEST)] print("Getting PHFs fires on all nodes correctly.") [test.PIR_retrieve(x) for x in range(NUM_NODES_PER_QUORUM_IN_TEST)] print("PIR retrieval fires on all nodes correctly.")