async def test_handle_talk(): async with PubsubFactory.create_batch_with_floodsub(1) as pubsubs_fsub: sub = await pubsubs_fsub[0].subscribe(TESTING_TOPIC) msg_0 = make_pubsub_msg( origin_id=pubsubs_fsub[0].my_id, topic_ids=[TESTING_TOPIC], data=b"1234", seqno=b"\x00" * 8, ) pubsubs_fsub[0].notify_subscriptions(msg_0) msg_1 = make_pubsub_msg( origin_id=pubsubs_fsub[0].my_id, topic_ids=["NOT_SUBSCRIBED"], data=b"1234", seqno=b"\x11" * 8, ) pubsubs_fsub[0].notify_subscriptions(msg_1) assert (len(pubsubs_fsub[0].topic_ids) == 1 and sub == pubsubs_fsub[0].subscribed_topics_receive[TESTING_TOPIC]) assert (await sub.get()) == msg_0
async def test_strict_signing_failed_validation(monkeypatch): async with PubsubFactory.create_batch_with_floodsub( 2, strict_signing=True) as pubsubs_fsub: msg = make_pubsub_msg( origin_id=pubsubs_fsub[0].my_id, topic_ids=[TESTING_TOPIC], data=TESTING_DATA, seqno=b"\x00" * 8, ) priv_key = pubsubs_fsub[0].sign_key signature = priv_key.sign(PUBSUB_SIGNING_PREFIX.encode() + msg.SerializeToString()) event = trio.Event() def _is_msg_seen(msg): return False # Use router publish to check if `push_msg` succeed. async def router_publish(*args, **kwargs): await trio.hazmat.checkpoint() # The event will only be set if `push_msg` succeed. event.set() monkeypatch.setattr(pubsubs_fsub[0], "_is_msg_seen", _is_msg_seen) monkeypatch.setattr(pubsubs_fsub[0].router, "publish", router_publish) # Test: no signature attached in `msg` await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg) await trio.sleep(0.01) assert not event.is_set() # Test: `msg.key` does not match `msg.from_id` msg.key = pubsubs_fsub[1].host.get_public_key().serialize() msg.signature = signature await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg) await trio.sleep(0.01) assert not event.is_set() # Test: invalid signature msg.key = pubsubs_fsub[0].host.get_public_key().serialize() msg.signature = b"\x12" * 100 await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg) await trio.sleep(0.01) assert not event.is_set() # Finally, assert the signature indeed will pass validation msg.key = pubsubs_fsub[0].host.get_public_key().serialize() msg.signature = signature await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg) await trio.sleep(0.01) assert event.is_set()
async def test_handle_talk(pubsubs_fsub): sub = await pubsubs_fsub[0].subscribe(TESTING_TOPIC) msg_0 = make_pubsub_msg( origin_id=pubsubs_fsub[0].my_id, topic_ids=[TESTING_TOPIC], data=b"1234", seqno=b"\x00" * 8, ) await pubsubs_fsub[0].handle_talk(msg_0) msg_1 = make_pubsub_msg( origin_id=pubsubs_fsub[0].my_id, topic_ids=["NOT_SUBSCRIBED"], data=b"1234", seqno=b"\x11" * 8, ) await pubsubs_fsub[0].handle_talk(msg_1) assert ( len(pubsubs_fsub[0].my_topics) == 1 and sub == pubsubs_fsub[0].my_topics[TESTING_TOPIC] ) assert sub.qsize() == 1 assert (await sub.get()) == msg_0
async def test_validate_msg(is_topic_1_val_passed, is_topic_2_val_passed): async with PubsubFactory.create_batch_with_floodsub(1) as pubsubs_fsub: def passed_sync_validator(peer_id, msg): return True def failed_sync_validator(peer_id, msg): return False async def passed_async_validator(peer_id, msg): await trio.hazmat.checkpoint() return True async def failed_async_validator(peer_id, msg): await trio.hazmat.checkpoint() return False topic_1 = "TEST_SYNC_VALIDATOR" topic_2 = "TEST_ASYNC_VALIDATOR" if is_topic_1_val_passed: pubsubs_fsub[0].set_topic_validator(topic_1, passed_sync_validator, False) else: pubsubs_fsub[0].set_topic_validator(topic_1, failed_sync_validator, False) if is_topic_2_val_passed: pubsubs_fsub[0].set_topic_validator(topic_2, passed_async_validator, True) else: pubsubs_fsub[0].set_topic_validator(topic_2, failed_async_validator, True) msg = make_pubsub_msg( origin_id=pubsubs_fsub[0].my_id, topic_ids=[topic_1, topic_2], data=b"1234", seqno=b"\x00" * 8, ) if is_topic_1_val_passed and is_topic_2_val_passed: await pubsubs_fsub[0].validate_msg(pubsubs_fsub[0].my_id, msg) else: with pytest.raises(ValidationError): await pubsubs_fsub[0].validate_msg(pubsubs_fsub[0].my_id, msg)
async def test_get_msg_validators(): async with PubsubFactory.create_batch_with_floodsub(1) as pubsubs_fsub: times_sync_validator_called = 0 def sync_validator(peer_id, msg): nonlocal times_sync_validator_called times_sync_validator_called += 1 times_async_validator_called = 0 async def async_validator(peer_id, msg): nonlocal times_async_validator_called times_async_validator_called += 1 await trio.hazmat.checkpoint() topic_1 = "TEST_VALIDATOR_1" topic_2 = "TEST_VALIDATOR_2" topic_3 = "TEST_VALIDATOR_3" # Register sync validator for topic 1 and 2 pubsubs_fsub[0].set_topic_validator(topic_1, sync_validator, False) pubsubs_fsub[0].set_topic_validator(topic_2, sync_validator, False) # Register async validator for topic 3 pubsubs_fsub[0].set_topic_validator(topic_3, async_validator, True) msg = make_pubsub_msg( origin_id=pubsubs_fsub[0].my_id, topic_ids=[topic_1, topic_2, topic_3], data=b"1234", seqno=b"\x00" * 8, ) topic_validators = pubsubs_fsub[0].get_msg_validators(msg) for topic_validator in topic_validators: if topic_validator.is_async: await topic_validator.validator(peer_id=IDFactory(), msg="msg") else: topic_validator.validator(peer_id=IDFactory(), msg="msg") assert times_sync_validator_called == 2 assert times_async_validator_called == 1
async def test_get_msg_validators(pubsubs_fsub): times_sync_validator_called = 0 def sync_validator(peer_id, msg): nonlocal times_sync_validator_called times_sync_validator_called += 1 times_async_validator_called = 0 async def async_validator(peer_id, msg): nonlocal times_async_validator_called times_async_validator_called += 1 topic_1 = "TEST_VALIDATOR_1" topic_2 = "TEST_VALIDATOR_2" topic_3 = "TEST_VALIDATOR_3" # Register sync validator for topic 1 and 2 pubsubs_fsub[0].set_topic_validator(topic_1, sync_validator, False) pubsubs_fsub[0].set_topic_validator(topic_2, sync_validator, False) # Register async validator for topic 3 pubsubs_fsub[0].set_topic_validator(topic_3, async_validator, True) msg = make_pubsub_msg( origin_id=pubsubs_fsub[0].my_id, topic_ids=[topic_1, topic_2, topic_3], data=b"1234", seqno=b"\x00" * 8, ) topic_validators = pubsubs_fsub[0].get_msg_validators(msg) for topic_validator in topic_validators: if topic_validator.is_async: await topic_validator.validator(peer_id=ID(b"peer"), msg="msg") else: topic_validator.validator(peer_id=ID(b"peer"), msg="msg") assert times_sync_validator_called == 2 assert times_async_validator_called == 1
async def test_validate_msg(pubsubs_fsub, is_topic_1_val_passed, is_topic_2_val_passed): def passed_sync_validator(peer_id, msg): return True def failed_sync_validator(peer_id, msg): return False async def passed_async_validator(peer_id, msg): return True async def failed_async_validator(peer_id, msg): return False topic_1 = "TEST_SYNC_VALIDATOR" topic_2 = "TEST_ASYNC_VALIDATOR" if is_topic_1_val_passed: pubsubs_fsub[0].set_topic_validator(topic_1, passed_sync_validator, False) else: pubsubs_fsub[0].set_topic_validator(topic_1, failed_sync_validator, False) if is_topic_2_val_passed: pubsubs_fsub[0].set_topic_validator(topic_2, passed_async_validator, True) else: pubsubs_fsub[0].set_topic_validator(topic_2, failed_async_validator, True) msg = make_pubsub_msg( origin_id=pubsubs_fsub[0].my_id, topic_ids=[topic_1, topic_2], data=b"1234", seqno=b"\x00" * 8, ) if is_topic_1_val_passed and is_topic_2_val_passed: await pubsubs_fsub[0].validate_msg(pubsubs_fsub[0].my_id, msg) else: with pytest.raises(ValidationError): await pubsubs_fsub[0].validate_msg(pubsubs_fsub[0].my_id, msg)
async def test_push_msg(pubsubs_fsub, monkeypatch): msg_0 = make_pubsub_msg( origin_id=pubsubs_fsub[0].my_id, topic_ids=[TESTING_TOPIC], data=TESTING_DATA, seqno=b"\x00" * 8, ) event = asyncio.Event() async def router_publish(*args, **kwargs): event.set() monkeypatch.setattr(pubsubs_fsub[0].router, "publish", router_publish) # Test: `msg` is not seen before `push_msg`, and is seen after `push_msg`. assert not pubsubs_fsub[0]._is_msg_seen(msg_0) await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg_0) assert pubsubs_fsub[0]._is_msg_seen(msg_0) # Test: Ensure `router.publish` is called in `push_msg` await asyncio.wait_for(event.wait(), timeout=0.1) # Test: `push_msg` the message again and it will be reject. # `router_publish` is not called then. event.clear() await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg_0) await asyncio.sleep(0.01) assert not event.is_set() sub = await pubsubs_fsub[0].subscribe(TESTING_TOPIC) # Test: `push_msg` succeeds with another unseen msg. msg_1 = make_pubsub_msg( origin_id=pubsubs_fsub[0].my_id, topic_ids=[TESTING_TOPIC], data=TESTING_DATA, seqno=b"\x11" * 8, ) assert not pubsubs_fsub[0]._is_msg_seen(msg_1) await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg_1) assert pubsubs_fsub[0]._is_msg_seen(msg_1) await asyncio.wait_for(event.wait(), timeout=0.1) # Test: Subscribers are notified when `push_msg` new messages. assert (await sub.get()) == msg_1 # Test: add a topic validator and `push_msg` the message that # does not pass the validation. # `router_publish` is not called then. def failed_sync_validator(peer_id, msg): return False pubsubs_fsub[0].set_topic_validator(TESTING_TOPIC, failed_sync_validator, False) msg_2 = make_pubsub_msg( origin_id=pubsubs_fsub[0].my_id, topic_ids=[TESTING_TOPIC], data=TESTING_DATA, seqno=b"\x22" * 8, ) event.clear() await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg_2) await asyncio.sleep(0.01) assert not event.is_set()
async def test_push_msg(monkeypatch): async with PubsubFactory.create_batch_with_floodsub(1) as pubsubs_fsub: msg_0 = make_pubsub_msg( origin_id=pubsubs_fsub[0].my_id, topic_ids=[TESTING_TOPIC], data=TESTING_DATA, seqno=b"\x00" * 8, ) @contextmanager def mock_router_publish(): event = trio.Event() async def router_publish(*args, **kwargs): event.set() await trio.hazmat.checkpoint() with monkeypatch.context() as m: m.setattr(pubsubs_fsub[0].router, "publish", router_publish) yield event with mock_router_publish() as event: # Test: `msg` is not seen before `push_msg`, and is seen after `push_msg`. assert not pubsubs_fsub[0]._is_msg_seen(msg_0) await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg_0) assert pubsubs_fsub[0]._is_msg_seen(msg_0) # Test: Ensure `router.publish` is called in `push_msg` with trio.fail_after(0.1): await event.wait() with mock_router_publish() as event: # Test: `push_msg` the message again and it will be reject. # `router_publish` is not called then. await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg_0) await trio.sleep(0.01) assert not event.is_set() sub = await pubsubs_fsub[0].subscribe(TESTING_TOPIC) # Test: `push_msg` succeeds with another unseen msg. msg_1 = make_pubsub_msg( origin_id=pubsubs_fsub[0].my_id, topic_ids=[TESTING_TOPIC], data=TESTING_DATA, seqno=b"\x11" * 8, ) assert not pubsubs_fsub[0]._is_msg_seen(msg_1) await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg_1) assert pubsubs_fsub[0]._is_msg_seen(msg_1) with trio.fail_after(0.1): await event.wait() # Test: Subscribers are notified when `push_msg` new messages. assert (await sub.get()) == msg_1 with mock_router_publish() as event: # Test: add a topic validator and `push_msg` the message that # does not pass the validation. # `router_publish` is not called then. def failed_sync_validator(peer_id, msg): return False pubsubs_fsub[0].set_topic_validator(TESTING_TOPIC, failed_sync_validator, False) msg_2 = make_pubsub_msg( origin_id=pubsubs_fsub[0].my_id, topic_ids=[TESTING_TOPIC], data=TESTING_DATA, seqno=b"\x22" * 8, ) await pubsubs_fsub[0].push_msg(pubsubs_fsub[0].my_id, msg_2) await trio.sleep(0.01) assert not event.is_set()