Como si no hubiéramos blockchain

¿Cómo usamos el contrato inteligente para construir un sistema para seleccionar los mejores proyectos tecnológicos en IT MTS? ¡Y en qué "trampas" caímos, pero pudimos salir, demostrando al final que es posible mantener un registro distribuido en dispositivos móviles!



¿Por qué se necesitaba un sistema basado en blockchain?


Comencemos desde el principio. MTS tiene una larga tradición: elegir el mejor proyecto tecnológico realizado en un año y recompensar a su equipo. El equipo recibe premios, respeto y fama. Con los años, varios proyectos se han convertido en ganadores: desde sistemas de telecomunicaciones altamente cargados hasta sistemas de inteligencia artificial.

La selección del mejor proyecto siempre se ha producido en varias etapas:

  • Equipos aplican
  • Se lleva a cabo la votación de expertos técnicos respetados.
  • Después de expertos, los gerentes seleccionan los proyectos
  • Al completar todas las etapas, el gran jefe elige el mejor proyecto.

Decidimos que este esquema no es lo suficientemente transparente para los participantes y pensamos: ¿por qué no dar a todos los expertos de la empresa la oportunidad de elegir el mejor proyecto tecnológico? 


Si implementamos esta oportunidad directamente por teléfono, veremos la calificación actual de los proyectos y quién vota por quién; esto garantizará una total transparencia del proceso.

Leímos varios artículos sobre blockchain, y la idea de construir un sistema de registro distribuido se estableció firmemente en nuestras cabezas. Pero, ¿qué pasa si aplicamos el contrato inteligente aquí ?

Nos atrajeron las siguientes propiedades:

  • apertura: no hay un servidor único donde pueda manipular la información;
  • la información colocada en un registro distribuido permanece allí para siempre;
  • la información no puede ser falsificada (bueno ... prácticamente)

No blockchain




Blockchain en sí no debe usarse para tales elecciones. Pero, ¿qué pasa si tomamos los protocolos para construir consenso en sistemas distribuidos y los aplicamos para construir consenso en relaciones humanas?

Dado que tendremos que trabajar en una red abierta, debemos protegernos de los ataques no bizantinos y de la sustitución de información en los dispositivos de los usuarios.

Cuales son las alternativas


El protocolo más famoso es PAXOS. Carece de un líder explícito, y todos los cambios pasan por una confirmación de dos fases. Al comienzo de cada cambio, se produce Proponer. Si tuvo éxito, entonces se envía Aceptar.


Una característica del algoritmo se puede llamar el hecho de que utiliza temporizadores globales para determinar qué solicitud se generó antes y que el nodo que realiza los cambios debe comunicarse con todos los nodos de la red. Lea más sobre el algoritmo aquí .

El algoritmo se usa en muchos lugares, por ejemplo, en el DBMS Cassandra. No nos gustó este protocolo en términos de la complejidad de su implementación para la tarea. Pero la segunda opción se nos ocurrió: es RAFT. De hecho, esta es una evolución del protocolo PAXOS con un líder claro.

El protocolo simplificado se puede describir de la siguiente manera:
· Se está construyendo una red de dispositivos que se conocen entre sí;
· Los dispositivos eligen entre ellos un líder que toma "decisiones importantes" (por ejemplo, agregar una entrada al registro o cambiar la composición de la red);
· El dispositivo líder es responsable de la distribución de información a través de la red para que sea idéntico en todas partes;
· Tan pronto como el líder deje de cumplir con sus deberes, elija un nuevo líder.

Lea sobre el protocolo aquí .

Nuestra implementacion


¿Qué estamos haciendo y por qué el mundo es otra bicicleta? 


Casi todos los usuarios de nuestro registro tienen dispositivos móviles que están casi siempre encendidos y casi siempre en línea, y en realidad los usan casi siempre, por lo que decidimos ejecutar el algoritmo de registro de distribución en dispositivos móviles, y no en la infraestructura del servidor, como otras implementaciones conocidas .

Veamos en qué "trampas" nos metimos, pero logramos salir ...

Trampa número 1: "Mi dirección no es mi casa ni mi calle"


"De repente" resultó que para construir una red P2P que implementa un registro distribuido utilizando el protocolo RAFT para la replicación de datos, cada dispositivo puede comunicarse entre sí, lo que significa que es un cliente y un servidor. Por lo tanto, necesitamos una dirección IP pública "blanca" para cada teléfono móvil (puede que no sea así).

El número de IPv4 reales es muy limitado, por lo tanto, los operadores de telecomunicaciones utilizan la tecnología NAT (traducción de direcciones de red) en modo PAT (traducción de direcciones de puerto), traduciendo varias direcciones IP de la red interna (que se distribuyen a los suscriptores) en una dirección IP pública externa. Por lo tanto, se excluye la capacidad de aceptar conexiones entrantes de Internet.

¡La buena noticia es que hay mucho IPv6! 


Tenemos soporte para IPv6 incluido en el paquete base. Además, todos los teléfonos modernos son compatibles con IPv6, y el operador asigna al suscriptor una dirección v6 pública "blanca". Nuestra elección es IPv6.

Trampa número 2: todos se fueron a dormir


A diferencia de los servidores, los teléfonos móviles a veces se apagan. Encontramos esto cuando probamos el primer prototipo. Además, la dirección IPv6 proporcionada por el operador es pública, pero no estática, cada nueva sesión de comunicación es una nueva dirección. La dirección del dispositivo móvil puede cambiar en cualquier momento. Y si no hay un solo teléfono con la dirección que conocemos en la red, simplemente deja de existir (no hay nada a lo que conectarse para "aumentarlo"). Por lo tanto, tuvimos que violar nuestra regla de "no servidor" hasta cierto punto. Creamos un nodo especial en la nube con una dirección estática conocida. Su tarea es recordar / actualizar la composición de la red y no apagarla. Es decir, es un sitio ordinario, nadie solo vota y, volviendo a él, siempre puede obtener la lista actual de direcciones de todos los participantes de la red.

3: 



Era necesario resolver el problema de alguna manera, para que no todos pudieran votar por proyectos, sino aquellos que lo necesitaran. La primera idea fue esta: mantener una base de datos de números de teléfono de expertos. Pero lo rechazaron porque no querían otorgar a la aplicación el derecho de acceder a esta información.
Como resultado, hicieron todo simple: decidieron proporcionar a cada voz, cada entrada del registro, una firma digital en curvas elípticas de moda, lo que determinará la autenticidad del registro. Se colocó un servicio WEB en la red corporativa que, mediante autorización de dominio, determinó al experto y generó un código QR único para él con las claves de cifrado públicas y privadas (por supuesto, en el lado del cliente). El experto escaneó el código de la aplicación y se conectó. Después de eso, la versión actual actual del registro "apareció" en su teléfono y apareció la oportunidad de votar.

4: Android 
 



Durante las pruebas, se reveló casi "de repente" que algunos usuarios son propietarios de modelos conocidos de dispositivos móviles que ejecutan el sistema operativo iOS. Y quedó claro que nuestro software de registro debería ejecutarse en diferentes plataformas. Buscamos el lenguaje de programación Kotlin, que no solo es "moderno, elegante, juvenil", sino también multiplataforma .

El concepto de multiplataforma en Kotlin implica que hay parte del código común y específico de la plataforma, pero dado que los recursos de nuestro equipo son limitados, nos propusimos la odiosa tarea de usar una única versión del código fuente para todas las plataformas. Por supuesto, el módulo ejecutable debe ser nativo para cada plataforma. Kotlin fue capaz de esto.

¡Dicho y hecho! Tenemos un único SourceSet con código fuente, del cual recopilamos binarios para todas las plataformas (!), Usando el ajuste "depende" . ¿Frio? ¡Muy genial!

Trampa número 5: el tráfico móvil no es gratuito 



¿Cómo podemos implementar de manera más efectiva la interacción entre los nodos de la red para no gastar todo el tráfico del suscriptor y no agotar la batería del dispositivo móvil? ¡Suponemos que la red puede constar de 1000 o más dispositivos! La opción más obvia es usar UDP en lugar de TCP, por ejemplo, en el procedimiento de selección de "líder" o al enviar Heartbeats sin datos. UDP es más económico porque utiliza un modelo de transferencia de datos simple, sin "apretones de manos" y confirmaciones. ¡Multa! ¿Qué más? Por supuesto, E / S asíncrona!

Leemos detenidamente la documentación de Kotlin Native.

Para todos los objetivos basados ​​en Unix o Windows (incluidos Android y iPhone), proporcionamos la plataforma posix lib. Contiene enlaces a la implementación de la plataforma del estándar POSIX.

¡Luego también leemos cuidadosamente la documentación estándar de POSIX y encontramos una función sorprendente que nos permite procesar eventos de socket en modo sin bloqueo! Habiéndonos sumergido de cabeza en el maravilloso mundo de la corutina, los enchufes y el C Interop, pudimos realizar un transporte muy eficiente. ¡Súper!

¿Y de qué forma enviar datos? 



¡Por supuesto CBOR!

Un formato de datos binarios compacto, que, por suerte, se implementa en la biblioteca multiplataforma kotlinx.serialization . ¡Simplemente impresionante!

Trampa número 6: serialización 



Esta vez, resultó inesperadamente que kotlinx.serialization no está en androidNative (en androidJvm, por supuesto, sí). Estimados colegas de JetBrains han confirmado que en este momento no están construyendo una biblioteca para androidNative, y antes del lanzamiento de Kotlin 1.4 ya no hay lugar para esta tarea en la hoja de ruta. 


¿Qué hacer? Si la montaña no va a Mahoma, ¡Mahoma va a la montaña!

Nosotros mismos hemos compilado kotlinx.serialization para todas las plataformas, incluido androidNative. ¡Lo más sorprendente es que funcionó! 


Trampa número 7: ¿Dónde almacenar el registro? 



Obviamente, en el almacenamiento de valor clave incrustado, pero ¿en qué? Elegimos lmdbx por la compacidad del código, la velocidad, la multiplataforma y la falta de un archivo WAL. Esta biblioteca está desarrollada por los chicos de Positive Techlologies y se origina en la legendaria biblioteca LMDB de uno de los autores de OpenLDAP Howard Chu. Y eso, a su vez, se basa en la implementación del árbol B + de Martin Hedenfalk. Por cierto, fuera de la caja, la biblioteca no fue construida para androidNative. Recolectamos cuidadosamente todos los errores, y los autores proporcionaron soluciones de inmediato, ¡por lo que les agradecemos especialmente!

Trampa número 8: interoperabilidad C 



Poner todo junto resultó ser una tarea muy poco trivial. Además de lmdbx y POSIX zócalos, hemos integrado para generar bibliotecas / validar las firmas digitales en curvas elípticas y calculando SHA256 utilizando la increíble C interoperabilidad mecanismo . En palabras simples: desde una aplicación nativa en Kotlin, puede llamar a la función de biblioteca C, incluso con punteros a punteros y otra magia, todo parece un poco extraño.

Por ejemplo, llamando a getaddrinfo para obtener sockaddr.



¿Cómo te gusta Ilon Mask?

Vincular la biblioteca C al ejecutable nativo de Kotlin es una búsqueda separada, que también logramos atravesar, pero no sin muletas. Generamos dinámicamente el archivo def directamente en el script de compilación para indicar la ruta correcta a las bibliotecas en relación con el directorio raíz del proyecto y luego sustituimos su descriptor (archivo def) en la sección cinterops. En el archivo def en sí, solo se determina la ruta absoluta, que no solo puede tener un formato diferente si el ensamblaje se realiza bajo un sistema operativo diferente, sino que en las máquinas locales de los desarrolladores, por supuesto, también puede ser diferente, lo que obviamente conduce a un error durante el enlace.

Sobre las elecciones


Las principales elecciones que celebramos durante el día. Un poco más de 20 expertos participaron en las pruebas con nuestra red. Se evaluaron 21 proyectos en 5 categorías, es decir, se agregaron al registro más de 100 entradas con votos para proyectos.

Conclusión


Como resultado de este pequeño proyecto de investigación, pudimos demostrar que es posible mantener un registro distribuido en dispositivos móviles. Esto abre muchas posibilidades para usar esta tecnología en dispositivos IoT para organizar la computación Edge-computing. Delante de nosotros todavía estamos esperando pruebas de carga, vulnerabilidad a ataques y tolerancia a fallas. ¡Pero creemos que tendremos éxito!

Autores del artículo: arquitectos y desarrolladores del Centro de I + D de MTS Dmitry Dzyuba, Alexey Vasilenko y Semen Nevrev.

All Articles