Допис
Діліться своїми знаннями.
Крос-модульне управління дітьми за допомогою public_receiver
Це частина 3 серії «Об'єкти батьків-дитини в Sui Move».
Іноді ваші батьківські та дитячі типи визначаються в різних модулах або навіть різних пакетах. Наприклад, у вас може бути загальний об'єкт Warehouse, який може зберігати будь-які об'єкти Parcel. Модуль «Склад» хоче витягнути дочірню групу «Посилка», але тип «Посилка» визначається в іншому місці. У таких випадках ми використовуємо transfer: :public_receiver, який є крос-модульним двоюрідним братом отримання.
###отримувати проти публічний_отримати
Як ми бачили, transfer: :receiver
transfer: :public_receiver — це
Навіщо потрібен магазин? Оскільки сховище позначає, що об'єкт можна безпечно зберігати та передати поза його визначальним модулем. Об'єкти, що містять лише ключі, можуть мати власні інваріанти, які оригінальний модуль хоче застосувати під час передачі та отримання; виключаючи ці з public_receiver, Sui змушує розробників обробляти їх у модулі (як ми побачимо з об'єктами, пов'язаними з душею). Якщо об'єкт має сховище, він є більш дозвільним, і Sui дозволяє загальній логіці передачі/прийому керувати ним зовні.
###Приклад: окремі батьківські та дочірні модулі
Проілюструємо простим сценарієм: Склад, який зберігає об'єкти Parcel. Тип посилки визначається у власному модулі, а Склад - в іншому. Ми покажемо, як Warehouse може отримати дочірню посилку за допомогою public_receiver.
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.
}
}
Давайте розберемо те, що відбувається в _parcel:
- Викликаємо public_receiver
(&mut warehouse.id, parcel_ticket). Оскільки Parcel має можливість магазину, цей дзвінок дозволений, навіть якщо ми не перебуваємо в модулі посилок. Під капотом це виконує ту саму перевірку та вилучення, що і отримання, але це дозволено крос-модуль, оскільки магазин вказує, що це безпечно. 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 - Потім негайно передаємо отриману посилку на адресу абонента (tx_context: :send (ctx)). Цей крок гарантує, що посилка покине склад і переходить до користувача, який ініціював виведення. Ми також могли просто повернути Parcel з функції, і Sui розглядає його як вихід, який належить адресі абонента (оскільки це вихід функції введення). Виконання явної передачі є більш детальним, але дає зрозуміти, що відбувається (і дозволяє нам робити будь-які перевірки перед випуском об'єкта).
Навіщо включати магазин у Parcel? Якщо у Parcel не вистачало можливості зберігання (тобто він мав лише ключ), виклик public_receiver
Шаблон виклику функції: Щоб використовувати їх у транзакції, потік буде таким:
1.**Депозит (переказ на об'єкт) :**Телефонуйте переказ: :public_transfer (parcel_obj, @warehouse_id) для відправки Посилки на Склад. Це позначає власника посилки як склад. (Ми використовуємо public_transfer тут, оскільки він знаходиться поза модулем Parcel, а Parcel має магазин. Всередині модуля посилки також спрацює звичайна передача.)
2.
Після дзвінка _parcel власник посилки повертається на адресу (вашу), тому це знову звичайний об'єкт, що належить адресі. Склад більше не володіє ним.
Міркування між модулями: Зверніть увагу, що модуль Склад повинен знати про тип посилки (ми використовуємо demo: :parcel: :Parcel). Це тому, що ми явно вводимо Прийом як отримання
Навіщо public_receiver замість того, щоб просто телефонувати Receive? Якщо ми спробуємо передати: :receiver
Не забудьте дозвіл батьків: Навіть з public_receiver вам все одно потрібен &mut warehouse.id. Ми отримали його, тому що _parcel знаходиться в модулі складу і приймає &mut Warehouse. Таким чином, вилучити посилку може тільки той, хто міг би на це подзвонити (власник складу). Якщо модуль складу не надав таку функцію публічно, ніхто не міг би ззовні викликати public_receiver на її дочірніх програмах. Таким чином, крос-модуль не обходить батьківський контроль; він просто дозволяє батьківському коду працювати з дочірніми типами, які він не визначив.
Примітка щодо можливості зберігання: Надання сховища об'єктів робить його більш гнучким, але трохи менш обмеженим - будь-який модуль з батьківським посиланням може витягнути його за допомогою public_receiver. Якщо ви хочетеобмежитиспосіб отримання об'єкта (наприклад, застосувати власну логіку або запобігти легкому вилученню), ви можете навмисно зробити його лише для ключів. Ми побачимо приклад цього з предметами, пов'язаними з душею. У таких випадках ви можете реалізувати спеціальну функцію отримання замість того, щоб покладатися на public_receiver.
Підсумовуючи цю частину: public_receiver є вашим другом для керування дочірніми об'єктами, визначеними в інших модулах, якщо ці об'єкти мають можливість зберігання. Це дозволяє створювати крос-модульні системи (наприклад, наш склад/посилка), дотримуючись при цьому права власності та контролю доступу. Просто не забудьте включити магазин за дочірніми типами та використовувати public_transfer, коли надсилаєте їх батькові за межами їхнього модуля.
- Sui
- Architecture