Пост
Поделитесь своими знаниями.
Межмодульное управление детьми с помощью public_receive
Это третья часть серии «Объекты для родителей и детей в режиме Sui Move».
Иногда ваши родительские и дочерние типы определяются в разных модулях или даже в разных пакетах. Например, у вас может быть обычный объект Warehouse, в котором можно хранить любые объекты Parcel. Модуль Warehouse хочет удалить дочерний объект Parcel, но тип участка определен в другом месте. В таких случаях мы используем transfer: :public_receive, который является межмодульным двоюродным братом receive.
###получать и public_receive
Как мы уже видели, transfer: :receive
transfer:
Зачем нужен магазин? Потому что хранилище отмечает, что объект можно безопасно сохранить и передать за пределы определяющего модуля. Объекты, содержащие только ключи, могут иметь собственные инварианты, которые исходный модуль хочет использовать при передаче и получении. Исключая объекты из public_receive, Sui заставляет разработчиков обрабатывать их в модуле (как мы увидим в случае с объектами, привязанными к душе). Если у объекта есть хранилище, оно более разрешительно, а Sui позволяет управлять им извне с помощью общей логики передачи/получения.
###Пример: отдельные родительские и дочерние модули
Давайте проиллюстрируем простой сценарий: склад, в котором хранятся объекты Parcel. Тип участка определяется в отдельном модуле, а склад — в другом. Мы покажем, как Хранилище может получить дочернюю посылку с помощью 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.
}
}
Давайте разберемся, что происходит в withdraw_parcel:
- Мы называем public_receive
(&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: :sender (ctx)). Этот шаг гарантирует, что посылка покинет склад и отправится пользователю, инициировавшему вывод средств. Кроме того, мы могли бы просто вернуть посылку из этой функции, и Суй воспримет ее как выходные данные, принадлежащие адресу вызывающего абонента (поскольку это выходные данные функции ввода). Выполнение явной передачи более подробное задание, но оно позволяет понять, что происходит (и позволяет нам выполнить любые проверки перед выпуском объекта).
Зачем включать магазин в посылку? Если в Parcel отсутствовала возможность хранения (то есть у нее был только ключ),
Шаблон вызова функций: Чтобы использовать их в транзакции, необходимо выполнить следующую последовательность действий:
1.**Депозит (перевод на объект) :**Позвоните в transfer: :public_transfer (parcel_obj, @warehouse_id), чтобы отправить посылку на склад. При этом владелец посылки считается складом. (Здесь мы используем public_transfer, потому что оно находится за пределами модуля Parcel и в Parcel есть магазин. В модуле посылки также подойдет простой перевод.)
2.
После вызова withdraw_parcel владелец посылки возвращается на адрес (ваш), так что это снова обычный объект, принадлежащий адресу. Склад больше не владеет им.
Межмодульные аспекты: Обратите внимание, что модулю Warehouse необходимо знать тип посылки (мы используем demo: :parcel: :Parcel).
Почему public_receive вместо простого вызова receive? Если мы попробуем transfer: :receive
Не забудьте разрешение родителя: Даже если вы используете public_receive, вам все равно нужен этот &mut warehouse.id. Мы его получили, потому что withdraw_parcel находится в модуле Warehouse и принимает &mut Warehouse. Таким образом, забрать посылку может только тот, кто так позвонит (владелец склада). Если складской модуль не предоставлял такую функцию публично, никто также не мог бы вызвать public_receive извне для своих дочерних элементов. Таким образом, кросс-модуль не обходит родительский контроль; он просто позволяет родительскому коду работать с дочерними типами типов, которые он не определил.
Примечание о возможности хранения: Предоставление хранилища объектов делает его более гибким, но немного менее ограниченным — любой модуль, имеющий ссылку на родительский объект, может удалить его с помощью public_receive. Если вы хотитеограничитьспособ извлечения объекта (например, ввести собственную логику или запретить простое извлечение), вы можете намеренно сделать его доступным только по ключу. Пример этого мы рассмотрим на примере объектов, привязанных к душе. В таких случаях вы можете реализовать собственную функцию получения, а не полагаться на public_receive.
Подводя итог этому, можно сказать, что public_receive — ваш друг в управлении дочерними объектами, определенными в других модулях, при условии, что эти объекты можно хранить. Он позволяет создавать кроссмодульные системы (например, наш склад/посылок), сохраняя при этом право собственности и контроль доступа. Просто не забудьте включить хранилище дочерних типов и использовать public_transfer при отправке их родителю из-за пределов модуля.
- Sui
- Architecture