Comme si nous n'avions pas de blockchain

Comment avons-nous utilisé le smart-contract pour construire un système de sélection des meilleurs projets technologiques en IT MTS? Et dans quels «pièges» nous sommes tombés, mais nous avons pu en sortir, prouvant au final qu'il est possible de maintenir un registre distribué sur les appareils mobiles!



Pourquoi un système basé sur la blockchain était-il nécessaire?


Commençons par le tout début. MTS a une longue tradition - choisir le meilleur projet technologique réalisé en un an et récompenser son équipe. L'équipe reçoit des prix, du respect et de la renommée. Au fil des ans, divers projets sont devenus gagnants: des systèmes de télécommunications très chargés aux systèmes d'intelligence artificielle.

La sélection du meilleur projet s'est toujours déroulée en plusieurs étapes:

  • Les équipes postulent
  • Le vote d'experts techniques respectés a lieu
  • Après experts, les projets sont sélectionnés par les managers
  • À la fin de toutes les étapes, le grand patron choisit le meilleur projet.

Nous avons décidé que ce schéma n'était pas suffisamment transparent pour les participants et nous avons pensé: pourquoi ne pas donner à tous les experts de l'entreprise la possibilité de choisir le meilleur projet technologique? 


Si nous mettons en œuvre cette opportunité directement par téléphone, nous verrons la notation actuelle des projets et qui votera pour qui - cela garantira une transparence totale du processus.

Nous avons lu plusieurs articles sur la blockchain et l'idée de construire un système de registre distribué fermement ancré dans nos têtes. Mais que se passe-t-il si nous appliquons le smart-contract ici ?

Nous avons été attirés par les propriétés suivantes:

  • ouverture - il n'y a pas de serveur unique où vous pouvez manipuler les informations;
  • les informations placées dans un registre distribué y restent pour toujours;
  • l'information ne peut pas être truquée (enfin ... pratiquement)

Pas de blockchain




La blockchain elle-même ne devrait pas être utilisée pour de telles élections. Mais que se passe-t-il si nous prenons les protocoles pour construire un consensus dans les systèmes distribués et les appliquons à la construction d'un consensus dans les relations humaines?

Étant donné que nous devrons travailler dans un réseau ouvert, nous devons nous protéger des attaques non byzantines et de la substitution d'informations sur les appareils des utilisateurs.

Quelles sont les alternatives


Le protocole le plus célèbre est PAXOS. Il manque un leader explicite et tous les changements passent par un commit en deux phases. Au début de chaque changement, Proposer se produit. S'il a réussi, alors Accepter est envoyé.


Une caractéristique de l'algorithme peut être appelée le fait qu'il utilise des temporisateurs globaux pour déterminer quelle requête est générée plus tôt et que le nœud effectuant les modifications doit communiquer avec tous les nœuds du réseau. En savoir plus sur l'algorithme ici .

L'algorithme est utilisé à de nombreux endroits, par exemple dans le SGBD Cassandra. Nous n'avons pas aimé ce protocole en termes de complexité de sa mise en œuvre pour la tâche. Mais la deuxième option nous est venue - c'est RAFT. En fait, il s'agit d'une évolution du protocole PAXOS avec un leader clair.

Le protocole simplifié peut être décrit comme suit:
· Un réseau d'appareils est en cours de construction qui se connaissent les uns les autres;
· Les appareils choisissent entre eux un leader qui prend des «décisions importantes» (par exemple, ajouter une entrée au registre ou changer la composition du réseau);
· L'appareil leader est responsable de la diffusion des informations à travers le réseau afin que partout elles soient identiques;
· Dès que le leader cesse de s'acquitter de ses fonctions - choisissez un nouveau leader.

Lisez le protocole ici .

Notre implémentation


Que faisons-nous et pourquoi le monde est-il un autre vélo? 


Presque tous les utilisateurs de notre registre ont des appareils mobiles qui sont presque toujours allumés et presque toujours en ligne, et les utilisent en fait presque toujours, nous avons donc décidé d'exécuter l'algorithme de registre de distribution sur les appareils mobiles, et non sur l'infrastructure du serveur, comme d'autres implémentations bien connues .

Voyons dans quels «pièges» nous sommes entrés, mais nous avons réussi à en sortir…

Piège numéro 1: "Mon adresse n'est pas la maison et pas la rue"


«Tout à coup», il s'est avéré que pour construire un réseau P2P qui implémente un registre distribué utilisant le protocole RAFT pour la réplication des données, chaque appareil peut communiquer entre eux, ce qui signifie qu'il est à la fois un client et un serveur. Par conséquent, nous avons besoin d'une adresse IP publique «blanche» pour chaque téléphone mobile (ce n'est peut-être pas le cas).

Le nombre de véritables IPv4 est très limité, donc les opérateurs de télécommunications utilisent la technologie NAT (Network Address Translation) en mode PAT (Port Address Translation), traduisant plusieurs adresses IP du réseau interne (qui sont distribuées aux abonnés) en une seule adresse IP publique externe. Ainsi, la possibilité d'accepter les connexions entrantes à partir d'Internet est exclue.

La bonne nouvelle, c'est qu'il y a beaucoup d'IPv6! 


Nous avons un support IPv6 inclus dans le package de base. De plus, tous les téléphones modernes prennent en charge IPv6 et l'opérateur attribue à l'abonné une adresse v6 «blanche» publique. Notre choix est IPv6.

Piège numéro 2: tout le monde s'est endormi


Contrairement aux serveurs, les téléphones mobiles s'éteignent parfois encore. Nous l'avons trouvé lors du test du premier prototype. De plus, l'adresse IPv6 fournie par l'opérateur est publique, mais pas statique, chaque nouvelle session de communication est une nouvelle adresse. L'adresse de l'appareil mobile peut changer à tout moment. Et s'il n'y a pas un seul téléphone avec l'adresse que nous connaissons sur le réseau, il cesse tout simplement d'exister (il n'y a rien à se connecter pour «l'augmenter»). Par conséquent, nous avons dû violer notre règle «pas de serveur» dans une certaine mesure. Nous avons créé un nœud spécial dans le cloud avec une adresse statique connue. Sa tâche consiste à mémoriser / mettre à jour la composition du réseau et à ne pas l'éteindre. C'est-à-dire que c'est un site ordinaire, personne ne vote que de lui, et en y tournant, vous pouvez toujours obtenir la liste actuelle des adresses de tous les participants du réseau.

3: 



Il était nécessaire de résoudre le problème d'une manière ou d'une autre, afin que tout le monde ne puisse voter pour des projets, mais ceux qui en avaient besoin. La première idée était la suivante: maintenir une base de données de numéros de téléphone d'experts. Mais ils l'ont refusé, car ils ne voulaient pas donner à l'application le droit d'accéder à ces informations.
En conséquence, ils ont rendu tout simple: ils ont décidé de fournir à chaque voix, chaque entrée de registre une signature numérique sur des courbes elliptiques à la mode, qui détermineront l'authenticité de l'enregistrement. Un service WEB a été placé sur le réseau de l'entreprise, qui, par autorisation de domaine, a déterminé l'expert et a généré pour lui un QR code unique avec les clés de chiffrement publiques et privées (bien sûr, côté client). L'expert a scanné le code de l'application et s'est connecté. Après cela, la version actuelle actuelle du registre a «roulé» sur son téléphone et la possibilité de voter est apparue.

4: Android 
 



Au cours des tests, il a été presque «soudainement» révélé que certains utilisateurs étaient propriétaires de modèles bien connus d'appareils mobiles exécutant le système d'exploitation iOS. Et il est devenu clair que notre logiciel de registre devrait fonctionner sur différentes plateformes. Nous nous sommes tournés vers le langage de programmation Kotlin, qui est non seulement «branché, élégant, jeune», mais également multi-plateforme .

Le concept de multi-plateforme dans Kotlin implique qu'il existe une partie du code commun et spécifique à la plate-forme, mais comme les ressources de notre équipe sont limitées, nous nous sommes fixé la tâche odieuse d'utiliser une seule version du code source pour toutes les plates-formes! Bien entendu, le module exécutable doit être natif pour chaque plateforme. Kotlin était capable de cela.

À peine dit que c'était fait! Nous avons un seul SourceSet avec le code source, à partir duquel nous collectons des binaires pour toutes les plates-formes (!), En utilisant le fitOn «dépend» . Cool? Très cool!

Piège numéro 5: le trafic mobile n'est pas gratuit 



Comment mettre en œuvre le plus efficacement possible l'interaction entre les nœuds du réseau afin de ne pas dépenser tout le trafic de l'abonné et de ne pas vider la batterie de l'appareil mobile? Nous supposons que le réseau peut comprendre 1000 appareils ou plus! L'option la plus évidente consiste à utiliser UDP au lieu de TCP, par exemple, dans la procédure de sélection «leader» ou lors de l'envoi de Heartbeats sans données. UDP est plus économique car il utilise un modèle de transfert de données simple, sans «poignée de main» et sans confirmations. Bien! Quoi d'autre? Bien sûr, les E / S asynchrones!

Nous lisons attentivement la documentation de Kotlin Native.

Pour toutes les cibles basées sur Unix ou Windows (y compris Android et iPhone), nous fournissons la plate-forme posix lib. Il contient des liaisons à l'implémentation de la plate-forme de la norme POSIX.

Ensuite, nous lisons également attentivement la documentation standard POSIX et trouvons une fonction étonnante qui nous permet de traiter les événements de socket en mode non bloquant! Après avoir plongé tête baissée dans le monde merveilleux de la coroutine, des prises et du C Interop, nous avons pu réaliser un transport très efficace. Super!

Et sous quelle forme envoyer des données? 



Bien sûr CBOR!

Un format de données binaires compact, qui, par chance, est implémenté dans la bibliothèque multi-plateforme kotlinx.serialization . Tout simplement génial!

Piège numéro 6: sérialisation 



Cette fois, il s'est avéré que kotlinx.serialization n'est pas sous androidNative (sous androidJvm, bien sûr, il y en a un). Chers collègues de JetBrains ont confirmé qu'ils ne sont pas en train de construire une bibliothèque pour androidNative, et avant la sortie de Kotlin 1.4, il n'y a plus de place pour cette tâche dans la feuille de route. 


Que faire? Si la montagne ne va pas à Mohammed, Mohammed va à la montagne!

Nous avons nous-mêmes compilé kotlinx.serialization pour toutes les plateformes, y compris androidNative! Le plus étonnant, c'est que ça a marché! 


Piège numéro 7: où stocker le journal? 



De toute évidence, dans le stockage de valeurs-clés intégré, mais dans lequel? Nous avons choisi lmdbx pour la compacité du code, la vitesse, la multiplateforme et l'absence de fichier WAL. Cette bibliothèque est développée par les gars de Positive Techlologies et provient de la légendaire bibliothèque LMDB de l'un des auteurs d'OpenLDAP Howard Chu. Et cela, à son tour, est enraciné dans la mise en œuvre de l'arbre B + de Martin Hedenfalk. Soit dit en passant, prête à l'emploi, la bibliothèque n'a pas été conçue pour androidNative. Nous avons soigneusement collecté toutes les erreurs, et les auteurs ont rapidement fourni des correctifs - pour lesquels un merci spécial à eux!

Piège numéro 8: C Interop 



Mettre tout cela ensemble s'est avéré être une tâche très simple. En plus des sockets lmdbx et posix, nous avons intégré des bibliothèques pour générer / valider des signatures numériques sur des courbes elliptiques et calculer SHA256 en utilisant l'incroyable mécanisme C Interop . En termes simples - à partir d'une application native sur Kotlin, vous pouvez appeler la fonction de bibliothèque C, y compris avec des pointeurs vers des pointeurs et d'autres magie, tout cela semble un peu étrange.

Par exemple, appeler getaddrinfo pour obtenir sockaddr.



Comment tu trouves Ilon Mask?

Lier la bibliothèque C à l'exécutable natif de Kotlin est une quête distincte, que nous avons également réussi à traverser, mais pas sans béquilles. Nous formons dynamiquement le fichier def directement dans le script de construction pour indiquer le chemin correct vers les bibliothèques par rapport au répertoire racine du projet, puis substituons son descripteur (fichier def) dans la section cinterops. Dans le fichier def lui-même, seul le chemin absolu est déterminé, qui peut non seulement avoir un format différent si l'assemblage est effectué sous un système d'exploitation différent, mais en fait sur les machines locales des développeurs, bien sûr, peut également différer, ce qui conduit évidemment à une erreur lors du lien.

À propos de l'élection


Les principales élections que nous avons tenues dans la journée. Un peu plus de 20 experts ont participé aux tests sur notre réseau. 21 projets ont été évalués dans 5 catégories, soit plus de 100 entrées avec votes pour des projets ont été ajoutées au registre.

Conclusion


Grâce à ce petit projet de recherche, nous avons pu prouver que le maintien d'un registre distribué sur les appareils mobiles est possible! Cela ouvre de nombreuses possibilités d'utilisation de cette technologie sur les appareils IoT pour organiser l'informatique Edge-computing. Nous attendons toujours des tests de charge, une vulnérabilité aux attaques et une tolérance aux pannes. Mais nous pensons que nous réussirons!

Auteurs de l'article: architectes et développeurs du Centre R&D de MTS Dmitry Dzyuba, Alexey Vasilenko et Semen Nevrev

All Articles