Move est né dès les premiers stades du projet Libra en 2018 - les deux fondateurs de Mysten (Evan et moi) étions également l'équipe fondatrice de Libra. Avant de décider de créer un nouveau langage, la première équipe Libra a étudié de manière approfondie les cas d'utilisation et les langages de contrats intelligents existants pour comprendre ce que les développeurs voulaient faire et où les langages existants ne fournissaient pas de résultats. Le problème clé que nous avons découvert est que les contrats intelligents concernent uniquement les actifs et le contrôle d’accès, mais les premiers langages de contrats intelligents manquaient de représentation type/valeur pour les deux. Notre hypothèse est que si nous fournissons des abstractions de première classe pour ces concepts clés, nous pouvons grandement améliorer la sécurité des contrats intelligents et la productivité des programmeurs de contrats intelligents – avoir le bon vocabulaire pour la tâche à accomplir peut faire toute la différence. Au fil des années, de nombreuses personnes ont contribué à la conception et à la mise en œuvre de Move alors que le langage évoluait d'une idée clé à un langage de contrat intelligent indépendant de la plate-forme, avec l'objectif audacieux de devenir le « JavaScript du web3 ».

Aujourd'hui, nous sommes ravis d'annoncer une étape importante dans l'intégration de Move et Sui. La fonctionnalité de Sui Move est complète, prise en charge par des outils avancés, et dispose d'une documentation et d'exemples complets, y compris les sections suivantes :

Une série de tutoriels sur la programmation à l'aide d'objets Sui Move. Un document de développement sur les connaissances de base, les modèles de conception et les exemples de Sui Move. Un plug-in d'amélioration VSCode développé par l'équipe Mysten Move, qui prend en charge l'analyse du code et le diagnostic des erreurs, et intègre. la construction, les tests et la gestion des packages de Move, la génération de documentation et le validateur Move intégrés à sui CLI.

Ce qui rend Move unique

Move est un langage embarqué multiplateforme. La syntaxe de base elle-même est très simple : elle comporte des concepts communs tels que les structures, les entiers et les adresses, mais elle n'a pas de concepts spécifiques à la blockchain tels que les comptes et les transactions), l'heure, la cryptographie, etc. Ces fonctions doivent être assurées par la plateforme blockchain intégrée à Move. Il est important de noter que ces blockchains ne nécessitent pas leurs propres forks Move - chaque plate-forme utilise la même machine virtuelle Move, le même validateur de bytecode, le même compilateur, le même validateur, le gestionnaire de packages et la CLI, mais en s'appuyant sur le code au-dessus de ces composants de base pour ajouter des fonctionnalités spécifiques à la blockchain. . Diem a été la première blockchain à intégrer Move, et les blockchains ultérieures basées sur Move (notamment 0L, StarCoin et Aptos) ont pour la plupart adopté une approche de style Diem. Bien que Move de style Diem présente de grandes qualités, la nature autorisée de Diem et certains détails de mise en œuvre de la blockchain Diem (en particulier le modèle de stockage) rendent certains cas d'utilisation de base de contrats intelligents difficiles à mettre en œuvre. En particulier, les conceptions originales de Move et Diem sont antérieures à l’explosion de la popularité des NFT et présentent quelques bizarreries qui rendent la mise en œuvre des cas d’utilisation liés aux NFT particulièrement délicate. Dans cet article, nous démontrerons les problèmes liés à l'intégration originale de Move de style Diem à travers trois exemples et décrirons comment nous avons résolu ce problème dans Sui Move. Nous supposons une certaine compréhension de base de Move, mais nous espérons que ces points clés seront compréhensibles par toute personne ayant une formation en programmation.

Expérience fluide lors de la création d'actifs à grande échelle

La capacité de créer et de distribuer des ressources en masse est essentielle à la fois pour l'intégration et l'engagement des utilisateurs Web3. Peut-être qu'un streamer Twitch souhaite distribuer des NFT commémoratifs, qu'un créateur souhaite envoyer des billets pour un événement spécial ou qu'un développeur de jeux souhaite diffuser de nouveaux objets à tous les joueurs. Voici une tentative (échouée) d'écrire du code pour la création en masse d'actifs dans Move de style Diem. Ce code prend en entrée un vecteur d'adresses de destinataires, génère un actif pour chaque destinataire et tente de transférer l'actif.

Dans un déplacement de type Diem, le stockage global est typé par paires (adresse, nom du type) - c'est-à-dire que chaque adresse peut stocker au plus un actif d'un type spécifique. Par conséquent, move_to(receiverient, CoolAsset { ...} tente de déplacer le CoolAsset en le stockant à l'adresse du destinataire. Cependant, ce code ne parvient pas à se compiler à la ligne move_to(receiverient, ...). La question clé est, dans un Move de style Diem, vous ne pouvez pas envoyer une valeur de type CoolAsset à une adresse A, sauf si une transaction est envoyée à partir d'une adresse autre que A, et que le propriétaire d'un compte créé dans A envoie une transaction et choisit explicitement de recevoir CoolAsset. type d'objet. Ce sont deux transactions, juste pour recevoir un actif ! La décision de faire cela est logique pour Diem, qui est un système autorisé qui doit soigneusement limiter la création de comptes et empêcher les comptes d'être limités par le système de stockage. Cependant, plutôt que de détenir trop d'actifs, pour un système ouvert qui souhaite utiliser l'allocation d'actifs comme mécanisme d'intégration, ou simplement permettre aux actifs de circuler librement entre les utilisateurs, comme ils le font sur Ethereum et des blockchains similaires, c'est très limité. .

Le code pour implémenter la même fonction dans Sui Move est le suivant :

Le stockage global de Sui Move est codé par ID d'objet. Chaque structure avec des paires clé-valeur est un "objet Sui", qui doit avoir un champ d'identification globalement unique. Sui Move introduit une primitive de transfert qui peut être utilisée sur n'importe quel objet Sui, au lieu d'utiliser la structure restrictive move_to. Sous le capot, cette primitive mappe l'identifiant à un CoolAsset dans le stockage global et ajoute des métadonnées pour indiquer que la valeur appartient au destinataire. Une propriété intéressante de la version Sui de mass_mint est qu'elle est échangée avec toutes les autres transactions (y compris les autres transactions qui appellent mass_mint !). Le runtime Sui le remarquera et enverra des transactions appelant cette fonction via le « chemin rapide » de diffusion du consensus byzantin qui ne nécessite pas de consensus. De telles transactions peuvent être soit validées, soit exécutées en parallèle. Cela ne nécessite aucun effort de la part du programmeur (il écrit simplement le code ci-dessus et le runtime gère le reste.) Peut-être subtilement, ce n'est pas le cas avec la variante Diem de ce code - même si le code ci-dessus fonctionne et existe. et les appels guid::create créeront des points de conflit avec d'autres transactions qui génèrent des GUID ou touchent des ressources de compte. Dans certains cas, il est possible de réécrire le code Move de style Diem pour éviter l'argument, mais la plupart des méthodes conventionnelles d'écriture de Move de style Diem introduisent de petits obstacles qui entravent l'exécution parallèle.

Propriété et transferts d’actifs locaux

Étendons le code Move de style Diem avec une solution de contournement qui se compile et s'exécute réellement. La manière habituelle de procéder est le "modèle de wrapper" : puisque Bob ne peut pas déplacer le CoolAsset directement vers l'adresse d'Alice, nous demandons à Alice de "s'inscrire" pour recevoir le CoolAsset, en publiant d'abord un type de wrapper CoolAssetStore, qui contient un type Collection. (tableau). Alice peut le faire en appelant la fonction opt_in. Nous ajoutons ensuite du code qui permet à Bob de déplacer le CoolAsset de son CoolAssetStore vers le CoolAssetStore d'Alice. Dans ce code, ajoutons une particularité supplémentaire : nous n'autoriserons les transferts de CoolAssets que s'ils ont été créés il y a au moins 30 jours. Ce type de politique est important pour les créateurs qui souhaitent (par exemple) empêcher les spéculateurs d'acheter des billets pour des événements afin qu'il soit plus facile pour les vrais fans d'obtenir ces billets à un prix raisonnable.

Ce code est valide. Mais c'est une manière assez compliquée d'accomplir la tâche de transfert d'actifs d'Alice à Bob ! Jetons un coup d'œil à une autre implémentation de Sui Move

Ce code est beaucoup plus court. L'élément clé à noter ici est que cool_transfer est une fonction d'entrée (ce qui signifie qu'elle peut être appelée directement par le runtime Sui via une transaction), mais elle a un paramètre de type CoolAsset en entrée. C'est encore la magie de Sui Runtime. Une transaction comprend un ensemble d'ID d'objet sur lesquels elle souhaite opérer, et lorsque Sui Runtime :

Analysez l'ID en une valeur d'objet (pas besoin des parties Borrow_global_mut et table_remove dans le code de style Diem ci-dessus). Vérifie si l'objet appartient à l'expéditeur de la transaction (élimine le besoin de la section signer::address_of et du code associé ci-dessus). Cette partie est particulièrement intéressante et nous l'expliquerons bientôt : Dans Sui, la vérification sécurisée de la propriété des objets fait partie du runtime. Vérifiez le type de la valeur de l'objet en fonction du type de paramètre de la fonction appelée cool_transfer. aux paramètres cool_transfer et appeler la fonction.

Cela a permis aux programmeurs de Sui Move d'ignorer le modèle de la partie « Retrait » de la logique et de passer directement à la partie amusante : vérifier la politique d'expiration de 30 jours. De même, la partie « dépôt » est également grandement simplifiée avec la structure de transfert Sui Move expliquée ci-dessus. Enfin, il n'est pas nécessaire d'introduire un type wrapper avec une collection interne comme CoolAssetStore - le magasin global Sui indexé par identifiant permet à une adresse de stocker n'importe quel nombre de valeurs d'un type donné. Une autre différence à souligner est que cool_transfer de style Diem a 5 façons d'abandonner (c'est-à-dire échoue et facture le gaz à l'utilisateur sans terminer le transfert), alors que Sui Move cool_transfer n'a qu'une seule façon d'abandonner : en cas de violation de la politique de 30 jours. Décharger les contrôles de propriété des objets vers le runtime est une grande victoire, non seulement en termes d'ergonomie, mais également en termes de sécurité. La mise en œuvre de la sécurité au niveau de l'exécution évite les erreurs lors de la mise en œuvre de ces contrôles sur la construction (ou leur oubli complet !). Enfin, notez que la signature de fonction de point d'entrée de Sui Move, cool_transfer(asset: CoolAsset, ...) nous donne beaucoup d'informations sur ce que cette fonction va faire (c'est plus opaque que les signatures de fonction de style Diem). On peut penser que cette fonction demande l'autorisation de transférer CoolAsset, tandis qu'une autre fonction f(asset: &mut CoolAsset, ...) demande l'autorisation d'écrire (mais pas de transférer) CoolAsset, et g(asset: &CoolAsset, . ..) demande simplement l'autorisation de lecture.

Parce que ces informations sont disponibles directement dans la signature de la fonction (aucune exécution ou analyse statique requise !), elles peuvent être utilisées directement par les portefeuilles et autres outils clients. Dans Sui Wallet, nous travaillons sur des demandes de signature lisibles par l'homme, en tirant parti de ces signatures de fonctions structurées pour fournir aux utilisateurs des invites d'autorisation de style iOS/Android. Le portefeuille peut dire quelque chose comme : "Cette transaction nécessite l'autorisation de lire votre CoolAsset, d'écrire dans votre AssetCollection et de transférer votre ConcertTicket. Continuer ?".

Les requêtes de signature lisibles par l'homme résolvent un vecteur d'attaque à grande échelle présent sur de nombreuses plates-formes existantes, y compris celles utilisant Move! de style Diem, où les utilisateurs de portefeuilles doivent signer aveuglément les transactions sans comprendre l'impact qu'elles peuvent avoir. Nous pensons que rendre l'expérience du portefeuille moins risquée est une étape essentielle dans la promotion de l'adoption généralisée des portefeuilles de crypto-monnaie, et avons conçu Sui Move pour soutenir cet objectif en activant des fonctionnalités telles que les demandes de signature lisibles par l'homme.

Regrouper différents actifs

Enfin, considérons un exemple de regroupement de différents types d'actifs. Il s'agit d'un cas d'utilisation assez courant : les programmeurs peuvent souhaiter regrouper différents types de NFT dans une collection, regrouper des articles pour les vendre sur une place de marché ou ajouter des pièces jointes à des articles existants. Supposons que nous ayons la situation suivante :

Alice définit un objet personnage à utiliser dans le jeu. Alice souhaite pouvoir décorer son personnage avec différents types d'accessoires tiers qui seront créés ultérieurement. N'importe qui devrait pouvoir créer un accessoire, mais le propriétaire d'un personnage doit décider s'il souhaite ajouter un accessoire. Le transfert d'un personnage devrait automatiquement transférer tous ses accessoires.

Cette fois, commençons par le code de Sui Move. Nous tirerons parti d'un autre aspect de la fonctionnalité de propriété d'objet intégrée au runtime Sui : un objet peut appartenir à un autre objet. Chaque objet a un propriétaire unique, mais un objet parent peut avoir un nombre illimité d'objets enfants. La relation objet parent/enfant est créée à l'aide de la fonction transfer_to_object, qui est l'objet associé de la fonction de transfert présentée ci-dessus.

Dans ce code, le module de rôle inclut une fonction accessoiriser qui permet au propriétaire du rôle d'ajouter un objet accessoire de n'importe quel type en tant qu'objet enfant. Cela permet à Bob et Clarissa de créer leurs propres types d'ornements avec des propriétés et des fonctionnalités différentes qu'Alice n'a pas, mais qui s'appuient sur ce qu'Alice a déjà fait. Par exemple, la chemise de Bob ne peut être équipée que si c'est la couleur préférée du personnage, et l'épée de Clarissa ne peut être utilisée que si le personnage est suffisamment puissant. Ce qui suit est une démonstration pratique du mouvement de style Diem, mais aucun d'entre eux n'a réussi, c'est-à-dire que le mouvement de style Diem ne peut pas réaliser le scénario ci-dessus.

On peut voir que les problèmes du Move de style Diem sont les suivants

Seules les collections du même type sont prises en charge (comme l'a montré le premier test), mais les accessoires ne sont pas fondamentalement un type d'objet. La relation entre les objets ne peut être obtenue que par « emballage » (c'est-à-dire en stockant un objet dans un autre objet) ; la collection d'objets pouvant être encapsulés doit être prédéfinie (comme dans la deuxième tentative) ou ajoutée de manière temporaire, et la composition d'objets supplémentaires n'est pas prise en charge (comme dans le troisième test).

Résumer

Sui est la première plate-forme à s'écarter considérablement de la conception originale de Diem dans la façon dont Move est utilisé. Concevoir une combinaison qui tire pleinement parti de Move et des capacités uniques de la plateforme est à la fois un art et une science, nécessitant une compréhension approfondie du langage Move et des capacités de la blockchain sous-jacente. Nous sommes très enthousiasmés par les progrès réalisés par Sui Move et les nouveaux cas d'utilisation qu'il permettra ! ". [1] Un autre argument en faveur d'une politique de déplacement de type Diem est que "vous devez vous inscrire avant de pouvoir recevoir un type d'actif spécifique". Il s'agit d'un bon mécanisme pour empêcher le spam. Cependant, nous pensons à la prévention du spam. comme appartenant à la couche application, plutôt que d'obliger les utilisateurs à envoyer des transactions qui coûtent de l'argent réel pour accepter de recevoir des actifs, le spam peut être facilement traité (par exemple) au niveau du portefeuille avec des politiques riches définies par l'utilisateur et des filtres anti-spam automatisés. .