Sui.

Publication

Partagez vos connaissances.

article banner.
harry phan.
Apr 13, 2025
Article

Mécanique de contrôle d'accès et de réception des objets

Il s'agit de la deuxième partie de la série « Les objets parent-enfant dans Sui Move ». Vous pouvez lire la partie 1 ici

Contrôle d'accès aux objets transfer : :receive Mechanics

Vous avez donc placé un objetXdans unPparent (en transférant X vers l'identifiant de P). Comment le récupérer ou l'utiliser ? 🤔 C'est là qu'intervient le mécanisme de réception spécial de Sui.

Lorsqu'un objet est transféré à un parent, il n'apparaît pas automatiquement. Il se trouve là et appartient àP. Pour utiliser ou supprimer cet objet enfant dans une transaction, vous devez lerecevoir. Sui propose un moyen structuré de le faire à l'aide d'un ticket de réceptionet de la fonction transfer : :receive.

##Le ticket de réception

Considérez la réception comme un ticket de réclamation pour un objet enfant de type T. Dans une transaction, au lieu de transmettre directement l'objet enfant (que vous ne possédez pas encore) à une fonction, vous transmettez une réception, essentiellement une référence à « l'objet portant l'ID Y qui appartient au parent X ». Cette réception est une structure Move spéciale qui contient l'ID, la version et le résumé de l'objet à recevoir. Il n'a que la capacité de dépôt, ce qui signifie qu'il peut exister de manière éphémère mais ne peut pas être stocké de manière persistante. En d'autres termes, il s'agit d'un ticket à usage unique intégré à une transaction.

Comment obtenez-vous un reçu ? Généralement, en effectuant le transfert. Dans un bloc de transaction programmable (PTB), une étape peut transférer l'objetCau parentP, ce qui génère un ticket de réception que l'étape suivante peut utiliser. Si l'enfant était déjà assis chez le parent lors d'une transaction précédente, vous pouvez fournir une réception comme entrée pour une nouvelle transaction (le Sui SDK/CLI vous permet de spécifier un objet enfant par son identifiant et le parent pour générer le ticket).

**Important :**Un objet reçu n'est pas l'objet lui-même, il ne vous en donne pas encore la propriété. Cela indique simplement que « l'objet de type T avec cet identifiant appartient à ce parent et j'ai l'intention de le prendre ». Pour récupérer réellement l'objet, vous devez appeler transfer : :receive :


module example::toy_box {
    use sui::transfer::{Self, Receiving};

    struct Toy has key { id: UID, name: vector<u8> }
    struct Box has key { id: UID }

    /// Remove a Toy child from a Box, returning the Toy to the caller.
    public entry fun take_toy(box: &mut Box, toy_ticket: Receiving<Toy>): Toy {
        // Use the parent's UID and the Receiving ticket to get the Toy
        let toy = transfer::receive(&mut box.id, toy_ticket);
        // Now `toy` is an owned value we can return (or transfer to someone).
        toy
    }
}

Dans take_toy, le toy_ticket : Receiving représente un jouet qui appartient à la boîte. En appelant transfer : :receive (&mut box.id, toy_ticket), nous invoquons la logique native de Sui pour récupérer l'objet Toy hors de la boîte. Cela permet de réaliser quelques tâches essentielles :

  • Ilvérifieau moment de l'exécution que le toy_ticket fait bien référence à un objet appartenant actuellement à la boîte (en utilisant l'UID du parent). Si quelque chose ne correspond pas (mauvais parent ou objet absent), il sera abandonné.
  • Il renvoie ensuite l'objet Jouet réel en tant que valeur détenue dans la transaction, ce qui signifie que le Jouet est désormais sous le contrôle de notre fonction.

Notez que nous avons dû transmettre &mut box.id. Sui impose que nous ayons uneréférence mutable à l'UID du parentpour appeler et recevoir.

Il s'agit d'un contrôle d'accès intelligent : seul le module qui peut produire un UID &mut du parent peut autoriser la réception. En règle générale, le module de définition du type parent exposera une fonction telle que take_toy que les appels reçoivent en interne. Si le module parent n'expose aucune fonction qui donne un UID &mut (directement ou indirectement), aucun code externe ne peut récupérer ses enfants. De cette façon, le module destiné aux parents contrôle comment et quand il est possible d'accéder aux enfants.

Dans l'exemple, Box possède un champ id : UID. Comme take_toy se trouve dans le même module, il peut emprunter &mut box.id. Les modules ou transactions externes peuvent appeler take_toy (box_obj, ticket) mais ils ne peuvent pas eux-mêmes appeler transfer : :receive sur Box car box.id est privé.

**Ce modèle garantit que seul le code autorisé peut récupérer les enfants.**✅

Quid des objets imbriqués ? Si Toy était littéralement un champ à l'intérieur de Box (disons Box {id, toy : Toy}), nous n'aurions pas besoin de réception : le jouet serait accessible chaque fois que vous avez &mut Box. Mais cela signifie également qu'il est plus difficile de le retirer ou de le traiter séparément (c'est comme si cela faisait partie de la boîte). Avec les objets pour enfants, nous découplons le rangement : le jouet est séparé et doit être explicitement retiré. C'est pour cette raison que Sui exige le ticket de réception et la réception d'un appel. Cela permet de faire de la récupération de l'enfant une action autorisée.

**Exigence de référence mutable :**Vous vous demandez peut-être pourquoi &mut UID et pas seulement &UID ? Mutable garantit que le parent est verrouillé pendant l'opération de réception, empêchant toute modification simultanée et garantissant que l'appelant a réellement le droit de modifier ce parent (ce qui implique généralement qu'il en est le propriétaire). Cela fait partie des contrôles dynamiques de propriété de Sui : en empruntant de manière modifiable l'UID du parent, Sui garantit qu'aucune autre transaction ou action parallèle ne peut interférer pendant que vous retirez l'enfant. C'est un peu comme verrouiller la porte des parents avant d'en retirer le contenu.

**Exemple d'utilisation dans un bloc de transaction :**Supposons qu'Alice possède un objet Box et qu'elle souhaite mettre un objet Toy dans la boîte, puis peut-être Alors sors-le plus tard. Voici à quoi cela pourrait ressembler :

-**Attachez le jouet à la boîte :**Alice appelle transfer : :public_transfer (toy, @) ; dans un PTB. Cela permet de transférer le jouet sous la boîte (nous utilisons public_transfer ici car il s'agit d'un contexte de transaction — nous y reviendrons bientôt). Maintenant, le jouet est un enfant de la boîte. -**Lors de la même transaction ou d'une autre transaction, retrieve toy :**Alice invoque example : :toy_box : :take_toy (box_obj,) où cette réception fait référence au jouet. Sous le capot, take_toy reçoit des appels, vérifie la relation et renvoie le Toy. Maintenant, le Toy serait une sortie de la transaction, se retrouvant généralement à l'adresse d'Alice (puisque nous l'avons renvoyé à partir d'une fonction de saisie).

Principaux points à retenir :

  • Les objets pour enfants ne sont pas accessibles par défaut ; vous avez besoin d'un ticket de réception et d'une fonction appropriée pour les sortir.
  • Le module du parent décide de la manière dont vous pouvez récupérer (en fournissant une fonction avec &mut UID).
  • transfer : :receive est utilisé dans le module de définition du parent pour les objets de ce module ou de ses amis. Si le type de l'enfant est défini ailleurs, vous aurez besoin d'une approche différente (entrez public_receive...).

Avant de poursuivre, encore un détail : si un objet de type T n'a que la capacité clé (pas de magasin), Sui la considère comme un peu plus restreinte. De tels objets ne peuvent pas être reçus par code générique en dehors de leur module. En pratique, cela signifie que si T est une clé uniquement et devient un enfant, vous devrez gérer sa récupération dans son propre module ou dans le module du parent avec une règle personnalisée. Si T a également store, nous avons plus de flexibilité via public_receive. Explorons-la ensuite.

  • Sui
2
Partager
Commentaires
.
Nous utilisons des cookies pour vous assurer la meilleure expérience sur notre site Web.
Plus d'infos