Improves add_appointment method. Has some pending fixmes

- Start and end time have to be dealt with (changes required on the tower side)
- Sends appointments to every register tower. We may want to manage this better
This commit is contained in:
Sergi Delgado Segura
2020-04-09 17:58:09 +02:00
parent 08701f0fee
commit 6b025a2d9c
2 changed files with 59 additions and 10 deletions

View File

@@ -1,19 +1,23 @@
class TowerInfo: class TowerInfo:
def __init__(self, endpoint, available_slots): def __init__(self, endpoint, available_slots, appointments=None):
self.endpoint = endpoint self.endpoint = endpoint
self.available_slots = available_slots self.available_slots = available_slots
if not appointments:
self.appointments = {}
else:
self.appointments = appointments
@classmethod @classmethod
def from_dict(cls, tower_data): def from_dict(cls, tower_data):
endpoint = tower_data.get("endpoint") endpoint = tower_data.get("endpoint")
available_slots = tower_data.get("available_slots") available_slots = tower_data.get("available_slots")
appointments = tower_data.get("appointments")
if any(v is None for v in [endpoint, available_slots]): if any(v is None for v in [endpoint, available_slots, appointments]):
raise ValueError("Wrong appointment data, some fields are missing") raise ValueError("Wrong appointment data, some fields are missing")
if available_slots is None:
raise ValueError("Wrong tower data, some fields are missing")
return cls(endpoint, available_slots) return cls(endpoint, available_slots, appointments)
def to_dict(self): def to_dict(self):
return self.__dict__ return self.__dict__

View File

@@ -3,15 +3,18 @@ import os
import plyvel import plyvel
from pyln.client import Plugin from pyln.client import Plugin
from common.tools import compute_locator
from common.appointment import Appointment
from common.config_loader import ConfigLoader from common.config_loader import ConfigLoader
from common.cryptographer import Cryptographer from common.cryptographer import Cryptographer
from common.exceptions import InvalidParameter, SignatureError, EncryptionError
import arg_parser import arg_parser
from tower_info import TowerInfo from tower_info import TowerInfo
from towers_dbm import TowersDBM from towers_dbm import TowersDBM
from keys import generate_keys, load_keys from keys import generate_keys, load_keys
from net.http import post_request, process_post_response from net.http import post_request, process_post_response
from exceptions import TowerConnectionError, TowerResponseError, InvalidParameter from exceptions import TowerConnectionError, TowerResponseError
DATA_DIR = os.path.expanduser("~/.watchtower/") DATA_DIR = os.path.expanduser("~/.watchtower/")
@@ -123,7 +126,7 @@ def get_appointment(plugin, *args):
:obj:`dict`: a dictionary containing the appointment data. :obj:`dict`: a dictionary containing the appointment data.
""" """
# FIXME: All responses from the tower should be signed. Not using teos_pk atm. # FIXME: All responses from the tower should be signed.
try: try:
tower_id, locator = arg_parser.parse_get_appointment_arguments(args) tower_id, locator = arg_parser.parse_get_appointment_arguments(args)
@@ -149,9 +152,51 @@ def get_appointment(plugin, *args):
@plugin.hook("commitment_revocation") @plugin.hook("commitment_revocation")
def add_appointment(plugin, **kwargs): def add_appointment(plugin, **kwargs):
commitment_txid = kwargs.get("commitment_txid") try:
penalty_tx = kwargs.get("penalty_tx") # FIXME: start_time and end_time are temporary. Fix it on the tower side and remove it from there
plugin.log("commitment_txid {}, penalty_tx: {}".format(commitment_txid, penalty_tx)) block_height = plugin.rpc.getchaininfo().get("blockcount")
start_time = block_height + 1
end_time = block_height + 10
commitment_txid, penalty_tx = arg_parser.parse_add_appointment_arguments(kwargs)
appointment = Appointment(
locator=compute_locator(commitment_txid),
start_time=start_time,
end_time=end_time,
to_self_delay=20,
encrypted_blob=Cryptographer.encrypt(penalty_tx, commitment_txid),
)
signature = Cryptographer.sign(appointment.serialize(), plugin.wt_client.sk)
data = {"appointment": appointment.to_dict(), "signature": signature}
# Send appointment to the server.
# FIXME: sending the appointment to all registered towers atm. Some management would be nice.
for tower_id, tower in plugin.wt_client.towers.items():
plugin.log("Sending appointment to the Eye of Satoshi at {}".format(tower.endpoint))
add_appointment_endpoint = "{}/add_appointment".format(tower.endpoint)
response = process_post_response(post_request(data, add_appointment_endpoint))
signature = response.get("signature")
# Check that the server signed the appointment as it should.
if not signature:
raise TowerResponseError("The response does not contain the signature of the appointment")
rpk = Cryptographer.recover_pk(appointment.serialize(), signature)
if not tower_id != Cryptographer.get_compressed_pk(rpk):
raise TowerResponseError("The returned appointment's signature is invalid")
plugin.log("Appointment accepted and signed by the Eye of Satoshi at {}".format(tower.endpoint))
plugin.log("Remaining slots: {}".format(response.get("available_slots")))
# TODO: Not storing the whole appointments for now. The node should be able to recreate all the required
# data if needed.
plugin.wt_client.towers[tower_id].appointments[appointment.locator] = signature
plugin.wt_client.db_manager.store_tower_record(tower_id, plugin.wt_client.towers[tower_id])
except (InvalidParameter, EncryptionError, SignatureError, TowerResponseError) as e:
plugin.log(str(e), level="error")
return {"result": "continue"} return {"result": "continue"}