Beispiel #1
0
def test_from_dict(ext_appointment_data):
    # The appointment should be build if we don't miss any field
    ext_appointment = ExtendedAppointment.from_dict(ext_appointment_data)
    assert isinstance(ext_appointment, ExtendedAppointment)

    # Otherwise it should fail
    for key in ext_appointment_data.keys():
        prev_val = ext_appointment_data[key]
        ext_appointment_data[key] = None

        with pytest.raises(ValueError, match="Wrong appointment data"):
            ExtendedAppointment.from_dict(ext_appointment_data)
            ext_appointment_data[key] = prev_val
Beispiel #2
0
    def build_appointments(appointments_data):
        """
        Builds an appointments dictionary (``uuid:ExtendedAppointment``) and a locator_uuid_map (``locator:uuid``)
        given a dictionary of appointments from the database.

        Args:
            appointments_data (:obj:`dict`): a dictionary of dictionaries representing all the
                :obj:`Watcher <teos.watcher.Watcher>` appointments stored in the database. The structure is as follows:

                    ``{uuid: {locator: str, ...}, uuid: {locator:...}}``

        Returns:
            :obj:`tuple`: A tuple with two dictionaries. ``appointments`` containing the appointment information in
            :obj:`ExtendedAppointment <teos.extended_appointment.ExtendedAppointment>` objects and ``locator_uuid_map``
            containing a map of appointment (``uuid:locator``).
        """

        appointments = {}
        locator_uuid_map = {}

        for uuid, data in appointments_data.items():
            appointment = ExtendedAppointment.from_dict(data)
            appointments[uuid] = appointment.get_summary()

            if appointment.locator in locator_uuid_map:
                locator_uuid_map[appointment.locator].append(uuid)

            else:
                locator_uuid_map[appointment.locator] = [uuid]

        return appointments, locator_uuid_map
Beispiel #3
0
def test_add_appointment_in_cache_invalid_transaction(api, client):
    api.watcher.gatekeeper.registered_users[user_id] = UserInfo(available_slots=1, subscription_expiry=0)

    # We need to create the appointment manually
    dispute_tx = create_dummy_transaction()
    dispute_txid = dispute_tx.tx_id.hex()
    penalty_tx = create_dummy_transaction(dispute_txid)

    locator = compute_locator(dispute_txid)
    dummy_appointment_data = {"tx": penalty_tx.hex(), "tx_id": dispute_txid, "to_self_delay": 20}
    encrypted_blob = Cryptographer.encrypt(dummy_appointment_data.get("tx")[::-1], dummy_appointment_data.get("tx_id"))

    appointment_data = {
        "locator": locator,
        "to_self_delay": dummy_appointment_data.get("to_self_delay"),
        "encrypted_blob": encrypted_blob,
        "user_id": get_random_value_hex(16),
    }

    appointment = ExtendedAppointment.from_dict(appointment_data)
    api.watcher.locator_cache.cache[appointment.locator] = dispute_tx.tx_id.hex()
    appointment_signature = Cryptographer.sign(appointment.serialize(), user_sk)

    # Add the data to the cache
    api.watcher.locator_cache.cache[dispute_txid] = appointment.locator

    # The appointment should be accepted
    r = add_appointment(client, {"appointment": appointment.to_dict(), "signature": appointment_signature}, user_id)
    assert (
        r.status_code == HTTP_OK
        and r.json.get("available_slots") == 0
        and r.json.get("start_block") == api.watcher.last_known_block
    )
Beispiel #4
0
    def filter_breaches(self, breaches):
        """
        Filters the valid from the invalid channel breaches.

        The :obj:`Watcher` cannot know if an ``encrypted_blob`` contains a valid transaction until a breach is seen.
        Blobs that contain arbitrary data are dropped and not sent to the :obj:`Responder <teos.responder.Responder>`.

        Args:
            breaches (:obj:`dict`): a dictionary containing channel breaches (``locator:txid``).

        Returns:
            :obj:`tuple`: A dictionary and a list. The former contains the valid breaches, while the latter contain the
            invalid ones.

            The valid breaches dictionary has the following structure:

            ``{locator, dispute_txid, penalty_txid, penalty_rawtx}``
        """

        valid_breaches = {}
        invalid_breaches = []

        # A cache of the already decrypted blobs so replicate decryption can be avoided
        decrypted_blobs = {}

        for locator, dispute_txid in breaches.items():
            for uuid in self.locator_uuid_map[locator]:
                appointment = ExtendedAppointment.from_dict(
                    self.db_manager.load_watcher_appointment(uuid))

                if appointment.encrypted_blob in decrypted_blobs:
                    penalty_txid, penalty_rawtx = decrypted_blobs[
                        appointment.encrypted_blob]
                    valid_breaches[uuid] = {
                        "locator": appointment.locator,
                        "dispute_txid": dispute_txid,
                        "penalty_txid": penalty_txid,
                        "penalty_rawtx": penalty_rawtx,
                    }

                else:
                    try:
                        penalty_txid, penalty_rawtx = self.check_breach(
                            uuid, appointment, dispute_txid)
                        valid_breaches[uuid] = {
                            "locator": appointment.locator,
                            "dispute_txid": dispute_txid,
                            "penalty_txid": penalty_txid,
                            "penalty_rawtx": penalty_rawtx,
                        }
                        decrypted_blobs[appointment.encrypted_blob] = (
                            penalty_txid, penalty_rawtx)

                    except (EncryptionError, InvalidTransactionFormat):
                        invalid_breaches.append(uuid)

        return valid_breaches, invalid_breaches
Beispiel #5
0
    def _generate_dummy_appointment():
        appointment_data = {
            "locator": get_random_value_hex(16),
            "to_self_delay": 20,
            "encrypted_blob": get_random_value_hex(150),
            "user_id": get_random_value_hex(16),
            "user_signature": get_random_value_hex(50),
            "start_block": 200,
        }

        return ExtendedAppointment.from_dict(appointment_data)
Beispiel #6
0
    def _generate_dummy_appointment():
        commitment_txid = get_random_value_hex(32)
        penalty_tx = get_random_value_hex(150)

        appointment_data = {
            "locator": compute_locator(commitment_txid),
            "to_self_delay": 20,
            "encrypted_blob": Cryptographer.encrypt(penalty_tx,
                                                    commitment_txid),
            "user_id": get_random_value_hex(16),
            "user_signature": get_random_value_hex(50),
            "start_block": 200,
        }

        return ExtendedAppointment.from_dict(appointment_data), commitment_txid
Beispiel #7
0
def generate_dummy_appointment():
    dispute_tx = create_dummy_transaction()
    dispute_txid = dispute_tx.tx_id.hex()
    penalty_tx = create_dummy_transaction(dispute_txid)

    locator = compute_locator(dispute_txid)
    dummy_appointment_data = {
        "tx": penalty_tx.hex(),
        "tx_id": dispute_txid,
        "to_self_delay": 20
    }
    encrypted_blob = Cryptographer.encrypt(dummy_appointment_data.get("tx"),
                                           dummy_appointment_data.get("tx_id"))

    appointment_data = {
        "locator": locator,
        "to_self_delay": dummy_appointment_data.get("to_self_delay"),
        "encrypted_blob": encrypted_blob,
        "user_id": get_random_value_hex(16),
    }

    return ExtendedAppointment.from_dict(appointment_data), dispute_tx.hex()
def test_add_appointment_in_cache_invalid_blob(watcher):
    # Generate an appointment with an invalid transaction and add the dispute txid to the cache
    user_sk, user_pk = generate_keypair()
    user_id = Cryptographer.get_compressed_pk(user_pk)
    watcher.gatekeeper.registered_users[user_id] = UserInfo(available_slots=1, subscription_expiry=10)

    # We need to create the appointment manually
    dispute_tx = create_dummy_transaction()
    dispute_txid = dispute_tx.tx_id.hex()
    penalty_tx = create_dummy_transaction(dispute_txid)

    locator = compute_locator(dispute_txid)
    dummy_appointment_data = {"tx": penalty_tx.hex(), "tx_id": dispute_txid, "to_self_delay": 20}
    encrypted_blob = Cryptographer.encrypt(dummy_appointment_data.get("tx")[::-1], dummy_appointment_data.get("tx_id"))

    appointment_data = {
        "locator": locator,
        "to_self_delay": dummy_appointment_data.get("to_self_delay"),
        "encrypted_blob": encrypted_blob,
        "user_id": get_random_value_hex(16),
    }

    appointment = ExtendedAppointment.from_dict(appointment_data)
    watcher.locator_cache.cache[appointment.locator] = dispute_tx.tx_id.hex()

    # Try to add the appointment
    response = watcher.add_appointment(appointment, Cryptographer.sign(appointment.serialize(), user_sk))

    # The appointment is accepted but dropped (same as an invalid appointment that gets triggered)
    assert (
        response
        and response.get("locator") == appointment.locator
        and Cryptographer.get_compressed_pk(watcher.signing_key.public_key)
        == Cryptographer.get_compressed_pk(Cryptographer.recover_pk(appointment.serialize(), response.get("signature")))
    )

    assert not watcher.locator_uuid_map.get(appointment.locator)
    assert appointment.locator not in [tracker.get("locator") for tracker in watcher.responder.trackers.values()]
Beispiel #9
0
def test_get_summary(ext_appointment_data):
    assert ExtendedAppointment.from_dict(
        ext_appointment_data).get_summary() == {
            "locator": ext_appointment_data["locator"],
            "user_id": ext_appointment_data["user_id"],
        }