Files
python-teos/watchtower-plugin/watchtower.py
Sergi Delgado Segura b3841c1da6 Updates defaults names
2020-04-20 17:30:15 +02:00

164 lines
5.6 KiB
Python
Executable File

#!/usr/bin/env python3
import os
import plyvel
from pyln.client import Plugin
from common.config_loader import ConfigLoader
from common.cryptographer import Cryptographer
import arg_parser
from tower_info import TowerInfo
from towers_dbm import TowersDBM
from keys import generate_keys, load_keys
from net.http import post_request, process_post_response
from exceptions import TowerConnectionError, TowerResponseError, InvalidParameter
DATA_DIR = os.path.expanduser("~/.watchtower/")
CONF_FILE_NAME = "watchtower.conf"
DEFAULT_CONF = {
"DEFAULT_PORT": {"value": 9814, "type": int},
"APPOINTMENTS_FOLDER_NAME": {"value": "appointment_receipts", "type": str, "path": True},
"TOWERS_DB": {"value": "towers", "type": str, "path": True},
"PRIVATE_KEY": {"value": "sk.der", "type": str, "path": True},
}
plugin = Plugin()
class WTClient:
def __init__(self, sk, user_id, config):
self.sk = sk
self.user_id = user_id
self.db_manager = TowersDBM(config.get("TOWERS_DB"), plugin)
self.towers = {}
self.config = config
# Populate the towers dict with data from the db
for tower_id, tower_info in self.db_manager.load_all_tower_records().items():
self.towers[tower_id] = TowerInfo.from_dict(tower_info)
@plugin.init()
def init(options, configuration, plugin):
try:
plugin.log("Generating a new key pair for the watchtower client. Keys stored at {}".format(DATA_DIR))
cli_sk, compressed_cli_pk = generate_keys(DATA_DIR)
except FileExistsError:
plugin.log("A key file for the watchtower client already exists. Loading it")
cli_sk, compressed_cli_pk = load_keys(DATA_DIR)
plugin.log("Plugin watchtower client initialized")
config_loader = ConfigLoader(DATA_DIR, CONF_FILE_NAME, DEFAULT_CONF, {})
try:
plugin.wt_client = WTClient(cli_sk, compressed_cli_pk, config_loader.build_config())
except plyvel.IOError:
plugin.log("Cannot load towers db. Resource temporarily unavailable")
# TODO: Check how to make the plugin stop
@plugin.method("register", desc="Register your public key with the tower")
def register(plugin, *args):
"""
Registers the user to the tower.
Args:
plugin (:obj:`Plugin`): this plugin.
args (:obj:`list`): a list of arguments. Must contain the tower_id and endpoint.
Accepted input formats:
- tower_id@host:port
- tower_id@host (will default port to DEFAULT_PORT)
- tower_id host port
- tower_id host (will default port to DEFAULT_PORT)
Returns:
:obj:`dict`: a dictionary containing the subscription data.
"""
try:
tower_id, tower_endpoint = arg_parser.parse_register_arguments(
args, plugin.wt_client.config.get("DEFAULT_PORT")
)
# Defaulting to http hosts for now
if not tower_endpoint.startswith("http"):
tower_endpoint = "http://" + tower_endpoint
# Send request to the server.
register_endpoint = "{}/register".format(tower_endpoint)
data = {"public_key": plugin.wt_client.user_id}
plugin.log("Registering in the Eye of Satoshi")
response = process_post_response(post_request(data, register_endpoint))
plugin.log("Registration succeeded. Available slots: {}".format(response.get("available_slots")))
# Save data
tower_info = TowerInfo(tower_endpoint, response.get("available_slots"))
plugin.wt_client.towers[tower_id] = tower_info
plugin.wt_client.db_manager.store_tower_record(tower_id, tower_info)
return response
except (InvalidParameter, TowerConnectionError, TowerResponseError) as e:
plugin.log(str(e), level="error")
return e.to_json()
@plugin.method("getappointment", desc="Gets appointment data from the tower given a locator")
def get_appointment(plugin, *args):
"""
Gets information about an appointment from the tower.
Args:
plugin (:obj:`Plugin`): this plugin.
args (:obj:`list`): a list of arguments. Must contain a single argument, the locator.
Returns:
:obj:`dict`: a dictionary containing the appointment data.
"""
# FIXME: All responses from the tower should be signed. Not using teos_pk atm.
try:
tower_id, locator = arg_parser.parse_get_appointment_arguments(args)
if tower_id not in plugin.wt_client.towers:
raise InvalidParameter("tower_id is not within the registered towers", tower_id=tower_id)
message = "get appointment {}".format(locator)
signature = Cryptographer.sign(message.encode(), plugin.wt_client.sk)
data = {"locator": locator, "signature": signature}
# Send request to the server.
get_appointment_endpoint = "{}/get_appointment".format(plugin.wt_client.towers[tower_id].endpoint)
plugin.log("Requesting appointment from the Eye of Satoshi at {}".format(get_appointment_endpoint))
response = process_post_response(post_request(data, get_appointment_endpoint))
return response
except (InvalidParameter, TowerConnectionError, TowerResponseError) as e:
plugin.log(str(e), level="error")
return e.to_json()
@plugin.hook("commitment_revocation")
def add_appointment(plugin, **kwargs):
commitment_txid = kwargs.get("commitment_txid")
penalty_tx = kwargs.get("penalty_tx")
plugin.log("commitment_txid {}, penalty_tx: {}".format(commitment_txid, penalty_tx))
return {"result": "continue"}
@plugin.method("listtowers")
def list_towers(plugin):
return {k: v.to_dict() for k, v in plugin.wt_client.towers.items()}
plugin.run()