Cryptomonnaie

Défis de la mise à niveau d'une blockchain – Consensus AI

Défis de la mise à niveau d'une blockchain - Consensus AI


Notre équipe chez Consensus vient de publier la dernière version du Sentient Hub, avec un nouveau démon, un nouveau pool de minage, un nouveau mineur et un nouveau logo. Pour ceux qui ne connaissent pas Sentient (SEN), ou l'application pour laquelle notre blockchain doit être utilisée en général, il y a d'excellents articles à plonger ici, ici et ici. Au contraire, cet article se veut plus examen pédagogique de certains des défis techniques spécifiques que nous avons rencontrés.

Chaque section donne un bref aperçu du problème pour les lecteurs plus occasionnels, puis plonge plus profondément dans les moindres détails qui peuvent être intéressants pour les amateurs ou développeurs de blockchain plus enclins à la technique.

Pour comprendre l'importance de la mise à jour SEN actuelle d'un point de vue technique, il faut d'abord comprendre l'origine de SEN, et à quel ensemble particulier de problèmes il a dû faire face. SEN a été développé à partir d'une fourchette de Sia, une blockchain populaire conçue pour une plate-forme de stockage décentralisée écrite en Go.

L'utilisation d'une blockchain existante a l'avantage de ne pas avoir à réinventer la roue, mais cela ne va pas sans son propre ensemble de problèmes.

Celles-ci vont de la conception de haut niveau aux complexités d'implémentation de niveau inférieur. En particulier, nous explorerons ajout de la prise en charge du contrôle de version aux blocs, réparer un pool minier cassé, et l'adaptation d'un mineur de GPU fortement optimisé pour accompagner les changements.

Dans la plupart des réseaux de blockchain, il y a l'idée que si les membres de la communauté acceptent de faire des changements, et qu'il y a un seuil de soutien particulier pour un changement donné, alors le réseau peut améliorer ses fonctionnalités de manière démocratique.

Ce type de changement nécessite une conception minutieuse du protocole, en particulier, les nœuds doivent tous savoir quel code ils exécutent, c'est-à-dire la version. Bitcoin, par exemple, spécifie la version du bloc dans son en-tête de bloc. Malheureusement, la base de code héritée de Sia, et donc aussi la version initiale de Sentient n'avait pas de champ de version, et fourni aucun moyen clair pour la communauté d'ajouter des modifications au réseau.

Le problème du poulet ou des œufs (Catch-22):

Nous avons besoin d'un champ de version pour ajouter des modifications au réseau, mais nous devons ajouter des modifications au réseau pour ajouter un champ de version.

Ce problème n'était pas trivial à résoudre et nécessitait une planification et des tests méticuleux pour s'assurer que nous ne corrompions pas la chaîne entière en le faisant. Inutile de dire que c'est ce qu'on appelle un «hard fork», où une fois le changement effectué, il n'y a pas de compatibilité avec les nœuds exécutant l'ancien code. Outre l'ajout d'un champ de version, nous avons également ajouté la prise en charge de la version les propositions sur la blockchain, que les mineurs peuvent accepter ou rejeter, pour soutenir les changements démocratiques du réseau.

Nous pouvons voir que les blocs et les en-têtes de blocs d'origine avaient la structure suivante:

Alors qu'une fois la mise à niveau de la version effectuée, elle aura la structure suivante:

Notez les nouveaux champs Version et Supports, ce dernier permettant aux mineurs d'exprimer leur soutien aux futures propositions de version, qui seront effectuées via des champs supplémentaires dans la structure de transaction.

Ces ajouts rendront bien sûr les en-têtes de bloc légèrement plus grands ( 80 bits à 100 bits de taille) qui a également nécessité une mise à niveau du code d'exploration.

L'un des aspects les plus compliqués de la mise à niveau de la blockchain est de savoir comment garantir que tous les nœuds sont mis à niveau en même temps (sur le même bloc), en synchronisation. Cela se fait avec des règles liées aux propositions de version. Lorsqu'une nouvelle version est prête à être examinée, nous afficherons le code correspondant dans notre github référentiel et créer une transaction de proposition de version sur la blockchain.

Les mineurs qui prennent en charge la mise à niveau vers cette nouvelle version de code peuvent exprimer leur soutien en utilisant le champ Supports lorsqu'ils soumettent des blocs extraits au réseau, ce qu'ils peuvent faire plus facilement en installant simplement le nouveau code. Une proposition de version comporte plusieurs champs qui contrôlent la durée pendant laquelle elle est active (une échéance et un délai d'expiration) et le pourcentage de blocs par rapport aux blocs les plus récents (le seuil d'activation et la fenêtre respectivement) doivent avoir exprimé leur soutien.

Lorsqu'une transaction avec une proposition de version est extraite dans un bloc, tous les nœuds du réseau gardent une trace de ces paramètres et, si les conditions sont remplies, la mise à niveau sera synchronisée. Cela nous permettra d'utiliser plus de fourches souples, par opposition aux fourches dures, lorsque nous voulons ajouter de nouvelles fonctionnalités à SEN à l'avenir.

L'ajout de cette fonctionnalité n'était pas sans complexité. Comme exemple de l'un des nombreux défis que nous avons surmontés, considérons le point souligné ci-dessus sur la façon dont la structure et la taille de l'en-tête du bloc ont dû changer. Pour que les nœuds exécutant notre code mis à niveau s'exécutent sur la blockchain en ce moment, lorsque la majorité des nœuds existants exécutent des versions antérieures et que tous les blocs produits sont de la structure / taille d'origine, notre nouveau code doit être capable d'analyser et de valider des blocs de l'une ou l'autre sorte, et de les distinguer. De cette façon, les utilisateurs peuvent passer au nouveau code sans abandonner la blockchain existante.

Cependant, comme il n'y a actuellement aucun champ dans la structure de bloc d'origine par lequel les nœuds existants peuvent indiquer qu'ils ont mis à niveau vers le nouveau code, nous avons imaginé une solution où cela peut être encodé dans le champ Données arbitraires des transactions sans interférer avec aucune des autres informations du bloc. Avec cette astuce, nous pouvons utiliser une proposition de version même maintenant pour aider à la mise à niveau vers la nouvelle structure de blocs versionnée, nous permettant de savoir combien de mineurs ont installé le nouveau code et quand il y a suffisamment de puissance de hachage pour prendre en charge la nouvelle structure de blocs.

Le pool d'exploration minière actuel provient d'un autre référentiel, qui prend en charge l'exploitation minière sur un protocole de type strate. Lorsque nous avons introduit le pool pour la première fois, nous avons décidé de ne pas trop nous éloigner de l'implémentation initiale tout en observant comment il a été adopté dans notre réseau. Cela signifiait que peu d'efforts avaient été déployés pour l'adapter au SEN, ni que la conception avait été mise à l'échelle. Avec les autres mises à niveau de SEN qui se produisent simultanément, le moment est venu de le faire. Compte tenu de l'histoire de la base de code du pool de minage et de son manque d'évolutivité, il était clair depuis un certain temps qu'il aurait besoin d'une réécriture complète.

Par exemple, de nombreux utilisateurs nous ont signalé que le pool de minage se bloquait fréquemment, ou se connectait et se déconnectait plusieurs fois par jour à certains moments. Il y avait de nombreux problèmes mineurs, mais fondamentalement, il y avait une faille dans la conception de la base de données entière, ce qui a conduit à des données inutiles étant stockées et des requêtes inutilement complexes exécutant des fonctions apparemment basiques.

À un niveau élevé, le pool de minage permet à des groupes d'utilisateurs de combiner plus efficacement leur puissance de hachage pour extraire des blocs et de partager les bénéfices en fonction de la proportion de puissance de hachage. De cette façon, même les utilisateurs avec des machines plus lentes peuvent obtenir des récompenses cohérentes, sans compter sur la chance pour extraire fortuitement un bloc entier, qui devient de moins en moins probable à mesure que le réseau se développe. Au lieu de cela, une fois qu'ils atteignent un certain objectif, ils le soumettent au pool de minage, et même s'il n'est pas suffisant pour exploiter un bloc entier, le partager établit toujours qu'ils ont miné pour la piscine et obtiennent donc une petite récompense.

Lorsque quelqu'un dans le pool atteint l'objectif de trouver un bloc, la récompense de bloc est distribuée à tous les membres en fonction du nombre de parts qu'ils ont gagnées. Dans la conception initiale, le pool enregistrerait pour toujours chaque partage soumis par un utilisateur dans la base de données. Un mineur moyen, sans équipement spécial, peut généralement gagner une part toutes les quelques secondes, et sur une courte période de temps, ce tableau est rapidement passé à des dizaines de millions de lignes, ce qui entraîne un ralentissement des arriérés de requêtes et même des blocages bloquant le pool entier sur certains répétitifs. requêtes. Au départ, nous avons fait la première chose que nous avons apprise dans nos classes de base de données des écoles élémentaires pour résoudre ce problème: ajouter un index.

Cela a temporairement accéléré les requêtes problématiques d'environ 1000 fois, mais n'a pas résolu le problème principal.

Nous avons ensuite commencé à créer des sauvegardes et à supprimer les entrées de table obsolètes, mais ce n'était également qu'une solution de pansement. Même si les journaux d'erreurs et les rapports d'erreur étaient cryptiques, ils pouvaient toujours être retracés dans la table des partages. Mais où exactement?

L'une des premières choses que la plupart des gens remarquent dans le Sentient Hub est les jolis graphiques montrant leurs hashrates et leurs actions gagnées au fil du temps. Il y a quelque chose de très amusant à cliquer sur les différents intervalles de temps et à regarder le Javascript dessiner l'image de manière dynamique. Sous le capot, c'était tout sauf joli. Notez que le hashrate dépend de la difficulté et de la fréquence des actions, et nous avons plusieurs millions d'actions qui doivent être constamment interrogées (tout en étant écrites simultanément par des mineurs actifs).

Étude de cas: Pour sélectionner les historiques pour dessiner le graphique pour le hashrate d'un travailleur particulier, la requête d'origine était:

Cette monstruosité a été appelée chaque fois qu'un utilisateur voulait voir un graphique, et il y avait près d'une douzaine de requêtes différentes d'une forme similaire pour divers appels d'API. Réfléchissons à cela: chaque fois qu'un utilisateur clique sur un graphique différent, la base de données effectue une sommation sur une double jointure imbriquée dans une requête de sélection dans une table contenant des dizaines de millions de lignes.

Un utilisateur individuel, parcourant les différents graphiques, peut presque à lui seul effectuer le DoS du pool de minage.

En utilisant ce petit aperçu, nous avons réalisé que la table des actions devait disparaître. Toutes les informations relatives aux graphiques entreraient dans leur propre tableau séparé, calculées une fois et supprimées une fois qu'elles sont devenues obsolètes, plafonnant ainsi le nombre d'entrées à 800 par travailleur (4 intervalles de temps possibles, jusqu'à 200 points de graphique les plus récents) en supposant que leur mineur est en continu. La requête finale pour le même appel d'API devient:

Étant donné que les actions ne sont pertinentes que pour les paiements et la génération des graphiques, nous augmentons désormais le solde d'un compte directement lorsqu'une action est soumise. En ce qui concerne les détails pertinents pour le graphique, nous ne conservons que les statistiques de partage jusqu'à ce qu'elles puissent être consolidées en une seule entrée de graphique pendant un intervalle de temps donné, et nous libérons efficacement notre base de données de l'indexation de dizaines de millions de lignes, et accélérez les requêtes plus de 180 000 fois!

Comme mentionné dans la première section, l'ajout d'un champ Version et supports aux en-têtes de bloc n'est pas un changement à prendre à la légère. Pour le mineur, en surface, cela semble arbitraire, étant donné que le mineur est censé moudre sur un en-tête de bloc incomplet encore et encore jusqu'à ce qu'il atteigne une certaine cible, il ne devrait pas vraiment se soucier des détails des données à l'intérieur. Malheureusement, un changement dans la structure de l'en-tête du bloc signifie que les mineurs doivent moudre sur différentes données, ce qui nécessite une modification du noyau GPU optimiséet le protocole d'envoi et de réception de ces en-têtes. Le marshaling et le dé-marshalling étaient également délicats ici, en partie à cause de la façon dont le protocole de strate a été implémenté dans la solution Sia qui était à l'origine adaptée à SEN. Cela se rapporte à l'ordre des champs struct et à la petite / grande endianité des entiers et des tableaux d'octets transmis.

Le mineur est écrit principalement en Go, communiquant avec le pool de minage ou créant des blocs pour le travail lui-même pour un minage autonome avec du code reflétant ce qui peut être trouvé dans les référentiels sentient-network ou sen-node, mais le minage proprement dit se fait à l'aide de liaisons OpenCL et un noyau de hachage blake-2b fortement optimisé et légèrement modifié écrit en C. Nous avons eu du mal à essayer de comprendre pourquoi le même code exact donnait le hachage correct pour les en-têtes de bloc d'origine, et renvoyait systématiquement des solutions invalides pour les nouveaux en-têtes de bloc.

Rappelons que les anciens en-têtes étaient de 80 bits et les nouveaux en-têtes de 100 bits. Considérez le fait que nos tableaux d'octets dans Go sont big-endian, et le GPU utilisant OpenCL est little-endian. Lors de l'écriture d'un tableau 80 bits, nous écrivons 10 octets complets, en gardant à l'esprit que chaque octet a des bits en endianisme inverse. Lors de l'écriture de 100 bits, nous avons 12 octets complets et 4 bits supplémentaires à la fin qui ne sont pas aussi facilement copiés que précédemment:

Au lieu de cela, en raison des limitations de l'implémentation OpenCL de C liées à l'alignement des octets, nous avons dû utiliser de la vraie magie C pour les placer au bon endroit:

En théorie, cela aurait dû tout réparer. Le hachage serait enfin correct puisque chaque bit était à sa place. Nous ne sortirions pas si facilement cependant, et il y avait une autre mine cachée - jeu de mots prévu.

Méticuleusement, ligne par ligne, nous sommes passés par le pseudocode blake-2b, essayant de retracer comment diable le même code fonctionnait pour les anciens blocs non versionnés mais pas pour les nouveaux blocs versionnés.

Nous avons examiné tous les ET, OU et XOR pour chaque rotation de matrice, déconcerté par le fonctionnement d'un en-tête non versionné, et un en-tête versionné ne fonctionne pas.

Il s'est avéré que nous avons ignoré l'étape qui détermine la longueur d'une entrée. Pendant la phase d'initialisation de la fonction de hachage, nous mélangeons la taille de la clé et la longueur de hachage souhaitée. Cependant, étant donné que nous avons affaire à du code fortement optimisé qui calculera toujours un hachage avec la même clé de longueur, la longueur est incrustée dans les constantes de la matrice blake-2b. Cela signifiait ceci:

Doit être changé en:

Repérer la différence est un exercice laissé au lecteur.

Le travail effectué cette année a été fastidieux, mais nécessaire à la fondation d'une blockchain forte. Nous avons maintenant un pool minier fonctionnel qui peut soutenir un nombre arbitraire de mineurs pour croître et renforcer le réseau sensible. De plus, nous avons la capacité de permettre aux utilisateurs de voter sur des propositions de modifications du réseau de manière démocratique en fonction de la puissance de hachage.

Cela ouvre la voie au déploiement de capacités plus intéressantes, à court terme étant la sortie de identité numérique et vote anonyme / vérifiable utilisant une agrégation de données basée sur des logarithmes discrets, développée mais toujours en cours de test. À long terme, nous planifions la structure économique d'un marché de l'apprentissage machine décentralisé.

Cet article n'est en aucun cas une analyse exhaustive de notre recherche et développement, mais peut-être que les lecteurs de tous les niveaux de compétences techniques pourraient apprendre quelque chose sur la technologie de la blockchain et certains des défis particuliers impliqués.

Si vous cherchez à acheter SEN, vous pouvez le trouver coté sur l'échange Coinbene, ou commencer à exploiter aujourd'hui avec notre Sentient Hub nouvellement amélioré

Afficher plus

SupportIvy

SupportIvy.com : Un lieu pour partager le savoir et mieux comprendre le monde. Meilleure plate-forme de support gratuit pour vous, Documentation &Tutoriels par les experts.

Articles similaires

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Bouton retour en haut de la page
Fermer