class KademliaProtocol(RPCProtocol): def __init__(self, sourceNode, storage, ksize): RPCProtocol.__init__(self) self.router = RoutingTable(self, ksize, sourceNode) self.storage = storage self.sourceNode = sourceNode def getRefreshIDs(self): """ Get ids to search for to keep old buckets up to date. """ ids = [] for bucket in self.router.getLonelyBuckets(): rid = random.randint(*bucket.range).to_bytes(20, byteorder='big') ids.append(rid) return ids def rpc_stun(self, sender): return sender def rpc_ping(self, sender, nodeid): source = Node(nodeid, sender[0], sender[1]) self.welcomeIfNewNode(source) return self.sourceNode.id def rpc_store(self, sender, nodeid, key, value): source = Node(nodeid, sender[0], sender[1]) self.welcomeIfNewNode(source) log.debug("got a store request from %s, storing '%s'='%s'", sender, key.hex(), value) self.storage[key] = value return True def rpc_find_node(self, sender, nodeid, key): log.info("finding neighbors of %i in local table", int(nodeid.hex(), 16)) source = Node(nodeid, sender[0], sender[1]) self.welcomeIfNewNode(source) node = Node(key) neighbors = self.router.findNeighbors(node, exclude=source) return list(map(tuple, neighbors)) def rpc_find_value(self, sender, nodeid, key): source = Node(nodeid, sender[0], sender[1]) self.welcomeIfNewNode(source) value = self.storage.get(key, None) if value is None: return self.rpc_find_node(sender, nodeid, key) return {'value': value} async def callFindNode(self, nodeToAsk, nodeToFind): address = (nodeToAsk.ip, nodeToAsk.port) result = await self.find_node(address, self.sourceNode.id, nodeToFind.id) return self.handleCallResponse(result, nodeToAsk) async def callFindValue(self, nodeToAsk, nodeToFind): address = (nodeToAsk.ip, nodeToAsk.port) result = await self.find_value(address, self.sourceNode.id, nodeToFind.id) return self.handleCallResponse(result, nodeToAsk) async def callPing(self, nodeToAsk): address = (nodeToAsk.ip, nodeToAsk.port) result = await self.ping(address, self.sourceNode.id) return self.handleCallResponse(result, nodeToAsk) async def callStore(self, nodeToAsk, key, value): address = (nodeToAsk.ip, nodeToAsk.port) result = await self.store(address, self.sourceNode.id, key, value) return self.handleCallResponse(result, nodeToAsk) def welcomeIfNewNode(self, node): """ Given a new node, send it all the keys/values it should be storing, then add it to the routing table. @param node: A new node that just joined (or that we just found out about). Process: For each key in storage, get k closest nodes. If newnode is closer than the furtherst in that list, and the node for this server is closer than the closest in that list, then store the key/value on the new node (per section 2.5 of the paper) """ if not self.router.isNewNode(node): return log.info("never seen %s before, adding to router", node) for key, value in self.storage.items(): keynode = Node(digest(key)) neighbors = self.router.findNeighbors(keynode) if len(neighbors) > 0: last = neighbors[-1].distanceTo(keynode) newNodeClose = node.distanceTo(keynode) < last first = neighbors[0].distanceTo(keynode) thisNodeClosest = self.sourceNode.distanceTo(keynode) < first if len(neighbors) == 0 or (newNodeClose and thisNodeClosest): asyncio.ensure_future(self.callStore(node, key, value)) self.router.addContact(node) def handleCallResponse(self, result, node): """ If we get a response, add the node to the routing table. If we get no response, make sure it's removed from the routing table. """ if not result[0]: log.warning("no response from %s, removing from router", node) self.router.removeContact(node) return result log.info("got successful response from %s", node) self.welcomeIfNewNode(node) return result
class KademliaProtocol(RPCProtocol): def __init__(self, sourceNode, storage, ksize): RPCProtocol.__init__(self) self.router = RoutingTable(self, ksize, sourceNode) self.storage = storage self.sourceNode = sourceNode self.log = Logger(system=self) def getRefreshIDs(self): """ Get ids to search for to keep old buckets up to date. """ ids = [] for bucket in self.router.getLonelyBuckets(): ids.append(random.randint(*bucket.range)) return ids def rpc_stun(self, sender): return sender def rpc_ping(self, sender, nodeid): source = Node(nodeid, sender[0], sender[1]) self.router.addContact(source) return self.sourceNode.id def rpc_store(self, sender, nodeid, key, value): source = Node(nodeid, sender[0], sender[1]) self.router.addContact(source) self.log.debug("got a store request from %s, storing value" % str(sender)) self.storage[key] = value return True def rpc_find_node(self, sender, nodeid, key): self.log.info("finding neighbors of %i in local table" % long(nodeid.encode('hex'), 16)) source = Node(nodeid, sender[0], sender[1]) self.router.addContact(source) node = Node(key) return map(tuple, self.router.findNeighbors(node, exclude=source)) def rpc_find_value(self, sender, nodeid, key): source = Node(nodeid, sender[0], sender[1]) self.router.addContact(source) value = self.storage.get(key, None) if value is None: return self.rpc_find_node(sender, nodeid, key) return { 'value': value } def callFindNode(self, nodeToAsk, nodeToFind): address = (nodeToAsk.ip, nodeToAsk.port) d = self.find_node(address, self.sourceNode.id, nodeToFind.id) return d.addCallback(self.handleCallResponse, nodeToAsk) def callFindValue(self, nodeToAsk, nodeToFind): address = (nodeToAsk.ip, nodeToAsk.port) d = self.find_value(address, self.sourceNode.id, nodeToFind.id) return d.addCallback(self.handleCallResponse, nodeToAsk) def callPing(self, nodeToAsk): address = (nodeToAsk.ip, nodeToAsk.port) d = self.ping(address, self.sourceNode.id) return d.addCallback(self.handleCallResponse, nodeToAsk) def callStore(self, nodeToAsk, key, value): address = (nodeToAsk.ip, nodeToAsk.port) d = self.store(address, self.sourceNode.id, key, value) return d.addCallback(self.handleCallResponse, nodeToAsk) def transferKeyValues(self, node): """ Given a new node, send it all the keys/values it should be storing. @param node: A new node that just joined (or that we just found out about). Process: For each key in storage, get k closest nodes. If newnode is closer than the furtherst in that list, and the node for this server is closer than the closest in that list, then store the key/value on the new node (per section 2.5 of the paper) """ ds = [] for key, value in self.storage.iteritems(): keynode = Node(digest(key)) neighbors = self.router.findNeighbors(keynode) if len(neighbors) > 0: newNodeClose = node.distanceTo(keynode) < neighbors[-1].distanceTo(keynode) thisNodeClosest = self.sourceNode.distanceTo(keynode) < neighbors[0].distanceTo(keynode) if len(neighbors) == 0 or (newNodeClose and thisNodeClosest): ds.append(self.callStore(node, key, value)) return defer.gatherResults(ds) def handleCallResponse(self, result, node): """ If we get a response, add the node to the routing table. If we get no response, make sure it's removed from the routing table. """ if result[0]: self.log.info("got response from %s, adding to router" % node) self.router.addContact(node) if self.router.isNewNode(node): self.transferKeyValues(node) else: self.log.debug("no response from %s, removing from router" % node) self.router.removeContact(node) return result
class KademliaProtocol(RPCProtocol): def __init__(self, sourceNode, storage, ksize): RPCProtocol.__init__(self) self.router = RoutingTable(self, ksize, sourceNode) self.storage = storage self.sourceNode = sourceNode self.log = Logger(system=self) def getRefreshIDs(self): """ Get ids to search for to keep old buckets up to date. """ ids = [] for bucket in self.router.getLonelyBuckets(): ids.append(random.randint(*bucket.range)) return ids def rpc_stun(self, sender): return sender def rpc_ping(self, sender, nodeid): source = Node(nodeid, sender[0], sender[1]) self.router.addContact(source) return self.sourceNode.id def rpc_store(self, sender, nodeid, key, value): source = Node(nodeid, sender[0], sender[1]) self.router.addContact(source) self.log.debug("got a store request from %s, storing value" % str(sender)) self.storage[key] = value return True def rpc_find_node(self, sender, nodeid, key): self.log.info("finding neighbors of %i in local table" % long(nodeid.encode('hex'), 16)) source = Node(nodeid, sender[0], sender[1]) self.router.addContact(source) node = Node(key) return map(tuple, self.router.findNeighbors(node, exclude=source)) def rpc_find_value(self, sender, nodeid, key): source = Node(nodeid, sender[0], sender[1]) self.router.addContact(source) value = self.storage.get(key, None) if value is None: return self.rpc_find_node(sender, nodeid, key) return {'value': value} def callFindNode(self, nodeToAsk, nodeToFind): address = (nodeToAsk.ip, nodeToAsk.port) d = self.find_node(address, self.sourceNode.id, nodeToFind.id) return d.addCallback(self.handleCallResponse, nodeToAsk) def callFindValue(self, nodeToAsk, nodeToFind): address = (nodeToAsk.ip, nodeToAsk.port) d = self.find_value(address, self.sourceNode.id, nodeToFind.id) return d.addCallback(self.handleCallResponse, nodeToAsk) def callPing(self, nodeToAsk): address = (nodeToAsk.ip, nodeToAsk.port) d = self.ping(address, self.sourceNode.id) return d.addCallback(self.handleCallResponse, nodeToAsk) def callStore(self, nodeToAsk, key, value): address = (nodeToAsk.ip, nodeToAsk.port) d = self.store(address, self.sourceNode.id, key, value) return d.addCallback(self.handleCallResponse, nodeToAsk) def transferKeyValues(self, node): """ Given a new node, send it all the keys/values it should be storing. @param node: A new node that just joined (or that we just found out about). Process: For each key in storage, get k closest nodes. If newnode is closer than the furtherst in that list, and the node for this server is closer than the closest in that list, then store the key/value on the new node (per section 2.5 of the paper) """ ds = [] for key, value in self.storage.iteritems(): keynode = Node(digest(key)) neighbors = self.router.findNeighbors(keynode) if len(neighbors) > 0: newNodeClose = node.distanceTo( keynode) < neighbors[-1].distanceTo(keynode) thisNodeClosest = self.sourceNode.distanceTo( keynode) < neighbors[0].distanceTo(keynode) if len(neighbors) == 0 or (newNodeClose and thisNodeClosest): ds.append(self.callStore(node, key, value)) return defer.gatherResults(ds) def handleCallResponse(self, result, node): """ If we get a response, add the node to the routing table. If we get no response, make sure it's removed from the routing table. """ if result[0]: self.log.info("got response from %s, adding to router" % node) self.router.addContact(node) if self.router.isNewNode(node): self.transferKeyValues(node) else: self.log.debug("no response from %s, removing from router" % node) self.router.removeContact(node) return result
class KademliaProtocol(RPCProtocol): def __init__(self, sourceNode, storage, ksize): RPCProtocol.__init__(self, waitTimeout=100) self.router = RoutingTable(self, ksize, sourceNode) self.storage = storage self.sourceNode = sourceNode def getRefreshIDs(self): """ Get ids to search for to keep old buckets up to date. """ ids = [] for bucket in self.router.getLonelyBuckets(): rid = random.randint(*bucket.range).to_bytes(20, byteorder='big') ids.append(rid) return ids def rpc_stun(self, sender): return sender def rpc_ping(self, sender, nodeid): source = Node(nodeid, sender[0], sender[1]) self.welcomeIfNewNode(source) return self.sourceNode.id async def rpc_store(self, sender, nodeid, key, value): log.debug("got a store request from %s, storing '%s'='%s'", sender, key.hex(), value) try: value_json = json.loads(value) if not self._get_dtl_record(key, value_json): raise UnauthorizedOperationException() source = Node(nodeid, sender[0], sender[1]) self.welcomeIfNewNode(source) log.debug(f"Received value for key {key.hex()} is valid," f" going to retrieve values stored under given key") stored_value = await self._get_most_common(key) new_value = Value.of_json(key, value_json) if not new_value.is_valid(): raise InvalidSignException( f"Invalid signature for value {value}") if stored_value: if isinstance(stored_value, ControlledValue): result = stored_value.add_value(new_value) else: validate_secure_value(key, new_value, stored_value) result = new_value else: result = ValueFactory.create_from_value(new_value) if not self._get_dtl_record(key, value_json): raise UnauthorizedOperationException() self.storage[key] = str(result) return True except AssertionError: log.exception( "Unable to store value, got value with unsupported format: %s", value) except UnauthorizedOperationException: log.exception( "Unable to store value, unauthorized storing attempt") except InvalidSignException: log.exception("Signature is not valid") except InvalidValueFormatException: log.exception( "Invalid value format, value should contain authorization") return False @staticmethod def _get_dtl_record(key, value_json): value__hash = digest256(key.hex() + value_json['authorization']['sign']).hex() return validatorRepository.get_by_id(value__hash) def rpc_find_node(self, sender, nodeid, key): log.info("finding neighbors of %i in local table", int(nodeid.hex(), 16)) source = Node(nodeid, sender[0], sender[1]) self.welcomeIfNewNode(source) node = Node(key) neighbors = self.router.findNeighbors(node, exclude=source) return list(map(tuple, neighbors)) def rpc_find_value(self, sender, nodeid, key): source = Node(nodeid, sender[0], sender[1]) self.welcomeIfNewNode(source) value = self.storage.get(key, None) if value is None: return self.rpc_find_node(sender, nodeid, key) signed_value = NodeMessage.of_params(key, value).to_json() return signed_value async def callFindNode(self, nodeToAsk, nodeToFind): address = (nodeToAsk.ip, nodeToAsk.port) result = await self.find_node(address, self.sourceNode.id, nodeToFind.id) return self.handleCallResponse(result, nodeToAsk) async def callFindValue(self, nodeToAsk, nodeToFind): address = (nodeToAsk.ip, nodeToAsk.port) result = await self.find_value(address, self.sourceNode.id, nodeToFind.id) return self.handleCallResponse(result, nodeToAsk) async def callPing(self, nodeToAsk): address = (nodeToAsk.ip, nodeToAsk.port) result = await self.ping(address, self.sourceNode.id) return self.handleCallResponse(result, nodeToAsk) async def callStore(self, nodeToAsk, key, value): address = (nodeToAsk.ip, nodeToAsk.port) result = await self.store(address, self.sourceNode.id, key, value) return self.handleCallResponse(result, nodeToAsk) def welcomeIfNewNode(self, node): """ Given a new node, send it all the keys/values it should be storing, then add it to the routing table. @param node: A new node that just joined (or that we just found out about). Process: For each key in storage, get k closest nodes. If newnode is closer than the furtherst in that list, and the node for this server is closer than the closest in that list, then store the key/value on the new node (per section 2.5 of the paper) """ if not self.router.isNewNode(node): return log.info("never seen %s before, adding to router", node) for key, value in self.storage.items(): keynode = Node(digest(key)) neighbors = self.router.findNeighbors(keynode) if len(neighbors) > 0: last = neighbors[-1].distanceTo(keynode) newNodeClose = node.distanceTo(keynode) < last first = neighbors[0].distanceTo(keynode) thisNodeClosest = self.sourceNode.distanceTo(keynode) < first if len(neighbors) == 0 or (newNodeClose and thisNodeClosest): values_to_republish = [] try: parsed_val = json.loads(value) if isinstance(parsed_val, list): [ values_to_republish.append(json.dumps(val)) for val in parsed_val ] else: values_to_republish.append(value) for val in values_to_republish: asyncio.ensure_future(self.callStore(node, key, val)) except Exception as ex: log.exception(ex) continue self.router.addContact(node) def handleCallResponse(self, result, node): """ If we get a response, add the node to the routing table. If we get no response, make sure it's removed from the routing table. """ if not result[0]: log.warning("no response from %s, removing from router", node) self.router.removeContact(node) return result log.info("got successful response from %s", node) self.welcomeIfNewNode(node) return result async def _get_most_common(self, key): log.info("Looking up key %s", key.hex()) node = Node(key) nearest = self.router.findNeighbors(node) if len(nearest) == 0: log.warning("There are no known neighbors to get key %s", key) return None # if this node has it, sign and add to found values it local_value = self.storage.get(key, None) spider = ValueSpiderCrawl(self, node, nearest, Config.K_SIZE, Config.ALPHA) if local_value: local_value = NodeMessage.of_params(key, local_value).to_json() responses = await spider.find([local_value]) else: responses = await spider.find() return select_most_common_response(key, responses)
class KademliaProtocol(RPCProtocol): def __init__(self, sourceNode, storage, ksize): RPCProtocol.__init__(self) self.router = RoutingTable(self, ksize, sourceNode) self.storage = storage self.sourceNode = sourceNode def getRefreshIDs(self): """ Get ids to search for to keep old buckets up to date. """ ids = [] for bucket in self.router.getLonelyBuckets(): print('DEBUG *bucket.range=', *bucket.range) rid = random.randint(*bucket.range).to_bytes(32, byteorder='big') ids.append(rid) return ids def rpc_stun(self, sender): return sender def rpc_ping(self, sender, nodeid): source = Node(nodeid, sender[0], sender[1]) self.welcomeIfNewNode(source) return self.sourceNode.id def rpc_store(self, sender, nodeid, key: bytes, value: bytes): """ Perform RPC store to local storage. There are two types of store requests, both represented as (key, value) pairs. A node can store (hash, transaction) or (hash, signature) """ log.debug("Someone with nodeid %s from %s asked me to store key=%s value=%s", nodeid.hex(), sender, key, value) # TODO # inspect message, verify transactions, store to local storage # or update signatures dirty = False source = Node(nodeid, sender[0], sender[1]) self.welcomeIfNewNode(source) # Decode payload from json tx = json.loads(value) log.debug('Decoded transaction%s type=%s', tx, type(tx)) # Load tx as a fitchain transaction ftx = FitchainTransaction(tx) # check validity of transaction if not ftx.valid: return False # if this transaction already exists, update signatures if self.storage.contains(key): # retrieve body and signatures from storage stored_ftx = decode_value(self.storage.get(key)) # e_value -> stored_ftx body, stored_sigs = stored_ftx['body'], stored_ftx['signatures'] # in both cases check the signatures of ftx new_sigs = ftx.get_signatures() for sender in new_sigs: # if sender is not stored already, add him and his signature if sender not in stored_sigs: # verify only new signatures # update local signatures stored_ftx['signatures'][sender] = new_sigs[sender] dirty = True else: # not in storage body = ftx.get_body() log.debug('Storing ftx') self.storage[key] = json.dumps(ftx.tx) if dirty: log.debug('Updating local storage with new signatures') self.storage[key] = json.dumps(e_value) # TODO broadcast to neighbors, except the node you received from # res = await Globals.kad_server.server.set_digest(key, ftx.tx, store_local) log.debug('sourceNode: %s', self.sourceNode) return True """ # if both fields in this tx, then this is a transaction if 'data' in tx and 'signatures' in tx: # load signatures of this transaction new_sigs = json.loads(tx['signatures']) # {'pubkey_sender': 'signature'} dirty = False # if this transaction already exists, update signatures if self.storage.contains(key): e_value = decode_value(self.storage.get(key)) body = e_value['body'] es = e_value['signatures'] else: # not in local storage # extract body # TODO check these fields exist body = tx['sender'] + tx['nonce'] + tx['data'] + tx['timestamp'] es = tx['signatures'] # extra check (optional) body_hash = digest(body).hex() assert key.decode() == body_hash, 'Corrupted transaction. Skipping...' log.debug("calculated hash %s claimed hash %s key %s", body_hash, tx['hash'], key.decode()) # in both cases check the signatures for sender in new_sigs: if sender not in es: # verify only new signatures pubkey = bytes.fromhex(sender) sig = bytes.fromhex(new_sigs[sender]) message = body.encode() is_valid = Globals.account.verify_sig_msg(message, sig, pubkey) if not is_valid: log.debug('from rpc_store signature is not valid!') return False else: # update local signatures e_value['signatures'][sender] = new_sigs[sender] dirty = True if dirty: log.debug('Updating local storage with new signatures') self.storage[key] = json.dumps(ev) # only signature elif 'signatures' in tx: new_sigs = tx['signatures'] if self.storage.contains(key): e_value = decode_value(self.storage.get(key)) body = e_value['body'] es = e_value['signatures'] else: log.info('Cannot verify signature of non existing transaction.') return False for sender in new_sigs: if sender not in es: # verify only new signatures pubkey = bytes.fromhex(sender) sig = bytes.fromhex(new_sigs[sender]) message = body.encode() # log.debug('SENDER COMPARISON %s == %s ', sender, es) is_valid = Globals.account.verify_sig_msg(message, sig, pubkey) if not is_valid: log.debug('from rpc_store signature is not valid!') return False else: # update local signatures e_value['signatures'][sender] = new_sigs[sender] dirty = True if dirty: log.debug('Updating local storage with new signatures') self.storage[key] = json.dumps(e_value) # no data nor signatures (non valid transaction) else: log.debug('This request is not valid. Skipping') return False """ def rpc_find_node(self, sender, nodeid, key): log.info("finding neighbors of %i in local table", int(nodeid.hex(), 16)) source = Node(nodeid, sender[0], sender[1]) self.welcomeIfNewNode(source) node = Node(key) neighbors = self.router.findNeighbors(node, exclude=source) return list(map(tuple, neighbors)) def rpc_find_value(self, sender, nodeid, key): source = Node(nodeid, sender[0], sender[1]) self.welcomeIfNewNode(source) value = self.storage.get(key, None) if value is None: return self.rpc_find_node(sender, nodeid, key) return {'value': value} async def callFindNode(self, nodeToAsk, nodeToFind): address = (nodeToAsk.ip, nodeToAsk.port) result = await self.find_node(address, self.sourceNode.id, nodeToFind.id) return self.handleCallResponse(result, nodeToAsk) async def callFindValue(self, nodeToAsk, nodeToFind): address = (nodeToAsk.ip, nodeToAsk.port) result = await self.find_value(address, self.sourceNode.id, nodeToFind.id) return self.handleCallResponse(result, nodeToAsk) async def callPing(self, nodeToAsk): address = (nodeToAsk.ip, nodeToAsk.port) result = await self.ping(address, self.sourceNode.id) return self.handleCallResponse(result, nodeToAsk) async def callStore(self, nodeToAsk, key, value): address = (nodeToAsk.ip, nodeToAsk.port) result = await self.store(address, self.sourceNode.id, key, value) return self.handleCallResponse(result, nodeToAsk) def welcomeIfNewNode(self, node): """ Given a new node, send it all the keys/values it should be storing, then add it to the routing table. @param node: A new node that just joined (or that we just found out about). Process: For each key in storage, get k closest nodes. If newnode is closer than the furtherst in that list, and the node for this server is closer than the closest in that list, then store the key/value on the new node (per section 2.5 of the paper) """ if not self.router.isNewNode(node): return log.info("never seen %s before, adding to router", node) for key, value in self.storage.items(): keynode = Node(digest(key)) neighbors = self.router.findNeighbors(keynode) if len(neighbors) > 0: last = neighbors[-1].distanceTo(keynode) newNodeClose = node.distanceTo(keynode) < last first = neighbors[0].distanceTo(keynode) thisNodeClosest = self.sourceNode.distanceTo(keynode) < first if len(neighbors) == 0 or (newNodeClose and thisNodeClosest): asyncio.ensure_future(self.callStore(node, key, value)) self.router.addContact(node) def handleCallResponse(self, result, node): """ If we get a response, add the node to the routing table. If we get no response, make sure it's removed from the routing table. """ if not result[0]: log.warning("no response from %s, removing from router", node) self.router.removeContact(node) return result log.info("got successful response from %s", node) self.welcomeIfNewNode(node) return result
class KademliaProtocol(RPCProtocol): def __init__(self, sourceNode, storage, ksize): RPCProtocol.__init__(self) self.router = RoutingTable(self, ksize, sourceNode) self.storage = storage self.sourceNode = sourceNode self.log = Logger(system=self) self.messages = [] def getRefreshIDs(self): """ Get ids to search for to keep old buckets up to date. """ ids = [] for bucket in self.router.getLonelyBuckets(): ids.append(random.randint(*bucket.range)) return ids def getMessages(self): if len(self.messages) == 0: return None newList = [] while len(self.messages) > 0: newList.append(self.messages.pop(0)) return newList def rpc_stun(self, sender): return sender def rpc_ping(self, sender, nodeid): source = Node(nodeid, sender[0], sender[1]) self.router.addContact(source) return self.sourceNode.id def rpc_store(self, sender, nodeid, key, value): source = Node(nodeid, sender[0], sender[1]) self.router.addContact(source) #Check if the timestamp of any existing value is larger than the new one. existingValue = self.storage.get(key, None) if existingValue: if existingValue[4] < value[4]: existingTimestamp = decodeTimestamp(existingValue[1], value[2]) else: self.log.debug("Local val unencrypted is too small") return True if (not existingValue) or (existingTimestamp < decodeTimestamp( value[1], value[2])): self.log.debug("got a store request from %s, storing value" % str(sender)) self.storage[key] = value return True else: self.log.debug( "IGNORING a store request from %s, existing timestamp %s is larger than new %s" % (str(sender), str(existingTimestamp), str(newTimestamp))) return True def rpc_send(self, sender, message): self.log.info("Received message: \"" + message.strip("\n") + "\" from address " + str(sender)) self.messages.append(message) return True def rpc_find_node(self, sender, nodeid, key): self.log.info("finding neighbors of %i in local table" % long(nodeid.encode('hex'), 16)) source = Node(nodeid, sender[0], sender[1]) self.router.addContact(source) node = Node(key) return map(tuple, self.router.findNeighbors(node, exclude=source)) def rpc_find_value(self, sender, nodeid, key): source = Node(nodeid, sender[0], sender[1]) self.router.addContact(source) value = self.storage.get(key, None) if value is None: return self.rpc_find_node(sender, nodeid, key) return {'value': value} def callFindNode(self, nodeToAsk, nodeToFind): address = (nodeToAsk.ip, nodeToAsk.port) d = self.find_node(address, self.sourceNode.id, nodeToFind.id) return d.addCallback(self.handleCallResponse, nodeToAsk) def callFindValue(self, nodeToAsk, nodeToFind): address = (nodeToAsk.ip, nodeToAsk.port) d = self.find_value(address, self.sourceNode.id, nodeToFind.id) return d.addCallback(self.handleCallResponse, nodeToAsk) def callPing(self, nodeToAsk): address = (nodeToAsk.ip, nodeToAsk.port) d = self.ping(address, self.sourceNode.id) return d.addCallback(self.handleCallResponse, nodeToAsk) def callStore(self, nodeToAsk, key, value): self.log.debug("Storing on %s" % str(nodeToAsk)) address = (nodeToAsk.ip, nodeToAsk.port) d = self.store(address, self.sourceNode.id, key, value) return d.addCallback(self.handleCallResponse, nodeToAsk) def callSend(self, message, addr, port): address = (addr, port) self.log.info("Sending message: \"" + message.strip("\n") + "\" to address " + str(address)) self.send(address, message) def transferKeyValues(self, node): """ Given a new node, send it all the keys/values it should be storing. @param node: A new node that just joined (or that we just found out about). Process: For each key in storage, get k closest nodes. If newnode is closer than the furtherst in that list, and the node for this server is closer than the closest in that list, then store the key/value on the new node (per section 2.5 of the paper) """ ds = [] for key, value in self.storage.iteritems(): keynode = Node(digest(key)) neighbors = self.router.findNeighbors(keynode) if len(neighbors) > 0: newNodeClose = node.distanceTo( keynode) < neighbors[-1].distanceTo(keynode) thisNodeClosest = self.sourceNode.distanceTo( keynode) < neighbors[0].distanceTo(keynode) if len(neighbors) == 0 or (newNodeClose and thisNodeClosest): ds.append(self.callStore(node, key, value)) return defer.gatherResults(ds) def handleCallResponse(self, result, node): """ If we get a response, add the node to the routing table. If we get no response, make sure it's removed from the routing table. """ if result[0]: self.log.debug("Result is %s" % str(result)) self.log.info("got response from %s, adding to router" % node) self.router.addContact(node) if self.router.isNewNode(node): self.transferKeyValues(node) else: self.log.debug("no response from %s, removing from router" % node) self.router.removeContact(node) return result