示例#1
0
    async def get_n_fastest_spvs(self,
                                 timeout=3.0
                                 ) -> Dict[Tuple[str, int], Optional[SPVPong]]:
        loop = asyncio.get_event_loop()
        pong_responses = asyncio.Queue()
        connection = SPVStatusClientProtocol(pong_responses)
        sent_ping_timestamps = {}
        _, ip_to_hostnames = await self.resolve_spv_dns()
        n = len(ip_to_hostnames)
        log.info("%i possible spv servers to try (%i urls in config)", n,
                 len(self.config['explicit_servers']))
        pongs = {}
        known_hubs = self.known_hubs
        try:
            await loop.create_datagram_endpoint(lambda: connection,
                                                ('0.0.0.0', 0))
            # could raise OSError if it cant bind
            start = perf_counter()
            for server in ip_to_hostnames:
                connection.ping(server)
                sent_ping_timestamps[server] = perf_counter()
            while len(pongs) < n:
                (remote, ts), pong = await asyncio.wait_for(
                    pong_responses.get(), timeout - (perf_counter() - start))
                latency = ts - start
                log.info(
                    "%s:%i has latency of %sms (available: %s, height: %i)",
                    '/'.join(ip_to_hostnames[remote]), remote[1],
                    round(latency * 1000, 2), pong.available, pong.height)

                known_hubs.hubs.setdefault(
                    (ip_to_hostnames[remote][0], remote[1]),
                    {}).update({"country": pong.country_name})
                if pong.available:
                    pongs[(ip_to_hostnames[remote][0], remote[1])] = pong
            return pongs
        except asyncio.TimeoutError:
            if pongs:
                log.info("%i/%i probed spv servers are accepting connections",
                         len(pongs), len(ip_to_hostnames))
                return pongs
            else:
                log.warning(
                    "%i spv status probes failed, retrying later. servers tried: %s",
                    len(sent_ping_timestamps),
                    ', '.join('/'.join(hosts) + f' ({ip})'
                              for ip, hosts in ip_to_hostnames.items()))
                random_server = random.choice(list(ip_to_hostnames.keys()))
                host, port = random_server
                log.warning("trying fallback to randomly selected spv: %s:%i",
                            host, port)
                known_hubs.hubs.setdefault((host, port), {})
                return {(host, port): None}
        finally:
            connection.close()
示例#2
0
    async def get_n_fastest_spvs(self,
                                 n=5,
                                 timeout=3.0
                                 ) -> Dict[Tuple[str, int], SPVPong]:
        loop = asyncio.get_event_loop()
        pong_responses = asyncio.Queue()
        connection = SPVStatusClientProtocol(pong_responses)
        sent_ping_timestamps = {}
        _, ip_to_hostnames = await self.resolve_spv_dns()
        log.info("%i possible spv servers to try (%i urls in config)",
                 len(ip_to_hostnames), len(self.config['default_servers']))
        pongs = {}
        try:
            await loop.create_datagram_endpoint(lambda: connection,
                                                ('0.0.0.0', 0))
            # could raise OSError if it cant bind
            start = perf_counter()
            for server in ip_to_hostnames:
                connection.ping(server)
                sent_ping_timestamps[server] = perf_counter()
            while len(pongs) < n:
                (remote, ts), pong = await asyncio.wait_for(
                    pong_responses.get(), timeout - (perf_counter() - start))
                latency = ts - start
                log.info(
                    "%s:%i has latency of %sms (available: %s, height: %i)",
                    '/'.join(ip_to_hostnames[remote]), remote[1],
                    round(latency * 1000, 2), pong.available, pong.height)

                if pong.available:
                    pongs[remote] = pong
            return pongs
        except asyncio.TimeoutError:
            if pongs:
                log.info("%i/%i probed spv servers are accepting connections",
                         len(pongs), len(ip_to_hostnames))
            else:
                log.warning(
                    "%i spv status probes failed, retrying later. servers tried: %s",
                    len(sent_ping_timestamps),
                    ', '.join('/'.join(hosts) + f' ({ip})'
                              for ip, hosts in ip_to_hostnames.items()))
            return pongs
        finally:
            connection.close()
示例#3
0
async def _get_external_ip(
    default_servers
) -> typing.Tuple[typing.Optional[str], typing.Optional[str]]:
    # used if upnp is disabled or non-functioning
    from lbry.wallet.server.udp import SPVStatusClientProtocol  # pylint: disable=C0415

    hostname_to_ip = {}
    ip_to_hostnames = collections.defaultdict(list)

    async def resolve_spv(server, port):
        try:
            server_addr = await resolve_host(server, port, 'udp')
            hostname_to_ip[server] = (server_addr, port)
            ip_to_hostnames[(server_addr, port)].append(server)
        except Exception:
            log.exception("error looking up dns for spv servers")

    # accumulate the dns results
    await asyncio.gather(*(resolve_spv(server, port)
                           for (server, port) in default_servers))

    loop = asyncio.get_event_loop()
    pong_responses = asyncio.Queue()
    connection = SPVStatusClientProtocol(pong_responses)
    try:
        await loop.create_datagram_endpoint(lambda: connection, ('0.0.0.0', 0))
        # could raise OSError if it cant bind
        randomized_servers = list(ip_to_hostnames.keys())
        random.shuffle(randomized_servers)
        for server in randomized_servers:
            connection.ping(server)
            try:
                _, pong = await asyncio.wait_for(pong_responses.get(), 1)
                if is_valid_public_ipv4(pong.ip_address):
                    return pong.ip_address, ip_to_hostnames[server][0]
            except asyncio.TimeoutError:
                pass
        return None, None
    finally:
        connection.close()