def __init__(self, receiving_queues, block_processor, bitcoind_feed_params): self.logger = get_logger(component=ChainMonitor.__name__) self.last_tips = [] self.check_tip = Event() self.lock = Condition() self.zmqContext = zmq.Context() self.zmqSubSocket = self.zmqContext.socket(zmq.SUB) self.zmqSubSocket.setsockopt(zmq.RCVHWM, 0) self.zmqSubSocket.setsockopt_string(zmq.SUBSCRIBE, "hashblock") self.zmqSubSocket.connect("%s://%s:%s" % ( bitcoind_feed_params.get("BTC_FEED_PROTOCOL"), bitcoind_feed_params.get("BTC_FEED_CONNECT"), bitcoind_feed_params.get("BTC_FEED_PORT"), )) self.receiving_queues = receiving_queues self.polling_delta = 60 self.max_block_window_size = 10 self.block_processor = block_processor self.queue = Queue() self.status = ChainMonitorStatus.IDLE
def __init__(self, watcher, internal_api_endpoint, max_workers, stop_command_event): self.logger = get_logger(component=InternalAPI.__name__) self.watcher = watcher self.endpoint = internal_api_endpoint self.stop_command_event = stop_command_event self.rpc_server = grpc.server( futures.ThreadPoolExecutor(max_workers=max_workers)) self.rpc_server.add_insecure_port(self.endpoint) add_TowerServicesServicer_to_server( _InternalAPI(watcher, stop_command_event, self.logger), self.rpc_server)
def __init__(self, db_manager, gatekeeper, carrier, block_processor): self.logger = get_logger(component=Responder.__name__) self.trackers = dict() self.tx_tracker_map = dict() self.unconfirmed_txs = [] self.missed_confirmations = dict() self.block_queue = Queue() self.db_manager = db_manager self.gatekeeper = gatekeeper self.carrier = carrier self.block_processor = block_processor self.last_known_block = db_manager.load_last_block_hash_responder() self.rw_lock = rwlock.RWLockWrite()
def __init__(self, db_path): if not isinstance(db_path, str): raise ValueError("db_path must be a valid path/name") self.logger = get_logger(component=AppointmentsDBM.__name__) try: super().__init__(db_path) except plyvel.Error as e: if "LOCK: Resource temporarily unavailable" in str(e): self.logger.info( "The db is already being used by another process (LOCK)") raise e
def main(config): setup_data_folder(config.get("DATA_DIR")) logging_server_ready = multiprocessing.Event() stop_logging_server = multiprocessing.Event() logging_port = multiprocessing.Value("i") logging_process = multiprocessing.Process( target=serve_logging, daemon=True, args=(config.get("LOG_FILE"), logging_port, config.get("DAEMON"), logging_server_ready, stop_logging_server), ) logging_process.start() logging_server_ready.wait() setup_logging(logging_port.value) logger = get_logger(component="Daemon") if not os.path.exists( config.get("TEOS_SECRET_KEY")) or config.get("OVERWRITE_KEY"): logger.info("Generating a new key pair") sk = Cryptographer.generate_key() Cryptographer.save_key_file(sk.to_der(), "teos_sk", config.get("DATA_DIR")) else: logger.info("Tower identity found. Loading keys") secret_key_der = Cryptographer.load_key_file( config.get("TEOS_SECRET_KEY")) if not secret_key_der: raise IOError("TEOS private key cannot be loaded") sk = Cryptographer.load_private_key_der(secret_key_der) # Windows cannot run gunicorn if os.name == "nt" and config.get("WSGI") == "gunicorn": logger.warning( "Windows cannot run gunicorn as WSGI. Changing to waitress") config["WSGI"] = "waitress" try: TeosDaemon(config, sk, logger, logging_port.value, stop_logging_server, logging_process).start() except Exception as e: logger.error("An error occurred: {}. Shutting down".format(e)) stop_logging_server.set() logging_process.join() exit(1)
def __init__(self, db_manager, gatekeeper, block_processor, responder, sk, max_appointments, blocks_in_cache): self.logger = get_logger(component=Watcher.__name__) self.appointments = dict() self.locator_uuid_map = dict() self.block_queue = Queue() self.db_manager = db_manager self.gatekeeper = gatekeeper self.block_processor = block_processor self.responder = responder self.max_appointments = max_appointments self.signing_key = sk self.last_known_block = db_manager.load_last_block_hash_watcher() self.locator_cache = LocatorCache(blocks_in_cache) self.rw_lock = rwlock.RWLockWrite()
def __init__(self, inspector, internal_api_endpoint): self.logger = get_logger(component=API.__name__) self.app = Flask(__name__) self.inspector = inspector self.internal_api_endpoint = internal_api_endpoint channel = grpc.insecure_channel(internal_api_endpoint) self.stub = TowerServicesStub(channel) # Adds all the routes to the functions listed above. routes = { "/register": (self.register, ["POST"]), "/add_appointment": (self.add_appointment, ["POST"]), "/get_appointment": (self.get_appointment, ["POST"]), "/get_subscription_info": (self.get_subscription_info, ["POST"]), } for url, params in routes.items(): self.app.add_url_rule(url, view_func=params[0], methods=params[1])
def __init__(self, blocks_in_cache): self.logger = get_logger(component=LocatorCache.__name__) self.cache = dict() self.blocks = OrderedDict() self.cache_size = blocks_in_cache self.rw_lock = rwlock.RWLockWrite()
def __init__(self, btc_connect_params): self.logger = get_logger(component=BlockProcessor.__name__) self.btc_connect_params = btc_connect_params
def __init__(self, btc_connect_params, bitcoind_reachable): self.logger = get_logger(component=BlockProcessor.__name__) self.btc_connect_params = btc_connect_params self.bitcoind_reachable = bitcoind_reachable
def test_get_logger(): # Test that get_logger actually adds a field called "component" with the expected value. # As the public interface of the class does not expose the initial_values, we rely on the output # of `repr` to check if the expected fields are indeed present. logger = get_logger("MyAwesomeComponent") assert "'component': 'MyAwesomeComponent'" in repr(logger)
def __init__(self, btc_connect_params, bitcoind_reachable): self.logger = get_logger(component=Carrier.__name__) self.btc_connect_params = btc_connect_params self.bitcoind_reachable = bitcoind_reachable self.issued_receipts = {}
class Cleaner: """ The :class:`Cleaner` is in charge of removing outdated/completed data from the tower. Mutable objects (like dicts) are passed-by-reference in Python, so no return is needed for the Cleaner. """ logger = get_logger(component="Cleaner") @staticmethod def delete_appointment_from_memory(uuid, appointments, locator_uuid_map): """ Deletes an appointment from memory (``appointments`` and ``locator_uuid_map`` dictionaries). If the given appointment does not share locator with any other, the map will completely removed, otherwise, the uuid will be removed from the map. Args: uuid (:obj:`str`): the identifier of the appointment to be deleted. appointments (:obj:`dict`): the appointments dictionary from where the appointment should be removed. locator_uuid_map (:obj:`dict`): the ``locator:uuid`` map from where the appointment should also be removed. """ locator = appointments[uuid].get("locator") # Delete the appointment appointments.pop(uuid) # If there was only one appointment that matches the locator we can delete the whole list if len(locator_uuid_map[locator]) == 1: locator_uuid_map.pop(locator) else: # Otherwise we just delete the appointment that matches locator:appointment_pos locator_uuid_map[locator].remove(uuid) @staticmethod def delete_appointment_from_db(uuid, db_manager): """ Deletes an appointment from the appointments database. Args: uuid (:obj:`str`): the identifier of the appointment to be deleted. db_manager (:obj:`AppointmentsDBM <teos.appointments_dbm.AppointmentsDBM>`): an instance of the appointment database manager to interact with the database. """ db_manager.delete_watcher_appointment(uuid) db_manager.delete_triggered_appointment_flag(uuid) @staticmethod def delete_appointments(outdated_appointments, appointments, locator_uuid_map, db_manager, outdated=False): """ Deletes appointments that have completed (with no trigger), or outdated, both from memory (:obj:`Watcher <teos.watcher.Watcher>`) and disk. Currently, an appointment is only completed if it cannot make it to the (:obj:`Responder <teos.responder.Responder>`), otherwise, it will be flagged as triggered and removed once the tracker is completed. Args: outdated_appointments (:obj:`list`): a list of appointments to be deleted. appointments (:obj:`dict`): a dictionary containing all the :mod:`Watcher <teos.watcher.Watcher>` appointments. locator_uuid_map (:obj:`dict`): a ``locator:uuid`` map for the :obj:`Watcher <teos.watcher.Watcher>` appointments. db_manager (:obj:`AppointmentsDBM <teos.appointments_dbm.AppointmentsDBM>`): an instance of the appointment database manager to interact with the database. outdated (:obj:`bool`): whether the appointments have been outdated or completed. """ for uuid in outdated_appointments: locator = appointments[uuid].get("locator") if outdated: Cleaner.logger.info( "End time reached with no breach. Deleting appointment", locator=locator, uuid=uuid) else: Cleaner.logger.warning( "Appointment cannot be completed, it contains invalid data. Deleting", locator=locator, uuid=uuid) Cleaner.delete_appointment_from_memory(uuid, appointments, locator_uuid_map) # Outdated appointments are not flagged, so they can be deleted without caring about the db flag. db_manager.batch_delete_watcher_appointments(outdated_appointments) @staticmethod def flag_triggered_appointments(triggered_appointments, appointments, locator_uuid_map, db_manager): """ Deletes a list of triggered appointment from memory (:obj:`Watcher <teos.watcher.Watcher>`) and flags them as triggered on disk. Args: triggered_appointments (:obj:`list`): a list of appointments to be flagged as triggered on the database. appointments (:obj:`dict`): a dictionary containing all the :obj:`Watcher <teos.watcher.Watcher>` appointments. locator_uuid_map (:obj:`dict`): a ``locator:uuid`` map for the :obj:`Watcher <teos.watcher.Watcher>` appointments. db_manager (:obj:`AppointmentsDBM <teos.appointments_dbm.AppointmentsDBM>`): an instance of the appointment database manager to interact with the database. """ for uuid in triggered_appointments: Cleaner.delete_appointment_from_memory(uuid, appointments, locator_uuid_map) db_manager.create_triggered_appointment_flag(uuid) @staticmethod def delete_trackers(completed_trackers, height, trackers, tx_tracker_map, db_manager, outdated=False): """ Deletes completed/outdated trackers both from memory (:obj:`Responder <teos.responder.Responder>`) and disk (from the :obj:`Responder`'s and :obj:`Watcher`'s databases). Args: completed_trackers (:obj:`dict`): a dict of completed/outdated trackers to be deleted (``uuid:confirmations``). trackers (:obj:`dict`): a dictionary containing all the :obj:`Responder <teos.responder.Responder>` trackers. height (:obj:`int`): the block height at which the trackers were completed. tx_tracker_map (:obj:`dict`): a ``penalty_txid:uuid`` map for the :obj:`Responder <teos.responder.Responder>` trackers. db_manager (:obj:`AppointmentsDBM <teos.appointments_dbm.AppointmentsDBM>`): an instance of the appointment database manager to interact with the database. outdated (:obj:`bool`): whether the trackers have been outdated or not. Defaults to False. """ for uuid in completed_trackers: if outdated: Cleaner.logger.info( "Appointment couldn't be completed. Expiry reached but penalty didn't make it to the chain", uuid=uuid, height=height, ) else: Cleaner.logger.info( "Appointment completed. Penalty transaction was irrevocably confirmed", uuid=uuid, height=height) penalty_txid = trackers[uuid].get("penalty_txid") trackers.pop(uuid) if len(tx_tracker_map[penalty_txid]) == 1: tx_tracker_map.pop(penalty_txid) Cleaner.logger.info("No more trackers for penalty transaction", penalty_txid=penalty_txid) else: tx_tracker_map[penalty_txid].remove(uuid) # Delete appointment from the db (from watcher's and responder's db) and remove flag db_manager.batch_delete_responder_trackers(completed_trackers) db_manager.batch_delete_watcher_appointments(completed_trackers) db_manager.batch_delete_triggered_appointment_flag(completed_trackers) @staticmethod def delete_gatekeeper_appointments(appointment_to_delete, registered_users, user_db): """ Deletes a list of outdated / completed appointments of a given user both from memory and the UserDB. Args: appointment_to_delete (:obj:`dict`): ``uuid:user_id`` dict containing the appointments to delete (outdated + completed) registered_users (:obj:`dict`): a dictionary of registered users from the gatekeeper. user_db (:obj:`UsersDBM <teos.user_dbm.UsersDBM>`): A user database manager instance to interact with the database. """ user_ids = [] # Remove appointments from memory for uuid, user_id in appointment_to_delete.items(): if user_id in registered_users and uuid in registered_users[ user_id].appointments: # Remove the appointment from the appointment list and update the available slots freed_slots = registered_users[user_id].appointments.pop(uuid) registered_users[user_id].available_slots += freed_slots if user_id not in user_ids: user_ids.append(user_id) # Store the updated users in the DB for user_id in user_ids: user_db.store_user(user_id, registered_users[user_id].to_dict()) @staticmethod def delete_outdated_users(outdated_users, registered_users, user_db): """ Deletes users whose subscription has been outdated, alongside all their associated data (appointments and trackers). Args: outdated_users (:obj:`list): a list of user_ids to be deleted. registered_users (:obj:`dict`): a dictionary of registered users from the gatekeeper. user_db (:obj:`UsersDBM <teos.user_dbm.UsersDBM>`): A user database manager instance to interact with the database. """ for user_id in outdated_users: registered_users.pop(user_id) user_db.delete_user(user_id)
def __init__(self, btc_connect_params): self.logger = get_logger(component=Carrier.__name__) self.btc_connect_params = btc_connect_params self.issued_receipts = {}