Sui.

Publicación

Comparte tu conocimiento.

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

Administración de niños entre módulos con public_receive

Esta es la tercera parte de la serie «Objetos entre padres e hijos en movimiento».

A veces, los tipos de padre e hijo se definen en diferentes módulos o incluso en paquetes diferentes. Por ejemplo, es posible que tengas un objeto Warehouse genérico que pueda almacenar cualquier tipo de objeto Parcel. El módulo Warehouse quiere extraer un paquete secundario, pero el tipo de paquete se define en otro lugar. En estos casos, utilizamos transfer: :public_receive, que es el primo de receive entre módulos.

###receive vs public_receive

Como hemos visto, transfer: :receive solo se puede invocar en el módulo que define T (o a un amigo) porque no requiere T: store. De hecho, el verificador de códigos de bytes Move garantiza que, en cualquier llamada que se reciba, el tipo T provenga del módulo actual. Se trata de una restricción de seguridad para objetos que solo pueden utilizarse con una llave.

transfer: :public_receive es una variante que requiere T: key + store, pero permite recibir datos fuera del módulo T. En otras palabras, si el tipo de objeto tiene la capacidad de almacenamiento (lo que significa que puede existir libremente en el almacenamiento global), cualquier módulo (con el UID &mut del elemento principal) puede recibirlo mediante public_receive. Esto es perfecto para los casos en los que el módulo principal es diferente del módulo secundario.

¿Por qué requerir una tienda? Porque almacenar marcas de que el objeto puede conservarse y distribuirse de forma segura fuera de su módulo de definición. Los objetos de solo clave pueden tener invariantes personalizados que el módulo original quiera aplicar al transferirlos o recibirlos; al excluir los de public_receive, Sui obliga a los desarrolladores a gestionarlos dentro del módulo (como veremos con los objetos enlazados al alma). Si un objeto tiene almacenamiento, es más permisivo, y Sui permite una lógica genérica de transferencia/recepción para gestionarlo externamente.

###Ejemplo: módulos separados para padres e hijo

Vamos a ilustrarlo con un escenario sencillo: un almacén que almacena objetos de paquetería. El tipo de paquete se define en su propio módulo y el almacén en otro. Mostraremos cómo Warehouse puede recibir un paquete secundario mediante public_receive.

module demo::parcel {  // Child module
    use sui::object::{Self, UID};
    use sui::tx_context::{Self, TxContext};

    /// A parcel object that can be stored in a Warehouse.
    /// It has both key and store, so it can be transferred across modules.
    struct Parcel has key, store { id: UID, contents: vector<u8> }

    public entry fun create_parcel(contents: vector<u8>, ctx: &mut TxContext): Parcel {
        Parcel { id: object::new(ctx), contents }
    }
}
module demo::warehouse {  // Parent module
    use sui::transfer::{Self, Receiving, public_receive};
    use demo::parcel::{Self, Parcel};
    use sui::object::{UID};
    use sui::tx_context::{Self, TxContext};

    struct Warehouse has key { id: UID, location: address }

    public entry fun create_warehouse(location: address, ctx: &mut TxContext): Warehouse {
        Warehouse { id: object::new(ctx), location }
    }

    /// Receive a Parcel that was sent to this Warehouse. 
    /// Returns the Parcel to the caller (transferred to caller's address).
    public entry fun withdraw_parcel(
        warehouse: &mut Warehouse, 
        parcel_ticket: Receiving<Parcel>, 
        ctx: &mut TxContext
    ): Parcel {
        // Using public_receive because Parcel is defined in another module and has store
        let parcel = public_receive<Parcel>(&mut warehouse.id, parcel_ticket) [oai_citation_attribution:27‡docs.sui.io](https://docs.sui.io/concepts/transfers/transfer-to-object#:~:text=%2F%2F%2F%20Given%20mutable%20,argument) [oai_citation_attribution:28‡github.com](https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/packages/sui-framework/sources/transfer.move#:~:text=public%20fun%20public_receive,T%3E%29%3A%20T);
        // Transfer the parcel to the transaction sender (so the caller gets ownership)
        transfer::transfer(parcel, tx_context::sender(ctx));
        // We return nothing because we've transferred the Parcel out to the caller.
    }
}

Analicemos lo que está sucediendo en draw_parcel:

  • Llamamos a public_receive (&mut warehouse.id, parcel_ticket). Como Parcel tiene la capacidad de almacenar, esta llamada está permitida aunque no estemos en el módulo de paquetería. En principio, realiza las mismas comprobaciones y extracciones que las de recepción, pero se permite usar varios módulos, ya que la tienda indica que es seguro hacerlo. https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/packages/sui-framework/sources/transfer.move#:~:text=public%20fun%20public_receive,T%3E%29%3A%20T
  • A continuación, transferimos inmediatamente el paquete recibido a la dirección de la persona que llama (tx_context: :sender (ctx)). Este paso garantiza que el paquete salga del almacén y llegue al usuario que inició la retirada. También podríamos haber devuelto Parcel desde la función y Sui lo trataría como una salida que pertenecería a la dirección de la persona que llama (ya que es una salida de una función de entrada). Hacer una transferencia explícita es más detallado, pero deja claro lo que está sucediendo (y nos permite hacer cualquier comprobación antes de liberar el objeto).

¿Por qué incluir la tienda en Parcel? Si Parcel carecía de la capacidad de almacenar (es decir, solo tenía una clave), la llamada public_receive de demo: :warehouse no se compilaba; Sui exige que T tenga store para public_receive. En ese caso, nos veríamos obligados a recuperar el paquete utilizando la función de recepción dentro del propio módulo de paquetería (o utilizar alguna relación de amistad), lo que complica el diseño de varios módulos. Al añadir store a Parcel, decimos que «este objeto se puede mover y recibir libremente en módulos externos», que es lo que queremos para un patrón de contenedor genérico.

Patrón de llamada a una función: Para usarlos en una transacción, el flujo sería:

1.**Depósito (transferencia al objeto) :**Llama a transfer: :public_transfer (parcel_obj, @warehouse_id) para enviar un paquete a un almacén. Esto marca al propietario del paquete como el almacén. (Aquí utilizamos public_transfer porque está fuera del módulo Parcel y Parcel tiene un almacén. Dentro del módulo del paquete, una simple transferencia también funcionaría). 2. **Retirar (recibir de nuevo) :**Más tarde, llama a draw_parcel (warehouse_obj, Receiving (parcel_id,...)). El SDK puede obtener la recepción haciendo referencia al identificador del paquete y a la última versión. La función llamará a public_receive y, a continuación, te transferirá el paquete.

Tras la llamada a withdrawal _parcel, el propietario del paquete vuelve a su dirección (la suya), por lo que vuelve a ser un objeto propiedad de una dirección normal. El almacén ya no es su propietario.

Consideraciones relacionadas con varios módulos: Tenga en cuenta que el módulo de almacén necesitaba conocer el tipo de paquete (utilizamos demo: :parcel: :Parcel). Esto se debe a que escribimos explícitamente la recepción como recepción. Si querías un contenedor verdaderamente genérico que pudiera recibir cualquier tipo de objeto, tendrías que usar genéricos o un enfoque diferente (posiblemente campos dinámicos con borrado de tipos). Sin embargo, en la mayoría de los casos de uso, sabrás qué tipo de niños esperas tener.

¿Por qué public_receive en lugar de simplemente llamar a receive? Si probáramos con transfer: :receive (&mut warehouse.id, parcel_ticket) en el módulo de almacén, el verificador de Move lo rechazaría porque Parcel no está definido en demo: :warehouse. Sui ofrece la opción public_receive como la mejor forma de hacerlo, con una comprobación de habilidad adicional (se requiere almacenar). Del mismo modo, Sui utiliza transfer vs public_transfer, freeze_object vs public_freeze_object, etc., siguiendo el mismo patrón: las versiones public_ son para usarse fuera del módulo de definición y requieren almacenamiento.

No olvides el permiso de los padres: Incluso con public_receive, necesitarás ese &mut warehouse.id. Lo tenemos porque draw_parcel está en el módulo de Warehouse y acepta &mut Warehouse. Por lo tanto, solo alguien que pueda llamar a esa persona (el propietario del almacén) puede retirar el paquete. Si el módulo de almacén no proporcionara esta función de forma pública, tampoco se podría invocar externamente a public_receive en sus módulos secundarios. Por lo tanto, los módulos cruzados no eluden el control del padre; solo permiten que el código del padre funcione con elementos secundarios de tipos que no ha definido.

Nota sobre la posibilidad de almacenar objetos: Ofrecer un almacén de objetos hace que sea más flexible, pero un poco menos restringido: cualquier módulo que tenga la referencia principal puede extraerla usando public_receive. Si quieresrestringirla forma en que se recupera un objeto (por ejemplo, aplicar una lógica personalizada o impedir que se extraiga fácilmente), puedes hacer que sea solo con clave de forma deliberada. Veremos un ejemplo de ello con objetos ligados al alma. En esos casos, puedes implementar una función de recepción personalizada en lugar de confiar en public_receive.

Para resumir esta parte: public_receive es tu mejor opción para administrar objetos secundarios definidos en otros módulos, siempre que esos objetos tengan la capacidad de almacenamiento. Te permite crear sistemas multimodulares (como nuestro almacén/paquetería) sin dejar de respetar la propiedad y el control de acceso. Solo recuerda incluir los tipos secundarios de almacenamiento y usar public_transfer cuando los envíes a un padre desde fuera de su módulo.

  • Sui
  • Architecture
5
Cuota
Comentarios
.

Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.

324Publicaciones469Respuestas
Sui.X.Peera.

Gana tu parte de 1000 Sui

Gana puntos de reputación y obtén recompensas por ayudar a crecer a la comunidad de Sui.

Campaña de RecompensasJunio
Usamos cookies para asegurarnos de que obtenga la mejor experiencia en nuestro sitio web.
Más información