mirror of
https://github.com/aljazceru/python-teos.git
synced 2025-12-18 14:44:21 +01:00
plugin - repurpose invalid_appointments and adds misbehaving_proof
invalid_appointments in TowerInfo was used to stored invalid appointment responses from the tower when it was misbehaving (i.e. wrong or missing signature). However, a single item was stored since the tower is abandoned after misbehaving. data reported as invalid by the tower was not backed up. invalid_appointments now stores the appointment rejected by the tower and reported as invalid. misbehaving_proof stores the proof of misbehaviour by the tower (single item)
This commit is contained in:
@@ -42,9 +42,8 @@ def add_appointment(plugin, tower_id, tower, appointment_dict, signature):
|
|||||||
raise TowerResponseError(message, status="subscription error")
|
raise TowerResponseError(message, status="subscription error")
|
||||||
|
|
||||||
elif data.get("error_code") >= errors.INVALID_REQUEST_FORMAT:
|
elif data.get("error_code") >= errors.INVALID_REQUEST_FORMAT:
|
||||||
# DISCUSS: It may be worth backing up the data since otherwise the update is dropped
|
|
||||||
message = f"Appointment sent to {tower_id} is invalid"
|
message = f"Appointment sent to {tower_id} is invalid"
|
||||||
raise TowerResponseError(message, status="reachable")
|
raise TowerResponseError(message, status="reachable", invalid_appointment=True)
|
||||||
|
|
||||||
elif status_code == constants.HTTP_SERVICE_UNAVAILABLE:
|
elif status_code == constants.HTTP_SERVICE_UNAVAILABLE:
|
||||||
# Flag appointment for retry
|
# Flag appointment for retry
|
||||||
@@ -68,9 +67,13 @@ def send_appointment(tower_id, tower, appointment_dict, signature):
|
|||||||
raise SignatureError("The response does not contain the signature of the appointment", signature=None)
|
raise SignatureError("The response does not contain the signature of the appointment", signature=None)
|
||||||
|
|
||||||
rpk = Cryptographer.recover_pk(Appointment.from_dict(appointment_dict).serialize(), tower_signature)
|
rpk = Cryptographer.recover_pk(Appointment.from_dict(appointment_dict).serialize(), tower_signature)
|
||||||
if tower_id != Cryptographer.get_compressed_pk(rpk):
|
recovered_id = Cryptographer.get_compressed_pk(rpk)
|
||||||
|
if tower_id != recovered_id:
|
||||||
raise SignatureError(
|
raise SignatureError(
|
||||||
"The returned appointment's signature is invalid", tower_id=tower_id, rpk=rpk, signature=tower_signature
|
"The returned appointment's signature is invalid",
|
||||||
|
tower_id=tower_id,
|
||||||
|
recovered_id=recovered_id,
|
||||||
|
signature=tower_signature,
|
||||||
)
|
)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|||||||
@@ -63,7 +63,11 @@ class Retrier:
|
|||||||
|
|
||||||
except SignatureError as e:
|
except SignatureError as e:
|
||||||
tower_update["status"] = "misbehaving"
|
tower_update["status"] = "misbehaving"
|
||||||
tower_update["invalid_appointment"] = (appointment_dict, e.kwargs.get("signature"))
|
tower_update["misbehaving_proof"] = {
|
||||||
|
"appointment": appointment_dict,
|
||||||
|
"signature": e.kwargs.get("signature"),
|
||||||
|
"recovered_id": e.kwargs.get("recovered_id"),
|
||||||
|
}
|
||||||
|
|
||||||
except TowerConnectionError:
|
except TowerConnectionError:
|
||||||
tower_update["status"] = "temporarily unreachable"
|
tower_update["status"] = "temporarily unreachable"
|
||||||
@@ -71,6 +75,9 @@ class Retrier:
|
|||||||
except TowerResponseError as e:
|
except TowerResponseError as e:
|
||||||
tower_update["status"] = e.kwargs.get("status")
|
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"]:
|
if tower_update["status"] in ["reachable", "misbehaving"]:
|
||||||
tower_update["pending_appointment"] = ([appointment_dict, signature], "remove")
|
tower_update["pending_appointment"] = ([appointment_dict, signature], "remove")
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +1,13 @@
|
|||||||
class TowerInfo:
|
class TowerInfo:
|
||||||
def __init__(
|
def __init__(self, netaddr, available_slots, status="reachable"):
|
||||||
self,
|
|
||||||
netaddr,
|
|
||||||
available_slots,
|
|
||||||
status="reachable",
|
|
||||||
appointments=None,
|
|
||||||
pending_appointments=None,
|
|
||||||
invalid_appointments=None,
|
|
||||||
):
|
|
||||||
|
|
||||||
self.netaddr = netaddr
|
self.netaddr = netaddr
|
||||||
self.available_slots = available_slots
|
self.available_slots = available_slots
|
||||||
self.status = status
|
self.status = status
|
||||||
|
|
||||||
if not appointments:
|
|
||||||
self.appointments = {}
|
self.appointments = {}
|
||||||
else:
|
|
||||||
self.appointments = appointments
|
|
||||||
|
|
||||||
if not pending_appointments:
|
|
||||||
self.pending_appointments = []
|
self.pending_appointments = []
|
||||||
else:
|
|
||||||
self.pending_appointments = pending_appointments
|
|
||||||
|
|
||||||
if not invalid_appointments:
|
|
||||||
self.invalid_appointments = []
|
self.invalid_appointments = []
|
||||||
else:
|
self.misbehaving_proof = None
|
||||||
self.invalid_appointments = invalid_appointments
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, tower_data):
|
def from_dict(cls, tower_data):
|
||||||
@@ -36,6 +17,7 @@ class TowerInfo:
|
|||||||
appointments = tower_data.get("appointments")
|
appointments = tower_data.get("appointments")
|
||||||
pending_appointments = tower_data.get("pending_appointments")
|
pending_appointments = tower_data.get("pending_appointments")
|
||||||
invalid_appointments = tower_data.get("invalid_appointments")
|
invalid_appointments = tower_data.get("invalid_appointments")
|
||||||
|
misbehaving_proof = tower_data.get("misbehaving_proof")
|
||||||
|
|
||||||
if any(
|
if any(
|
||||||
v is None
|
v is None
|
||||||
@@ -43,7 +25,13 @@ class TowerInfo:
|
|||||||
):
|
):
|
||||||
raise ValueError("Wrong appointment data, some fields are missing")
|
raise ValueError("Wrong appointment data, some fields are missing")
|
||||||
|
|
||||||
return cls(netaddr, available_slots, status, appointments, pending_appointments, invalid_appointments)
|
tower = cls(netaddr, available_slots, status)
|
||||||
|
tower.appointments = appointments
|
||||||
|
tower.pending_appointments = pending_appointments
|
||||||
|
tower.invalid_appointments = invalid_appointments
|
||||||
|
tower.misbehaving_proof = misbehaving_proof
|
||||||
|
|
||||||
|
return tower
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return self.__dict__
|
return self.__dict__
|
||||||
|
|||||||
@@ -71,6 +71,9 @@ class WTClient:
|
|||||||
if "invalid_appointment" in tower_update:
|
if "invalid_appointment" in tower_update:
|
||||||
tower_info.invalid_appointments.append(list(tower_update.get("invalid_appointment")))
|
tower_info.invalid_appointments.append(list(tower_update.get("invalid_appointment")))
|
||||||
|
|
||||||
|
if "misbehaving_proof" in tower_update:
|
||||||
|
tower_info.misbehaving_proof = tower_update.get("misbehaving_proof")
|
||||||
|
|
||||||
self.towers[tower_id] = tower_info.get_summary()
|
self.towers[tower_id] = tower_info.get_summary()
|
||||||
self.db_manager.store_tower_record(tower_id, tower_info)
|
self.db_manager.store_tower_record(tower_id, tower_info)
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
@@ -280,7 +283,11 @@ def on_commitment_revocation(plugin, **kwargs):
|
|||||||
|
|
||||||
except SignatureError as e:
|
except SignatureError as e:
|
||||||
tower_update["status"] = "misbehaving"
|
tower_update["status"] = "misbehaving"
|
||||||
tower_update["invalid_appointment"] = (appointment.to_dict(), e.kwargs.get("signature"))
|
tower_update["misbehaving_proof"] = {
|
||||||
|
"appointment": appointment.to_dict(),
|
||||||
|
"signature": e.kwargs.get("signature"),
|
||||||
|
"recovered_id": e.kwargs.get("recovered_id"),
|
||||||
|
}
|
||||||
|
|
||||||
except TowerConnectionError:
|
except TowerConnectionError:
|
||||||
# All TowerConnectionError are transitory. Connections are tried on register so URLs cannot be malformed.
|
# All TowerConnectionError are transitory. Connections are tried on register so URLs cannot be malformed.
|
||||||
@@ -300,6 +307,9 @@ def on_commitment_revocation(plugin, **kwargs):
|
|||||||
if tower_update["status"] == "temporarily unreachable":
|
if tower_update["status"] == "temporarily unreachable":
|
||||||
tower_update["retry"] = True
|
tower_update["retry"] = True
|
||||||
|
|
||||||
|
if e.kwargs.get("invalid_appointment"):
|
||||||
|
tower_update["invalid_appointment"] = (appointment.to_dict(), signature)
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Update memory and TowersDB
|
# Update memory and TowersDB
|
||||||
plugin.wt_client.update_tower_state(tower_id, tower_update)
|
plugin.wt_client.update_tower_state(tower_id, tower_update)
|
||||||
|
|||||||
Reference in New Issue
Block a user