async def test_advertisement_collector_validate_multiple_mismatched_hash_tree_roots( bob, alice_alexandria_network, ): ad_collector = alice_alexandria_network.advertisement_collector advertisement_a = AdvertisementFactory(private_key=bob.private_key) advertisement_b = AdvertisementFactory( private_key=bob.private_key, content_key=advertisement_a.content_key, ) advertisement_c = AdvertisementFactory( private_key=bob.private_key, content_key=advertisement_a.content_key, ) assert advertisement_a.hash_tree_root != advertisement_b.hash_tree_root assert advertisement_b.hash_tree_root != advertisement_c.hash_tree_root advertisement_db = alice_alexandria_network.remote_advertisement_db advertisement_db.add(advertisement_a) advertisement_db.add(advertisement_b) with pytest.raises(NotImplementedError): await ad_collector.validate_advertisement(advertisement_c)
async def test_broadcast_log(alice, bob, conn): ad_a = AdvertisementFactory(private_key=alice.private_key) ad_b = AdvertisementFactory(private_key=alice.private_key) ad_c = AdvertisementFactory(private_key=alice.private_key) ad_d = AdvertisementFactory(private_key=bob.private_key) ad_e = AdvertisementFactory(private_key=bob.private_key) all_ads = (ad_a, ad_b, ad_c, ad_d, ad_e) broadcast_log = BroadcastLog(conn, max_records=8) # not logged when empty for ad in all_ads: assert not broadcast_log.was_logged(alice.node_id, ad) assert not broadcast_log.was_logged(bob.node_id, ad) broadcast_log.log(alice.node_id, ad_a) assert broadcast_log.was_logged(alice.node_id, ad_a) assert not broadcast_log.was_logged(bob.node_id, ad_a) # we insert 8 more, which should evict alice's entries for `ad_a` for ad in all_ads[1:]: broadcast_log.log(alice.node_id, ad) broadcast_log.log(bob.node_id, ad) assert not broadcast_log.was_logged(alice.node_id, ad_a)
def test_advertisement_to_and_from_sedes_payload(): advertisement = AdvertisementFactory() sedes_payload = advertisement.to_sedes_payload() result = Advertisement.from_sedes_payload(sedes_payload) assert result == advertisement
async def test_alexandria_network_locate_multiple_responses( alice, bob, bob_network, bob_alexandria_client, alice_alexandria_network): advertisements = tuple( AdvertisementFactory(content_key=b"\x01test-key") for _ in range(20)) num_messages = len( partition_advertisements(advertisements, MAX_PAYLOAD_SIZE)) assert num_messages > 1 async with bob_alexandria_client.subscribe(LocateMessage) as subscription: async def _respond(): request = await subscription.receive() await bob_alexandria_client.send_locations( request.sender_node_id, request.sender_endpoint, advertisements=advertisements, request_id=request.request_id, ) async with trio.open_nursery() as nursery: nursery.start_soon(_respond) with trio.fail_after(1): locations = await alice_alexandria_network.locate( bob.node_id, content_key=b"\x01test-key", ) assert locations == advertisements
async def test_alexandria_network_locate_single_response( alice, bob, bob_network, bob_alexandria_client, alice_alexandria_network): advertisement = AdvertisementFactory(private_key=alice.private_key) async with bob_alexandria_client.subscribe(LocateMessage) as subscription: async def _respond(): request = await subscription.receive() await bob_alexandria_client.send_locations( request.sender_node_id, request.sender_endpoint, advertisements=(advertisement, ), request_id=request.request_id, ) async with trio.open_nursery() as nursery: nursery.start_soon(_respond) with trio.fail_after(1): locations = await alice_alexandria_network.locate( bob.node_id, content_key=advertisement.content_key, ) assert len(locations) == 1 location = locations[0] assert location == advertisement
async def test_alexandria_network_advertise_multiple_messages( alice, bob, bob_network, bob_alexandria_client, alice_alexandria_network): advertisements = tuple( AdvertisementFactory(private_key=alice.private_key) for _ in range(20)) num_messages = len( partition_advertisements(advertisements, MAX_PAYLOAD_SIZE)) async with bob_alexandria_client.subscribe( AdvertiseMessage) as subscription: async def _respond(): for _ in range(num_messages): request = await subscription.receive() await bob_alexandria_client.send_ack( request.sender_node_id, request.sender_endpoint, advertisement_radius=12345, acked=(True, ) * len(request.message.payload), request_id=request.request_id, ) async with trio.open_nursery() as nursery: nursery.start_soon(_respond) with trio.fail_after(1): ack_payload = await alice_alexandria_network.advertise( bob.node_id, advertisements=advertisements, ) assert len(ack_payload.acked) == len(advertisements)
async def test_advertisement_collector_validate_remote_node_unreachable( bob, alice_alexandria_network, autojump_clock): ad_collector = alice_alexandria_network.advertisement_collector advertisement = AdvertisementFactory(private_key=bob.private_key) with pytest.raises(ValidationError, match="liveliness"): await ad_collector.validate_advertisement(advertisement)
async def test_advertisement_collector_handle_existing_valid_remote_advertisement( alice, bob, alice_alexandria_network, bob_alexandria_network, autojump_clock, ): content = ContentFactory(2048) proof = compute_proof(content, sedes=content_sedes) ad_collector = alice_alexandria_network.advertisement_collector advertisement = AdvertisementFactory( private_key=bob.private_key, hash_tree_root=proof.get_hash_tree_root(), ) bob_alexandria_network.commons_content_storage.set_content( advertisement.content_key, content, ) alice_alexandria_network.remote_advertisement_db.add(advertisement) assert alice_alexandria_network.remote_advertisement_db.exists( advertisement) async with ad_collector.new_advertisement.subscribe() as subscription: with trio.fail_after(5): await ad_collector.handle_advertisement(advertisement) with pytest.raises(trio.TooSlowError): with trio.fail_after(5): await subscription.receive() assert alice_alexandria_network.remote_advertisement_db.exists( advertisement)
async def test_alexandria_client_locate_single_response( alice, bob, alice_alexandria_client, bob_alexandria_client): advertisements = (AdvertisementFactory(private_key=alice.private_key), ) async with bob_alexandria_client.subscribe(LocateMessage) as subscription: async def _respond(): request = await subscription.receive() await bob_alexandria_client.send_locations( request.sender_node_id, request.sender_endpoint, advertisements=advertisements, request_id=request.request_id, ) async with trio.open_nursery() as nursery: nursery.start_soon(_respond) responses = await alice_alexandria_client.locate( bob.node_id, bob.endpoint, content_key=advertisements[0].content_key, request_id=b"\x01", ) assert len(responses) == 1 response = responses[0] assert isinstance(response.message, LocationsMessage) assert response.message.payload.total == 1 assert response.message.payload.locations == advertisements assert response.request_id == b"\x01"
async def test_advertisement_collector_acks_false_if_advertisements_already_known( alice, bob, alice_alexandria_client, bob_alexandria_network, autojump_clock, ): content = ContentFactory(2048) proof = compute_proof(content, sedes=content_sedes) advertisement = AdvertisementFactory( private_key=bob.private_key, hash_tree_root=proof.get_hash_tree_root(), ) bob_alexandria_network.commons_content_storage.set_content( advertisement.content_key, content, ) bob_alexandria_network.local_advertisement_db.add(advertisement) with pytest.raises(trio.TooSlowError): with trio.fail_after(10): async with bob_alexandria_network.advertisement_collector.new_advertisement.subscribe_and_wait( ): # noqa: E501 ack_message = await alice_alexandria_client.advertise( bob.node_id, bob.endpoint, advertisements=(advertisement, ), ) assert len(ack_message.payload.acked) == 1 assert ack_message.payload.acked[0] is False
async def test_advertisement_collector_check_interest_outside_radius( alice, alice_alexandria_network, ): ad_collector = alice_alexandria_network.advertisement_collector advertisement_db = alice_alexandria_network.remote_advertisement_db assert alice_alexandria_network.max_advertisement_count <= 32 advertisements = tuple( sorted( (AdvertisementFactory() for _ in range(alice_alexandria_network.max_advertisement_count + 1)), key=lambda ad: compute_content_distance(ad.content_id, alice. node_id), )) furthest = advertisements[-1] assert alice_alexandria_network.local_advertisement_radius == 2**256 - 1 assert ad_collector.check_interest(furthest) is True for ad in advertisements[:alice_alexandria_network. max_advertisement_count]: advertisement_db.add(ad) expected_radius = compute_content_distance(advertisements[-2].content_id, alice.node_id) assert expected_radius < 2**256 - 1 assert alice_alexandria_network.local_advertisement_radius == expected_radius assert ad_collector.check_interest(furthest) is False
async def test_advertisement_collector_handle_new_valid_remote_advertisement( alice, bob, alice_alexandria_network, bob_alexandria_network, ): content = ContentFactory(2048) proof = compute_proof(content, sedes=content_sedes) ad_collector = alice_alexandria_network.advertisement_collector advertisement = AdvertisementFactory( private_key=bob.private_key, hash_tree_root=proof.get_hash_tree_root(), ) bob_alexandria_network.commons_content_storage.set_content( advertisement.content_key, content, ) assert not alice_alexandria_network.remote_advertisement_db.exists( advertisement) with trio.fail_after(5): async with ad_collector.new_advertisement.subscribe_and_wait(): await ad_collector.handle_advertisement(advertisement) assert alice_alexandria_network.remote_advertisement_db.exists( advertisement)
async def test_alexandria_network_advertise_wrong_ack_count( alice, bob, bob_network, bob_alexandria_client, alice_alexandria_network): advertisements = (AdvertisementFactory(private_key=alice.private_key), ) async with bob_alexandria_client.subscribe( AdvertiseMessage) as subscription: async def _respond(): request = await subscription.receive() await bob_alexandria_client.send_ack( request.sender_node_id, request.sender_endpoint, advertisement_radius=12345, acked=(True, False, True), # wrong number request_id=request.request_id, ) async with trio.open_nursery() as nursery: nursery.start_soon(_respond) with pytest.raises(ValidationError): with trio.fail_after(2): await alice_alexandria_network.advertise( bob.node_id, advertisements=advertisements, )
async def test_remote_advertisement_manager_advertisement_radius( alice, alice_alexandria_network, autojump_clock, ): ad_manager = alice_alexandria_network.remote_advertisement_manager assert ad_manager.max_advertisement_count <= 32 assert ad_manager.advertisement_radius == MAX_RADIUS advertisements = tuple( sorted( (AdvertisementFactory() for _ in range(alice_alexandria_network.max_advertisement_count + 1)), key=lambda ad: compute_content_distance(alice.node_id, ad. content_id), )) for advertisement in advertisements: ad_manager.advertisement_db.add(advertisement) await ad_manager.purge_distant_ads() expected_radius = compute_content_distance(advertisements[-2].content_id, alice.node_id) assert expected_radius < MAX_RADIUS assert ad_manager.advertisement_radius == expected_radius
async def test_content_provider_serves_advertisements( alice, bob, alice_alexandria_network, bob_alexandria_client, ): content_key = b"test-content-key" advertisement_db = AdvertisementDatabase(sqlite3.connect(":memory:")) advertisements = tuple( AdvertisementFactory(content_key=content_key) for _ in range(5)) for advertisement in advertisements: advertisement_db.add(advertisement) advertisement_provider = AdvertisementProvider( bob_alexandria_client, (advertisement_db, ), ) async with background_trio_service(advertisement_provider): # this ensures that the subscription is in place. await advertisement_provider.ready() with trio.fail_after(2): result = await alice_alexandria_network.locate( bob.node_id, content_key=content_key, ) assert set(result) == set(advertisements)
async def test_alexandria_client_advertise(alice, bob, bob_network, bob_alexandria_client, alice_alexandria_client): advertisements = (AdvertisementFactory(private_key=alice.private_key), ) async with bob_network.dispatcher.subscribe( TalkRequestMessage) as subscription: async def _respond(): request = await subscription.receive() await bob_alexandria_client.send_ack( request.sender_node_id, request.sender_endpoint, advertisement_radius=12345, acked=(True, ), request_id=request.request_id, ) async with trio.open_nursery() as nursery: nursery.start_soon(_respond) with trio.fail_after(1): ack_message = await alice_alexandria_client.advertise( bob.node_id, bob.endpoint, advertisements=advertisements, ) assert isinstance(ack_message, AckMessage) assert ack_message.payload.advertisement_radius == 12345
async def test_alexandria_network_advertise_multi_message( alice, bob, bob_network, bob_alexandria_client, alice_alexandria_network): advertisements = tuple( AdvertisementFactory(private_key=alice.private_key) for i in range(16)) assert len(partition_advertisements(advertisements, MAX_PAYLOAD_SIZE)) > 1 async with bob_alexandria_client.subscribe( AdvertiseMessage) as subscription: async def _respond(): async for request in subscription: await bob_alexandria_client.send_ack( request.sender_node_id, request.sender_endpoint, advertisement_radius=12345, acked=(True, ) * len(request.message.payload), request_id=request.request_id, ) async with trio.open_nursery() as nursery: nursery.start_soon(_respond) with trio.fail_after(2): ack_payload = await alice_alexandria_network.advertise( bob.node_id, advertisements=advertisements, ) nursery.cancel_scope.cancel() assert isinstance(ack_payload, AckPayload) assert ack_payload.advertisement_radius == 12345 assert ack_payload.acked == (True, ) * 16
async def test_advertisement_collector_validate_local_expired( alice, alice_alexandria_network, ): ad_collector = alice_alexandria_network.advertisement_collector advertisement = AdvertisementFactory.expired(private_key=alice.private_key) with pytest.raises(ValidationError, match="expired"): await ad_collector.validate_advertisement(advertisement)
async def test_advertisement_collector_validate_already_in_remote_database( bob, alice_alexandria_network, ): ad_collector = alice_alexandria_network.advertisement_collector advertisement = AdvertisementFactory(private_key=bob.private_key) alice_alexandria_network.remote_advertisement_db.add(advertisement) with pytest.raises(ValidationError, match="known"): await ad_collector.validate_advertisement(advertisement)
async def test_advertisement_collector_validate_invalid_signature( alice_alexandria_network, ): ad_collector = alice_alexandria_network.advertisement_collector advertisement = AdvertisementFactory( signature_v=1, signature_r=1357924680, signature_s=2468013579, ) with pytest.raises(ValidationError, match="signature"): await ad_collector.validate_advertisement(advertisement)
def test_advertisement_with_invalid_signature(): ad = AdvertisementFactory.invalid() assert not ad.is_valid with pytest.raises(BadSignature): ad.node_id with pytest.raises(BadSignature): ad.public_key with pytest.raises(BadSignature): ad.verify()
async def test_advertisement_collector_validate_remote_fail_initial_proof_check( bob, alice_alexandria_network, bob_alexandria_network, autojump_clock, ): ad_collector = alice_alexandria_network.advertisement_collector advertisement = AdvertisementFactory(private_key=bob.private_key) with pytest.raises(ValidationError, match="proof retrieval"): await ad_collector.validate_advertisement(advertisement)
async def test_advertisement_collector_validate_local_content_not_found( alice, alice_alexandria_network, ): ad_collector = alice_alexandria_network.advertisement_collector advertisement = AdvertisementFactory(private_key=alice.private_key) content_storage = alice_alexandria_network.commons_content_storage assert not content_storage.has_content(advertisement.content_key) with pytest.raises(ValidationError, match="not found"): await ad_collector.validate_advertisement(advertisement)
async def test_advertisement_collector_validate_local_hash_tree_root_mismatch( alice, alice_alexandria_network, ): ad_collector = alice_alexandria_network.advertisement_collector advertisement = AdvertisementFactory(private_key=alice.private_key) content_storage = alice_alexandria_network.commons_content_storage content_storage.set_content(advertisement.content_key, ContentFactory()) with pytest.raises(ValidationError, match="Mismatched roots"): await ad_collector.validate_advertisement(advertisement)
async def test_advertisement_collector_check_interest_already_in_remote_db( bob, alice_alexandria_network, ): ad_collector = alice_alexandria_network.advertisement_collector advertisement = AdvertisementFactory(private_key=bob.private_key) assert ad_collector.check_interest(advertisement) is True alice_alexandria_network.remote_advertisement_db.add(advertisement) assert ad_collector.check_interest(advertisement) is False
def test_advertise_message_encoded_size(content_keys): advertisements = tuple( AdvertisementFactory(content_key=content_key) for content_key in content_keys ) ssz_payload = tuple(ad.to_sedes_payload() for ad in advertisements) encoded = ssz.encode(ssz_payload, sedes=AdvertiseSedes) num_advertisements = len(advertisements) assert len(encoded) == ( num_advertisements * ADVERTISEMENT_FIXED_SIZE + sum(len(content_key) for content_key in content_keys) )
async def test_alexandria_client_advertise_timeout( alice, bob, alice_alexandria_client, autojump_clock, ): advertisements = (AdvertisementFactory(private_key=alice.private_key), ) with pytest.raises(trio.TooSlowError): await alice_alexandria_client.advertise( bob.node_id, bob.endpoint, advertisements=advertisements, )
async def test_alexandria_client_advertise_too_many( alice, bob, alice_alexandria_client, bob_alexandria_client, ): advertisements = tuple( AdvertisementFactory(private_key=alice.private_key) for _ in range(32)) with pytest.raises(Exception, match="Payload too large"): await alice_alexandria_client.advertise( bob.node_id, bob.endpoint, advertisements=advertisements, )
async def test_alexandria_network_advertise_invalid_signature( alice, bob, bob_network, bob_alexandria_client, alice_alexandria_network): advertisement = AdvertisementFactory( signature_v=1, signature_r=1357924680, signature_s=2468013579, ) assert not advertisement.is_valid with pytest.raises(Exception, match="invalid"): await alice_alexandria_network.advertise( bob.node_id, advertisements=(advertisement, ), )
async def test_alexandria_client_send_advertisements(alice, bob, bob_network, alice_alexandria_client): advertisements = (AdvertisementFactory(private_key=alice.private_key), ) async with bob_network.dispatcher.subscribe( TalkRequestMessage) as subscription: await alice_alexandria_client.send_advertisements( bob.node_id, bob.endpoint, advertisements=advertisements, ) with trio.fail_after(1): talk_response = await subscription.receive() message = decode_message(talk_response.message.payload) assert isinstance(message, AdvertiseMessage) assert message.payload == advertisements