From 3009bb83ba824b83290f6e2a87bff987cb4ed612 Mon Sep 17 00:00:00 2001 From: Sergi Delgado Segura Date: Wed, 1 Apr 2020 18:26:36 +0200 Subject: [PATCH] Addaps api and daemon to use userDB --- teos/__init__.py | 1 + teos/api.py | 2 +- teos/teosd.py | 5 ++- teos/users_dbm.py | 100 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 teos/users_dbm.py diff --git a/teos/__init__.py b/teos/__init__.py index 0eba6bd..811e16d 100644 --- a/teos/__init__.py +++ b/teos/__init__.py @@ -23,4 +23,5 @@ DEFAULT_CONF = { "LOG_FILE": {"value": "teos.log", "type": str, "path": True}, "TEOS_SECRET_KEY": {"value": "teos_sk.der", "type": str, "path": True}, "APPOINTMENTS_DB_PATH": {"value": "appointments", "type": str, "path": True}, + "USERS_DB_PATH": {"value": "users", "type": str, "path": True}, } diff --git a/teos/api.py b/teos/api.py index ec65329..ec71206 100644 --- a/teos/api.py +++ b/teos/api.py @@ -213,7 +213,7 @@ class API: response = { "locator": appointment.locator, "signature": signature, - "available_slots": self.gatekeeper.registered_users[user_pk], + "available_slots": self.gatekeeper.registered_users[user_pk].get("available_slots"), } else: diff --git a/teos/teosd.py b/teos/teosd.py index f23447a..694b664 100644 --- a/teos/teosd.py +++ b/teos/teosd.py @@ -14,12 +14,13 @@ from teos.help import show_usage from teos.watcher import Watcher from teos.builder import Builder from teos.carrier import Carrier +from teos.users_dbm import UsersDBM from teos.inspector import Inspector from teos.responder import Responder -from teos.appointments_dbm import AppointmentsDBM from teos.gatekeeper import Gatekeeper from teos.chain_monitor import ChainMonitor from teos.block_processor import BlockProcessor +from teos.appointments_dbm import AppointmentsDBM from teos.tools import can_connect_to_bitcoind, in_correct_network from teos import LOG_PREFIX, DATA_DIR, DEFAULT_CONF, CONF_FILE_NAME @@ -151,7 +152,7 @@ def main(command_line_conf): # Fire the API and the ChainMonitor # FIXME: 92-block-data-during-bootstrap-db chain_monitor.monitor_chain() - gatekeeper = Gatekeeper(config.get("DEFAULT_SLOTS")) + gatekeeper = Gatekeeper(UsersDBM(config.get("USERS_DB_PATH")), config.get("DEFAULT_SLOTS")) API(Inspector(block_processor, config.get("MIN_TO_SELF_DELAY")), watcher, gatekeeper).start() except Exception as e: logger.error("An error occurred: {}. Shutting down".format(e)) diff --git a/teos/users_dbm.py b/teos/users_dbm.py new file mode 100644 index 0000000..d0caac2 --- /dev/null +++ b/teos/users_dbm.py @@ -0,0 +1,100 @@ +import json +import plyvel + +from teos import LOG_PREFIX +from teos.db_manager import DBManager + +from common.logger import Logger + +logger = Logger(actor="UsersDBM", log_name_prefix=LOG_PREFIX) + + +class UsersDBM(DBManager): + """ + The :class:`UsersDBM` is the class in charge of interacting with the users 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 create if the specified path does not contain one. + + Raises: + ValueError: If the provided ``db_path`` is not a string. + plyvel.Error: If the db is currently unavailable (being used by another process). + """ + + def __init__(self, db_path): + if not isinstance(db_path, str): + raise ValueError("db_path must be a valid path/name") + + try: + super().__init__(db_path) + + except plyvel.Error as e: + if "LOCK: Resource temporarily unavailable" in str(e): + logger.info("The db is already being used by another process (LOCK)") + + raise e + + def store_user(self, user_pk, user_data): + """ + Stores a user record to the database. ``user_pk`` is used as identifier. + + Args: + user_pk (:obj:`str`): a 33-byte hex-encoded string identifying the user. + user_data (:obj:`dict`): the user associated data, as a dictionary. + """ + + self.create_entry(user_pk, json.dumps(user_data)) + logger.info("Adding user to Gatekeeper's db", uuid=user_pk) + + def load_user(self, user_pk): + """ + Loads a user record from the database using the ``user_pk`` as identifier. + + use_pk (:obj:`str`): a 33-byte hex-encoded string identifying the user. + + Returns: + :obj:`dict`: A dictionary containing the appointment data if they ``key`` is found. + + Returns ``None`` otherwise. + """ + + data = self.load_entry(user_pk) + + try: + data = json.loads(data) + except (TypeError, json.decoder.JSONDecodeError): + data = None + + return data + + def delete_user(self, user_pk): + """ + Deletes a user record from the database. + + Args: + user_pk (:obj:`str`): a 33-byte hex-encoded string identifying the user. + """ + + self.delete_entry(user_pk) + logger.info("Deleting user from Gatekeeper's db", uuid=user_pk) + + def load_all_users(self): + """ + Loads all user records from the database. + + Returns: + :obj:`dict`: A dictionary containing all users indexed by ``user_pk``. + + 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 + user_pk = k.decode("utf-8") + data[user_pk] = json.loads(v) + + return data