Exemple #1
0
    async def test_update_token(self):
        loop = asyncio.get_event_loop()
        with dht_mocks.mock_network_loop(loop):
            node_id1 = constants.generate_id()
            peer1 = KademliaProtocol(loop, PeerManager(loop), node_id1,
                                     '1.2.3.4', 4444, 3333)
            peer2 = KademliaProtocol(loop, PeerManager(loop),
                                     constants.generate_id(), '1.2.3.5', 4444,
                                     3333)
            await loop.create_datagram_endpoint(lambda: peer1,
                                                ('1.2.3.4', 4444))
            await loop.create_datagram_endpoint(lambda: peer2,
                                                ('1.2.3.5', 4444))

            peer = peer2.peer_manager.get_kademlia_peer(node_id1,
                                                        '1.2.3.4',
                                                        udp_port=4444)
            self.assertEqual(None,
                             peer2.peer_manager.get_node_token(peer.node_id))
            await peer2.get_rpc_peer(peer).find_value(b'1' * 48)
            self.assertNotEqual(
                None, peer2.peer_manager.get_node_token(peer.node_id))
            peer1.stop()
            peer2.stop()
            peer1.disconnect()
            peer2.disconnect()
Exemple #2
0
class PeerTest(AsyncioTestCase):
    def setUp(self):
        self.loop = asyncio.get_event_loop()
        self.peer_manager = PeerManager(self.loop)
        self.node_ids = [generate_id(), generate_id(), generate_id()]
        self.first_contact = self.peer_manager.get_kademlia_peer(
            self.node_ids[1], '127.0.0.1', udp_port=1000)
        self.second_contact = self.peer_manager.get_kademlia_peer(
            self.node_ids[0], '192.168.0.1', udp_port=1000)

    def test_make_contact_error_cases(self):
        self.assertRaises(ValueError, self.peer_manager.get_kademlia_peer,
                          self.node_ids[1], '192.168.1.20', 100000)
        self.assertRaises(ValueError, self.peer_manager.get_kademlia_peer,
                          self.node_ids[1], '192.168.1.20.1', 1000)
        self.assertRaises(ValueError, self.peer_manager.get_kademlia_peer,
                          self.node_ids[1], 'this is not an ip', 1000)
        self.assertRaises(ValueError, self.peer_manager.get_kademlia_peer,
                          self.node_ids[1], '192.168.1.20', -1000)
        self.assertRaises(ValueError, self.peer_manager.get_kademlia_peer,
                          b'not valid node id', '192.168.1.20', 1000)

    def test_boolean(self):
        self.assertNotEqual(self.first_contact, self.second_contact)
        self.assertEqual(
            self.second_contact,
            self.peer_manager.get_kademlia_peer(self.node_ids[0],
                                                '192.168.0.1',
                                                udp_port=1000))

    def test_compact_ip(self):
        self.assertEqual(self.first_contact.compact_ip(), b'\x7f\x00\x00\x01')
        self.assertEqual(self.second_contact.compact_ip(), b'\xc0\xa8\x00\x01')
Exemple #3
0
 def setUp(self):
     self.loop = asyncio.get_event_loop()
     self.peer_manager = PeerManager(self.loop)
     self.node_ids = [generate_id(), generate_id(), generate_id()]
     self.first_contact = self.peer_manager.get_kademlia_peer(
         self.node_ids[1], '127.0.0.1', udp_port=1000)
     self.second_contact = self.peer_manager.get_kademlia_peer(
         self.node_ids[0], '192.168.0.1', udp_port=1000)
Exemple #4
0
class PeerTest(AsyncioTestCase):
    def setUp(self):
        self.loop = asyncio.get_event_loop()
        self.peer_manager = PeerManager(self.loop)
        self.node_ids = [generate_id(), generate_id(), generate_id()]
        self.first_contact = make_kademlia_peer(self.node_ids[1], '1.0.0.1', udp_port=1000)
        self.second_contact = make_kademlia_peer(self.node_ids[0], '1.0.0.2', udp_port=1000)

    def test_peer_is_good_unknown_peer(self):
        # Scenario: peer replied, but caller doesn't know the node_id.
        # Outcome: We can't say it's good or bad.
        # (yes, we COULD tell the node id, but not here. It would be
        # a side effect and the caller is responsible to discover it)
        peer = make_kademlia_peer(None, '1.2.3.4', 4444)
        self.peer_manager.report_last_requested('1.2.3.4', 4444)
        self.peer_manager.report_last_replied('1.2.3.4', 4444)
        self.assertIsNone(self.peer_manager.peer_is_good(peer))

    def test_make_contact_error_cases(self):
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '1.2.3.4', 100000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '1.2.3.4.5', 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], 'this is not an ip', 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '1.2.3.4', -1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '1.2.3.4', 0)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '1.2.3.4', 70000)
        self.assertRaises(ValueError, make_kademlia_peer, b'not valid node id', '1.2.3.4', 1000)

        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '0.0.0.0', 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '10.0.0.1', 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '100.64.0.1', 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '127.0.0.1', 1000)
        self.assertIsNotNone(make_kademlia_peer(self.node_ids[1], '127.0.0.1', 1000, allow_localhost=True))
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '192.168.0.1', 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '172.16.0.1', 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '169.254.1.1', 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '192.0.0.2', 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '192.0.2.2', 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '192.88.99.2', 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '198.18.1.1', 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '198.51.100.2', 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '198.51.100.2', 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '203.0.113.4', 1000)
        for i in range(32):
            self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], f"{224 + i}.0.0.0", 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1], '255.255.255.255', 1000)

    def test_boolean(self):
        self.assertNotEqual(self.first_contact, self.second_contact)
        self.assertEqual(
            self.second_contact, make_kademlia_peer(self.node_ids[0], '1.0.0.2', udp_port=1000)
        )

    def test_compact_ip(self):
        self.assertEqual(b'\x01\x00\x00\x01', self.first_contact.compact_ip())
        self.assertEqual(b'\x01\x00\x00\x02', self.second_contact.compact_ip())
Exemple #5
0
    async def test_store_to_peer(self):
        loop = asyncio.get_event_loop()
        with dht_mocks.mock_network_loop(loop):
            node_id1 = constants.generate_id()
            peer1 = KademliaProtocol(loop, PeerManager(loop), node_id1,
                                     '1.2.3.4', 4444, 3333)
            peer2 = KademliaProtocol(loop, PeerManager(loop),
                                     constants.generate_id(), '1.2.3.5', 4444,
                                     3333)
            await loop.create_datagram_endpoint(lambda: peer1,
                                                ('1.2.3.4', 4444))
            await loop.create_datagram_endpoint(lambda: peer2,
                                                ('1.2.3.5', 4444))

            peer = make_kademlia_peer(node_id1, '1.2.3.4', udp_port=4444)
            peer2_from_peer1 = make_kademlia_peer(peer2.node_id,
                                                  peer2.external_ip,
                                                  udp_port=peer2.udp_port)
            peer2_from_peer1.update_tcp_port(3333)
            peer3 = make_kademlia_peer(constants.generate_id(),
                                       '1.2.3.6',
                                       udp_port=4444)
            store_result = await peer2.store_to_peer(b'2' * 48, peer)
            self.assertEqual(store_result[0], peer.node_id)
            self.assertEqual(True, store_result[1])
            self.assertEqual(True,
                             peer1.data_store.has_peers_for_blob(b'2' * 48))
            self.assertEqual(False,
                             peer1.data_store.has_peers_for_blob(b'3' * 48))
            self.assertListEqual([peer2_from_peer1],
                                 peer1.data_store.get_storing_contacts())
            peer1.data_store.completed_blobs.add(
                binascii.hexlify(b'2' * 48).decode())
            find_value_response = peer1.node_rpc.find_value(peer3, b'2' * 48)
            self.assertEqual(len(find_value_response[b'contacts']), 0)
            self.assertSetEqual(
                {b'2' * 48, b'token', b'protocolVersion', b'contacts', b'p'},
                set(find_value_response.keys()))
            self.assertEqual(2, len(find_value_response[b'2' * 48]))
            self.assertEqual(find_value_response[b'2' * 48][0],
                             peer2_from_peer1.compact_address_tcp())
            self.assertDictEqual(bdecode(bencode(find_value_response)),
                                 find_value_response)

            find_value_page_above_pages_response = peer1.node_rpc.find_value(
                peer3, b'2' * 48, page=10)
            self.assertNotIn(b'2' * 48, find_value_page_above_pages_response)

            peer1.stop()
            peer2.stop()
            peer1.disconnect()
            peer2.disconnect()
Exemple #6
0
    def __init__(self,
                 conf: Config,
                 analytics_manager=None,
                 skip_components=None,
                 peer_manager=None,
                 **override_components):
        self.conf = conf
        self.skip_components = skip_components or []
        self.loop = asyncio.get_event_loop()
        self.analytics_manager = analytics_manager
        self.component_classes = {}
        self.components = set()
        self.started = asyncio.Event(loop=self.loop)
        self.peer_manager = peer_manager or PeerManager(
            asyncio.get_event_loop_policy().get_event_loop())

        for component_name, component_class in self.default_component_classes.items(
        ):
            if component_name in override_components:
                component_class = override_components.pop(component_name)
            if component_name not in self.skip_components:
                self.component_classes[component_name] = component_class

        if override_components:
            raise SyntaxError("unexpected components: %s" %
                              override_components)

        for component_class in self.component_classes.values():
            self.components.add(component_class(self))
Exemple #7
0
async def main(host: str, port: int, db_file_path: str,
               bootstrap_node: Optional[str], prometheus_port: int):
    loop = asyncio.get_event_loop()
    conf = Config()
    storage = SQLiteStorage(conf, db_file_path, loop, loop.time)
    if bootstrap_node:
        nodes = bootstrap_node.split(':')
        nodes = [(nodes[0], int(nodes[1]))]
    else:
        nodes = conf.known_dht_nodes
    await storage.open()
    node = Node(loop,
                PeerManager(loop),
                generate_id(),
                port,
                port,
                3333,
                None,
                storage=storage)
    if prometheus_port > 0:
        metrics = SimpleMetrics(prometheus_port, node)
        await metrics.start()
    node.start(host, nodes)
    while True:
        await asyncio.sleep(10)
        PEERS.labels('main').set(len(node.protocol.routing_table.get_peers()))
        BLOBS_STORED.labels('main').set(
            len(node.protocol.data_store.get_storing_contacts()))
        log.info(
            "Known peers: %d. Storing contact information for %d blobs from %d peers.",
            len(node.protocol.routing_table.get_peers()),
            len(node.protocol.data_store),
            len(node.protocol.data_store.get_storing_contacts()))
Exemple #8
0
 async def add_peer(self, node_id, address, add_to_routing_table=True):
     #print('add', node_id.hex()[:8], address)
     n = Node(self.loop, PeerManager(self.loop), node_id, 4444, 4444, 3333, address)
     await n.start_listening(address)
     self.nodes.update({len(self.nodes): n})
     if add_to_routing_table:
         self.add_peer_to_routing_table(self.node, n)
Exemple #9
0
 async def _make_protocol(self, other_peer, node_id, address, udp_port, tcp_port):
     proto = KademliaProtocol(
         self.loop, PeerManager(self.loop), node_id, address, udp_port, tcp_port
     )
     await self.loop.create_datagram_endpoint(lambda: proto, (address, 4444))
     proto.start()
     return proto, make_kademlia_peer(node_id, address, udp_port=udp_port)
Exemple #10
0
    async def asyncSetUp(self):
        self.loop = asyncio.get_event_loop()

        self.client_dir = tempfile.mkdtemp()
        self.server_dir = tempfile.mkdtemp()
        self.addCleanup(shutil.rmtree, self.client_dir)
        self.addCleanup(shutil.rmtree, self.server_dir)
        self.server_config = Config(data_dir=self.server_dir, download_dir=self.server_dir, wallet=self.server_dir,
                                    reflector_servers=[])
        self.server_storage = SQLiteStorage(self.server_config, os.path.join(self.server_dir, "lbrynet.sqlite"))
        self.server_blob_manager = BlobManager(self.loop, self.server_dir, self.server_storage, self.server_config)
        self.server = BlobServer(self.loop, self.server_blob_manager, 'bQEaw42GXsgCAGio1nxFncJSyRmnztSCjP')

        self.client_config = Config(data_dir=self.client_dir, download_dir=self.client_dir, wallet=self.client_dir,
                                    reflector_servers=[])
        self.client_storage = SQLiteStorage(self.client_config, os.path.join(self.client_dir, "lbrynet.sqlite"))
        self.client_blob_manager = BlobManager(self.loop, self.client_dir, self.client_storage, self.client_config)
        self.client_peer_manager = PeerManager(self.loop)
        self.server_from_client = make_kademlia_peer(b'1' * 48, "127.0.0.1", tcp_port=33333, allow_localhost=True)

        await self.client_storage.open()
        await self.server_storage.open()
        await self.client_blob_manager.setup()
        await self.server_blob_manager.setup()

        self.server.start_server(33333, '127.0.0.1')
        self.addCleanup(self.server.stop_server)
        await self.server.started_listening.wait()
Exemple #11
0
 async def add_peer(self, node_id, address, add_to_routing_table=True):
     n = Node(self.loop, PeerManager(self.loop), node_id, 4444, 4444, 3333,
              address)
     await n.start_listening(address)
     self.nodes.update({len(self.nodes): n})
     if add_to_routing_table:
         self.node.protocol.add_peer(
             make_kademlia_peer(n.protocol.node_id, n.protocol.external_ip,
                                n.protocol.udp_port))
Exemple #12
0
 async def setup_node(self, peer_addresses, address, node_id):
     self.nodes: typing.Dict[int, Node] = {}
     self.advance = dht_mocks.get_time_accelerator(self.loop,
                                                   self.loop.time())
     self.conf = Config()
     self.storage = SQLiteStorage(self.conf, ":memory:", self.loop,
                                  self.loop.time)
     await self.storage.open()
     self.peer_manager = PeerManager(self.loop)
     self.node = Node(self.loop, self.peer_manager, node_id, 4444, 4444,
                      3333, address)
     await self.node.start_listening(address)
     self.blob_announcer = BlobAnnouncer(self.loop, self.node, self.storage)
     for node_id, address in peer_addresses:
         await self.add_peer(node_id, address)
     self.node.joined.set()
     self.node._refresh_task = self.loop.create_task(
         self.node.refresh_node())
Exemple #13
0
    async def test_add_peer_after_handle_request(self):
        with dht_mocks.mock_network_loop(self.loop):
            node_id1 = constants.generate_id()
            node_id2 = constants.generate_id()
            node_id3 = constants.generate_id()
            node_id4 = constants.generate_id()

            peer1 = KademliaProtocol(
                self.loop, PeerManager(self.loop), node_id1, '1.2.3.4', 4444, 3333
            )
            await self.loop.create_datagram_endpoint(lambda: peer1, ('1.2.3.4', 4444))
            peer1.start()

            peer2, peer_2_from_peer_1 = await self._make_protocol(peer1, node_id2, '1.2.3.5', 4444, 3333)
            peer3, peer_3_from_peer_1 = await self._make_protocol(peer1, node_id3, '1.2.3.6', 4444, 3333)
            peer4, peer_4_from_peer_1 = await self._make_protocol(peer1, node_id4, '1.2.3.7', 4444, 3333)

            # peers who reply should be added
            await peer1.get_rpc_peer(peer_2_from_peer_1).ping()
            await asyncio.sleep(0.5)
            self.assertListEqual([peer_2_from_peer_1], peer1.routing_table.get_peers())
            peer1.routing_table.remove_peer(peer_2_from_peer_1)

            # peers not known by be good/bad should be enqueued to maybe-ping
            peer1_from_peer3 = peer3.get_rpc_peer(make_kademlia_peer(node_id1, '1.2.3.4', 4444))
            self.assertEqual(0, len(peer1.ping_queue._pending_contacts))
            pong = await peer1_from_peer3.ping()
            self.assertEqual(b'pong', pong)
            self.assertEqual(1, len(peer1.ping_queue._pending_contacts))
            peer1.ping_queue._pending_contacts.clear()

            # peers who are already good should be added
            peer1_from_peer4 = peer4.get_rpc_peer(make_kademlia_peer(node_id1, '1.2.3.4', 4444))
            peer1.peer_manager.update_contact_triple(node_id4,'1.2.3.7', 4444)
            peer1.peer_manager.report_last_replied('1.2.3.7', 4444)
            self.assertEqual(0, len(peer1.ping_queue._pending_contacts))
            pong = await peer1_from_peer4.ping()
            self.assertEqual(b'pong', pong)
            await asyncio.sleep(0.5)
            self.assertEqual(1, len(peer1.routing_table.get_peers()))
            self.assertEqual(0, len(peer1.ping_queue._pending_contacts))
            peer1.routing_table.buckets[0].peers.clear()

            # peers who are known to be bad recently should not be added or maybe-pinged
            peer1_from_peer4 = peer4.get_rpc_peer(make_kademlia_peer(node_id1, '1.2.3.4', 4444))
            peer1.peer_manager.update_contact_triple(node_id4,'1.2.3.7', 4444)
            peer1.peer_manager.report_failure('1.2.3.7', 4444)
            peer1.peer_manager.report_failure('1.2.3.7', 4444)
            self.assertEqual(0, len(peer1.ping_queue._pending_contacts))
            pong = await peer1_from_peer4.ping()
            self.assertEqual(b'pong', pong)
            self.assertEqual(0, len(peer1.routing_table.get_peers()))
            self.assertEqual(0, len(peer1.ping_queue._pending_contacts))

            for p in [peer1, peer2, peer3, peer4]:
                p.stop()
                p.disconnect()
Exemple #14
0
class PeerTest(AsyncioTestCase):
    def setUp(self):
        self.loop = asyncio.get_event_loop()
        self.peer_manager = PeerManager(self.loop)
        self.node_ids = [generate_id(), generate_id(), generate_id()]
        self.first_contact = make_kademlia_peer(self.node_ids[1],
                                                '127.0.0.1',
                                                udp_port=1000)
        self.second_contact = make_kademlia_peer(self.node_ids[0],
                                                 '192.168.0.1',
                                                 udp_port=1000)

    def test_peer_is_good_unknown_peer(self):
        # Scenario: peer replied, but caller doesn't know the node_id.
        # Outcome: We can't say it's good or bad.
        # (yes, we COULD tell the node id, but not here. It would be
        # a side effect and the caller is responsible to discover it)
        peer = make_kademlia_peer(None, '1.2.3.4', 4444)
        self.peer_manager.report_last_requested('1.2.3.4', 4444)
        self.peer_manager.report_last_replied('1.2.3.4', 4444)
        self.assertIsNone(self.peer_manager.peer_is_good(peer))

    def test_make_contact_error_cases(self):
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1],
                          '192.168.1.20', 100000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1],
                          '192.168.1.20.1', 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1],
                          'this is not an ip', 1000)
        self.assertRaises(ValueError, make_kademlia_peer, self.node_ids[1],
                          '192.168.1.20', -1000)
        self.assertRaises(ValueError, make_kademlia_peer, b'not valid node id',
                          '192.168.1.20', 1000)

    def test_boolean(self):
        self.assertNotEqual(self.first_contact, self.second_contact)
        self.assertEqual(
            self.second_contact,
            make_kademlia_peer(self.node_ids[0], '192.168.0.1', udp_port=1000))

    def test_compact_ip(self):
        self.assertEqual(self.first_contact.compact_ip(), b'\x7f\x00\x00\x01')
        self.assertEqual(self.second_contact.compact_ip(), b'\xc0\xa8\x00\x01')
Exemple #15
0
 async def test_cant_add_peer_without_a_node_id_gracefully(self):
     loop = asyncio.get_event_loop()
     node = Node(loop, PeerManager(loop), constants.generate_id(), 4444,
                 4444, 3333, '1.2.3.4')
     bad_peer = make_kademlia_peer(None, '1.2.3.4', 5555)
     with self.assertLogs(level='WARNING') as logged:
         self.assertFalse(await node.protocol._add_peer(bad_peer))
         self.assertEqual(1, len(logged.output))
         self.assertTrue(logged.output[0].endswith(
             'Tried adding a peer with no node id!'))
Exemple #16
0
    async def test_ping(self):
        loop = asyncio.get_event_loop()
        with dht_mocks.mock_network_loop(loop):
            node_id1 = constants.generate_id()
            peer1 = KademliaProtocol(
                loop, PeerManager(loop), node_id1, '1.2.3.4', 4444, 3333
            )
            peer2 = KademliaProtocol(
                loop, PeerManager(loop), constants.generate_id(), '1.2.3.5', 4444, 3333
            )
            await loop.create_datagram_endpoint(lambda: peer1, ('1.2.3.4', 4444))
            await loop.create_datagram_endpoint(lambda: peer2, ('1.2.3.5', 4444))

            peer = make_kademlia_peer(node_id1, '1.2.3.4', udp_port=4444)
            result = await peer2.get_rpc_peer(peer).ping()
            self.assertEqual(result, b'pong')
            peer1.stop()
            peer2.stop()
            peer1.disconnect()
            peer2.disconnect()
Exemple #17
0
 async def setup_network(self, size: int, start_port=40000, seed_nodes=1):
     for i in range(size):
         node_port = start_port + i
         node = Node(self.loop, PeerManager(self.loop), node_id=constants.generate_id(i),
                                udp_port=node_port, internal_udp_port=node_port,
                                peer_port=3333, external_ip='127.0.0.1')
         self.nodes.append(node)
         self.known_node_addresses.append(('127.0.0.1', node_port))
         await node.start_listening('127.0.0.1')
         self.addCleanup(node.stop)
     for node in self.nodes:
         node.protocol.rpc_timeout = .5
         node.protocol.ping_queue._default_delay = .5
         node.start('127.0.0.1', self.known_node_addresses[:seed_nodes])
     await asyncio.gather(*[node.joined.wait() for node in self.nodes])
Exemple #18
0
 async def create_node(self, node_id, port, external_ip='127.0.0.1'):
     storage = SQLiteStorage(Config(), ":memory:", self.loop,
                             self.loop.time)
     await storage.open()
     node = Node(self.loop,
                 PeerManager(self.loop),
                 node_id=node_id,
                 udp_port=port,
                 internal_udp_port=port,
                 peer_port=3333,
                 external_ip=external_ip,
                 storage=storage)
     self.addCleanup(node.stop)
     node.protocol.rpc_timeout = .5
     node.protocol.ping_queue._default_delay = .5
     return node
Exemple #19
0
 async def test_split_buckets(self):
     loop = asyncio.get_event_loop()
     peer_addresses = [
         (constants.generate_id(1), '1.2.3.1'),
     ]
     for i in range(2, 200):
         peer_addresses.append((constants.generate_id(i), f'1.2.3.{i}'))
     with dht_mocks.mock_network_loop(loop):
         nodes = {
             i: Node(loop, PeerManager(loop), node_id, 4444, 4444, 3333,
                     address)
             for i, (node_id, address) in enumerate(peer_addresses)
         }
         node_1 = nodes[0]
         for i in range(1, len(peer_addresses)):
             node = nodes[i]
             peer = node_1.protocol.peer_manager.get_kademlia_peer(
                 node.protocol.node_id,
                 node.protocol.external_ip,
                 udp_port=node.protocol.udp_port)
             # set all of the peers to good (as to not attempt pinging stale ones during split)
             node_1.protocol.peer_manager.report_last_replied(
                 peer.address, peer.udp_port)
             node_1.protocol.peer_manager.report_last_replied(
                 peer.address, peer.udp_port)
             await node_1.protocol._add_peer(peer)
             # check that bucket 0 is always the one covering the local node id
             self.assertEqual(
                 True,
                 node_1.protocol.routing_table.buckets[0].key_in_range(
                     node_1.protocol.node_id))
         self.assertEqual(40,
                          len(node_1.protocol.routing_table.get_peers()))
         self.assertEqual(len(expected_ranges),
                          len(node_1.protocol.routing_table.buckets))
         covered = 0
         for (expected_min, expected_max), bucket in zip(
                 expected_ranges, node_1.protocol.routing_table.buckets):
             self.assertEqual(expected_min, bucket.range_min)
             self.assertEqual(expected_max, bucket.range_max)
             covered += bucket.range_max - bucket.range_min
         self.assertEqual(2**384, covered)
         for node in nodes.values():
             node.stop()
Exemple #20
0
async def main(host: str, port: int, db_file_path: str,
               bootstrap_node: Optional[str], prometheus_port: int,
               export: bool):
    loop = asyncio.get_event_loop()
    conf = Config()
    if not db_file_path.startswith(':memory:'):
        node_id_file_path = db_file_path + 'node_id'
        if os.path.exists(node_id_file_path):
            with open(node_id_file_path, 'rb') as node_id_file:
                node_id = node_id_file.read()
        else:
            with open(node_id_file_path, 'wb') as node_id_file:
                node_id = generate_id()
                node_id_file.write(node_id)

    storage = SQLiteStorage(conf, db_file_path, loop, loop.time)
    if bootstrap_node:
        nodes = bootstrap_node.split(':')
        nodes = [(nodes[0], int(nodes[1]))]
    else:
        nodes = conf.known_dht_nodes
    await storage.open()
    node = Node(loop,
                PeerManager(loop),
                node_id,
                port,
                port,
                3333,
                None,
                storage=storage)
    if prometheus_port > 0:
        metrics = SimpleMetrics(prometheus_port, node if export else None)
        await metrics.start()
    node.start(host, nodes)
    log.info("Peer with id %s started", node_id.hex())
    while True:
        await asyncio.sleep(10)
        log.info(
            "Known peers: %d. Storing contact information for %d blobs from %d peers.",
            len(node.protocol.routing_table.get_peers()),
            len(node.protocol.data_store),
            len(node.protocol.data_store.get_storing_contacts()))
Exemple #21
0
 async def setup_node(self, peer_addresses, address, node_id):
     self.nodes: typing.Dict[int, Node] = {}
     self.advance = dht_mocks.get_time_accelerator(self.loop)
     self.instant_advance = dht_mocks.get_time_accelerator(self.loop)
     self.conf = Config()
     self.peer_manager = PeerManager(self.loop)
     self.node = Node(self.loop, self.peer_manager, node_id, 4444, 4444, 3333, address)
     await self.node.start_listening(address)
     await asyncio.gather(*[self.add_peer(node_id, address) for node_id, address in peer_addresses])
     for first_peer in self.nodes.values():
         for second_peer in self.nodes.values():
             if first_peer == second_peer:
                 continue
             self.add_peer_to_routing_table(first_peer, second_peer)
             self.add_peer_to_routing_table(second_peer, first_peer)
     await self.advance(0.1)  # just to make pings go through
     self.node.joined.set()
     self.node._refresh_task = self.loop.create_task(self.node.refresh_node())
     self.storage = SQLiteStorage(self.conf, ":memory:", self.loop, self.loop.time)
     await self.storage.open()
     self.blob_announcer = BlobAnnouncer(self.loop, self.node, self.storage)
Exemple #22
0
    async def test_fill_one_bucket(self):
        loop = asyncio.get_event_loop()
        peer_addresses = [
            (constants.generate_id(1), '1.2.3.1'),
            (constants.generate_id(2), '1.2.3.2'),
            (constants.generate_id(3), '1.2.3.3'),
            (constants.generate_id(4), '1.2.3.4'),
            (constants.generate_id(5), '1.2.3.5'),
            (constants.generate_id(6), '1.2.3.6'),
            (constants.generate_id(7), '1.2.3.7'),
            (constants.generate_id(8), '1.2.3.8'),
            (constants.generate_id(9), '1.2.3.9'),
        ]
        with dht_mocks.mock_network_loop(loop):
            nodes = {
                i: Node(loop, PeerManager(loop), node_id, 4444, 4444, 3333,
                        address)
                for i, (node_id, address) in enumerate(peer_addresses)
            }
            node_1 = nodes[0]
            contact_cnt = 0
            for i in range(1, len(peer_addresses)):
                self.assertEqual(
                    len(node_1.protocol.routing_table.get_peers()),
                    contact_cnt)
                node = nodes[i]
                peer = node_1.protocol.peer_manager.get_kademlia_peer(
                    node.protocol.node_id,
                    node.protocol.external_ip,
                    udp_port=node.protocol.udp_port)
                added = await node_1.protocol._add_peer(peer)
                self.assertEqual(True, added)
                contact_cnt += 1

            self.assertEqual(len(node_1.protocol.routing_table.get_peers()), 8)
            self.assertEqual(
                node_1.protocol.routing_table.buckets_with_contacts(), 1)
            for node in nodes.values():
                node.protocol.stop()
Exemple #23
0
class DataStoreTests(TestCase):
    def setUp(self):
        self.loop = mock.Mock(spec=asyncio.BaseEventLoop)
        self.loop.time = lambda: 0.0
        self.peer_manager = PeerManager(self.loop)
        self.data_store = DictDataStore(self.loop, self.peer_manager)

    def _test_add_peer_to_blob(self,
                               blob=b'2' * 48,
                               node_id=b'1' * 48,
                               address='1.2.3.4',
                               tcp_port=3333,
                               udp_port=4444):
        peer = self.peer_manager.get_kademlia_peer(node_id, address, udp_port)
        peer.update_tcp_port(tcp_port)
        before = self.data_store.get_peers_for_blob(blob)
        self.data_store.add_peer_to_blob(peer, blob)
        self.assertListEqual(before + [peer],
                             self.data_store.get_peers_for_blob(blob))
        return peer

    def test_refresh_peer_to_blob(self):
        blob = b'f' * 48
        self.assertListEqual([], self.data_store.get_peers_for_blob(blob))
        peer = self._test_add_peer_to_blob(blob=blob,
                                           node_id=b'a' * 48,
                                           address='1.2.3.4')
        self.assertTrue(self.data_store.has_peers_for_blob(blob))
        self.assertEqual(len(self.data_store.get_peers_for_blob(blob)), 1)
        self.assertEqual(self.data_store._data_store[blob][0][1], 0)
        self.loop.time = lambda: 100.0
        self.assertEqual(self.data_store._data_store[blob][0][1], 0)
        self.data_store.add_peer_to_blob(peer, blob)
        self.assertEqual(self.data_store._data_store[blob][0][1], 100)

    def test_add_peer_to_blob(self, blob=b'f' * 48, peers=None):
        peers = peers or [
            (b'a' * 48, '1.2.3.4'),
            (b'b' * 48, '1.2.3.5'),
            (b'c' * 48, '1.2.3.6'),
        ]
        self.assertListEqual([], self.data_store.get_peers_for_blob(blob))
        peer_objects = []
        for (node_id, address) in peers:
            peer_objects.append(
                self._test_add_peer_to_blob(blob=blob,
                                            node_id=node_id,
                                            address=address))
            self.assertTrue(self.data_store.has_peers_for_blob(blob))
        self.assertEqual(len(self.data_store.get_peers_for_blob(blob)),
                         len(peers))
        return peer_objects

    def test_get_storing_contacts(self,
                                  peers=None,
                                  blob1=b'd' * 48,
                                  blob2=b'e' * 48):
        peers = peers or [
            (b'a' * 48, '1.2.3.4'),
            (b'b' * 48, '1.2.3.5'),
            (b'c' * 48, '1.2.3.6'),
        ]
        peer_objs1 = self.test_add_peer_to_blob(blob=blob1, peers=peers)
        self.assertEqual(len(peers), len(peer_objs1))
        self.assertEqual(len(peers),
                         len(self.data_store.get_storing_contacts()))

        peer_objs2 = self.test_add_peer_to_blob(blob=blob2, peers=peers)
        self.assertEqual(len(peers), len(peer_objs2))
        self.assertEqual(len(peers),
                         len(self.data_store.get_storing_contacts()))

        for o1, o2 in zip(peer_objs1, peer_objs2):
            self.assertIs(o1, o2)

    def test_remove_expired_peers(self):
        peers = [
            (b'a' * 48, '1.2.3.4'),
            (b'b' * 48, '1.2.3.5'),
            (b'c' * 48, '1.2.3.6'),
        ]
        blob1 = b'd' * 48
        blob2 = b'e' * 48

        self.data_store.removed_expired_peers()  # nothing should happen
        self.test_get_storing_contacts(peers, blob1, blob2)
        self.assertEqual(len(self.data_store.get_peers_for_blob(blob1)),
                         len(peers))
        self.assertEqual(len(self.data_store.get_peers_for_blob(blob2)),
                         len(peers))
        self.assertEqual(len(self.data_store.get_storing_contacts()),
                         len(peers))

        # expire the first peer from blob1
        first = self.data_store._data_store[blob1][0][0]
        self.data_store._data_store[blob1][0] = (first, -86401)
        self.assertEqual(len(self.data_store.get_storing_contacts()),
                         len(peers))
        self.data_store.removed_expired_peers()
        self.assertEqual(len(self.data_store.get_peers_for_blob(blob1)),
                         len(peers) - 1)
        self.assertEqual(len(self.data_store.get_peers_for_blob(blob2)),
                         len(peers))
        self.assertEqual(len(self.data_store.get_storing_contacts()),
                         len(peers))

        # expire the first peer from blob2
        first = self.data_store._data_store[blob2][0][0]
        self.data_store._data_store[blob2][0] = (first, -86401)
        self.data_store.removed_expired_peers()
        self.assertEqual(len(self.data_store.get_peers_for_blob(blob1)),
                         len(peers) - 1)
        self.assertEqual(len(self.data_store.get_peers_for_blob(blob2)),
                         len(peers) - 1)
        self.assertEqual(len(self.data_store.get_storing_contacts()),
                         len(peers) - 1)

        # expire the second and third peers from blob1
        first = self.data_store._data_store[blob2][0][0]
        self.data_store._data_store[blob1][0] = (first, -86401)
        second = self.data_store._data_store[blob2][1][0]
        self.data_store._data_store[blob1][1] = (second, -86401)
        self.data_store.removed_expired_peers()
        self.assertEqual(len(self.data_store.get_peers_for_blob(blob1)), 0)
        self.assertEqual(len(self.data_store.get_peers_for_blob(blob2)),
                         len(peers) - 1)
        self.assertEqual(len(self.data_store.get_storing_contacts()),
                         len(peers) - 1)
Exemple #24
0
class TestBlobAnnouncer(AsyncioTestCase):
    async def setup_node(self, peer_addresses, address, node_id):
        self.nodes: typing.Dict[int, Node] = {}
        self.advance = dht_mocks.get_time_accelerator(self.loop,
                                                      self.loop.time())
        self.conf = Config()
        self.storage = SQLiteStorage(self.conf, ":memory:", self.loop,
                                     self.loop.time)
        await self.storage.open()
        self.peer_manager = PeerManager(self.loop)
        self.node = Node(self.loop, self.peer_manager, node_id, 4444, 4444,
                         3333, address)
        await self.node.start_listening(address)
        self.blob_announcer = BlobAnnouncer(self.loop, self.node, self.storage)
        for node_id, address in peer_addresses:
            await self.add_peer(node_id, address)
        self.node.joined.set()
        self.node._refresh_task = self.loop.create_task(
            self.node.refresh_node())

    async def add_peer(self, node_id, address, add_to_routing_table=True):
        n = Node(self.loop, PeerManager(self.loop), node_id, 4444, 4444, 3333,
                 address)
        await n.start_listening(address)
        self.nodes.update({len(self.nodes): n})
        if add_to_routing_table:
            self.node.protocol.add_peer(
                self.peer_manager.get_kademlia_peer(n.protocol.node_id,
                                                    n.protocol.external_ip,
                                                    n.protocol.udp_port))

    @contextlib.asynccontextmanager
    async def _test_network_context(self, peer_addresses=None):
        self.peer_addresses = peer_addresses or [
            (constants.generate_id(2), '1.2.3.2'),
            (constants.generate_id(3), '1.2.3.3'),
            (constants.generate_id(4), '1.2.3.4'),
            (constants.generate_id(5), '1.2.3.5'),
            (constants.generate_id(6), '1.2.3.6'),
            (constants.generate_id(7), '1.2.3.7'),
            (constants.generate_id(8), '1.2.3.8'),
            (constants.generate_id(9), '1.2.3.9'),
        ]
        try:
            with dht_mocks.mock_network_loop(self.loop):
                await self.setup_node(self.peer_addresses, '1.2.3.1',
                                      constants.generate_id(1))
                yield
        finally:
            self.blob_announcer.stop()
            self.node.stop()
            for n in self.nodes.values():
                n.stop()

    async def chain_peer(self, node_id, address):
        previous_last_node = self.nodes[len(self.nodes) - 1]
        await self.add_peer(node_id, address, False)
        last_node = self.nodes[len(self.nodes) - 1]
        peer = last_node.protocol.get_rpc_peer(
            last_node.protocol.peer_manager.get_kademlia_peer(
                previous_last_node.protocol.node_id,
                previous_last_node.protocol.external_ip,
                previous_last_node.protocol.udp_port))
        await peer.ping()
        return peer

    async def test_announce_blobs(self):
        blob1 = binascii.hexlify(b'1' * 48).decode()
        blob2 = binascii.hexlify(b'2' * 48).decode()

        async with self._test_network_context():
            await self.storage.add_blobs((blob1, 1024), (blob2, 1024),
                                         finished=True)
            await self.storage.db.execute(
                "update blob set next_announce_time=0, should_announce=1 where blob_hash in (?, ?)",
                (blob1, blob2))
            to_announce = await self.storage.get_blobs_to_announce()
            self.assertEqual(2, len(to_announce))
            self.blob_announcer.start(
                batch_size=1)  # so it covers batching logic
            # takes 60 seconds to start, but we advance 120 to ensure it processed all batches
            await self.advance(60.0 * 2)
            to_announce = await self.storage.get_blobs_to_announce()
            self.assertEqual(0, len(to_announce))
            self.blob_announcer.stop()

            # test that we can route from a poorly connected peer all the way to the announced blob

            await self.chain_peer(constants.generate_id(10), '1.2.3.10')
            await self.chain_peer(constants.generate_id(11), '1.2.3.11')
            await self.chain_peer(constants.generate_id(12), '1.2.3.12')
            await self.chain_peer(constants.generate_id(13), '1.2.3.13')
            await self.chain_peer(constants.generate_id(14), '1.2.3.14')
            await self.advance(61.0)

            last = self.nodes[len(self.nodes) - 1]
            search_q, peer_q = asyncio.Queue(loop=self.loop), asyncio.Queue(
                loop=self.loop)
            search_q.put_nowait(blob1)

            _, task = last.accumulate_peers(search_q, peer_q)
            found_peers = await peer_q.get()
            task.cancel()

            self.assertEqual(1, len(found_peers))
            self.assertEqual(self.node.protocol.node_id,
                             found_peers[0].node_id)
            self.assertEqual(self.node.protocol.external_ip,
                             found_peers[0].address)
            self.assertEqual(self.node.protocol.peer_port,
                             found_peers[0].tcp_port)

    async def test_popular_blob(self):
        peer_count = 150
        addresses = [
            (constants.generate_id(i + 1),
             socket.inet_ntoa(int(i + 1).to_bytes(length=4, byteorder='big')))
            for i in range(peer_count)
        ]
        blob_hash = b'1' * 48

        async with self._test_network_context(peer_addresses=addresses):
            total_seen = set()
            announced_to = self.nodes[0]
            for i in range(1, peer_count):
                node = self.nodes[i]
                kad_peer = announced_to.protocol.peer_manager.get_kademlia_peer(
                    node.protocol.node_id, node.protocol.external_ip,
                    node.protocol.udp_port)
                await announced_to.protocol._add_peer(kad_peer)
                peer = node.protocol.get_rpc_peer(
                    node.protocol.peer_manager.get_kademlia_peer(
                        announced_to.protocol.node_id,
                        announced_to.protocol.external_ip,
                        announced_to.protocol.udp_port))
                response = await peer.store(blob_hash)
                self.assertEqual(response, b'OK')
                peers_for_blob = await peer.find_value(blob_hash, 0)
                if i == 1:
                    self.assertTrue(blob_hash not in peers_for_blob)
                    self.assertEqual(peers_for_blob[b'p'], 0)
                else:
                    self.assertEqual(len(peers_for_blob[blob_hash]),
                                     min(i - 1, constants.k))
                    self.assertEqual(
                        len(
                            announced_to.protocol.data_store.
                            get_peers_for_blob(blob_hash)), i)
                if i - 1 > constants.k:
                    self.assertEqual(len(peers_for_blob[b'contacts']),
                                     constants.k)
                    self.assertEqual(peers_for_blob[b'p'],
                                     ((i - 1) // (constants.k + 1)) + 1)
                    seen = set(peers_for_blob[blob_hash])
                    self.assertEqual(len(seen), constants.k)
                    self.assertEqual(len(peers_for_blob[blob_hash]), len(seen))

                    for pg in range(1, peers_for_blob[b'p']):
                        page_x = await peer.find_value(blob_hash, pg)
                        self.assertNotIn(b'contacts', page_x)
                        page_x_set = set(page_x[blob_hash])
                        self.assertEqual(len(page_x[blob_hash]),
                                         len(page_x_set))
                        self.assertTrue(len(page_x_set) > 0)
                        self.assertSetEqual(seen.intersection(page_x_set),
                                            set())
                        seen.intersection_update(page_x_set)
                        total_seen.update(page_x_set)
                else:
                    self.assertEqual(len(peers_for_blob[b'contacts']), i - 1)
            self.assertEqual(len(total_seen), peer_count - 2)
Exemple #25
0
 def setUp(self):
     self.loop = asyncio.get_event_loop()
     self.address_generator = address_generator()
     self.peer_manager = PeerManager(self.loop)
     self.kbucket = KBucket(self.peer_manager, 0, 2**constants.hash_bits, generate_id())
Exemple #26
0
class TestKBucket(AsyncioTestCase):
    def setUp(self):
        self.loop = asyncio.get_event_loop()
        self.address_generator = address_generator()
        self.peer_manager = PeerManager(self.loop)
        self.kbucket = KBucket(self.peer_manager, 0, 2**constants.hash_bits, generate_id())

    def test_add_peer(self):
        peer = KademliaPeer(None, '1.2.3.4', constants.generate_id(2), udp_port=4444)
        peer_update2 = KademliaPeer(None, '1.2.3.4', constants.generate_id(2), udp_port=4445)

        self.assertListEqual([], self.kbucket.peers)

        # add the peer
        self.kbucket.add_peer(peer)
        self.assertListEqual([peer], self.kbucket.peers)

        # re-add it
        self.kbucket.add_peer(peer)
        self.assertListEqual([peer], self.kbucket.peers)
        self.assertEqual(self.kbucket.peers[0].udp_port, 4444)

        # add a new peer object with the same id and address but a different port
        self.kbucket.add_peer(peer_update2)
        self.assertListEqual([peer_update2], self.kbucket.peers)
        self.assertEqual(self.kbucket.peers[0].udp_port, 4445)

        # modify the peer object to have a different port
        peer_update2.udp_port = 4444
        self.kbucket.add_peer(peer_update2)
        self.assertListEqual([peer_update2], self.kbucket.peers)
        self.assertEqual(self.kbucket.peers[0].udp_port, 4444)

        self.kbucket.peers.clear()

        # Test if contacts can be added to empty list
        # Add k contacts to bucket
        for i in range(constants.k):
            peer = self.peer_manager.get_kademlia_peer(generate_id(), next(self.address_generator), 4444)
            self.assertTrue(self.kbucket.add_peer(peer))
            self.assertEqual(peer, self.kbucket.peers[i])

        # Test if contact is not added to full list
        peer = self.peer_manager.get_kademlia_peer(generate_id(), next(self.address_generator), 4444)
        self.assertFalse(self.kbucket.add_peer(peer))

        # Test if an existing contact is updated correctly if added again
        existing_peer = self.kbucket.peers[0]
        self.assertTrue(self.kbucket.add_peer(existing_peer))
        self.assertEqual(existing_peer, self.kbucket.peers[-1])

    # def testGetContacts(self):
    #     # try and get 2 contacts from empty list
    #     result = self.kbucket.getContacts(2)
    #     self.assertFalse(len(result) != 0, "Returned list should be empty; returned list length: %d" %
    #                 (len(result)))
    #
    #     # Add k-2 contacts
    #     node_ids = []
    #     if constants.k >= 2:
    #         for i in range(constants.k-2):
    #             node_ids.append(generate_id())
    #             tmpContact = self.contact_manager.make_contact(node_ids[-1], next(self.address_generator), 4444, 0,
    #                                                            None)
    #             self.kbucket.addContact(tmpContact)
    #     else:
    #         # add k contacts
    #         for i in range(constants.k):
    #             node_ids.append(generate_id())
    #             tmpContact = self.contact_manager.make_contact(node_ids[-1], next(self.address_generator), 4444, 0,
    #                                                            None)
    #             self.kbucket.addContact(tmpContact)
    #
    #     # try to get too many contacts
    #     # requested count greater than bucket size; should return at most k contacts
    #     contacts = self.kbucket.getContacts(constants.k+3)
    #     self.assertTrue(len(contacts) <= constants.k,
    #                     'Returned list should not have more than k entries!')
    #
    #     # verify returned contacts in list
    #     for node_id, i in zip(node_ids, range(constants.k-2)):
    #         self.assertFalse(self.kbucket._contacts[i].id != node_id,
    #                     "Contact in position %s not same as added contact" % (str(i)))
    #
    #     # try to get too many contacts
    #     # requested count one greater than number of contacts
    #     if constants.k >= 2:
    #         result = self.kbucket.getContacts(constants.k-1)
    #         self.assertFalse(len(result) != constants.k-2,
    #                     "Too many contacts in returned list %s - should be %s" %
    #                     (len(result), constants.k-2))
    #     else:
    #         result = self.kbucket.getContacts(constants.k-1)
    #         # if the count is <= 0, it should return all of it's contats
    #         self.assertFalse(len(result) != constants.k,
    #                     "Too many contacts in returned list %s - should be %s" %
    #                     (len(result), constants.k-2))
    #         result = self.kbucket.getContacts(constants.k-3)
    #         self.assertFalse(len(result) != constants.k-3,
    #                     "Too many contacts in returned list %s - should be %s" %
    #                     (len(result), constants.k-3))

    def test_remove_peer(self):
        # try remove contact from empty list
        peer = self.peer_manager.get_kademlia_peer(generate_id(), next(self.address_generator), 4444)
        self.assertRaises(ValueError, self.kbucket.remove_peer, peer)

        added = []
        # Add couple contacts
        for i in range(constants.k-2):
            peer = self.peer_manager.get_kademlia_peer(generate_id(), next(self.address_generator), 4444)
            self.assertTrue(self.kbucket.add_peer(peer))
            added.append(peer)

        while added:
            peer = added.pop()
            self.assertIn(peer, self.kbucket.peers)
            self.kbucket.remove_peer(peer)
            self.assertNotIn(peer, self.kbucket.peers)
Exemple #27
0
    async def test_ping_queue_discover(self):
        loop = asyncio.get_event_loop()
        loop.set_debug(False)

        peer_addresses = [
            (constants.generate_id(1), '1.2.3.1'),
            (constants.generate_id(2), '1.2.3.2'),
            (constants.generate_id(3), '1.2.3.3'),
            (constants.generate_id(4), '1.2.3.4'),
            (constants.generate_id(5), '1.2.3.5'),
            (constants.generate_id(6), '1.2.3.6'),
            (constants.generate_id(7), '1.2.3.7'),
            (constants.generate_id(8), '1.2.3.8'),
            (constants.generate_id(9), '1.2.3.9'),
        ]
        with dht_mocks.mock_network_loop(loop):
            advance = dht_mocks.get_time_accelerator(loop)
            # start the nodes
            nodes: typing.Dict[int, Node] = {
                i: Node(loop, PeerManager(loop), node_id, 4444, 4444, 3333,
                        address)
                for i, (node_id, address) in enumerate(peer_addresses)
            }
            for i, n in nodes.items():
                n.start(peer_addresses[i][1], [])

            await advance(1)

            node_1 = nodes[0]

            # ping 8 nodes from node_1, this will result in a delayed return ping
            futs = []
            for i in range(1, len(peer_addresses)):
                node = nodes[i]
                assert node.protocol.node_id != node_1.protocol.node_id
                peer = make_kademlia_peer(node.protocol.node_id,
                                          node.protocol.external_ip,
                                          udp_port=node.protocol.udp_port)
                futs.append(node_1.protocol.get_rpc_peer(peer).ping())
            await advance(3)
            replies = await asyncio.gather(*tuple(futs))
            self.assertTrue(all(map(lambda reply: reply == b"pong", replies)))

            # run for long enough for the delayed pings to have been sent by node 1
            await advance(1000)

            # verify all of the previously pinged peers have node_1 in their routing tables
            for n in nodes.values():
                peers = n.protocol.routing_table.get_peers()
                if n is node_1:
                    self.assertEqual(8, len(peers))
                # TODO: figure out why this breaks
                # else:
                #     self.assertEqual(1, len(peers))
                #     self.assertEqual((peers[0].node_id, peers[0].address, peers[0].udp_port),
                #                      (node_1.protocol.node_id, node_1.protocol.external_ip, node_1.protocol.udp_port))

            # run long enough for the refresh loop to run
            await advance(3600)

            # verify all the nodes know about each other
            for n in nodes.values():
                if n is node_1:
                    continue
                peers = n.protocol.routing_table.get_peers()
                self.assertEqual(8, len(peers))
                self.assertSetEqual(
                    {
                        n_id[0]
                        for n_id in peer_addresses
                        if n_id[0] != n.protocol.node_id
                    }, {c.node_id
                        for c in peers})
                self.assertSetEqual(
                    {
                        n_addr[1]
                        for n_addr in peer_addresses
                        if n_addr[1] != n.protocol.external_ip
                    }, {c.address
                        for c in peers})

            # teardown
            for n in nodes.values():
                n.stop()
Exemple #28
0
    async def test_losing_connection(self):
        async def wait_for(check_ok, insist, timeout=20):
            start = time.time()
            while time.time() - start < timeout:
                if check_ok():
                    break
                await asyncio.sleep(0)
            else:
                insist()

        loop = self.loop
        loop.set_debug(False)

        peer_addresses = [('1.2.3.4', 40000 + i) for i in range(10)]
        node_ids = [constants.generate_id(i) for i in range(10)]

        nodes = [
            Node(loop,
                 PeerManager(loop),
                 node_id,
                 udp_port,
                 udp_port,
                 3333,
                 address,
                 storage=SQLiteStorage(Config(), ":memory:", self.loop,
                                       self.loop.time))
            for node_id, (address, udp_port) in zip(node_ids, peer_addresses)
        ]
        dht_network = {
            peer_addresses[i]: node.protocol
            for i, node in enumerate(nodes)
        }
        num_seeds = 3

        with dht_mocks.mock_network_loop(loop, dht_network):
            for i, n in enumerate(nodes):
                await n._storage.open()
                self.addCleanup(n.stop)
                n.start(peer_addresses[i][0], peer_addresses[:num_seeds])
            await asyncio.gather(*[n.joined.wait() for n in nodes])

            node = nodes[-1]
            advance = dht_mocks.get_time_accelerator(loop)
            await advance(500)

            # Join the network, assert that at least the known peers are in RT
            self.assertTrue(node.joined.is_set())
            self.assertTrue(
                len(node.protocol.routing_table.get_peers()) >= num_seeds)

            # Refresh, so that the peers are persisted
            self.assertFalse(
                len(await node._storage.get_persisted_kademlia_peers()) >
                num_seeds)
            await advance(4000)
            self.assertTrue(
                len(await node._storage.get_persisted_kademlia_peers()) >
                num_seeds)

            # We lost internet connection - all the peers stop responding
            dht_network.pop(
                (node.protocol.external_ip, node.protocol.udp_port))

            # The peers are cleared on refresh from RT and storage
            await advance(4000)
            self.assertListEqual([], await
                                 node._storage.get_persisted_kademlia_peers())
            await wait_for(
                lambda: len(node.protocol.routing_table.get_peers()) == 0,
                lambda: self.assertListEqual(
                    node.protocol.routing_table.get_peers(), []))

            # Reconnect
            dht_network[(node.protocol.external_ip,
                         node.protocol.udp_port)] = node.protocol

            # Check that node reconnects at least to them
            await advance(1000)
            await wait_for(
                lambda: len(node.protocol.routing_table.get_peers()) >=
                num_seeds, lambda: self.assertGreaterEqual(
                    len(node.protocol.routing_table.get_peers()), num_seeds))
Exemple #29
0
import asyncio
from aiohttp import web

from lbry.blob_exchange.serialization import BlobRequest, BlobResponse
from lbry.dht.constants import generate_id
from lbry.dht.node import Node
from lbry.dht.peer import make_kademlia_peer, PeerManager
from lbry.extras.daemon.storage import SQLiteStorage

loop = asyncio.get_event_loop()
NODE = Node(loop,
            PeerManager(loop),
            generate_id(),
            60600,
            60600,
            3333,
            None,
            storage=SQLiteStorage(None, ":memory:", loop, loop.time))


async def check_p2p(ip, port):
    writer = None
    try:
        reader, writer = await asyncio.open_connection(ip, port)
        writer.write(
            BlobRequest.make_request_for_blob_hash('0' * 96).serialize())
        return BlobResponse.deserialize(
            await
            reader.readuntil(b'}')).get_address_response().lbrycrd_address
    except OSError:
        return None
Exemple #30
0
 def setUp(self):
     self.loop = mock.Mock(spec=asyncio.BaseEventLoop)
     self.loop.time = lambda: 0.0
     self.peer_manager = PeerManager(self.loop)
     self.data_store = DictDataStore(self.loop, self.peer_manager)