Sui.

Publicación

Comparte tu conocimiento.

article banner.
harry phan.
Apr 13, 2025
Artículo

Mecánica de recepción y control de acceso a objetos

Esta es la segunda parte de la serie «Objetos entre padres e hijos en movimiento». Puedes leer la primera parte aquí

Mecánica de transferencia de control de acceso a objetos: :receive

Si has colocado un objetoXdentro de unPprincipal (transfiriendo X al identificador de P), ¿cómo puedes recuperarlo o usarlo? 🤔 Ahí es donde entra en juego el mecanismo de recepción especial de Sui.

Cuando un objeto se transfiere a un objeto principal, no aparece automáticamente. Está ahí, propiedad deP. Para usar o eliminar ese objeto secundario en una transacción, debesrecibirlo. Sui proporciona una forma estructurada de hacerlo mediante unticket de recepcióny la función transfer: :receive.

##El billete de recepción

Piense en Recibir como un ticket de reclamación para un objeto secundario del tipo T. En una transacción, en lugar de transferir directamente el objeto secundario (que aún no es de su propiedad) a una función, se pasa un Receiving, básicamente una referencia al «objeto con el identificador Y que pertenece al padre X». Esta recepción es una estructura de movimiento especial que contiene el identificador, la versión y el resumen del objeto que se va a recibir. Solo tiene la habilidad de soltar, lo que significa que puede existir de forma efímera pero no se puede almacenar de forma persistente. En otras palabras, es un ticket de un solo uso dentro de una transacción.

¿Cómo se obtiene un recibo? Por lo general, al realizar la transferencia. En un bloque de transacciones programable (PTB), un paso puede transferir el objetoCalPprincipal, lo que produce un ticket de recepción que puede usar el siguiente paso. Si el niño ya estaba en el lugar principal de una transacción anterior, puedes introducir un recibo como entrada para una nueva transacción (el SDK/CLI de Sui te permite especificar un objeto secundario por su identificador y su padre para generar el ticket).

Importante: Recibir no es el objeto en sí, sino que aún no te otorga la propiedad. Solo indica que «el objeto de tipo T con este identificador es propiedad de ese progenitor y tengo la intención de quedármelo». Para capturar realmente el objeto, debes llamar a 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
    }
}

En take_toy, el toy_ticket: Receiving representa un juguete que pertenece a la caja. Al llamar a transfer: :receive (&mut box.id, toy_ticket), invocamos la lógica nativa de Sui para sacar el objeto Toy de la caja. Esto hace algunas cosas fundamentales:

-verificaen tiempo de ejecución que el toy_ticket realmente hace referencia a un objeto que actualmente es propiedad de la caja (utilizando el UID del dispositivo principal). Si algo no coincide (el objeto principal es incorrecto o el objeto no está realmente ahí), se cancelará.

  • A continuación, devuelve el objeto Toy real como un valor propio en la transacción, lo que significa que ahora el Toy está bajo el control de nuestra función.

Tenga en cuenta que tuvimos que pasar &mut box.id. Sui exige que tengamos unareferencia mutable al UID del padrepara llamar a receive.

Se trata de un control de acceso inteligente: solo el módulo que puede generar un UID &mut del elemento principal puede permitir la recepción. Normalmente, el módulo de definición del tipo principal mostrará una función como take_toy que llama internamente a receive. Si el módulo principal no muestra ninguna función que proporcione un UID de &mut (directa o indirectamente), ningún código externo podrá arrebatar a sus hijos. De esta forma, el módulo principal controla cómo y cuándo se puede acceder a los niños.

En el ejemplo, Box tiene un campo id: UID. Como take_toy está en el mismo módulo, puede tomar prestado &mut box.id. Los módulos o transacciones externos pueden llamar a take_toy (box_obj, ticket), pero no pueden llamar por sí mismos a transfer: :receive en Box porque box.id es privado.

**Este patrón garantiza que solo el código autorizado pueda recuperar a los niños.**✅

¿Qué pasa con los objetos anidados? Si Toy fuera literalmente un campo dentro de Box (por ejemplo, Box {id, toy: Toy}), no necesitaríamos recibirlo: el juguete estaría accesible siempre que tuvieras &mut Box. Pero eso también significa que es más difícil quitarlo o tratarlo por separado (es como si fuera parte de la caja). En el caso de los objetos infantiles, desvinculamos el almacenamiento: el juguete está separado y debe sacarse de forma explícita. Esta es la razón por la que Sui exige la recepción del billete y la recepción de la llamada, lo que convierte la recogida de un niño en una acción autorizada.

Requisito de referencia mutable: Quizás te preguntes, ¿por qué &mut UID y no solo &UID? Mutable garantiza que el elemento principal esté bloqueado durante la operación de recepción, lo que evita la modificación simultánea y garantiza que la persona que llama tiene realmente el derecho de modificar ese elemento principal (lo que normalmente implica que es el propietario). Forma parte de las comprobaciones dinámicas de propiedad de Sui: al tomar un préstamo mutable del UID de la madre, Sui garantiza que ninguna otra transacción o acción paralela pueda interferir mientras tú extraes al hijo. Es un poco como cerrar la puerta de los padres antes de retirar el contenido.

Ejemplo de uso en un bloque de transacciones: Supongamos que un objeto Box es propiedad de Alicia, y ella tiene un objeto Toy que quiere poner dentro de la caja y, después, quizás Así que sácalo más tarde. Así es como podría verse bien:

-Fija el juguete a la caja: Alice llama a transfer: :public_transfer (toy, @); en un PTB. Esto hace que el juguete pase a la parte inferior de la caja (aquí usamos public_transfer porque se trata de un contexto de transacción; hablaremos de esto próximamente). Ahora el juguete es un derivado de la caja. -En la misma transacción o en otra, recupera el juguete: Alicia invoca example: :toy_box: :take_toy (box_obj,) donde ese recibo hace referencia al juguete. Tras el capó, take_toy llama a recibir, verifica la relación y devuelve el juguete. Ahora el Toy sería el resultado de la transacción y normalmente acabaría en la dirección de Alice (ya que lo devolvimos a través de una función de registro).

Conclusiones clave:

  • Los objetos infantiles no son accesibles de forma predeterminada; necesitas un ticket de recepción y una función adecuada para sacarlos.
  • El módulo principal decide cómo se puede recuperar (proporcionando una función con el UID &mut).
  • transfer: :receive se usa dentro del módulo de definición del módulo principal para los objetos de ese módulo o de sus amigos. Si el tipo del niño está definido en otro lugar, necesitarás un enfoque diferente (escribe public_receive...).

Antes de continuar, un detalle más: si un objeto del tipo T solo tiene la habilidad clave (sin almacenar), Sui la considera un poco más restringida. Estos objetos no pueden recibirse mediante código genérico fuera de su módulo. En la práctica, esto significa que si T solo utiliza una clave y se convierte en un elemento secundario, tendrás que gestionar su recuperación en su propio módulo o en el módulo principal con una regla personalizada. Si T también tiene almacenamiento, tenemos más flexibilidad a través de public_receive. Exploremos eso a continuación.

  • Sui
2
Cuota
Comentarios
.
Usamos cookies para asegurarnos de que obtenga la mejor experiencia en nuestro sitio web.
Más información