Example #1
0
    def do_retry(self, plugin, tower_id, tower):
        """
        Retries to send a list of pending appointments to a temporarily unreachable tower. This function is managed by
        manage_retries and run in a different thread per tower.

        For every pending appointment the worker thread tries to send the data to the tower. If the tower keeps being
        unreachable, the job is retries up to MAX_RETRIES. If MAX_RETRIES is reached, the worker thread gives up and the
        tower is flagged as unreachable.

        Args:
            plugin (:obj:`Plugin`): the plugin object.
            tower_id (:obj:`str`): the id of the tower managed by the thread.
            tower: (:obj:`TowerSummary`): the tower data.

        Returns:
            :obj:`str`: the tower status if it is not reachable.
        """

        for appointment_dict, signature in plugin.wt_client.towers[tower_id].pending_appointments:
            tower_update = {}
            try:
                tower_signature, available_slots = add_appointment(plugin, tower_id, tower, appointment_dict, signature)
                tower_update["status"] = "reachable"
                tower_update["appointment"] = (appointment_dict.get("locator"), tower_signature)
                tower_update["available_slots"] = available_slots

            except SignatureError as e:
                tower_update["status"] = "misbehaving"
                tower_update["misbehaving_proof"] = {
                    "appointment": appointment_dict,
                    "signature": e.kwargs.get("signature"),
                    "recovered_id": e.kwargs.get("recovered_id"),
                    "receipt": e.kwargs.get("receipt"),
                }

            except TowerConnectionError:
                tower_update["status"] = "temporarily unreachable"

            except TowerResponseError as e:
                tower_update["status"] = e.kwargs.get("status")

                if e.kwargs.get("invalid_appointment"):
                    tower_update["invalid_appointment"] = (appointment_dict, signature)

            if tower_update["status"] in ["reachable", "misbehaving"]:
                tower_update["pending_appointment"] = ([appointment_dict, signature], "remove")

            if tower_update["status"] != "temporarily unreachable":
                # Update memory and TowersDB
                plugin.wt_client.update_tower_state(tower_id, tower_update)

            # Continue looping if reachable, return for either retry or stop otherwise
            if tower_update["status"] != "reachable":
                return tower_update.get("status")
Example #2
0
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"}