Sui.

Beitrag

Teile dein Wissen.

article banner.
harry phan.
Apr 24, 2025
Artikel

Modulübergreifende Kinderverwaltung mit public_receive

Dies ist Teil 3 der Reihe „Eltern-Kind-Objekte in Sui Move“.

Manchmal sind Ihre Eltern- und Kindertypen in verschiedenen Modulen oder sogar in verschiedenen Paketen definiert. Beispielsweise könnten Sie ein generisches Warehouse-Objekt haben, das jede Art von Parcel-Objekten speichern kann. Das Warehouse-Modul möchte ein untergeordnetes Paket abrufen, aber der Flurstückstyp ist an anderer Stelle definiert. In solchen Fällen verwenden wir transfer: :public_receive, den modulübergreifenden Cousin von receive.

###receive gegen public_receive

Wie wir gesehen haben, kann transfer: :receive nur in dem Modul aufgerufen werden, das T (oder einen Freund) definiert, da T: store nicht benötigt wird. Der Move-Bytecode-Verifier stellt tatsächlich sicher, dass bei jedem zu empfangenden Anruf der Typ T vom aktuellen Modul stammt. Dies ist eine Sicherheitsbeschränkung für Objekte, die nur Schlüssel enthalten.

transfer: :public_receive ist eine Variante, die einen T: key+-Speicher erfordert, aber den Empfang außerhalb des Moduls von T ermöglicht. Mit anderen Worten, wenn der Objekttyp die Fähigkeit zum Speichern hat (was bedeutet, dass er im globalen Speicher frei existieren darf), dann kann jedes Modul (mit einer &mut-UID des übergeordneten Objekts) ihn mit public_receive empfangen. Dies ist perfekt für Fälle, in denen sich das Modul des Elternteils vom Modul des Kindes unterscheidet.

Warum brauchen Sie ein Geschäft? Weil Store bedeutet, dass das Objekt sicher gespeichert und außerhalb seines Definitionsmoduls weitergegeben werden kann. Objekte, die nur Schlüssel enthalten, können benutzerdefinierte Invarianten haben, die das Originalmodul beim Übertragen/Empfangen erzwingen möchte. Indem Sui diese aus public_receive ausschließt, zwingt Sui die Entwickler, sie im Modul zu behandeln (wie wir bei soul-gebundenen Objekten sehen werden). Wenn ein Objekt einen Speicher hat, ist es freizügiger, und Sui ermöglicht eine generische Übertragungs-/Empfangslogik, um es extern zu verwalten.

###Beispiel: Separate Eltern- und Untermodule

Lassen Sie uns das anhand eines einfachen Szenarios veranschaulichen: einem Warehouse, das Parcel-Objekte speichert. Der Flurstückstyp ist in einem eigenen Modul definiert und das Warehouse in einem anderen. Wir zeigen, wie Warehouse mithilfe von public_receive ein untergeordnetes Paket empfangen kann.

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.
    }
}

Lassen Sie uns aufschlüsseln, was in withdraw_parcel passiert:

  • Wir rufen public_receive (&mut warehouse.id, parcel_ticket) auf. Da Parcel die Fähigkeit hat, zu speichern, ist dieser Aufruf zulässig, auch wenn wir uns nicht im Paketmodul befinden. Unter der Haube führt dies die gleiche Prüfung und Extraktion durch wie Receive, aber es ist modulübergreifend zulässig, da Store angibt, dass dies sicher ist. 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
  • Wir übertragen das empfangene Paket dann sofort an die Adresse des Anrufers (tx_context: :sender (ctx)). Dieser Schritt stellt sicher, dass das Paket das Lager verlässt und an den Benutzer geht, der die Auszahlung veranlasst hat. Wir hätten auch einfach Parcel von der Funktion zurückgeben können, und Sui würde es als eine Ausgabe behandeln, die der Adresse des Anrufers gehört (da es sich um eine Eingabefunktionsausgabe handelt). Eine explizite Übertragung ist ausführlicher, macht aber deutlich, was passiert (und ermöglicht es uns, alle Überprüfungen durchzuführen, bevor wir das Objekt freigeben).

Warum sollte „Shop“ in das Paket aufgenommen werden? Wenn Parcel nicht speichern kann (d. h. es hat nur den Schlüssel), würde der public_receive -Aufruf in demo: :warehouse nicht kompiliert werden — Sui erzwingt, dass T Speicher für public_receive hat. In diesem Fall wären wir gezwungen, das Paket mithilfe von Receive im Paketmodul selbst abzurufen (oder eine Freundschaftsbeziehung zu nutzen), was das modulübergreifende Design erschwert. Indem wir dem Paket Speicher hinzufügen, sagen wir quasi: „Dieses Objekt kann frei bewegt und von externen Modulen empfangen werden“, was wir für ein generisches Containermuster brauchen.

Muster für Funktionsaufrufe: Um diese in einer Transaktion zu verwenden, würde der Ablauf wie folgt aussehen:

1.**Einzahlung (Transfer zum Objekt) :**Rufen Sie transfer: :public_transfer (parcel_obj, @warehouse_id) auf, um ein Paket in ein Warehouse zu schicken. Dies kennzeichnet den Eigentümer des Pakets als das Lagerhaus. (Wir verwenden hier public_transfer, weil es sich außerhalb des Paketmoduls befindet und Parcel einen Speicher hat. Innerhalb des Paketmoduls würde auch eine einfache Übertragung funktionieren.) 2. **Abheben (zurückerhalten) :**Rufen Sie später withdraw_parcel (warehouse_obj, Receiving (parcel_id,...)) auf. Die Empfangsbestätigung kann vom SDK abgerufen werden, indem die ID des Pakets und die neueste Version angegeben werden. Die Funktion ruft public_receive auf und überträgt dann das Paket an Sie.

Nach dem Aufruf von withdraw_parcel hat der Besitzer des Pakets wieder eine Adresse (Ihre), es handelt sich also wieder um ein normales Objekt, das der Adresse gehört. Das Lagerhaus besitzt es nicht mehr.

Modulübergreifende Überlegungen: Beachten Sie, dass das Warehouse-Modul den Pakettyp kennen musste (wir verwenden demo: :parcel: :Parcel). Das liegt daran, dass wir „Empfangen“ explizit als „Empfangen“ eingeben. Wenn Sie einen wirklich generischen Container wollten, der jede Art von Objekt aufnehmen kann, müssten Sie generische oder einen anderen Ansatz verwenden (möglicherweise dynamische Felder mit Typlöschung). In den meisten Anwendungsfällen wissen Sie jedoch, welche Arten von Kindern Sie erwarten.

Warum public_receive, anstatt nur receive aufzurufen? Wenn wir transfer: :receive (&mut warehouse.id, parcel_ticket) im Warehouse-Modul versuchen würden, würde der Move-Verifier dies ablehnen, da Parcel in demo: :warehouse nicht definiert ist. Sui bietet Public_Receive als gesegnete Möglichkeit, dies zu tun, mit einer zusätzlichen Fähigkeitsprüfung (Speicherplatz erforderlich). In ähnlicher Weise hat Sui transfer vs public_transfer, freeze_object vs public_freeze_object usw., und zwar nach demselben Muster: Die public_-Versionen sind für die Verwendung außerhalb des definierenden Moduls vorgesehen und benötigen Speicher.

Vergessen Sie nicht die Erlaubnis der Eltern: Selbst mit public_receive benötigen Sie immer noch die &mut warehouse.id. Wir haben sie bekommen, weil withdraw_parcel im Modul von Warehouse ist und &mut Warehouse akzeptiert. Somit kann nur jemand, der das anrufen kann (der Besitzer des Lagers), das Paket zurückziehen. Wenn das Warehouse-Modul eine solche Funktion nicht öffentlich zur Verfügung stellen würde, könnte auch niemand extern public_receive für seine untergeordneten Elemente aufrufen. Das Cross-Modul umgeht also nicht die Steuerung des übergeordneten Elements; es ermöglicht lediglich, dass der Code des Elternteils mit untergeordneten Typen funktioniert, die es nicht definiert hat.

Ein Hinweis zur Speicherfähigkeit: Wenn Sie einen Objektspeicher angeben, ist er flexibler, aber etwas weniger eingeschränkt — jedes Modul mit der übergeordneten Referenz kann ihn mit public_receive abrufen. Wenn Sie die Art und Weise, wie ein Objekt abgerufen wird,einschränkenmöchten (z. B. eine benutzerdefinierte Logik erzwingen oder ein einfaches Extrahieren verhindern), können Sie es bewusst nur als Schlüssel verwenden. Wir werden uns ein Beispiel dafür mit seelengebundenen Objekten ansehen. In diesen Fällen könnten Sie eine benutzerdefinierte Empfangsfunktion implementieren, anstatt sich auf public_receive zu verlassen.

Um diesen Teil zusammenzufassen: public_receive ist Ihr Freund für die Verwaltung untergeordneter Objekte, die in anderen Modulen definiert sind, solange diese Objekte die Fähigkeit zum Speichern haben. Es ermöglicht Ihnen, modulübergreifende Systeme (wie unser Lager/Paket) zu erstellen und gleichzeitig die Eigentums- und Zugriffskontrolle zu respektieren. Denken Sie nur daran, Store bei untergeordneten Typen anzugeben und public_transfer zu verwenden, wenn Sie sie von außerhalb ihres Moduls an ein übergeordnetes Objekt senden.

  • Sui
  • Architecture
5
Teilen
Kommentare
.

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

259Beiträge368Antworten
Sui.X.Peera.

Verdiene deinen Anteil an 1000 Sui

Sammle Reputationspunkte und erhalte Belohnungen für deine Hilfe beim Wachstum der Sui-Community.

BelohnungskampagneMai
      Wir verwenden Cookies, um sicherzustellen, dass Sie die beste Erfahrung auf unserer Website haben.
      Mehr Infos