Sui.

Пост

Поделитесь своими знаниями.

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

Механика контроля доступа к объектам и приема

Это вторая часть серии «Предметы для родителей и детей в режиме Sui Move». Первую часть можно прочитать здесь

Передача контроля доступа к объектам: :receive Mechanics

Итак, вы поместили объектXв родительский объектP(переведя X в идентификатор P). Как его вернуть или использовать? 🤔 Вот тут-то и пригодится специальный механизм приема Sui.

Когда объект передается родителю, он не появляется автоматически. Он находится там и принадлежитP. Чтобы использовать или удалить этот дочерний объект в транзакции, вы должныполучитьего. Sui предоставляет структурированный способ сделать это, используяПолучение билетаи функцию transfer: :receive.

##Получающий билет

Предположим, что получение — это заявка на получение дочернего объекта типа T. В транзакции вместо прямой передачи дочернего объекта (которым вы пока не владеете) функции, вы передаете приемку — по сути, ссылку на «объект с идентификатором Y, принадлежащий родителю X». Получение — это специальная структура Move, содержащая идентификатор, версию и дайджест получаемого объекта. Её можно только удалять, то есть она может существовать эфемерно, но не может постоянно храниться. Другими словами, это одноразовый билет на транзакцию.

Как получить квитанцию? Обычно, осуществляя перевод. В программируемом блоке транзакций (PTB) объектCпередается родительскому блокуP, в результате чего получается квитанция о получении, которую можно использовать на следующем этапе. Если во время предыдущей транзакции ребенок уже сидел в родительском доме, в качестве входных данных для новой транзакции можно указать приемку (в интерфейсе SDK/CLI Sui SDK/CLI можно указать дочерний объект по его идентификатору и родительскому объекту для создания заявки).

Важно: Получение не является самим объектом — оно еще не означает, что вы получаете право собственности*. Это просто означает, что «объект типа T с этим идентификатором принадлежит этому родителю, и я собираюсь его забрать». Чтобы действительно забрать объект, вы должны вызвать 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
    }
}

В take_toy символ toy_ticket: Получение представляет собой игрушку, принадлежащую коробке. Вызывая команду transfer: :receive (&mut box.id, toy_ticket), мы используем встроенную логику Суи и извлекаем предмет «Игрушка» из коробки. Это приводит к нескольким важным вещам:

  • Онпроверяетво время выполнения, действительно ли 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: :receive on Box, так как box.id является приватным.

**Этот шаблон позволяет извлекать детей только с авторизованным кодом.**✅

Как насчет вложенных объектов? Если бы игрушка была буквально полем в коробке (скажем, Box {id, toy: Toy}), нам не нужно было бы ничего получать — игрушку можно было бы получить в любое время, когда у вас есть коробка &mut Box. Но это также означает, что удалить игрушку или обработать ее по отдельности сложнее (это как часть коробки). С детскими предметами мы разделяем кладовую: игрушка стоит отдельно, и ее нужно специально вынуть. Именно по этой причине Суй требует предъявить билет на получение билета и получить звонок. Это означает, что забрать ребенка можно только по разрешенной процедуре.

Изменчивое требование к справочным данным: Возможно, вы задаетесь вопросом, почему &mut UID, а не просто &UID? Mutable гарантирует блокировку родительского элемента во время операции приема, предотвращая одновременное внесение изменений и гарантируя, что абонент действительно имеет право изменять родительский объект (обычно подразумевая, что он является владельцем). Это один из способов динамической проверки прав собственности, применяемый компанией Sui. Взяв взаймы идентификационный номер родителя, Суй гарантирует, что никакая другая транзакция или параллельное действие не смогут помешать вам выводить ребенка из семьи. Это все равно, что запереть дверь родителя перед тем, как убрать содержимое.

Пример использования в блоке транзакций: Предположим, что объект Box принадлежит Алисе и у нее есть предмет Toy, который она хочет положить в коробку, а затем, возможно, Поэтому выньте его позже. Вот как это могло бы выглядеть нормально:

-Прикрепите игрушку к коробке: Алиса звонит по телефону transfer: :public_transfer (игрушка, @); в PTB. При этом игрушка переносится под коробку (здесь мы используем public_transfer, потому что это контекст транзакции — об этом чуть позже). Теперь игрушка — это дитя коробки. -В той же или другой транзакции заберите игрушку: Алиса вызывает пример: :toy_box: :take_toy (box_obj,), где под получением подразумевается игрушка. Под капотом команда take_toy принимает звонки, проверяет связь и возвращает игрушку. Теперь результатом транзакции будет игрушка, которая, как правило, возвращается на адрес Алисы (поскольку мы вернули её из функции ввода).

Ключевые выводы:

  • Детские предметы по умолчанию недоступны; чтобы их достать, вам понадобится билет на получение и соответствующая функция.
  • Модуль для родителей решает, как их можно извлечь (предоставляя функцию с &mut UID).
  • transfer: :receive используется в определяющем родительском модуле для объектов этого модуля или его друзей. Если тип ребенка определен в другом месте, вам понадобится другой подход (введите public_receive...).

Прежде чем перейти к более подробному описанию: если объект типа T имеет только ключевое значение (нет хранилища), Суй считает, что его использование несколько ограничено. Такие объекты не не могут быть получены обычным кодом за пределами модуля. На практике это означает, что если T используется только ключ и становится дочерним модулем, вам придется обрабатывать его извлечение в отдельном модуле или родительском модуле с помощью специального правила. Если у T также есть хранилище, то с помощью public_receive у нас будет больше возможностей. Давайте рассмотрим это дальше.

  • Sui
3
Поделиться
Комментарии
.