Ensuite, nous examinerons en détail les principales caractéristiques du langage Move et quelles sont ses principales différences avec un autre langage déjà populaire pour les contrats intelligents - Solidity (sur la plate-forme Ethereum). Le matériel est basé sur une étude d'un livre blanc disponible en ligne de 26 pages.
Présentation
Move est un langage de bytecode exécutable utilisé pour exécuter des transactions utilisateur et des contrats intelligents. Faites attention à deux points:
- Alors que Move est un langage de bytecode qui peut être directement exécuté sur la machine virtuelle Move, Solidity (langage de contrat intelligent dans Ethereum) est un langage de niveau supérieur qui est d'abord compilé en bytecode avant d'être exécuté dans EVM (Ethereum Virtual Machine )
- Move peut être utilisé non seulement pour la mise en œuvre de contrats intelligents, mais également pour les transactions des utilisateurs (plus d'informations à ce sujet plus tard), tandis que Solidity est un langage uniquement pour les contrats intelligents.
Traduction réalisée par l'équipe du projet INDEX Protocol. Nous avons déjà traduit beaucoup de documents décrivant le projet Libra , maintenant c'est au tour de regarder le langage Move un peu plus en profondeur. La traduction a été faite en collaboration avec coolsiuUne caractéristique clé de Move est la possibilité de définir des types de ressources personnalisés avec une sémantique de logique linéaire: une ressource ne peut jamais être copiée ou supprimée implicitement, seulement déplacée. Fonctionnellement, cela est similaire aux capacités du langage Rust. Les valeurs de Rust ne peuvent être attribuées qu'à un seul nom à la fois. L'attribution d'une valeur à un autre nom la rend inaccessible sous le nom précédent.

Par exemple, le fragment de code suivant générera une erreur:
Utilisation de la valeur déplacée «x». En effet, il n'y a pas de collecte des ordures dans Rust. Lorsque les variables sortent du domaine, la mémoire à laquelle elles se réfèrent est également libérée. Autrement dit, il ne peut y avoir qu'un seul «propriétaire» de données. Dans cet exemple,
x est le propriétaire d'origine, puis
y devient le nouveau propriétaire.
En savoir plus sur ce comportement ici .
Représentation des actifs numériques dans les systèmes ouverts
Il existe deux propriétés des actifs physiques qui sont difficiles à représenter numériquement:
- Rareté (rareté, dans l'original - rareté). Le montant des actifs (émission) dans le système doit être contrôlé. La duplication des actifs existants doit être interdite et la création de nouveaux est une opération privilégiée.
- Contrôle d'accès . Le participant au système doit être en mesure de protéger les actifs avec des politiques de contrôle d'accès.
Ces deux caractéristiques, naturelles pour les actifs physiques, doivent être implémentées pour les objets numériques, si l'on veut les considérer comme des actifs. Par exemple, un métal rare - a un déficit naturel, et vous seul y avez accès (en le tenant par la main, par exemple) et vous pouvez le vendre ou le dépenser.
Pour illustrer comment nous sommes arrivés à ces deux propriétés, commençons par les phrases suivantes:
Proposition 1: La règle la plus simple sans rareté et contrôle d'accès

- G [K]: = n signifie mettre à jour le numéro accessible par la clé K dans l'état global de la blockchain avec la nouvelle valeur n .
- transaction «Alice, 100» signifie que le solde du compte d'Alice est de 100.
La solution ci-dessus présente plusieurs problèmes graves:
- Alice peut recevoir un nombre illimité de pièces simplement en envoyant la transaction «Alice, 100».
- Les pièces que Alice envoie à Bob sont inutiles, car Bob pourrait s'envoyer un nombre illimité de pièces en utilisant la même technique.
Proposition n ° 2: nous prenons en compte le déficit

Nous surveillons maintenant la situation afin que le nombre de pièces
Ka soit au moins
n avant la transaction de transfert. Néanmoins, bien que cela résout le problème de pénurie, il n'y a aucune information sur qui peut envoyer des pièces d'Alice (jusqu'à présent, tout le monde peut le faire, l'essentiel est de ne pas violer la règle des limites de quantité).
Proposition 3: Combiner le déficit et le contrôle d'accès

Nous résolvons ce problème avec le
mécanisme de signature numérique
verify_sig avant de vérifier le solde, ce qui signifie qu'Alice utilise sa clé privée pour signer la transaction et confirmer qu'elle possède ses pièces.
Langages de programmation Blockchain
Les langues de blockchain existantes sont confrontées aux problèmes suivants (tous ont été résolus dans Move (remarque:
malheureusement, l'auteur de l'article ne fait appel à Ethereum que dans ses comparaisons, vous ne devez donc les prendre que dans ce contexte. Par exemple, la plupart des problèmes suivants sont également résolus dans EOS )):
Représentation indirecte des actifs . Un actif est codé à l'aide d'un entier, mais une valeur entière n'est pas la même chose qu'un actif. En fait, il n'y a pas de type ou de valeur représentant bitcoin / éther / <Any Coin>! Cela rend difficile l'écriture de programmes utilisant des ressources et des erreurs. Les modèles tels que le transfert d'actifs vers / depuis des procédures ou le stockage d'actifs dans des structures nécessitent un support linguistique spécial.
Le déficit n'est pas extensible . La langue ne représente qu'un atout rare. De plus, les remèdes contre le déficit sont directement liés à la sémantique de la langue elle-même. Un développeur, s'il veut créer un actif utilisateur, doit surveiller attentivement tous les aspects de la ressource lui-même. Ce ne sont que les problèmes des contrats intelligents Ethereum.
Les utilisateurs émettent leurs actifs, des jetons standard ERC-20, à l'aide d'entiers pour déterminer à la fois le coût et le problème total. Chaque fois que de nouveaux jetons sont créés, le code de contrat intelligent doit vérifier indépendamment la conformité aux règles d'émission. De plus, la représentation indirecte des actifs entraîne, dans certains cas, de graves erreurs - duplication, double dépense ou même perte totale d'actifs.
Manque de contrôle d'accès flexible . La seule politique de contrôle d'accès actuellement utilisée est un schéma de signature utilisant la cryptographie asymétrique. Tout comme la protection contre le déficit, les politiques de contrôle d'accès sont profondément ancrées dans la sémantique du langage. Mais comment étendre le langage pour permettre aux programmeurs de définir leurs propres politiques de contrôle d'accès est souvent une tâche très simple.
Cela est également vrai pour Ethereum, où les contrats intelligents ne prennent pas en charge la cryptographie native pour le contrôle d'accès. Les développeurs doivent prescrire manuellement le contrôle d'accès, par exemple, à l'aide du modificateur onlyOwner.
Bien que je sois un grand fan d'Ethereum, je pense que les propriétés des actifs devraient être prises en charge nativement par le langage pour des raisons de sécurité. En particulier, le transfert d'Ether vers un contrat intelligent implique une répartition dynamique, ce qui a conduit à l'émergence d'une nouvelle classe d'erreurs connue sous le nom de vulnérabilités de ré-entrée. La répartition dynamique signifie ici que la logique d'exécution du code sera déterminée au moment de l'exécution (dynamique) et non au moment de la compilation (statique).
Ainsi, dans Solidity, lorsque le contrat A appelle la fonction du contrat B, le contrat B peut exécuter du code qui n'a pas été fourni par le développeur du contrat A, ce qui peut entraîner des
vulnérabilités de rentrée (le contrat A remplit accidentellement la fonction du contrat B pour retirer de l'argent avant la déduction effective). soldes des comptes).
Déplacer les bases de la conception de la langue
Ressources de premier ordre
Parlant à un niveau supérieur, l'interaction entre les modules / ressources / procédures dans le langage Move est très similaire aux relations entre les classes / objets et les méthodes dans les langages OOP.
Les modules de Move sont similaires aux contrats intelligents dans d'autres chaînes de blocs. Le module déclare les types de ressources et les procédures qui définissent les règles de création, de destruction et de mise à jour des ressources déclarées. Mais tout cela n'est que des conventions («
jargon ») dans Move. Un peu plus tard, nous illustrerons ce point.
Flexibilité
Move ajoute de la flexibilité à Libra grâce aux scripts. Chaque transaction dans Libra comprend un script, qui est en fait la procédure de transaction principale. Le script peut effectuer une action spécifiée, par exemple, des paiements en fonction de la liste de destinataires spécifiée, ou réutiliser d'autres ressources - par exemple, en appelant une procédure dans laquelle la logique générale est spécifiée. C'est pourquoi les scripts de transaction Move offrent plus de flexibilité. Le script peut utiliser à la fois des comportements ponctuels et répétitifs, tandis qu'Ethereum ne peut exécuter que des scripts répétitifs (appeler une méthode de contrat intelligent en appelant une méthode). La raison pour laquelle il est appelé «multiple» est que les fonctions d'un contrat intelligent peuvent être exécutées plusieurs fois. (Remarque:
le moment est très délicat ici. D'une part, les scripts de transaction sous forme de pseudo-bytecode sont également en Bitcoin. D'un autre côté, si je comprends bien, Move étend cette langue, en fait, au niveau d'un langage de contrat intelligent à part entière ).
La sécurité
Le format exécutable Move est un bytecode, qui, d'une part, est un langage de niveau supérieur à l'assembleur, mais de niveau inférieur au code source. Le bytecode est vérifié en chaîne pour la disponibilité des ressources, des types et de la sécurité de la mémoire à l'aide du vérificateur de bytecode, puis exécuté par l'interpréteur. Cette approche permet à Move de fournir une sécurité spécifique au code source, mais sans le processus de compilation ni la nécessité d'ajouter un compilateur au système. Faire de Move un langage de bytecode est une très bonne solution. Il n'est pas nécessaire de le compiler depuis la source, comme c'est le cas avec Solidity, pas besoin de s'inquiéter d'éventuels plantages ou attaques sur l'infrastructure du compilateur.
Vérifiabilité
Nous visons à effectuer des vérifications aussi simples que possible, car tout cela se fait en chaîne (remarque:
en ligne, dans le processus de chaque transaction, donc tout retard entraîne un ralentissement de l'ensemble du réseau ), cependant, la conception du langage est initialement prête à l'emploi et moyens hors chaîne de vérification statique. Bien que cela soit plus préférable, jusqu'à présent, le développement d'outils de vérification (en tant que boîte à outils séparée) a été reporté à l'avenir, et maintenant seule la vérification dynamique au moment de l'exécution (en chaîne) est prise en charge.
Modularité
Les modules Move fournissent une abstraction des données et localisent les opérations critiques sur les ressources. L'encapsulation fournie par le module, combinée à la protection fournie par le système de type Move, garantit que les propriétés définies pour les types de module ne peuvent pas être violées par du code à l'extérieur du module. Il s'agit d'une conception d'abstraction bien pensée, ce qui signifie que les données à l'intérieur du contrat ne peuvent être modifiées que dans le cadre du contrat, mais pas de l'extérieur.

Déplacer la révision
Un exemple de script de transaction montre que les actions malveillantes ou imprudentes d'un programmeur en dehors d'un module ne peuvent pas violer la sécurité des ressources du module. Ensuite, nous examinerons des exemples d'utilisation des modules, des ressources et des procédures pour programmer la blockchain Libra.
Paiements d'égal à égal

Le montant des pièces spécifié dans le montant sera transféré du solde de l'expéditeur au destinataire.
Il y a plusieurs nouveaux points (surlignés en rouge):
- 0x0 : adresse du compte où le module est stocké
- Devise : nom du module
- Coin : type de ressource
- La valeur de pièce retournée par la procédure est une valeur de ressource dont le type est 0x0.Currency.Coin
- move () : la valeur ne peut plus être utilisée
- copy () : la valeur peut être utilisée plus tard
Nous analysons le code: dans la première étape, l'expéditeur appelle une procédure appelée
remove_from_sender à partir du module stocké dans
0x0.Currency . À la deuxième étape, l'expéditeur transfère les fonds au destinataire, déplaçant la valeur de la ressource de pièce dans la procédure de dépôt du module
0x0.Currency .
Voici trois exemples d'erreurs de code qui seront rejetées par les contrôles:
Duplication de fonds en changeant l'appel à déplacer (pièce) à copier (pièce) . Les ressources ne peuvent être déplacées. Tenter de dupliquer la quantité d'une ressource (par exemple, en appelant
copy (coin) dans l'exemple ci-dessus) entraînera une erreur lors de la vérification du bytecode.
Réutilisation des fonds en spécifiant le mouvement (pièce) deux fois . L'ajout de la ligne
0x0.Currency.deposit (copy (some_other_payee), move (coin)) pour l'exemple ci-dessus permettra à l'expéditeur de "dépenser" les pièces deux fois - la première fois avec le bénéficiaire, et la seconde avec
some_other_payee . Il s'agit d'un comportement indésirable, impossible avec un actif physique. Heureusement, Move rejettera ce programme.
Perte de fonds due à l'échec du mouvement (pièce) . Si vous ne déplacez pas la ressource (par exemple, en supprimant la ligne contenant
move (coin) ), une erreur sera générée en vérifiant le bytecode. Cela protège les programmeurs Move de toute perte de fonds accidentelle ou malveillante.
Module de devise

Chaque compte peut contenir 0 ou plusieurs modules (représentés sous forme de rectangles) et une ou plusieurs valeurs de ressources (représentées sous forme de cylindres). Par exemple, un compte à
0x0 contient un module
0x0.Currency et une valeur de ressource de type
0x0.Currency.Coin . Le compte à
0x1 a deux ressources et un module; Le compte à
0x2 a deux modules et une valeur de ressource.
Quelques points:
- Le script de transaction est atomique - soit complètement exécuté, soit pas du tout.
- Un module est un morceau de code de longue durée disponible dans le monde entier.
- L'état global est structuré comme une table de hachage, où la clé sera l'adresse du compte
- Les comptes ne peuvent pas contenir plus d'une valeur de ressource de ce type et pas plus d'un module avec un nom donné (un compte à 0x0 ne peut pas contenir une ressource supplémentaire 0x0.Currency.Coin ou un autre module nommé Currency )
- L'adresse du module déclaré fait partie du type ( 0x0.Currency.Coin et 0x1.Currency.Coin sont des types distincts qui ne peuvent pas être utilisés de manière interchangeable)
- Les programmeurs peuvent stocker plusieurs instances de ce type de ressource dans le compte en définissant leur ressource personnalisée - ( ressource TwoCoins {c1: 0x0.Currency.Coin, c2: 0x0.Currency.Coin} )
- Vous pouvez faire référence à une ressource par son nom sans conflits, par exemple, vous pouvez faire référence à deux ressources à l'aide de TwoCoins.c1 et TwoCoins.c2 .
Annonce de ressource de pièce de monnaie

Un module nommé
Currency et un type de ressource nommé
CoinQuelques points:
- La pièce est une structure à champ unique de type u64 (entier non signé 64 bits)
- Seules les procédures du module Devise peuvent créer ou détruire des valeurs de pièces .
- D'autres modules et scripts peuvent écrire ou référencer le champ de valeur uniquement via des procédures ouvertes fournies par le module.
Mise en œuvre du dépôt

Cette procédure prend la ressource
Coin en entrée et la combine avec la ressource
Coin stockée dans le compte du destinataire:
- Détruire la ressource d'entrée Coin et enregistrer sa valeur.
- Obtenir un lien vers une ressource Coin unique stockée sur le compte du destinataire.
- Modification de la valeur du montant de la pièce par la valeur transmise dans le paramètre lors de l'appel de la procédure.
Quelques points:
- Déballer, BorrowGlobal - procédures intégrées
- Décompresser <T> est le seul moyen de supprimer une ressource de type T. La procédure prend la ressource en entrée, la détruit et renvoie la valeur associée aux champs de la ressource.
- BorrowGlobal <T> accepte l'adresse en entrée et renvoie un lien vers une instance unique de T publiée (détenue) par cette adresse
- & mut Coin est un lien vers la ressource Coin
Mettre en œuvre le retrait_de_l'expéditeur

Cette procédure:
- Obtient un lien vers une ressource Coin unique liée au compte de l'expéditeur
- Diminue la valeur de la ressource Coin par référence au montant spécifié
- Crée et renvoie une nouvelle ressource Coin avec un solde mis à jour.
Quelques points:
- Le dépôt peut être appelé par n'importe qui, mais retir_de_l'expéditeur n'a accès qu'aux pièces du compte appelant
- GetTxnSenderAddress est similaire à msg.sender dans Solidity
- RejectUnless est similaire à exiger dans Solidity. Si cette vérification échoue, la transaction est arrêtée et toutes les modifications sont annulées.
- Pack <T> est également une procédure intégrée qui crée une nouvelle ressource de type T.
- Comme Unpack <T> , Pack <T> ne peut être appelé qu'à l'intérieur du module où la ressource T est décrite
Conclusion
Nous avons examiné les principales caractéristiques du langage Move, l'avons comparé à Ethereum et nous nous sommes également familiarisés avec la syntaxe de base des scripts. En conclusion, je recommande fortement de parcourir le
livre blanc d'origine . Il comprend de nombreux détails concernant les principes de conception du langage de programmation, ainsi que de nombreux liens utiles.