Exemplo n.º 1
0
    async def lookup(self, node_id: int) -> Tuple[NodeAPI, ...]:
        """Lookup performs a network search for nodes close to the given target.

        It approaches the target by querying nodes that are closer to it on each iteration.  The
        given target does not need to be an actual node identifier.
        """
        nodes_asked: Set[NodeAPI] = set()
        nodes_seen: Set[NodeAPI] = set()

        async def _find_node(node_id: int,
                             remote: NodeAPI) -> Tuple[NodeAPI, ...]:
            self._send_find_node(remote, node_id)
            candidates = await self.wait_neighbours(remote)
            if not candidates:
                self.logger.debug("got no candidates from %s, returning",
                                  remote)
                return tuple()
            all_candidates = tuple(c for c in candidates
                                   if c not in nodes_seen)
            candidates = tuple(
                c for c in all_candidates
                if (not self.ping_channels.already_waiting_for(c)
                    and not self.pong_channels.already_waiting_for(c)))
            self.logger.debug2("got %s new candidates", len(candidates))
            # Add new candidates to nodes_seen so that we don't attempt to bond with failing ones
            # in the future.
            nodes_seen.update(candidates)
            bonded = await trio_utils.gather(*((self.bond, c)
                                               for c in candidates))
            self.logger.debug2("bonded with %s candidates", bonded.count(True))
            return tuple(c for c in candidates if bonded[candidates.index(c)])

        def _exclude_if_asked(nodes: Iterable[NodeAPI]) -> List[NodeAPI]:
            nodes_to_ask = list(set(nodes).difference(nodes_asked))
            return sort_by_distance(
                nodes_to_ask, node_id)[:constants.KADEMLIA_FIND_CONCURRENCY]

        closest = self.routing.neighbours(node_id)
        self.logger.debug("starting lookup; initial neighbours: %s", closest)
        nodes_to_ask = _exclude_if_asked(closest)
        while nodes_to_ask:
            self.logger.debug2("node lookup; querying %s", nodes_to_ask)
            nodes_asked.update(nodes_to_ask)
            next_find_node_queries = (
                (_find_node, node_id, n) for n in nodes_to_ask
                if not self.neighbours_channels.already_waiting_for(n))
            results = await trio_utils.gather(*next_find_node_queries)
            for candidates in results:
                closest.extend(candidates)
            # Need to sort again and pick just the closest k nodes to ensure we converge.
            closest = sort_by_distance(
                eth_utils.toolz.unique(closest),
                node_id)[:constants.KADEMLIA_BUCKET_SIZE]
            nodes_to_ask = _exclude_if_asked(closest)

        self.logger.debug(
            "lookup finished for target %s; closest neighbours: %s",
            to_hex(node_id), closest)
        return tuple(closest)
Exemplo n.º 2
0
    async def lookup(self, node_id: int) -> List[kademlia.Node]:
        """Lookup performs a network search for nodes close to the given target.

        It approaches the target by querying nodes that are closer to it on each iteration.  The
        given target does not need to be an actual node identifier.
        """
        nodes_asked: Set[kademlia.Node] = set()
        nodes_seen: Set[kademlia.Node] = set()

        async def _find_node(
                node_id: int,
                remote: kademlia.Node) -> Tuple[kademlia.Node, ...]:
            # Short-circuit in case our token has been triggered to avoid trying to send requests
            # over a transport that is probably closed already.
            self.cancel_token.raise_if_triggered()
            self.send_find_node(remote, node_id)
            candidates = await self.wait_neighbours(remote)
            if not candidates:
                self.logger.debug("got no candidates from %s, returning",
                                  remote)
                return tuple()
            all_candidates = tuple(c for c in candidates
                                   if c not in nodes_seen)
            candidates = tuple(c for c in all_candidates
                               if (not self.ping_callbacks.locked(c)
                                   and not self.pong_callbacks.locked(c)))
            self.logger.debug("got %s new candidates", len(candidates))
            # Add new candidates to nodes_seen so that we don't attempt to bond with failing ones
            # in the future.
            nodes_seen.update(candidates)
            bonded = await asyncio.gather(*(self.bond(c) for c in candidates))
            self.logger.debug("bonded with %s candidates", bonded.count(True))
            return tuple(c for c in candidates if bonded[candidates.index(c)])

        def _exclude_if_asked(
                nodes: Iterable[kademlia.Node]) -> List[kademlia.Node]:
            nodes_to_ask = list(set(nodes).difference(nodes_asked))
            return kademlia.sort_by_distance(
                nodes_to_ask, node_id)[:kademlia.k_find_concurrency]

        closest = self.routing.neighbours(node_id)
        self.logger.debug("starting lookup; initial neighbours: %s", closest)
        nodes_to_ask = _exclude_if_asked(closest)
        while nodes_to_ask:
            self.logger.debug("node lookup; querying %s", nodes_to_ask)
            nodes_asked.update(nodes_to_ask)
            results = await asyncio.gather(
                *(_find_node(node_id, n) for n in nodes_to_ask
                  if not self.neighbours_callbacks.locked(n)))
            for candidates in results:
                closest.extend(candidates)
            closest = kademlia.sort_by_distance(
                closest, node_id)[:kademlia.k_bucket_size]
            nodes_to_ask = _exclude_if_asked(closest)

        self.logger.debug("lookup finished for %s: %s", node_id, closest)
        return closest
Exemplo n.º 3
0
 def _exclude_if_asked(nodes: Iterable[NodeAPI]) -> List[NodeAPI]:
     nodes_to_ask = list(set(nodes).difference(nodes_asked))
     return sort_by_distance(
         nodes_to_ask, node_id)[:constants.KADEMLIA_FIND_CONCURRENCY]
Exemplo n.º 4
0
 def _exclude_if_asked(
         nodes: Iterable[kademlia.Node]) -> List[kademlia.Node]:
     nodes_to_ask = list(set(nodes).difference(nodes_asked))
     return kademlia.sort_by_distance(
         nodes_to_ask, node_id)[:kademlia.k_find_concurrency]