Example #1
0
def test_closest_nodes_only_additional(empty_routing_table):
    target = NodeIDFactory()
    nodes = [NodeIDFactory() for _ in range(10)]
    closest_nodes = list(iter_closest_nodes(target, empty_routing_table,
                                            nodes))
    assert closest_nodes == sorted(
        nodes, key=lambda node: compute_distance(target, node))
Example #2
0
def test_closest_nodes_only_routing(empty_routing_table):
    target = NodeIDFactory()
    nodes = [NodeIDFactory() for _ in range(10)]
    for node in nodes:
        empty_routing_table.update(node)

    closest_nodes = list(iter_closest_nodes(target, empty_routing_table, []))
    assert closest_nodes == sorted(
        nodes, key=lambda node: compute_distance(target, node))
Example #3
0
def test_lookup_generator_mixed(empty_routing_table):
    target = NodeIDFactory()
    nodes = sorted(
        [NodeIDFactory() for _ in range(10)],
        key=lambda node: compute_distance(node, target),
    )
    nodes_in_routing_table = nodes[:3] + nodes[6:8]
    nodes_in_additional = nodes[3:6] + nodes[8:]
    for node in nodes_in_routing_table:
        empty_routing_table.update(node)
    closest_nodes = list(
        iter_closest_nodes(target, empty_routing_table, nodes_in_additional))
    assert closest_nodes == nodes
Example #4
0
    async def _collate(
        self,
        enr_send: trio.abc.SendChannel[ENRAPI],
        enr_receive: trio.abc.ReceiveChannel[ENRAPI],
    ) -> None:
        enr_buffer: List[ENRAPI] = []
        yielded_node_ids: Set[NodeID] = {self._network.local_node_id}

        while self.manager.is_running:
            # First wait for at least one ENR to be available...
            enr = await enr_receive.receive()
            if enr.node_id in yielded_node_ids:
                continue

            enr_buffer.append(enr)

            # Next, empty any additional values that are in the channel buffer.
            while True:
                try:
                    enr = enr_receive.receive_nowait()  # type: ignore
                except trio.WouldBlock:
                    break
                else:
                    if enr.node_id in yielded_node_ids:
                        continue
                    enr_buffer.append(enr)

            # deduplicate
            enr_buffer = list(reduce_enrs(enr_buffer))

            # sort and pull off closest
            closest_enr, *enr_buffer = list(
                sorted(
                    enr_buffer,
                    key=lambda enr: compute_distance(
                        self._network.local_node_id, enr.node_id),
                ))
            yielded_node_ids.add(closest_enr.node_id)
            await enr_send.send(closest_enr)
Example #5
0
    async def recursive_find_nodes(self, target: NodeID) -> Tuple[ENRAPI, ...]:
        self.logger.debug("Recursive find nodes: %s", humanize_node_id(target))

        queried_node_ids = set()
        unresponsive_node_ids = set()
        received_enrs: List[ENRAPI] = []
        received_node_ids: Set[NodeID] = set()

        async def do_lookup(node_id: NodeID) -> None:
            queried_node_ids.add(node_id)

            distance = compute_log_distance(node_id, target)
            try:
                enrs = await self.find_nodes(node_id, distance)
            except trio.TooSlowError:
                unresponsive_node_ids.add(node_id)
                return

            for enr in enrs:
                received_node_ids.add(enr.node_id)
                try:
                    self.enr_db.set_enr(enr)
                except OldSequenceNumber:
                    received_enrs.append(self.enr_db.get_enr(enr.node_id))
                else:
                    received_enrs.append(enr)

        for lookup_round_counter in itertools.count():
            candidates = iter_closest_nodes(target, self.routing_table,
                                            received_node_ids)
            responsive_candidates = itertools.dropwhile(
                lambda node: node in unresponsive_node_ids, candidates)
            closest_k_candidates = take(self.routing_table.bucket_size,
                                        responsive_candidates)
            closest_k_unqueried_candidates = (
                candidate for candidate in closest_k_candidates
                if candidate not in queried_node_ids)
            nodes_to_query = tuple(take(3, closest_k_unqueried_candidates))

            if nodes_to_query:
                self.logger.debug(
                    "Starting lookup round %d for %s",
                    lookup_round_counter + 1,
                    humanize_node_id(target),
                )
                async with trio.open_nursery() as nursery:
                    for peer in nodes_to_query:
                        nursery.start_soon(do_lookup, peer)
            else:
                self.logger.debug(
                    "Lookup for %s finished in %d rounds",
                    humanize_node_id(target),
                    lookup_round_counter,
                )
                break

        # now sort and return the ENR records in order of closesness to the target.
        return tuple(
            sorted(
                _reduce_enrs(received_enrs),
                key=lambda enr: compute_distance(enr.node_id, target),
            ))
Example #6
0
def test_distance(left_node_id, right_node_id, distance):
    assert compute_distance(left_node_id, right_node_id) == distance
    assert compute_distance(right_node_id, left_node_id) == distance
Example #7
0
async def test_alexandria_network_broadcast_api(
    tester,
    alice,
    alice_alexandria_network,
    autojump_clock,
):
    async with AsyncExitStack() as stack:
        network_group = await stack.enter_async_context(
            tester.alexandria.network_group(10))

        furthest_network = max(
            network_group,
            key=lambda network: compute_distance(alice.node_id, network.
                                                 local_node_id),
        )
        closest_network = min(
            network_group,
            key=lambda network: compute_distance(alice.node_id, network.
                                                 local_node_id),
        )

        furthest_node_distance_from_alice = compute_distance(
            furthest_network.local_node_id,
            alice.node_id,
        )

        furthest_ad = AdvertisementFactory()

        for _ in range(100):
            advertisement = AdvertisementFactory()

            distance_from_alice = compute_content_distance(
                alice.node_id, advertisement.content_id)
            distance_from_furthest = compute_content_distance(
                alice.node_id, furthest_ad.content_id)

            if distance_from_alice > distance_from_furthest:
                furthest_ad = advertisement

            if distance_from_furthest >= furthest_node_distance_from_alice:
                break

        async with trio.open_nursery() as nursery:

            async def _respond(network, subscription):
                request = await subscription.receive()
                await network.client.send_ack(
                    request.sender_node_id,
                    request.sender_endpoint,
                    advertisement_radius=network.local_advertisement_radius,
                    acked=(True, ) * len(request.message.payload),
                    request_id=request.request_id,
                )

            for network in network_group:
                subscription = await stack.enter_async_context(
                    network.client.subscribe(AdvertiseMessage))
                nursery.start_soon(_respond, network, subscription)

            alice_alexandria_network.enr_db.set_enr(
                closest_network.enr_manager.enr)
            await alice_alexandria_network.bond(closest_network.local_node_id)

            for _ in range(10000):
                await trio.lowlevel.checkpoint()

            with trio.fail_after(30):
                result = await alice_alexandria_network.broadcast(advertisement
                                                                  )
                assert len(result) > 0

            nursery.cancel_scope.cancel()