mirror of
https://github.com/aljazceru/python-teos.git
synced 2025-12-17 14:14:22 +01:00
First commands with basic structure.
- Moves DBManager to common.
This commit is contained in:
@@ -278,7 +278,7 @@ class Cryptographer:
|
||||
return None
|
||||
|
||||
if not isinstance(sk, PrivateKey):
|
||||
logger.error("The value passed as sk is not a private key (EllipticCurvePrivateKey)")
|
||||
logger.error("The value passed as sk is not a private key (PrivateKey)")
|
||||
return None
|
||||
|
||||
try:
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import json
|
||||
import plyvel
|
||||
|
||||
from teos.db_manager import DBManager
|
||||
|
||||
from teos import LOG_PREFIX
|
||||
|
||||
from common.logger import Logger
|
||||
from common.db_manager import DBManager
|
||||
|
||||
logger = Logger(actor="AppointmentsDBM", log_name_prefix=LOG_PREFIX)
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ import json
|
||||
import plyvel
|
||||
|
||||
from teos import LOG_PREFIX
|
||||
from teos.db_manager import DBManager
|
||||
|
||||
from common.logger import Logger
|
||||
from common.db_manager import DBManager
|
||||
from common.tools import is_compressed_pk
|
||||
|
||||
logger = Logger(actor="UsersDBM", log_name_prefix=LOG_PREFIX)
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import pytest
|
||||
import random
|
||||
from shutil import rmtree
|
||||
|
||||
from common.db_manager import DBManager
|
||||
|
||||
|
||||
@pytest.fixture(scope="session", autouse=True)
|
||||
@@ -7,6 +10,17 @@ def prng_seed():
|
||||
random.seed(0)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def db_manager():
|
||||
manager = DBManager("test_db")
|
||||
# Add last know block for the Responder in the db
|
||||
|
||||
yield manager
|
||||
|
||||
manager.db.close()
|
||||
rmtree("test_db")
|
||||
|
||||
|
||||
def get_random_value_hex(nbytes):
|
||||
pseudo_random_value = random.getrandbits(8 * nbytes)
|
||||
prv_hex = "{:x}".format(pseudo_random_value)
|
||||
|
||||
@@ -2,8 +2,8 @@ import os
|
||||
import shutil
|
||||
import pytest
|
||||
|
||||
from teos.db_manager import DBManager
|
||||
from test.teos.unit.conftest import get_random_value_hex
|
||||
from common.db_manager import DBManager
|
||||
from test.common.unit.conftest import get_random_value_hex
|
||||
|
||||
|
||||
def open_create_db(db_path):
|
||||
74
watchtower-plugin/arg_parser.py
Normal file
74
watchtower-plugin/arg_parser.py
Normal file
@@ -0,0 +1,74 @@
|
||||
from common.tools import is_compressed_pk, is_locator
|
||||
from exceptions import InvalidParameter
|
||||
|
||||
|
||||
def parse_register_arguments(args, default_port):
|
||||
# Sanity checks
|
||||
if len(args) == 0:
|
||||
raise InvalidParameter("missing required parameter: tower_id")
|
||||
|
||||
if len(args) > 3:
|
||||
raise InvalidParameter("too many parameters: got {}, expected 3".format(len(args)))
|
||||
|
||||
tower_id = args[0]
|
||||
|
||||
if not isinstance(tower_id, str):
|
||||
raise InvalidParameter("tower id must be a compressed public key (33-byte hex value) " + str(args))
|
||||
|
||||
# tower_id is of the form tower_id@[ip][:][port]
|
||||
if "@" in tower_id:
|
||||
if len(args) == 1:
|
||||
tower_id, tower_endpoint = tower_id.split("@")
|
||||
|
||||
if not tower_endpoint:
|
||||
raise InvalidParameter("no tower endpoint was provided")
|
||||
|
||||
# Only host was specified
|
||||
if ":" not in tower_endpoint:
|
||||
tower_endpoint = "{}:{}".format(tower_endpoint, default_port)
|
||||
|
||||
# Colons where specified but no port, defaulting
|
||||
elif tower_endpoint.endswith(":"):
|
||||
tower_endpoint = "{}{}".format(tower_endpoint, default_port)
|
||||
|
||||
else:
|
||||
raise InvalidParameter("cannot specify host as both xxx@yyy and separate arguments")
|
||||
|
||||
# host was specified, but no port, defaulting
|
||||
elif len(args) == 2:
|
||||
tower_endpoint = "{}:{}".format(args[1], default_port)
|
||||
|
||||
# host and port specified
|
||||
elif len(args) == 3:
|
||||
tower_endpoint = "{}:{}".format(args[1], args[2])
|
||||
|
||||
else:
|
||||
raise InvalidParameter("tower host is missing")
|
||||
|
||||
if not is_compressed_pk(tower_id):
|
||||
raise InvalidParameter("tower id must be a compressed public key (33-byte hex value)")
|
||||
|
||||
return tower_id, tower_endpoint
|
||||
|
||||
|
||||
def parse_get_appointment_arguments(args):
|
||||
# Sanity checks
|
||||
if len(args) == 0:
|
||||
raise InvalidParameter("missing required parameter: tower_id")
|
||||
|
||||
if len(args) == 1:
|
||||
raise InvalidParameter("missing required parameter: locator")
|
||||
|
||||
if len(args) > 2:
|
||||
raise InvalidParameter("too many parameters: got {}, expected 2".format(len(args)))
|
||||
|
||||
tower_id = args[0]
|
||||
locator = args[1]
|
||||
|
||||
if not is_compressed_pk(tower_id):
|
||||
raise InvalidParameter("tower id must be a compressed public key (33-byte hex value)")
|
||||
|
||||
if not is_locator(locator):
|
||||
raise InvalidParameter("The provided locator is not valid", locator=locator)
|
||||
|
||||
return tower_id, locator
|
||||
37
watchtower-plugin/exceptions.py
Normal file
37
watchtower-plugin/exceptions.py
Normal file
@@ -0,0 +1,37 @@
|
||||
class BasicException(Exception):
|
||||
def __init__(self, msg, **kwargs):
|
||||
self.msg = msg
|
||||
self.kwargs = kwargs
|
||||
|
||||
def __str__(self):
|
||||
if len(self.kwargs) > 2:
|
||||
params = "".join("{}={}, ".format(k, v) for k, v in self.kwargs.items())
|
||||
|
||||
# Remove the extra 2 characters (space and comma) and add all data to the final message.
|
||||
message = self.msg + " ({})".format(params[:-2])
|
||||
|
||||
else:
|
||||
message = self.msg
|
||||
|
||||
return message
|
||||
|
||||
def to_json(self):
|
||||
response = {"error": self.msg}
|
||||
response.update(self.kwargs)
|
||||
return response
|
||||
|
||||
|
||||
class InvalidParameter(BasicException):
|
||||
"""Raised when a command line parameter is invalid (either missing or wrong)"""
|
||||
|
||||
|
||||
class InvalidKey(BasicException):
|
||||
"""Raised when there is an error loading the keys"""
|
||||
|
||||
|
||||
class TowerConnectionError(BasicException):
|
||||
"""Raised when the tower responds with an error"""
|
||||
|
||||
|
||||
class TowerResponseError(BasicException):
|
||||
"""Raised when the tower responds with an error"""
|
||||
94
watchtower-plugin/keys.py
Normal file
94
watchtower-plugin/keys.py
Normal file
@@ -0,0 +1,94 @@
|
||||
import os.path
|
||||
from pathlib import Path
|
||||
from binascii import hexlify
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
|
||||
from exceptions import InvalidKey
|
||||
from common.cryptographer import Cryptographer
|
||||
|
||||
|
||||
def save_key(sk, filename):
|
||||
"""
|
||||
Saves secret key on disk.
|
||||
|
||||
Args:
|
||||
sk (:obj:`PrivateKey`): a private key file to be saved on disk.
|
||||
filename (:obj:`str`): the name that will be given to the key file.
|
||||
"""
|
||||
|
||||
der = sk.private_bytes(
|
||||
encoding=serialization.Encoding.DER,
|
||||
format=serialization.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=serialization.NoEncryption(),
|
||||
)
|
||||
|
||||
with open(filename, "wb") as der_out:
|
||||
der_out.write(der)
|
||||
|
||||
|
||||
def generate_keys(data_dir):
|
||||
"""
|
||||
Generates a key pair for the client.
|
||||
|
||||
Args:
|
||||
data_dir (:obj:`str`): path to data directory where the keys will be stored.
|
||||
|
||||
Returns:
|
||||
:obj:`tuple`: a tuple containing a ``PrivateKey`` and a ``str`` representing the client sk and compressed
|
||||
pk respectively.
|
||||
|
||||
Raises:
|
||||
:obj:`FileExistsError`: if the key pair already exists in the given directory.
|
||||
"""
|
||||
|
||||
# Create the output folder it it does not exist (and all the parents if they don't either)
|
||||
Path(data_dir).mkdir(parents=True, exist_ok=True)
|
||||
sk_file_name = os.path.join(data_dir, "cli_sk.der")
|
||||
|
||||
if os.path.exists(sk_file_name):
|
||||
raise FileExistsError("The client key pair already exists")
|
||||
|
||||
sk = ec.generate_private_key(ec.SECP256K1, default_backend())
|
||||
save_key(sk, sk_file_name)
|
||||
|
||||
compressed_pk = sk.public_key().public_bytes(
|
||||
encoding=serialization.Encoding.X962, format=serialization.PublicFormat.CompressedPoint
|
||||
)
|
||||
|
||||
return sk, hexlify(compressed_pk).decode("utf-8")
|
||||
|
||||
|
||||
def load_keys(data_dir):
|
||||
"""
|
||||
Loads a the client key pair.
|
||||
|
||||
Args:
|
||||
data_dir (:obj:`str`): path to data directory where the keys are stored.
|
||||
|
||||
Returns:
|
||||
:obj:`tuple`: a tuple containing a ``PrivateKey`` and a ``str`` representing the client sk and compressed
|
||||
pk respectively.
|
||||
|
||||
Raises:
|
||||
:obj:`InvalidKey <cli.exceptions.InvalidKey>`: if any of the keys is invalid or cannot be loaded.
|
||||
"""
|
||||
|
||||
if not isinstance(data_dir, str):
|
||||
raise ValueError("Invalid data_dir. Please check your settings")
|
||||
|
||||
sk_file_path = os.path.join(data_dir, "cli_sk.der")
|
||||
|
||||
cli_sk_der = Cryptographer.load_key_file(sk_file_path)
|
||||
cli_sk = Cryptographer.load_private_key_der(cli_sk_der)
|
||||
|
||||
if cli_sk is None:
|
||||
raise InvalidKey("Client private key is invalid or cannot be parsed")
|
||||
|
||||
compressed_cli_pk = Cryptographer.get_compressed_pk(cli_sk.public_key)
|
||||
|
||||
if compressed_cli_pk is None:
|
||||
raise InvalidKey("Client public key cannot be loaded")
|
||||
|
||||
return cli_sk, compressed_cli_pk
|
||||
69
watchtower-plugin/net/http.py
Normal file
69
watchtower-plugin/net/http.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import json
|
||||
import requests
|
||||
from requests import ConnectionError, ConnectTimeout
|
||||
from requests.exceptions import MissingSchema, InvalidSchema, InvalidURL
|
||||
|
||||
from common import constants
|
||||
from exceptions import TowerConnectionError, TowerResponseError
|
||||
|
||||
|
||||
def post_request(data, endpoint):
|
||||
"""
|
||||
Sends a post request to the tower.
|
||||
|
||||
Args:
|
||||
data (:obj:`dict`): a dictionary containing the data to be posted.
|
||||
endpoint (:obj:`str`): the endpoint to send the post request.
|
||||
|
||||
Returns:
|
||||
:obj:`dict`: a json-encoded dictionary with the server response if the data can be posted.
|
||||
|
||||
Raises:
|
||||
:obj:`ConnectionError`: if the client cannot connect to the tower.
|
||||
"""
|
||||
|
||||
try:
|
||||
return requests.post(url=endpoint, json=data, timeout=5)
|
||||
|
||||
except ConnectTimeout:
|
||||
message = "Can't connect to the Eye of Satoshi at {}. Connection timeout".format(endpoint)
|
||||
|
||||
except ConnectionError:
|
||||
message = "Can't connect to the Eye of Satoshi at {}. Tower cannot be reached".format(endpoint)
|
||||
|
||||
except (InvalidSchema, MissingSchema, InvalidURL):
|
||||
message = "Invalid URL. No schema, or invalid schema, found ({})".format(endpoint)
|
||||
|
||||
raise TowerConnectionError(message)
|
||||
|
||||
|
||||
def process_post_response(response):
|
||||
"""
|
||||
Processes the server response to a post request.
|
||||
|
||||
Args:
|
||||
response (:obj:`requests.models.Response`): a ``Response`` object obtained from the request.
|
||||
|
||||
Returns:
|
||||
:obj:`dict`: a dictionary containing the tower's response data if the response type is
|
||||
``HTTP_OK``.
|
||||
|
||||
Raises:
|
||||
:obj:`TowerResponseError <cli.exceptions.TowerResponseError>`: if the tower responded with an error, or the
|
||||
response was invalid.
|
||||
"""
|
||||
|
||||
try:
|
||||
response_json = response.json()
|
||||
|
||||
except (json.JSONDecodeError, AttributeError):
|
||||
raise TowerResponseError(
|
||||
"The server returned a non-JSON response", status_code=response.status_code, reason=response.reason
|
||||
)
|
||||
|
||||
if response.status_code not in [constants.HTTP_OK, constants.HTTP_NOT_FOUND]:
|
||||
raise TowerResponseError(
|
||||
"The server returned an error", status_code=response.status_code, reason=response.reason, data=response_json
|
||||
)
|
||||
|
||||
return response_json
|
||||
6
watchtower-plugin/requirements.txt
Normal file
6
watchtower-plugin/requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
pyln-client
|
||||
requests
|
||||
coincurve
|
||||
cryptography
|
||||
pyzbase32
|
||||
plyvel
|
||||
3
watchtower-plugin/template.conf
Normal file
3
watchtower-plugin/template.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
[teos]
|
||||
api_port = 9814
|
||||
|
||||
19
watchtower-plugin/tower_info.py
Normal file
19
watchtower-plugin/tower_info.py
Normal file
@@ -0,0 +1,19 @@
|
||||
class TowerInfo:
|
||||
def __init__(self, endpoint, available_slots):
|
||||
self.endpoint = endpoint
|
||||
self.available_slots = available_slots
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, tower_data):
|
||||
endpoint = tower_data.get("endpoint")
|
||||
available_slots = tower_data.get("available_slots")
|
||||
|
||||
if any(v is None for v in [endpoint, available_slots]):
|
||||
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)
|
||||
|
||||
def to_dict(self):
|
||||
return self.__dict__
|
||||
119
watchtower-plugin/towers_dbm.py
Normal file
119
watchtower-plugin/towers_dbm.py
Normal file
@@ -0,0 +1,119 @@
|
||||
import json
|
||||
|
||||
from common.db_manager import DBManager
|
||||
from common.tools import is_compressed_pk
|
||||
|
||||
|
||||
class TowersDBM(DBManager):
|
||||
"""
|
||||
The :class:`TowersDBM` is in charge of interacting with the towers database (``LevelDB``).
|
||||
Keys and values are stored as bytes in the database but processed as strings by the manager.
|
||||
|
||||
Args:
|
||||
db_path (:obj:`str`): the path (relative or absolute) to the system folder containing the database. A fresh
|
||||
database will be created if the specified path does not contain one.
|
||||
|
||||
Raises:
|
||||
:obj:`ValueError`: If the provided ``db_path`` is not a string.
|
||||
:obj:`plyvel.Error`: If the db is currently unavailable (being used by another process).
|
||||
"""
|
||||
|
||||
def __init__(self, db_path, plugin):
|
||||
if not isinstance(db_path, str):
|
||||
raise ValueError("db_path must be a valid path/name")
|
||||
|
||||
super().__init__(db_path)
|
||||
self.plugin = plugin
|
||||
|
||||
def store_tower_record(self, tower_id, tower_data):
|
||||
"""
|
||||
Stores a tower record to the database. ``tower_id`` is used as identifier.
|
||||
|
||||
Args:
|
||||
tower_id (:obj:`str`): a 33-byte hex-encoded string identifying the tower.
|
||||
tower_data (:obj:`dict`): the tower associated data, as a dictionary.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: True if the tower record was stored in the database, False otherwise.
|
||||
"""
|
||||
|
||||
if is_compressed_pk(tower_id):
|
||||
try:
|
||||
self.create_entry(tower_id, json.dumps(tower_data.to_dict()))
|
||||
self.plugin.log("Adding tower to Tower's db (id={})".format(tower_id))
|
||||
return True
|
||||
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
self.plugin.log(
|
||||
"Could't add tower to db. Wrong tower data format (tower_id={}, tower_data={})".format(
|
||||
tower_id, tower_data
|
||||
)
|
||||
)
|
||||
return False
|
||||
|
||||
else:
|
||||
self.plugin.log(
|
||||
"Could't add user to db. Wrong pk format (tower_id={}, tower_data={})".format(tower_id, tower_data)
|
||||
)
|
||||
return False
|
||||
|
||||
def load_tower_record(self, tower_id):
|
||||
"""
|
||||
Loads a tower record from the database using the ``tower_id`` as identifier.
|
||||
|
||||
Args:
|
||||
|
||||
tower_id (:obj:`str`): a 33-byte hex-encoded string identifying the tower.
|
||||
|
||||
Returns:
|
||||
:obj:`dict`: A dictionary containing the tower data if the ``key`` is found.
|
||||
|
||||
Returns ``None`` otherwise.
|
||||
"""
|
||||
|
||||
try:
|
||||
data = self.load_entry(tower_id)
|
||||
data = json.loads(data)
|
||||
except (TypeError, json.decoder.JSONDecodeError):
|
||||
data = None
|
||||
|
||||
return data
|
||||
|
||||
def delete_tower_record(self, tower_id):
|
||||
"""
|
||||
Deletes a tower record from the database.
|
||||
|
||||
Args:
|
||||
tower_id (:obj:`str`): a 33-byte hex-encoded string identifying the tower.
|
||||
|
||||
Returns:
|
||||
:obj:`bool`: True if the tower was deleted from the database or it was non-existent, False otherwise.
|
||||
"""
|
||||
|
||||
try:
|
||||
self.delete_entry(tower_id)
|
||||
self.plugin.log("Deleting tower from Tower's db (id={})".format(tower_id))
|
||||
return True
|
||||
|
||||
except TypeError:
|
||||
self.plugin.log("Cannot delete user from db, user key has wrong type (id={})".format(tower_id))
|
||||
return False
|
||||
|
||||
def load_all_tower_records(self):
|
||||
"""
|
||||
Loads all tower records from the database.
|
||||
|
||||
Returns:
|
||||
:obj:`dict`: A dictionary containing all tower records indexed by ``tower_id``.
|
||||
|
||||
Returns an empty dictionary if no data is found.
|
||||
"""
|
||||
|
||||
data = {}
|
||||
|
||||
for k, v in self.db.iterator():
|
||||
# Get uuid and appointment_data from the db
|
||||
tower_id = k.decode("utf-8")
|
||||
data[tower_id] = json.loads(v)
|
||||
|
||||
return data
|
||||
163
watchtower-plugin/watchtower.py
Executable file
163
watchtower-plugin/watchtower.py
Executable file
@@ -0,0 +1,163 @@
|
||||
#!/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("~/.teos_cli/")
|
||||
CONF_FILE_NAME = "teos_cli.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},
|
||||
"CLI_PRIVATE_KEY": {"value": "cli_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()
|
||||
Reference in New Issue
Block a user