def test_ne(self): a = Kbucket(20, 0, 63, 5, 7) n = Node(None, None, 100) self.assertTrue(a != n) n = Node(None, None, 50) self.assertFalse(a != n)
def test_ge(self): a = Kbucket(20, 64, 127, 5, 7) n = Node(None, None, 65) self.assertTrue(a >= n) n = Node(None, None, 128) self.assertFalse(a >= n)
def test_eq(self): a = Kbucket(20, 0, 127, 5, 7) n = Node(None, None, 100) self.assertTrue(a == n) n = Node(None, None, 128) self.assertFalse(a == n)
def test_le(self): a = Kbucket(20, 0, 63, 5, 7) n = Node(None, None, 63) self.assertTrue(a <= n) n = Node(None, None, -1) self.assertFalse(a <= n)
def test_add_node(self): node = Node(None, None, 100) tree = RoutingTree(node, 2, 5, 5, 8) add_node = Node(None, None, 25) self.assertEqual(tree.addNode(add_node), None) index = tree.bucketIndex(add_node) self.assertIn(add_node, tree.buckets[index].nodes)
def test_seen(self,time_mock): time_mock.time.return_value = 1000 n = Node('127.0.0.1',5000,100) n.error() self.assertEquals(n.errors, 1) self.assertEquals(n.last_seen, 0) n.seen() self.assertEquals(n.errors, 0) self.assertEquals(n.last_seen, 1000)
def setUp(self): self.key_space = 8 self.bucket = Kbucket(5, 0, (2**self.key_space) - 1, 5, self.key_space) self.nodes = [ Node(None, None, 0), Node(None, None, 25), Node(None, None, 50), Node(None, None, 255), Node(None, None, 230), ]
def test_split(self): for n in self.nodes: self.bucket.addNode(n) expected_kept = [ Node(None, None, 0), Node(None, None, 25), Node(None, None, 50) ] expected_evicted = [Node(None, None, 255), Node(None, None, 230)] evicted = self.bucket.updateRange(128) self.assertEquals(expected_evicted, evicted) self.assertEquals(expected_kept, self.bucket.nodes)
def test_split_bucket(self): this = Node(None, None, 100) r = RoutingTree(this, 2, 5, 5, 8) nodes = [ Node(None, None, 0), Node(None, None, 25), Node(None, None, 30), Node(None, None, 50), Node(None, None, 200), Node(None, None, 255), Node(None, None, 230), ] for node in nodes: r.addNode(node) # expected nodes split into buckets along with the bucket min/max expected = [(0, 15, [0]), (16, 31, [25, 30]), (32, 63, [50]), (64, 127, [100]), (128, 191, []), (192, 223, [200]), (224, 255, [255, 230])] for i in zip(r.buckets, expected): bucket, (mi, mx, nodes) = i self.assertEqual(bucket.range_min, mi) self.assertEqual(bucket.range_max, mx) for j in zip(bucket.nodes, nodes): self.assertEqual(j[0].id, j[1])
def __init__(self, network, client_chan, node=None, alpha = 3): self.alpha = alpha # concurrent network queries self.chan = client_chan # channel for internal & network rpcs self.network = network # interface to our network (nonblocking sends) if node == None: node = Node(None, None, None) self.addr, self.port = self.network.connect(self.chan.fetch_chan('rpc'), node.addr, node.port) node.addr = self.addr node.port = self.port if node.id is None: node.id = random.randint(1,2**160) self.node = node self.routing = RoutingTree(self.node) self.rpc_actions = { 'PING' : self.rpc_handle_ping, 'PONG' : self.rpc_handle_pong, 'STORE' : self.rpc_handle_store, 'FIND_VALUE' : self.rpc_handle_find_value, 'FIND_NODE' : self.rpc_handle_find_node, 'RETURN_NODE' : self.rpc_handle_return_node, 'RETURN_VALUE' : self.rpc_handle_return_value } self.internal_actions = { 'ADD_NODE' : self.int_add_node, 'FIND_CLOSEST_NODES' : self.int_find_closest_nodes, 'SEND_FIND_NODE' : self.int_send_find_node, 'REFRESH_BUCKETS' : self.int_refresh_buckets, 'STORE_VALUE' : self.int_store_value, 'SEND_FIND_VALUE' : self.int_send_find_value } self.data_store = simple() self.rpc_xids = {} self.debug = True
def rpc_handle_find_node(self, message): ''' rpc_handle_find_node looks for a node_id in the 'data portion of the message. The client then returns upto k nodes from our routing tree which are closest to the requested node. ''' source = message['source'] node_to_find = Node(None, None, message['data']) nodes = self.routing.findClosestNodes(node_to_find) m = self.rpc_create_message('RETURN_NODE', message['xid']) m['data'] = [(n.addr, n.port, n.id) for n in nodes] self.rpc_send_message(source.addr, source.port, m)
def rpc_handle_message(self, m): ''' rpc_handle_message is the initial handler for all rpc messages the correct method will be called based on the message type ''' if 'type' in m and m['type'] in self.rpc_actions: # add node into our routing tree node = Node(m['source'][0], m['source'][1], m['source'][2]) self.routing.addNode(node) m['source'] = node # handle message self.rpc_actions[m['type']](m) else: self.log("process_message malformed message: %s" % m)
def test_seen(self, time_mock): time_mock.time.return_value = 1000 n = Node('127.0.0.1', 5000, 100) n.error() self.assertEquals(n.errors, 1) self.assertEquals(n.last_seen, 0) n.seen() self.assertEquals(n.errors, 0) self.assertEquals(n.last_seen, 1000)
def _store_value(self, key, value): ''' _store_value is a blocking method call that stores a value under a key within the kad network nodes do not respond with an ack saying they ''' key_hash = long(hashlib.sha1(key).hexdigest(), 16) nodes = self._node_lookup(Node(None, None, key_hash)) for node in nodes: m, chan = self.create_message('STORE_VALUE') m['data']['key'] = key m['data']['key_hash'] = key_hash m['data']['value'] = value m['data']['node'] = node self.send_message(m)
def rpc_handle_return_node(self, message): ''' rpc_handle_return_node handles a 'RETURN_NODE' message, add all returned nodes to the routing tree if there is a channel associated with this request then send the node list back ''' if 'xid' in message and message['xid'] in self.rpc_xids: nodes = [] for node in message['data']: n = Node(node[0], node[1], node[2]) self.routing.addNode(n) nodes.append(n) if self.rpc_xids[message['xid']]['chan']: m = { 'timeout' : False, 'nodes' : nodes } self.rpc_xids[message['xid']]['chan'].put(m) del self.rpc_xids[message['xid']]
def rpc_handle_find_value(self, message): ''' rpc_handle_find_value handles the rpc 'FIND_VALUE' message it either returns the value, if it is stored at this node or returns the closest k nodes to the requested key from our routing tree ''' key_hash = message['data']['key_hash'] source = message['source'] value = self.data_store.retrieve(key_hash) if value is not None: response = { 'value' : value, 'found' : True } else: node_to_find = Node(None, None, key_hash) nodes = self.routing.findClosestNodes(node_to_find) nodes = [(n.addr, n.port, n.id) for n in nodes] response = { 'nodes' : nodes, 'found' : False } m = self.rpc_create_message('RETURN_VALUE', message['xid']) m['data'] = response self.rpc_send_message(source.addr, source.port, m)
def test_create(self): n = Node('127.0.0.1', 5000, 100) self.assertIsInstance(n, Node)
def test_create(self): r = RoutingTree(Node(None, None, 100)) self.assertIsInstance(r, RoutingTree)
def _fetch_value(self, key): ''' _fetch_value is a blocking method call that returns a value from the kad network or None if key is not found ''' key_hash = long(hashlib.sha1(key).hexdigest(), 16) node = Node(None, None, key_hash) return self._node_lookup(node, key)
def test_not_equal(self): a = Node('127.0.0.1', 5000, 100) b = Node('127.0.0.1', 5000, 500) self.assertNotEqual(a, b)
def test_greater_than(self): a = Node('127.0.0.1', 5000, 100) b = Node('127.0.0.1', 5000, 500) self.assertTrue(b > a)
def test_raises_KbucketWrong_exception(self): b = Kbucket(20, 0, 127, 5, 7) self.assertRaises(KbucketWrong, b.addNode, Node(None, None, 255))
def test_raises_KbucketFull_exception(self): for n in self.nodes: self.bucket.addNode(n) self.assertRaises(KbucketFull, self.bucket.addNode, Node(None, None, 231))
def return_node(self): ''' return a copy of our node ''' return Node(self.node.addr, self.node.port, self.node.id)
def test_less_than(self): a = Node('127.0.0.1', 5000, 100) b = Node('127.0.0.1', 5000, 500) self.assertTrue(a < b)
def createNodes(self, numberOfNodes): self.nodes = [] for _ in range(numberOfNodes): self.nodes.append(Node(f'Node{_}'))