Sui.

Допис

Діліться своїми знаннями.

article banner.
harry phan.
Apr 13, 2025
Стаття

Механіка контролю доступу та прийому об'єктів

Це частина 2 серії «Об'єкти батьків-дитини в Sui Move». Ви можете прочитати частину 1 тут

Передача контролю доступу об'єкта: :отримання Механіка

Отже, ви помістили об'єктXвсередині батьківськогоP(передаючи X на ідентифікатор P) - як його повернути або використовувати? 🤔 Ось тут і приходить спеціальний механізм прийому Суй.

Коли об'єкт передається батькові, він автоматично не вискакує. Він знаходиться там, належитьP. Щоб * використати* або * видалити* цей дочірній об'єкт у транзакції, ви повинніотриматийого. Sui надає структурований спосіб зробити це за допомогоюОтримання квит ката функції transfer: :reception.

##Приймальний квитк

Подумайте про отримання як заявку на дочірній об'єкт типу Т. У транзакції замість того, щоб безпосередньо передавати дочірній об'єкт (яким ви ще не володієте) функції, ви передаєте Прийняття — по суті посилання на «об'єкт з ідентифікатором Y, який належить батькові X». Цей прийом є спеціальною структурою Move, яка містить ідентифікатор, версію та дайджест об'єкта, який потрібно отримати. Він має лише здатність падати, тобто він може існувати ефемерно, але не може зберігатися наполегливо. Іншими словами, це квиток одноразового використання всередині транзакції.

Як ви отримуєте отримання? Зазвичай, виконуючи передачу. У програмованому блоці транзакцій (PTB) один крок може перенести об'єктCбатьківськомуP, що дає запит отримання, який може використовувати наступний крок. Якщо дитина вже сиділа у батькові з попередньої транзакції, ви можете вказати Приймання як вхід до нової транзакції (Sui SDK/CLI дозволяє вказати дочірній об'єкт за його ідентифікатором та батьком для створення квитка).

Важливо: Прийом не є самим об'єктом - він ще не дає тобі володіння. Це просто сигналізує про те, що «об'єкт типу Т з цим ідентифікатором належить цьому батькові, і я маю намір його взяти». Щоб насправді захопити об'єкт, необхідно викликати transfer: :receiver:


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

У take_toy, toy_ticket: Отримання представляє іграшку, яка належить до коробки. Викликаючи transfer: :receiver (&mut box.id, toy_ticket), ми викликаємо власну логіку Суї, щоб фактично витягти об'єкт Toy з коробки. Це робить кілька критичних речей:

  • Вінперевіряєпід час виконання, що toy_ticket дійсно посилається на об'єкт, який зараз належить полі (використовуючи UID батька). Якщо щось не збігається (неправильний батько або об'єкт, який насправді немає), це буде перервано.
  • Потім він повертає фактичний об'єкт Toy як власне значення в транзакції, тобто тепер іграшка знаходиться під контролем нашої функції.

Зверніть увагу, що нам довелося передати &mut box.id. Sui стверджує, що у нас єзмінне посилання на UID батьківдля отримання виклику.

Це розумний контроль доступу: лише модуль, який може створити & mut UID батька, може дозволити отримання. Як правило, модуль визначення батьківського типу відкриває таку функцію, як take_toy, яку отримують внутрішні виклики. Якщо батьківський модуль не відкриває жодної функції, яка видає UID &mut (прямо чи опосередковано), то жоден зовнішній код не зможе вилучити його дочірні дані. Таким чином, батьківський модуль контролює, як і коли можна отримати доступ до дітей.

У прикладі Box має поле id: UID. Оскільки take_toy знаходиться в тому ж модулі, він може запозичити &mut box.id. Зовнішні модулі або транзакції можуть викликати take_toy (box_obj, ticket), але самі не можуть викликати transfer: :receiver на Box, оскільки box.id є приватним.

**Цей шаблон гарантує, що лише авторизований код може отримати дітей.**✅

А як щодо вкладених об'єктів? Якби іграшка була буквально полем всередині коробки (скажімо, Box {id, toy: Toy}), нам не потрібен був би жоден прийом - іграшка була б доступна, коли у вас є &mut Box. Але це також означає, що видалити його або обробити окремо важче (це як частина коробки). З дитячими предметами ми роз'єднуємо сховище: іграшка відокремлена і її потрібно явно вийняти. Ця чіткість є причиною того, що Sui вимагає отримання квитка та отримання дзвінка - це робить пошук дитини дозволеною дією.

Змінювана довідкова вимога: Ви можете задатися питанням, чому &mut UID, а не лише &UID? Mutable гарантує, що батько буде заблоковані під час операції прийому, запобігаючи одночасним змінам та гарантуючи, що абонент насправді має право змінювати цього батька (зазвичай маючи на увазі, що він є власником). Це частина динамічних перевірок власності Sui - беручи змінну позику UID батьків, Sui гарантує, що жодна інша транзакція чи паралельна дія не можуть перешкодити під час вилучення дитини. Це трохи схоже на блокування батьківських дверей перед видаленням вмісту.

Приклад використання в блоці транзакцій: Припустимо, що об'єкт Box належить Алісі, і у неї є об'єкт Toy, який вона хоче помістити в коробку, а потім випадково Вийміть його пізніше. Ось як це могло б бути k:

-Прикріпіть іграшку до коробки: Аліса викликає transfer: :public_transfer (toy, @); в PTB. Це переносить іграшку під коробку (ми використовуємо public_transfer тут, оскільки це контекст транзакції — про це незабаром докладніше). Тепер іграшка - дитина коробки. -У тій же чи іншій транзакції, витягніть іграшку: Аліса викликає example: :toy_box: :take_toy (box_obj,), де отримання відноситься до іграшки. Під капотом виклики take_toy приймають, перевіряють зв'язок і повертає Toy. Тепер іграшка буде виведенням транзакції, зазвичай повертаючись під адресою Аліси (оскільки ми повернули її з функції введення).

Ключові висновки:

  • Дочірні об'єкти за замовчуванням недоступні; вам потрібен квиток прийому та відповідна функція, щоб вивести їх.
  • Батьківський модуль вирішує, як ви можете отримати (надаючи функцію з &mut UID).
  • transfer: :receiver використовується в модулі визначення батьків для об'єктів цього модуля або його друзів. Якщо тип дитини визначено в іншому місці, вам знадобиться інший підхід (введіть public_receiver...).

Перш ніж рухатися далі, ще одна деталь: якщо об'єкт типу T має лише здатність ключа (немає магазину), Sui розглядає його як трохи більш обмежений. Такі об'єкти неможна отримувати за допомогою узагальненого коду поза їх модулем. На практиці це означає, що якщо T є лише ключем і стає дочірнім, вам потрібно буде обробляти його пошук у власному модулі або батьківському модулі за допомогою спеціального правила. Якщо T також має магазин, ми маємо більшу гнучкість через public_receiver. Давайте дослідимо це далі.

  • Sui
2
Поділитися
Коментарі
.
Ми використовуємо файли cookie, щоб гарантувати вам найкращий досвід на нашому сайті.
Детальніше