コード例 #1
0
ファイル: crawl.py プロジェクト: njgheorghita/ddht
    def __init__(self, args: argparse.Namespace, boot_info: BootInfo) -> None:
        super().__init__(args, boot_info)
        self.concurrency = args.crawl_concurrency

        self.enr_db = ENRDB(dict(), default_identity_scheme_registry)

        self.sock = trio.socket.socket(
            family=trio.socket.AF_INET, type=trio.socket.SOCK_DGRAM
        )

        self.private_key = get_local_private_key(boot_info)

        self.enr_manager = ENRManager(private_key=self.private_key, enr_db=self.enr_db)
        self.enr_manager.update((b"udp", boot_info.port))

        my_node_id = self.enr_manager.enr.node_id
        self.client = Client(self.private_key, self.enr_db, my_node_id, self.sock)

        self.active_tasks = ActiveTaskCounter()

        self.seen_nodeids: Set[NodeID] = set()
        self.responsive_peers: Set[NodeID] = set()

        self.enrqueue_send, self.enrqueue_recv = trio.open_memory_channel[ENRAPI](
            10_000
        )
コード例 #2
0
ファイル: component.py プロジェクト: wangroot/trinity
    async def do_run(self, event_bus: EndpointAPI) -> None:
        boot_info = self._boot_info
        config = boot_info.trinity_config
        db = DBClient.connect(config.database_ipc_path)

        if boot_info.args.disable_discovery:
            discovery_service: async_service.Service = NoopDiscoveryService(
                event_bus, )
        else:
            vm_config = config.get_app_config(
                Eth1AppConfig).get_chain_config().vm_configuration
            headerdb = TrioHeaderDB(db)
            eth_cap_provider = functools.partial(generate_eth_cap_enr_field,
                                                 vm_config, headerdb)
            socket = trio.socket.socket(family=trio.socket.AF_INET,
                                        type=trio.socket.SOCK_DGRAM)
            await socket.bind(("0.0.0.0", config.port))
            base_db = LevelDB(config.enr_db_dir)
            enr_db = ENRDB(base_db)
            discovery_service = PreferredNodeDiscoveryService(
                config.nodekey,
                config.port,
                config.port,
                config.bootstrap_nodes,
                config.preferred_nodes,
                event_bus,
                socket,
                enr_db,
                (eth_cap_provider, ),
            )

        with db:
            await async_service.run_trio_service(discovery_service)
コード例 #3
0
async def _manually_driven_discovery(seed, socket, nursery):
    _, port = socket.getsockname()
    discovery = ManuallyDrivenDiscoveryService(
        keys.PrivateKey(keccak(seed)),
        port,
        port,
        bootstrap_nodes=[],
        event_bus=None,
        socket=socket,
        enr_db=ENRDB({}),
    )
    async with background_trio_service(discovery):
        # Wait until we're fully initialized (i.e. until the ENR stub created in the constructor
        # is replaced with the real one).
        while discovery.this_node.enr.sequence_number == 0:
            await trio.lowlevel.checkpoint()
        yield discovery
コード例 #4
0
ファイル: tester.py プロジェクト: pipermerriam/ddht
 def node(
     self,
     private_key: Optional[keys.PrivateKey] = None,
     endpoint: Optional[Endpoint] = None,
     enr_db: Optional[ENRDatabaseAPI] = None,
     events: Optional[EventsAPI] = None,
 ) -> Node:
     if private_key is None:
         private_key = PrivateKeyFactory()
     if endpoint is None:
         endpoint = EndpointFactory.localhost()
     if enr_db is None:
         enr_db = ENRDB({})
     return Node(private_key=private_key,
                 endpoint=endpoint,
                 enr_db=enr_db,
                 events=events)
コード例 #5
0
async def enr_db(enr, remote_enr):
    enr_db = ENRDB({})
    enr_db.set_enr(enr)
    enr_db.set_enr(remote_enr)
    return enr_db
コード例 #6
0
ファイル: app.py プロジェクト: pipermerriam/ddht
    async def run(self) -> None:
        identity_scheme_registry = default_identity_scheme_registry

        message_type_registry = v51_registry

        enr_database_dir = self._boot_info.base_dir / ENR_DATABASE_DIR_NAME
        enr_database_dir.mkdir(exist_ok=True)
        enr_db = ENRDB(LevelDB(enr_database_dir), identity_scheme_registry)

        local_private_key = get_local_private_key(self._boot_info)

        enr_manager = ENRManager(
            enr_db=enr_db,
            private_key=local_private_key,
        )

        port = self._boot_info.port
        events = Events()

        if b"udp" not in enr_manager.enr:
            enr_manager.update((b"udp", port))

        listen_on_ip_address: AnyIPAddress
        if self._boot_info.listen_on is None:
            listen_on_ip_address = DEFAULT_LISTEN
        else:
            listen_on_ip_address = self._boot_info.listen_on
            # Update the ENR if an explicit listening address was provided
            enr_manager.update(
                (IP_V4_ADDRESS_ENR_KEY, listen_on_ip_address.packed))

        listen_on = Endpoint(listen_on_ip_address.packed, self._boot_info.port)

        if self._boot_info.is_upnp_enabled:
            upnp_service = UPnPService(port)
            self.manager.run_daemon_child_service(upnp_service)
            self.manager.run_daemon_task(self._update_enr_ip_from_upnp,
                                         enr_manager, upnp_service)

        bootnodes = self._boot_info.bootnodes

        client = Client(
            local_private_key=local_private_key,
            listen_on=listen_on,
            enr_db=enr_db,
            events=events,
            message_type_registry=message_type_registry,
        )
        network = Network(
            client=client,
            bootnodes=bootnodes,
        )

        logger.info("Protocol-Version: %s",
                    self._boot_info.protocol_version.value)
        logger.info("DDHT base dir: %s", self._boot_info.base_dir)
        logger.info("Starting discovery service...")
        logger.info("Listening on %s:%d", listen_on, port)
        logger.info("Local Node ID: %s", encode_hex(enr_manager.enr.node_id))
        logger.info("Local ENR: seq=%d enr=%s",
                    enr_manager.enr.sequence_number, enr_manager.enr)

        await run_trio_service(network)
コード例 #7
0
ファイル: app.py プロジェクト: Tubbz-alt/ddht
    async def run(self) -> None:
        identity_scheme_registry = default_identity_scheme_registry
        message_type_registry = v5_registry

        enr_database_dir = self._boot_info.base_dir / ENR_DATABASE_DIR_NAME
        enr_database_dir.mkdir(exist_ok=True)
        enr_db = ENRDB(LevelDB(enr_database_dir), identity_scheme_registry)
        self.enr_db = enr_db

        local_private_key = get_local_private_key(self._boot_info)

        enr_manager = ENRManager(
            private_key=local_private_key,
            enr_db=enr_db,
        )

        port = self._boot_info.port

        if b"udp" not in enr_manager.enr:
            enr_manager.update((b"udp", port))

        listen_on: AnyIPAddress
        if self._boot_info.listen_on is None:
            listen_on = DEFAULT_LISTEN
        else:
            listen_on = self._boot_info.listen_on
            # Update the ENR if an explicit listening address was provided
            enr_manager.update((IP_V4_ADDRESS_ENR_KEY, listen_on.packed))

        if self._boot_info.is_upnp_enabled:
            upnp_service = UPnPService(port)
            self.manager.run_daemon_child_service(upnp_service)

        routing_table = KademliaRoutingTable(enr_manager.enr.node_id,
                                             ROUTING_TABLE_BUCKET_SIZE)
        self.routing_table = routing_table

        for enr in self._boot_info.bootnodes:
            try:
                enr_db.set_enr(enr)
            except OldSequenceNumber:
                pass
            routing_table.update(enr.node_id)

        sock = trio.socket.socket(family=trio.socket.AF_INET,
                                  type=trio.socket.SOCK_DGRAM)
        outbound_datagram_channels = trio.open_memory_channel[
            OutboundDatagram](0)
        inbound_datagram_channels = trio.open_memory_channel[InboundDatagram](
            0)
        outbound_packet_channels = trio.open_memory_channel[OutboundPacket](0)
        inbound_packet_channels = trio.open_memory_channel[InboundPacket](0)
        outbound_message_channels = trio.open_memory_channel[
            AnyOutboundMessage](0)
        inbound_message_channels = trio.open_memory_channel[AnyInboundMessage](
            0)
        endpoint_vote_channels = trio.open_memory_channel[EndpointVote](0)

        # types ignored due to https://github.com/ethereum/async-service/issues/5
        datagram_sender = DatagramSender(  # type: ignore
            outbound_datagram_channels[1], sock)
        datagram_receiver = DatagramReceiver(  # type: ignore
            sock, inbound_datagram_channels[0])

        packet_encoder = PacketEncoder(  # type: ignore
            outbound_packet_channels[1], outbound_datagram_channels[0])
        packet_decoder = PacketDecoder(  # type: ignore
            inbound_datagram_channels[1], inbound_packet_channels[0])

        packer = Packer(
            local_private_key=local_private_key.to_bytes(),
            local_node_id=enr_manager.enr.node_id,
            enr_db=enr_db,
            message_type_registry=message_type_registry,
            inbound_packet_receive_channel=inbound_packet_channels[1],
            inbound_message_send_channel=inbound_message_channels[0],
            outbound_message_receive_channel=outbound_message_channels[1],
            outbound_packet_send_channel=outbound_packet_channels[0],
        )

        message_dispatcher = MessageDispatcher(
            enr_db=enr_db,
            inbound_message_receive_channel=inbound_message_channels[1],
            outbound_message_send_channel=outbound_message_channels[0],
        )

        endpoint_tracker = EndpointTracker(
            local_private_key=local_private_key.to_bytes(),
            local_node_id=enr_manager.enr.node_id,
            enr_db=enr_db,
            identity_scheme_registry=identity_scheme_registry,
            vote_receive_channel=endpoint_vote_channels[1],
        )

        routing_table_manager = RoutingTableManager(
            local_node_id=enr_manager.enr.node_id,
            routing_table=routing_table,
            message_dispatcher=message_dispatcher,
            enr_db=enr_db,
            outbound_message_send_channel=outbound_message_channels[0],
            endpoint_vote_send_channel=endpoint_vote_channels[0],
        )

        self.logger.info(f"DDHT base dir: {self._boot_info.base_dir}")
        self.logger.info("Starting discovery service...")
        self.logger.info(f"Listening on {listen_on}:{port}")
        self.logger.info(
            f"Local Node ID: {encode_hex(enr_manager.enr.node_id)}")
        self.logger.info(f"Local ENR: {enr_manager.enr}")

        services = (
            datagram_sender,
            datagram_receiver,
            packet_encoder,
            packet_decoder,
            packer,
            message_dispatcher,
            endpoint_tracker,
            routing_table_manager,
        )
        await sock.bind((str(listen_on), port))
        with sock:
            for service in services:
                self.manager.run_daemon_child_service(service)
            await self.manager.wait_finished()
コード例 #8
0
ファイル: crawl.py プロジェクト: njgheorghita/ddht
class Crawler(BaseApplication):
    def __init__(self, args: argparse.Namespace, boot_info: BootInfo) -> None:
        super().__init__(args, boot_info)
        self.concurrency = args.crawl_concurrency

        self.enr_db = ENRDB(dict(), default_identity_scheme_registry)

        self.sock = trio.socket.socket(
            family=trio.socket.AF_INET, type=trio.socket.SOCK_DGRAM
        )

        self.private_key = get_local_private_key(boot_info)

        self.enr_manager = ENRManager(private_key=self.private_key, enr_db=self.enr_db)
        self.enr_manager.update((b"udp", boot_info.port))

        my_node_id = self.enr_manager.enr.node_id
        self.client = Client(self.private_key, self.enr_db, my_node_id, self.sock)

        self.active_tasks = ActiveTaskCounter()

        self.seen_nodeids: Set[NodeID] = set()
        self.responsive_peers: Set[NodeID] = set()

        self.enrqueue_send, self.enrqueue_recv = trio.open_memory_channel[ENRAPI](
            10_000
        )

    async def fetch_enr_bucket(
        self, remote_enr: ENRAPI, bucket: int
    ) -> Tuple[ENRAPI, ...]:
        peer_id = remote_enr.node_id

        logger.debug(f"sending FindNode. nodeid={encode_hex(peer_id)} bucket={bucket}")

        try:
            with trio.fail_after(2):
                find_node = FindNodeMessage(
                    request_id=self.client.message_dispatcher.get_free_request_id(
                        peer_id
                    ),
                    distance=bucket,
                )

                responses = await self.client.message_dispatcher.request_nodes(
                    peer_id, find_node
                )

                return tuple(
                    enr for response in responses for enr in response.message.enrs
                )
        except trio.TooSlowError:
            logger.info(
                f"no response from peer. nodeid={encode_hex(peer_id)} bucket={bucket}"
            )
            raise _StopScanning()
        except UnexpectedMessage:
            logger.exception(
                "received a bad message from the peer. " f"nodeid={encode_hex(peer_id)}"
            )
            raise _StopScanning()

    async def scan_enr_buckets(self, remote_enr: ENRAPI) -> None:
        """
        Attempts to read ENRs from the outermost few buckets of the remote node.
        """
        peerid = remote_enr.node_id
        logger.debug(f"scanning. nodeid={encode_hex(peerid)}")

        SCAN_DEPTH = 100

        consecutive_empty_buckets = 0

        try:
            for bucket in range(256, 256 - SCAN_DEPTH, -1):
                enrs = await self.fetch_enr_bucket(remote_enr, bucket)

                novel_enrs = len(
                    [enr for enr in enrs if enr.node_id not in self.seen_nodeids]
                )

                logger.info(
                    f"received ENRs. nodeid={encode_hex(peerid)} bucket={bucket} "
                    f"enrs={len(enrs)} novel={novel_enrs}"
                )

                if len(enrs) == 0:
                    consecutive_empty_buckets += 1
                else:
                    self.responsive_peers.add(peerid)
                    consecutive_empty_buckets = 0

                # as we get deeper into the peer's routing table the chance that there are
                # any peers in the given bucket decreases exponentially. we save some time
                # by moving on to the next peer once we notice that the buckets have
                # started to think out.
                if consecutive_empty_buckets >= 5:
                    return

                for enr in enrs:
                    await self.schedule_enr_to_be_visited(enr)

                await trio.sleep(0.1)  # don't overburden this peer
        except _StopScanning:
            pass
        finally:
            # once this coro finishes we will never talk to this peer again, do some
            # cleanup now or we'll leak memory.
            self.client.discard_peer(remote_enr.node_id)

    async def read_from_queue_until_done(self) -> None:
        while True:

            # CAUTION: Do not insert any code here. There must not be any checkpoints
            #          between leaving active_tasks (in the previous loop iteration) and
            #          trying to read from the queue.

            try:
                enr = self.enrqueue_recv.receive_nowait()
            except trio.WouldBlock:
                if len(self.active_tasks) > 0:
                    # some tasks are still active so it's too soon to quit. Instead, block
                    # until new work comes in. If we've truly run out of work then this
                    # will block forever but that's okay, another task will notice and
                    # cause our cancellation.
                    enr = await self.enrqueue_recv.receive()
                else:
                    # nobody else is performing any work and there's also no work left to
                    # be done. It's time to quit! Calling this causes all other tasks to
                    # be canceled.
                    await self.manager.stop()
                    return

            # CAUTION: Do not insert any code here. There must not be any checkpoints
            #          between popping from the queue and entering active_tasks.

            with self.active_tasks.enter():
                await self.scan_enr_buckets(enr)

    async def schedule_enr_to_be_visited(self, enr: ENRAPI) -> None:
        if enr.node_id in self.seen_nodeids:
            # since each trip to a peer dumps their entire routing table there's no need
            # to visit a peer twice. This assumes that nodeids don't move around between
            # servers which is wrong but likely close enough.
            return

        if IP_V4_ADDRESS_ENR_KEY not in enr:
            logger.info(f"dropping ENR without IP address enr={enr} kv={enr.items()}")
            return

        self.seen_nodeids.add(enr.node_id)

        try:
            self.enr_db.set_enr(enr)
        except OldSequenceNumber:
            logger.info(f"dropping old ENR. enr={enr} kv={enr.items()}")
            return

        enqueued = self.enrqueue_send.statistics().current_buffer_used
        logger.info(
            f"found ENR. count={len(self.seen_nodeids)} enr={enr} node_id={enr.node_id.hex()}"
            f" sequence_number={enr.sequence_number} kv={enr.items()} queue_len={enqueued}"
        )

        await self.enrqueue_send.send(enr)

    async def run(self) -> None:
        logger.info("crawling!")

        boot_info = self._boot_info

        if boot_info.is_upnp_enabled:
            logger.info(
                "uPNP will not be used; crawling does not require listening for "
                "incoming connections."
            )

        for bootnode in boot_info.bootnodes:
            await self.schedule_enr_to_be_visited(bootnode)

        listen_on = boot_info.listen_on or DEFAULT_LISTEN
        logger.info(f"about to bind to port. bind={listen_on}:{boot_info.port}")
        await self.sock.bind((str(listen_on), boot_info.port))

        try:
            with self.sock:
                self.manager.run_daemon_child_service(self.client)

                for _ in range(self.concurrency):
                    self.manager.run_daemon_task(self.read_from_queue_until_done)

                # When it is time to quit one of the `read_from_queue_until_done` tasks will
                # notice and trigger a shutdown.
                await self.manager.wait_finished()
        finally:
            logger.info(
                f"scaning finished. found_peers={len(self.seen_nodeids)} "
                f"responsive_peers={len(self.responsive_peers)} "
            )
コード例 #9
0
async def enr_db(enr, remote_enr):
    db = ENRDB({})
    db.set_enr(enr)
    db.set_enr(remote_enr)
    return db
コード例 #10
0
async def enr_db(initial_enr):
    enr_db = ENRDB({})
    enr_db.set_enr(initial_enr)
    return enr_db