def test_federated_nodes_connect_via_tls_and_verify( ursula_federated_test_config): node = make_federated_ursulas(ursula_config=ursula_federated_test_config, quantity=1).pop() node_deployer = node.get_deployer() node_deployer.addServices() node_deployer.catalogServers(node_deployer.hendrix) node_deployer.start() cert = node_deployer.cert.to_cryptography() cert_bytes = cert.public_bytes(serialization.Encoding.PEM) def check_node_with_cert(node, cert_file): response = requests.get("https://{}/public_information".format( node.rest_url()), verify=cert_file) ursula = Ursula.from_bytes(response.content, federated_only=True) assert ursula == node try: with open("test-cert", "wb") as f: # f.write(cert.tbs_certificate_bytes.hex()) f.write(cert_bytes) yield threads.deferToThread(check_node_with_cert, node, "test-cert") finally: os.remove("test-cert")
def test_one_node_stores_a_bunch_of_others(federated_ursulas, ursula_federated_test_config): the_chosen_seednode = list(federated_ursulas)[2] seed_node = the_chosen_seednode.seed_node_metadata() newcomer = make_federated_ursulas( ursula_config=ursula_federated_test_config, quantity=1, know_each_other=False, save_metadata=True, seed_nodes=[seed_node]).pop() assert not newcomer.known_nodes def start_lonely_learning_loop(): newcomer.start_learning_loop() start = maya.now() # Loop until the_chosen_seednode is in storage. while not the_chosen_seednode in newcomer.node_storage.all( federated_only=True): passed = maya.now() - start if passed.seconds > 2: pytest.fail("Didn't find the seed node.") yield deferToThread(start_lonely_learning_loop) # The known_nodes are all saved in storage (and no others have been saved) assert list(newcomer.known_nodes.values()) == list( newcomer.node_storage.all(True))
def fleet_of_highperf_mocked_ursulas(ursula_federated_test_config, request): try: quantity = request.param except AttributeError: quantity = 5000 # Bigass fleet by default; that's kinda the point. with GlobalLoggerSettings.pause_all_logging_while(): with mock_secret_source(): with mock_cert_storage, mock_cert_loading, mock_rest_app_creation, mock_cert_generation, mock_remember_node, mock_message_verification: _ursulas = make_federated_ursulas(ursula_config=ursula_federated_test_config, quantity=quantity, know_each_other=False) all_ursulas = {u.checksum_address: u for u in _ursulas} for ursula in _ursulas: ursula.known_nodes._nodes = all_ursulas ursula.known_nodes.checksum = b"This is a fleet state checksum..".hex() return _ursulas
def test_new_federated_ursula_announces_herself(ursula_federated_test_config): ursula_in_a_house, ursula_with_a_mouse = make_federated_ursulas(ursula_config=ursula_federated_test_config, quantity=2, know_each_other=False, network_middleware=MockRestMiddleware()) # Neither Ursula knows about the other. assert ursula_in_a_house.known_nodes == ursula_with_a_mouse.known_nodes == {} ursula_in_a_house.remember_node(ursula_with_a_mouse) # OK, now, ursula_in_a_house knows about ursula_with_a_mouse, but not vice-versa. assert ursula_with_a_mouse in ursula_in_a_house.known_nodes.values() assert not ursula_in_a_house in ursula_with_a_mouse.known_nodes.values() # But as ursula_in_a_house learns, she'll announce herself to ursula_with_a_mouse. ursula_in_a_house.learn_from_teacher_node() assert ursula_with_a_mouse in ursula_in_a_house.known_nodes.values() assert ursula_in_a_house in ursula_with_a_mouse.known_nodes.values()
def federated_ursulas(ursula_federated_test_config): _ursulas = make_federated_ursulas( ursula_config=ursula_federated_test_config, quantity=NUMBER_OF_URSULAS_IN_DEVELOPMENT_NETWORK) yield _ursulas
def test_emit_warning_upon_new_version(ursula_federated_test_config, caplog): nodes = make_federated_ursulas(ursula_config=ursula_federated_test_config, quantity=3, know_each_other=False) teacher, learner, new_node = nodes learner.remember_node(teacher) teacher.remember_node(learner) teacher.remember_node(new_node) new_node.TEACHER_VERSION = learner.LEARNER_VERSION + 1 warnings = [] def warning_trapper(event): if event['log_level'] == LogLevel.warn: warnings.append(event) globalLogPublisher.addObserver(warning_trapper) learner.learn_from_teacher_node() assert len(warnings) == 1 assert warnings[0]['log_format'] == learner.unknown_version_message.format( new_node, new_node.TEACHER_VERSION, learner.LEARNER_VERSION) # Now let's go a little further: make the version totally unrecognizable. # First, there's enough garbage to at least scrape a potential checksum address fleet_snapshot = os.urandom(32 + 4) random_bytes = os.urandom(50) # lots of garbage in here future_version = learner.LEARNER_VERSION + 42 version_bytes = future_version.to_bytes(2, byteorder="big") crazy_bytes = fleet_snapshot + VariableLengthBytestring(version_bytes + random_bytes) signed_crazy_bytes = bytes(teacher.stamp(crazy_bytes)) Response = namedtuple("MockResponse", ("content", "status_code")) response = Response(content=signed_crazy_bytes + crazy_bytes, status_code=200) learner._current_teacher_node = teacher learner.network_middleware.get_nodes_via_rest = lambda *args, **kwargs: response learner.learn_from_teacher_node() # If you really try, you can read a node representation from the garbage accidental_checksum = to_checksum_address(random_bytes[:20]) accidental_nickname = nickname_from_seed(accidental_checksum)[0] accidental_node_repr = Character._display_name_template.format( "Ursula", accidental_nickname, accidental_checksum) assert len(warnings) == 2 assert warnings[1]['log_format'] == learner.unknown_version_message.format( accidental_node_repr, future_version, learner.LEARNER_VERSION) # This time, however, there's not enough garbage to assume there's a checksum address... random_bytes = os.urandom(2) crazy_bytes = fleet_snapshot + VariableLengthBytestring(version_bytes + random_bytes) signed_crazy_bytes = bytes(teacher.stamp(crazy_bytes)) response = Response(content=signed_crazy_bytes + crazy_bytes, status_code=200) learner._current_teacher_node = teacher learner.learn_from_teacher_node() assert len(warnings) == 3 # ...so this time we get a "really unknown version message" assert warnings[2][ 'log_format'] == learner.really_unknown_version_message.format( future_version, learner.LEARNER_VERSION) globalLogPublisher.removeObserver(warning_trapper)