Exemplo n.º 1
0
def test_delete_appointment_from_memory(db_manager):
    appointments, locator_uuid_map = set_up_appointments(db_manager, MAX_ITEMS)

    for uuid in list(appointments.keys()):
        Cleaner.delete_appointment_from_memory(uuid, appointments,
                                               locator_uuid_map)

        # The appointment should have been deleted from memory, but not from the db
        assert uuid not in appointments
        assert db_manager.load_watcher_appointment(uuid) is not None
Exemplo n.º 2
0
    def do_watch(self):
        """
        Monitors the blockchain for channel breaches.

        This is the main method of the :obj:`Watcher` and the one in charge to pass appointments to the
        :obj:`Responder <teos.responder.Responder>` upon detecting a breach.
        """

        # Distinguish fresh bootstraps from bootstraps from db
        if self.last_known_block is None:
            self.last_known_block = self.block_processor.get_best_block_hash()
            self.db_manager.store_last_block_hash_watcher(
                self.last_known_block)

        # Initialise the locator cache with the last ``cache_size`` blocks.
        self.locator_cache.init(self.last_known_block, self.block_processor)

        while True:
            block_hash = self.block_queue.get()

            # When the ChainMonitor is stopped, a final ChainMonitor.END_MESSAGE message is sent
            if block_hash == ChainMonitor.END_MESSAGE:
                break

            block = self.block_processor.get_block(block_hash)
            self.logger.info("New block received",
                             block_hash=block_hash,
                             prev_block_hash=block.get("previousblockhash"))

            # If a reorg is detected, the cache is fixed to cover the last `cache_size` blocks of the new chain
            if self.last_known_block != block.get("previousblockhash"):
                self.locator_cache.fix(block_hash, self.block_processor)

            txids = block.get("tx")
            # Compute the locator for every transaction in the block and add them to the cache
            locator_txid_map = {compute_locator(txid): txid for txid in txids}
            self.locator_cache.update(block_hash, locator_txid_map)

            with self.rw_lock.gen_wlock():
                if len(self.appointments) > 0 and locator_txid_map:
                    outdated_appointments = self.gatekeeper.get_outdated_appointments(
                        block["height"])
                    # Make sure we only try to delete what is on the Watcher (some appointments may have been triggered)
                    outdated_appointments = list(
                        set(outdated_appointments).intersection(
                            self.appointments.keys()))

                    Cleaner.delete_outdated_appointments(
                        outdated_appointments, self.appointments,
                        self.locator_uuid_map, self.db_manager)

                    valid_breaches, invalid_breaches = self.filter_breaches(
                        self.get_breaches(locator_txid_map))

                    triggered_flags = []
                    appointments_to_delete = []

                    for uuid, breach in valid_breaches.items():
                        self.logger.info(
                            "Notifying responder and deleting appointment",
                            penalty_txid=breach["penalty_txid"],
                            locator=breach["locator"],
                            uuid=uuid,
                        )

                        receipt = self.responder.handle_breach(
                            uuid,
                            breach["locator"],
                            breach["dispute_txid"],
                            breach["penalty_txid"],
                            breach["penalty_rawtx"],
                            self.appointments[uuid].get("user_id"),
                            block_hash,
                        )

                        # FIXME: Only necessary because of the triggered appointment approach. Fix if it changes.

                        if receipt.delivered:
                            Cleaner.delete_appointment_from_memory(
                                uuid, self.appointments, self.locator_uuid_map)
                            triggered_flags.append(uuid)
                        else:
                            appointments_to_delete.append(uuid)

                    # Appointments are only flagged as triggered if they are delivered, otherwise they are just deleted.
                    appointments_to_delete.extend(invalid_breaches)
                    appointments_to_delete_gatekeeper = {
                        uuid: self.appointments[uuid].get("user_id")
                        for uuid in appointments_to_delete
                    }
                    self.db_manager.batch_create_triggered_appointment_flag(
                        triggered_flags)

                    Cleaner.delete_completed_appointments(
                        appointments_to_delete, self.appointments,
                        self.locator_uuid_map, self.db_manager)
                    # Remove invalid appointments from the Gatekeeper
                    self.gatekeeper.delete_appointments(
                        appointments_to_delete_gatekeeper)

                    if not self.appointments:
                        self.logger.info("No more pending appointments")

            # Register the last processed block for the Watcher
            self.db_manager.store_last_block_hash_watcher(block_hash)
            self.last_known_block = block.get("hash")
            self.block_queue.task_done()
Exemplo n.º 3
0
    def do_watch(self):
        """
        Monitors the blockchain whilst there are pending appointments.

        This is the main method of the :obj:`Watcher` and the one in charge to pass appointments to the
        :obj:`Responder <teos.responder.Responder>` upon detecting a breach.
        """

        while True:
            block_hash = self.block_queue.get()
            block = self.block_processor.get_block(block_hash)
            logger.info("New block received", block_hash=block_hash, prev_block_hash=block.get("previousblockhash"))

            if len(self.appointments) > 0 and block is not None:
                txids = block.get("tx")

                expired_appointments = [
                    uuid
                    for uuid, appointment_data in self.appointments.items()
                    if block["height"] > appointment_data.get("end_time") + self.expiry_delta
                ]

                Cleaner.delete_expired_appointments(
                    expired_appointments, self.appointments, self.locator_uuid_map, self.db_manager
                )

                valid_breaches, invalid_breaches = self.filter_valid_breaches(self.get_breaches(txids))

                triggered_flags = []
                appointments_to_delete = []

                for uuid, breach in valid_breaches.items():
                    logger.info(
                        "Notifying responder and deleting appointment",
                        penalty_txid=breach["penalty_txid"],
                        locator=breach["locator"],
                        uuid=uuid,
                    )

                    receipt = self.responder.handle_breach(
                        uuid,
                        breach["locator"],
                        breach["dispute_txid"],
                        breach["penalty_txid"],
                        breach["penalty_rawtx"],
                        self.appointments[uuid].get("end_time"),
                        block_hash,
                    )

                    # FIXME: Only necessary because of the triggered appointment approach. Fix if it changes.

                    if receipt.delivered:
                        Cleaner.delete_appointment_from_memory(uuid, self.appointments, self.locator_uuid_map)
                        triggered_flags.append(uuid)
                    else:
                        appointments_to_delete.append(uuid)

                # Appointments are only flagged as triggered if they are delivered, otherwise they are just deleted.
                appointments_to_delete.extend(invalid_breaches)
                self.db_manager.batch_create_triggered_appointment_flag(triggered_flags)

                Cleaner.delete_completed_appointments(
                    appointments_to_delete, self.appointments, self.locator_uuid_map, self.db_manager
                )

                if len(self.appointments) is 0:
                    logger.info("No more pending appointments")

            # Register the last processed block for the watcher
            self.db_manager.store_last_block_hash_watcher(block_hash)
            self.block_queue.task_done()