def test_find_node_Received_sendsValidResponseMultipleNodes(self): # Create the protocol and populate its # routing table with nodes kresponder = Patched_KRPC_Responder() node_list = [] node_gen = lambda num: contact.Node(num, ("127.0.0.%d" % num, num)) for i in range(100): n = node_gen(i) if kresponder.routing_table.offer_node(n): node_list.append(n) querying_node = contact.Node(123, test_address) incoming_query = Query() incoming_query.rpctype = "find_node" incoming_query._from = querying_node.node_id incoming_query._transaction_id = 15 incoming_query.target_id = 777777 expected_response = Response() expected_response._from = kresponder.node_id expected_response._transaction_id = 15 expected_response.rpctype = "find_node" node_list.sort( key=lambda node: node.distance(incoming_query.target_id)) node_list = node_list[:constants.k] expected_response.nodes = node_list kresponder.datagramReceived(krpc_coder.encode(incoming_query), test_address) actual_response = kresponder.sendResponse.response self.assertEquals(expected_response, actual_response)
def test_better_than_bothOldWithNoQueries(self): self.clock.set(0) n1 = contact.Node(2**1, ("127.0.0.1", 1111)) n2 = contact.Node(2**2, ("127.0.0.1", 2222)) self.clock.set(constants.node_timeout + 1) self.assertFalse(n1.better_than(n2)) self.assertFalse(n2.better_than(n1))
def test_better_than_oneFreshOneOld(self): self.clock.set(0) n_old = contact.Node(2**1, ("127.0.0.1", 1111)) n_fresh = contact.Node(2**2, ("127.0.0.1", 2222)) self.clock.set(constants.node_timeout + 1) n_fresh.successful_query(10) self.assertTrue(n_fresh.better_than(n_old)) self.assertFalse(n_old.better_than(n_fresh))
def test_better_than_oneBetterRTT(self): self.clock.set(0) n_slow = contact.Node(2**1, ("127.0.0.1", 1111)) n_fast = contact.Node(2**2, ("127.0.0.1", 2222)) self.clock.set(10) n_slow.successful_query(0) n_fast.successful_query(5) self.assertTrue(n_fast.better_than(n_slow)) self.assertFalse(n_slow.better_than(n_fast))
def test_distance(self): node_ids1 = [0, 1024, 2**150, 2**159 + 124, 2**34 - 58] node_ids2 = [0, 857081, 6**7, 8**9 + 7**3, 4**8 + 9**10 + 18] for id1 in node_ids1: for id2 in node_ids2: n = contact.Node(id1, ("127.0.0.1", 8000)) self.assertEqual(id1 ^ id2, n.distance(id2)) n = contact.Node(id2, ("127.0.0.1", 8000)) self.assertEqual(id2 ^ id1, n.distance(id1))
def test_get_peers_Received_sendsValidResponseWithNodes(self): # Create the protocol and populate its # routing table with nodes # We need a node_id close to 'target_id' so that our kbuckets split # in a way that we will have the target id target_id = 76 our_id = target_id - 1 kresponder = Patched_KRPC_Responder(node_id=our_id) node_list = [] node_gen = lambda num: contact.Node(num, ("127.0.0.%d" % num, num)) for i in range(100): if i != our_id: n = node_gen(i) if kresponder.routing_table.offer_node(n): node_list.append(n) # simulate that a get_peers query has been # received by making a fake get_peers query # and feeding it into "datagramReceived()" querying_node = contact.Node(123, test_address) incoming_query = Query() incoming_query.rpctype = "get_peers" incoming_query._from = querying_node.node_id incoming_query._transaction_id = 15 # We have this target id in our routing table incoming_query.target_id = target_id # Create a response object and ensure # and the response (that the node sends) # matches what we made expected_response = Response() expected_response._from = kresponder.node_id expected_response._transaction_id = incoming_query._transaction_id expected_response.rpctype = "get_peers" # the specification calls for the resulting # nodes to be sorted by distance node_list.sort( key=lambda node: node.distance(incoming_query.target_id)) node_list = node_list[:constants.k] expected_response.nodes = node_list # simulating the incoming query and capture # the outgoing response kresponder.datagramReceived(krpc_coder.encode(incoming_query), test_address) actual_response = kresponder.sendResponse.response # Grab the autogenerated token expected_response.token = actual_response.token self.assertEquals(expected_response, actual_response)
def test_fresh(self): n = contact.Node(2**17, ("127.0.0.1", 8012)) self.assertTrue(n.fresh()) # Simulate that `constants.node_timeout' time has passed self.clock.set(constants.node_timeout + 1) self.assertFalse(n.fresh()) # Refresh the node with a new query n.successful_query(10) self.assertTrue(n.fresh())
def _save_received_node(self, krpc, address): """ save node from the out request node if it is not in routing table yet """ if self.routing_table.get_node(krpc._from): return node = contact.Node(node_id=krpc._from, address=address) self.routing_table.offer_node(node) log.msg("save the request from node(%s:%s) done" % address)
def test_find_node_Received_sendsValidResponseWithTargetNode(self): # Create the protocol and populate its # routing table with nodes # We need a node_id close to 'target_id' so that our kbuckets split # in a way that we will have the target id target_id = 76 our_id = target_id - 1 kresponder = Patched_KRPC_Responder(node_id=our_id) node_list = [] node_gen = lambda num: contact.Node(num, ("127.0.0.%d" % num, num)) for i in range(100): if i != our_id: n = node_gen(i) node_was_accepted = kresponder.routing_table.offer_node(n) if node_was_accepted: node_list.append(n) querying_node = contact.Node(123, test_address) incoming_query = Query() incoming_query.rpctype = "find_node" incoming_query._from = querying_node.node_id incoming_query._transaction_id = 15 # We have this target id in our routing table incoming_query.target_id = target_id expected_response = Response() expected_response._from = kresponder.node_id expected_response._transaction_id = 15 expected_response.rpctype = "find_node" # The response node_list should contain only the target node node_list = filter(lambda node: node.node_id == target_id, node_list) expected_response.nodes = node_list if len(node_list) != 1: self.fail("Too many or too few nodes!") kresponder.datagramReceived(krpc_coder.encode(incoming_query), test_address) actual_response = kresponder.sendResponse.response self.assertEquals(expected_response, actual_response)
def _init_routing_table(): """ restore routing table """ node_list = database["routing_table"].find() for _node in node_list: node = contact.Node( node_id=int(_node["_id"]), address=(_node["ip"], _node["port"]), last_updated=_node["last_updated"], totalrtt=_node["totalrtt"], successcount=_node["successcount"], failcount=_node["failcount"], ) TreeRoutingTable._instance.offer_node(node)
def _query_success(self, response, address, transaction): """ Handle a valid Response to an outstanding Query This callback records changes to the statistics for the node behind the address/response (ie, it updates its RTT and makes sures it is in the routing table) """ # Pull the node corresponding to this response out # of our routing table, or create it if it doesn't exist response_node = self.routing_table.get_node(response._from) if response_node is None: response_node = contact.Node(response._from, address) response_node.successful_query(transaction.time) self.routing_table.offer_node(response_node) # Pass the response further down the callback chain return response
def _query_success_callback(self, response, address, transaction): """ Handle a valid Response to an outstanding Query This callback records changes to the statistics for the node behind the address/response (ie, it updates its RTT and makes sures it is in the routing table) """ # Pull the node corresponding to this response out # of our routing table, or create it if it doesn't exist log.msg("_query_success_callback") # get/create node, and update the node's activeness for later calculation rt_node = self.routing_table.get_node(response._from) responsenode = (rt_node if rt_node is not None else contact.Node(response._from, address)) responsenode.successful_query(transaction.time) self.routing_table.offer_node(responsenode) # Pass the response further down the callback chain return response
from mdht import contact _make_node = lambda x: contact.Node(x, ('127.0.0.1', x)) test_nodes = [_make_node(x) for x in range(1, 256)] class Clock(object): """ >>> import time >>> time.time = Clock() >>> time.time() 0 >>> time.time.set(5) >>> time.time() 5 >>> """ def __init__(self): self._time = 0 def __call__(self): return self.time() def time(self): return self._time def set(self, time): self._time = time class Counter(object):