def inspect(self, appointment_data): """ Inspects whether the data provided by the user is correct. Args: appointment_data (:obj:`dict`): a dictionary containing the appointment data. Returns: :obj:`Appointment <common.appointment.Appointment>`: An appointment initialized with the provided data. Raises: :obj:`InspectionFailed`: if any of the fields is wrong. """ if appointment_data is None: raise InspectionFailed(errors.APPOINTMENT_EMPTY_FIELD, "empty appointment received") elif not isinstance(appointment_data, dict): raise InspectionFailed(errors.APPOINTMENT_WRONG_FIELD, "wrong appointment format") self.check_locator(appointment_data.get("locator")) self.check_to_self_delay(appointment_data.get("to_self_delay")) self.check_blob(appointment_data.get("encrypted_blob")) return Appointment( appointment_data.get("locator"), appointment_data.get("encrypted_blob"), appointment_data.get("to_self_delay"), )
def set_up_appointments(db_manager, total_appointments): appointments = dict() locator_uuid_map = dict() for i in range(total_appointments): uuid = uuid4().hex locator = get_random_value_hex(LOCATOR_LEN_BYTES) appointment = Appointment(locator, None, None, None, None) appointments[uuid] = {"locator": appointment.locator} locator_uuid_map[locator] = [uuid] db_manager.store_watcher_appointment(uuid, appointment.to_json()) db_manager.create_append_locator_map(locator, uuid) # Each locator can have more than one uuid assigned to it. if i % 2: uuid = uuid4().hex appointments[uuid] = {"locator": appointment.locator} locator_uuid_map[locator].append(uuid) db_manager.store_watcher_appointment(uuid, appointment.to_json()) db_manager.create_append_locator_map(locator, uuid) return appointments, locator_uuid_map
def test_delete_outdated_users(gatekeeper): # This tests the deletion of users whose subscription has outdated (subscription expires now) # Create some users with associated data and add them to the gatekeeper users = {} current_height = gatekeeper.block_processor.get_block_count() for _ in range(10): appointments = { get_random_value_hex(32): Appointment(get_random_value_hex(32), None, None) } user_id = get_random_value_hex(16) user_info = UserInfo(available_slots=100, subscription_expiry=current_height, appointments=appointments) users[user_id] = user_info gatekeeper.registered_users[user_id] = user_info # Get a list of the users that should be deleted at this block height (must match the newly generated ones) users_to_be_deleted = gatekeeper.get_outdated_user_ids( current_height + gatekeeper.expiry_delta) assert users_to_be_deleted == list(users.keys()) # Delete the users Cleaner.delete_outdated_users(users_to_be_deleted, gatekeeper.registered_users, gatekeeper.user_db) # Check that the users are not in the gatekeeper anymore for user_id in users_to_be_deleted: assert user_id not in gatekeeper.registered_users assert not gatekeeper.user_db.load_user(user_id)
def add_appointment(self, request, context): """Processes the request to add an appointment from a user.""" try: appointment = Appointment(request.appointment.locator, request.appointment.encrypted_blob, request.appointment.to_self_delay) return AddAppointmentResponse( **self.watcher.add_appointment(appointment, request.signature)) except (AuthenticationFailure, NotEnoughSlots): msg = "Invalid signature or user does not have enough slots available" status_code = grpc.StatusCode.UNAUTHENTICATED except AppointmentLimitReached: msg = "Appointment limit reached" status_code = grpc.StatusCode.RESOURCE_EXHAUSTED except SubscriptionExpired as e: msg = str(e) status_code = grpc.StatusCode.UNAUTHENTICATED except AppointmentAlreadyTriggered: msg = "The provided appointment has already been triggered" status_code = grpc.StatusCode.ALREADY_EXISTS except ConnectionRefusedError: msg = "Service unavailable" status_code = grpc.StatusCode.UNAVAILABLE context.set_details(msg) context.set_code(status_code) return AddAppointmentResponse()
def test_init_appointment(appointment_data): # The appointment has no checks whatsoever, since the inspector is the one taking care or that, and the only one # creating appointments. appointment = Appointment(appointment_data["locator"], appointment_data["encrypted_blob"], appointment_data["to_self_delay"]) assert (appointment_data["locator"] == appointment.locator and appointment_data["to_self_delay"] == appointment.to_self_delay and appointment_data["encrypted_blob"] == appointment.encrypted_blob)
def test_to_dict(appointment_data): appointment = Appointment(appointment_data["locator"], appointment_data["encrypted_blob"], appointment_data["to_self_delay"]) dict_appointment = appointment.to_dict() assert (appointment_data["locator"] == dict_appointment["locator"] and appointment_data["to_self_delay"] == dict_appointment["to_self_delay"] and appointment_data["encrypted_blob"] == dict_appointment["encrypted_blob"])
def set_up_trackers(db_manager, total_trackers): trackers = dict() tx_tracker_map = dict() for i in range(total_trackers): uuid = uuid4().hex # We use the same txid for penalty and dispute here, it shouldn't matter penalty_txid = get_random_value_hex(32) dispute_txid = get_random_value_hex(32) locator = dispute_txid[:LOCATOR_LEN_HEX] # Appointment data appointment = Appointment(locator, None, None) # Store the data in the database and create a flag db_manager.store_watcher_appointment(uuid, appointment.to_dict()) db_manager.create_triggered_appointment_flag(uuid) # Assign both penalty_txid and dispute_txid the same id (it shouldn't matter) tracker = TransactionTracker(locator, dispute_txid, penalty_txid, None, None) trackers[uuid] = { "locator": tracker.locator, "penalty_txid": tracker.penalty_txid } tx_tracker_map[penalty_txid] = [uuid] db_manager.store_responder_tracker(uuid, tracker.to_dict()) # Each penalty_txid can have more than one uuid assigned to it. if i % 2: uuid = uuid4().hex trackers[uuid] = { "locator": tracker.locator, "penalty_txid": tracker.penalty_txid } tx_tracker_map[penalty_txid].append(uuid) db_manager.store_responder_tracker(uuid, tracker.to_dict()) # Add them to the Watcher's db too db_manager.store_watcher_appointment(uuid, appointment.to_dict()) db_manager.create_triggered_appointment_flag(uuid) return trackers, tx_tracker_map
def test_to_json(appointment_data): appointment = Appointment( appointment_data["locator"], appointment_data["start_time"], appointment_data["end_time"], appointment_data["to_self_delay"], appointment_data["encrypted_blob"], ) dict_appointment = json.loads(appointment.to_json()) assert (appointment_data["locator"] == dict_appointment["locator"] and appointment_data["start_time"] == dict_appointment["start_time"] and appointment_data["end_time"] == dict_appointment["end_time"] and appointment_data["to_self_delay"] == dict_appointment["to_self_delay"] and EncryptedBlob(appointment_data["encrypted_blob"]) == EncryptedBlob(dict_appointment["encrypted_blob"]))
def test_init_appointment(appointment_data): # The appointment has no checks whatsoever, since the inspector is the one taking care or that, and the only one # creating appointments. # DISCUSS: whether this makes sense by design or checks should be ported from the inspector to the appointment # 35-appointment-checks appointment = Appointment( appointment_data["locator"], appointment_data["start_time"], appointment_data["end_time"], appointment_data["to_self_delay"], appointment_data["encrypted_blob"], ) assert (appointment_data["locator"] == appointment.locator and appointment_data["start_time"] == appointment.start_time and appointment_data["end_time"] == appointment.end_time and appointment_data["to_self_delay"] == appointment.to_self_delay and EncryptedBlob(appointment_data["encrypted_blob"]) == appointment.encrypted_blob)
def on_commitment_revocation(plugin, **kwargs): """ Sends an appointment to all registered towers for every net commitment transaction. kwargs should contain the commitment identifier (commitment_txid) and the penalty transaction (penalty_tx) Args: plugin (:obj:`Plugin`): this plugin. """ try: commitment_txid, penalty_tx = arg_parser.parse_add_appointment_arguments(kwargs) appointment = Appointment( locator=compute_locator(commitment_txid), encrypted_blob=Cryptographer.encrypt(penalty_tx, commitment_txid), to_self_delay=20, # does not matter for now, any value 20-2^32-1 would do ) signature = Cryptographer.sign(appointment.serialize(), plugin.wt_client.sk) except (InvalidParameter, EncryptionError, SignatureError) as e: plugin.log(str(e), level="warn") return {"result": "continue"} # Send appointment to the towers. # FIXME: sending the appointment to all registered towers atm. Some management would be nice. for tower_id, tower in plugin.wt_client.towers.items(): tower_update = {} if tower.status == "misbehaving": continue try: if tower.status == "reachable": tower_signature, available_slots = add_appointment( plugin, tower_id, tower, appointment.to_dict(), signature ) tower_update["appointment"] = (appointment.locator, tower_signature) tower_update["available_slots"] = available_slots else: if tower.status in ["temporarily unreachable", "unreachable"]: plugin.log(f"{tower_id} is {tower.status}. Adding {appointment.locator} to pending") elif tower.status == "subscription error": plugin.log(f"There is a subscription issue with {tower_id}. Adding appointment to pending") tower_update["pending_appointment"] = (appointment.to_dict(), signature), "add" except SignatureError as e: tower_update["status"] = "misbehaving" tower_update["misbehaving_proof"] = { "appointment": appointment.to_dict(), "signature": e.kwargs.get("signature"), "recovered_id": e.kwargs.get("recovered_id"), "receipt": e.kwargs.get("receipt"), } except TowerConnectionError: # All TowerConnectionError are transitory. Connections are tried on register so URLs cannot be malformed. # Flag appointment for retry tower_update["status"] = "temporarily unreachable" plugin.log(f"Adding {appointment.locator} to pending") tower_update["pending_appointment"] = (appointment.to_dict(), signature), "add" tower_update["retry"] = True except TowerResponseError as e: tower_update["status"] = e.kwargs.get("status") if tower_update["status"] in ["temporarily unreachable", "subscription error"]: plugin.log(f"Adding {appointment.locator} to pending") tower_update["pending_appointment"] = (appointment.to_dict(), signature), "add" if tower_update["status"] == "temporarily unreachable": tower_update["retry"] = True if e.kwargs.get("invalid_appointment"): tower_update["invalid_appointment"] = (appointment.to_dict(), signature) finally: # Update memory and TowersDB plugin.wt_client.update_tower_state(tower_id, tower_update) if tower_update.get("retry"): plugin.wt_client.retrier.temp_unreachable_towers.put(tower_id) return {"result": "continue"}