---
# System prepended metadata

title: Manejo de Mensajes para LC

---

# Manejo de Mensajes para LC

---

En este texto introducimos y analizamos la forma en la que se manejan los mensajes para los Light Clients dentro de Lumino.

* ¿Qué son los mensajes?
* ¿Cómo se almacenan?
* ¿Cómo se obtienen?
* Code walkthrough (almacenamiento)
* Code walkthrough (consulta)

---

### ¿Qué son los mensajes?

Los mensajes funcionan como forma de notificar al LC que debe realizar una acción o bien simplemente para informar de algun cambio en los datos.

### ¿Cómo se almacenan?

Se almacenan en la base de datos bajo la tabla ```light_client_protocol_message```. A nivel de código como el tipo de mensajes que tenemos actualmente es de un tipo unicamente se transportan bajo la estructura ```PaymentHubMessage``` ubicada en el archivo **raiden/lightclient/lightclientmessages/payment_hub_message.py** que a su vez es encapsulada por la estructura ```HubResponseMessage``` ubicada en **raiden/lightclient/lightclientmessages/hub_response_message.py**. Esta última se utiliza para crear la respuesta de la API.

Los mensajes son manejados por ```LightClientMessageHandler``` ubicado en **raiden/lightclient/handlers/light_client_message_handler.py**, allí tenemos todos los métodos para almacenar y obtener los mensajes.

### ¿Cómo se obtienen?

El método para obtener los mensajes es con Long Polling. Lumino posee un endpoint para obtener los mensajes, el mismo se ubica bajo la url ```/api/v1/light_client_messages```. Dicho endpoint es manejado por ```LightClientMessageResource``` que utiliza ```get_light_client_protocol_message``` del archivo **raiden/api/rest.py:2089** para obtener los mensajes.

### Code walkthrough (almacenamiento)

En cualquier parte donde se quiera o bien obtener o bien almacenar los mensajes se utiliza el manejador de mensajes ```LightClientMessageHandler``` mencionado anteriormente. En este caso nos enfocaremos en el almacenamiento por lo que nos va a interesar la llamada a la función ```store_light_client_protocol_message``` del mismo y todas sus ocurrencias. Analizando el codigo podemos encontrar las siguientes ocurrencias de ésta llamada:

![](https://i.imgur.com/a6uXkIA.png)

A grandes rasgos podemos ver que se almacenan mensajes cuando: 
* se recibe un evento que nos dice que debemos almacenar el mensaje, esto es para cuando se quiere almacenar mensajes de estado dentro del nodo, se crea un evento llamado ```StoreMessageEvent``` que dispara el almacenamiento.
* se manejan mensajes del protocolo de pagos, ya sea delivered o processed.
* se crea un pago

En todos los casos el almacenamiento se hace utilizando el handler que tiene un manejo básico, recibe los datos y los almacena en la base para ser consultados más adelante. El almacenamiento lo podemos ver aquí **raiden/lightclient/handlers/light_client_message_handler.py:43**:

```python
@classmethod
    def store_light_client_protocol_message(cls, identifier: int, message: Message, signed: bool, payment_id: int,
                                            order: int, message_type: LightClientProtocolMessageType,
                                            wal: WriteAheadLog):
        return wal.storage.write_light_client_protocol_message(
            message,
            build_light_client_protocol_message(identifier, message, signed,
                                                payment_id, order, message_type)
        )
```

Que como podemos ver en el fragmento la función es bastante simple, se reciben los parámetros y se almacenan en la base creando el mensaje génerico del tipo ```LightClientProtocolMessage```.

Dicho mensaje es almacenado a continuación por la base de datos:

```python
    def write_light_client_protocol_message(self, new_message, msg_dto):
        serialized_data = self.serializer.serialize(new_message)
        if msg_dto.is_signed:
            msg_dto.signed_message = serialized_data
        else:
            msg_dto.unsigned_message = serialized_data
        return super().write_light_client_protocol_message(msg_dto)
```

en el archivo **raiden/storage/sqlite.py:1466**, que como vemos utiliza otra función para el guardado final de los datos en el mismo archivo linea 190:

```python
    def write_light_client_protocol_message(self, msg_dto):
        with self.write_lock, self.conn:
            cursor = self.conn.execute(
                "INSERT INTO light_client_protocol_message("
                "identifier, "
                "message_order, "
                "message_type, "
                "unsigned_message, "
                "signed_message, "
                "light_client_payment_id "
                ")"
                "VALUES(?, ?, ?, ?, ?, ?)",
                (str(msg_dto.identifier),
                 msg_dto.message_order,
                 str(msg_dto.message_type.value),
                 msg_dto.unsigned_message,
                 msg_dto.signed_message,
                 str(msg_dto.light_client_payment_id) if msg_dto.light_client_payment_id else None
                 ),
            )
            last_id = cursor.lastrowid
        return last_id
```

### Code walkthrough (consulta)

Los mensajes como mencionamos anteriormente se consultan utilizando Long Polling en el recurso mencionado más arriba. Aqui mostramos un breve análisis de ese código y de como funciona. El recurso que maneja el long polling está ubicado en el archivo **raiden/api/rest.py:232**: ```("/light_client_messages", LightClientMessageResource, "Message polling"),```

Dicho recurso es manejado por ```LightClientMessageResource``` ubicado en **raiden/api/v1/resources.py:306**:

```python
class LightClientMessageResource(BaseResource):
    get_schema = LightClientMessageGetSchema

    @use_kwargs(get_schema, locations=("query",))
    def get(self, from_message):
        return self.rest_api.get_light_client_protocol_message(from_message)
```

que termina utilizando ```get_light_client_protocol_message``` dentro del archivo **raiden/api/rest.py:2089**:

```python
    def get_light_client_protocol_message(self, from_message: int):
        headers = request.headers
        api_key = headers.get("x-api-key")
        if not api_key:
            return ApiErrorBuilder.build_and_log_error(errors="Missing api_key auth header",
                                                       status_code=HTTPStatus.BAD_REQUEST, log=log)

        light_client = LightClientService.get_by_api_key(api_key, self.raiden_api.raiden.wal)
        if not light_client:
            return ApiErrorBuilder.build_and_log_error(
                errors="There is no light client associated with the api key provided",
                status_code=HTTPStatus.FORBIDDEN, log=log)

        messages = LightClientService.get_light_client_messages(from_message, light_client.address,
                                                                self.raiden_api.raiden.wal)
        response = [message.to_dict() for message in messages]
        return api_response(response)
```

Allí podemos apreciar como se chequean los parámetros del request y se obtienen los mensajes desde la base de datos, luego simplemente se responde con la lista de mensajes obtenida. A continuacion veremos como se obtiene la lista de mensajes. Esto ocurre en el ```LightClientService``` en la función ```get_light_client_messages``` ubicada en el archivo **raiden/lightclient/handlers/light_client_service.py:40**:

```python
@classmethod
    def get_light_client_messages(cls, from_message: int, light_client: ChecksumAddress, wal: WriteAheadLog):
        messages = wal.storage.get_light_client_messages(from_message, light_client)
        result: List[HubResponseMessage] = []
        for message in messages:
            signed = message[0]
            order = message[1]
            payment_id = message[2]
            unsigned_msg = message[3]
            signed_msg = message[4]
            internal_identifier = message[6]
            message_type = LightClientProtocolMessageType[message[7]]
            message = signed_msg if signed_msg is not None else unsigned_msg
            payment_hub_message = PaymentHubMessage(payment_id=payment_id,
                                                    message_order=order,
                                                    message=message, is_signed=signed)
            hub_message = HubResponseMessage(internal_identifier, message_type, payment_hub_message)
            result.append(hub_message)
        return result
```

donde vemos como se consultan los mensajes a la base y se crean las estructuras para utilizar en la respuesta, luego si vemos la llamada a la base veremos que es tan solo una simple consulta de datos ubicada en **raiden/storage/sqlite.py:1425**:

```python
    def get_light_client_messages(self, from_message, light_client):
        messages = super().get_light_client_messages(from_message, light_client)
        result = []
        if messages:
            for message in messages:
                signed = False
                if message[3] is not None:
                    signed = True
                if message[3] is not None:
                    serialized_signed_msg = self.serializer.deserialize(message[3])
                else:
                    serialized_signed_msg = None
                if message[2] is not None:
                    serialized_unsigned_msg = self.serializer.deserialize(message[2])
                else:
                    serialized_unsigned_msg = None
                result.append(
                    (signed, message[1], message[4], serialized_unsigned_msg,
                     serialized_signed_msg, message[0], message[5], message[6]))
        return result
```

que se forma a partir de la llamada en el mismo archivo bajo la linea 1404:

```python
    def get_light_client_messages(self, from_message, light_client):
        cursor = self.conn.cursor()
        cursor.execute(
            "SELECT identifier, message_order, unsigned_message, signed_message, light_client_payment_id, internal_msg_identifier, message_type" +
            " FROM light_client_protocol_message A INNER JOIN light_client_payment B" +
            " ON A.light_client_payment_id = B.payment_id" +
            " WHERE A.internal_msg_identifier >= ?" +
            " AND B.light_client_address = ?" +
            "ORDER BY light_client_payment_id, message_order ASC",
            (from_message, light_client),

        )
        return cursor.fetchall()
```

Donde podemos apreciar la consulta a la base que permite obtener los datos necesarios para crear la lista de mensajes que sera utilizada en niveles superiores.


### Action Items

1. **Ver cómo se están guardando los mensajes en la base de datos para luego hacer un polling (e.g. store message event)**
    * Esto se analizó mas arriba
2. **Hoy en día tenemos distintos tipos de mensaje; habría que ver cómo diseñar/extender la tabla para que acepte información de los mensajes que necesitamos. Esto sería conveniente de investigar.**
    * Ya existe el tipo ```SettlementRequired``` que no esta siendo usado pero que probablemente fue pensado para este caso. Podemos usar la misma tabla y ver de guardar los datos que sabemos son necesarios para la operación.
3. **Una vez resuelto eso, el LC se entera del mensaje, lo firma, y debe mandar la transacción. Hay un endpoint en la API REST que es `Settle`, habría que tener otro que sea `SettlementLight`. Éste último habría uqe investigarlo en comparación con el `Settle` para adaptarlo.**
    * No hay ningun endpoint `Settle` o al menos no se encontro ninguno, por lo que veo no seria necesario ya que el settle es directamente ejecutado por el nodo actualmente.
4. **Investigar como análogo de Close/Close Light.**
    * Pendiente
