Sui.

Bài viết

Chia sẻ kiến thức của bạn.

article banner.
harry phan.
Apr 24, 2025
Bài Viết

Quản lý con đa mô-đun với public_receiver

Đây là phần 3 của loạt phim “Đối tượng cha mẹ-con trong Sui Move”.

Đôi khi loại cha mẹ và con của bạn được xác định trong các mô-đun khác nhau hoặc thậm chí các gói khác nhau. Ví dụ: bạn có thể có một đối tượng Warehouse chung có thể lưu trữ bất kỳ loại đối tượng Parcel nào. Mô-đun Warehouse muốn rút ra một con Parcel, nhưng loại Parcel được định nghĩa ở nơi khác. Trong những trường hợp như vậy, chúng tôi sử dụng transfer: :public_receiver, là người anh em họ chênh lệch mô-đun của receiver.

###** nhận so với public_receive**

Như chúng ta đã thấy, transfer:: receiver chỉ có thể được gọi trong mô-đun định nghĩa T (hoặc một người bạn) vì nó không yêu cầu T: store. Trình xác minh Move bytecode thực sự đảm bảo rằng trong bất kỳ cuộc gọi nào để nhận, loại T là từ mô-đun hiện tại. Đây là một hạn chế an toàn đối với các đối tượng chỉ có khóa.

transfer: :public_receiver là một biến thể mà yêu cầu T: key + lưu trữ nhưng cho phép nhận bên ngoài mô-đun của T. Nói cách khác, nếu kiểu đối tượng có khả năng lưu trữ (nghĩa là nó được phép tồn tại trong bộ lưu trữ toàn cầu một cách tự do), thì bất kỳ mô-đun nào (được cho UID & mut của cha mẹ) đều có thể nhận nó bằng cách sử dụng public_receiver. Điều này hoàn hảo cho các trường hợp mô-đun của phụ huynh khác với mô-đun của trẻ em.

Tại sao yêu cầu cửa hàng? Bởi vì cửa hàng đánh dấu rằng đối tượng có thể được duy trì một cách an toàn và chuyển xung quanh bên ngoài mô-đun xác định của nó. Các đối tượng chỉ có khóa có thể có bất biến tùy chỉnh mà mô-đun ban đầu muốn thực thi khi chuyển nhận/nhận; bằng cách loại trừ những đối tượng đó khỏi public_receiver, Sui buộc các nhà phát triển xử lý chúng trong mô-đun (như chúng ta sẽ thấy với các đối tượng bị ràng buộc linh hồn). Nếu một đối tượng có kho, nó dễ cho phép hơn và Sui cho phép logic chuyển nhận/nhận chung để quản lý nó bên ngoài.

###Ví dụ: Các mô-đun phụ huynh và con riêng biệt

Hãy minh họa bằng một kịch bản đơn giản: Kho lưu trữ các đối tượng Parcel. Loại Bưu kiện được xác định trong mô-đun riêng của nó và Kho trong một mô-đun khác. Chúng tôi sẽ chỉ ra cách Warehouse có thể nhận được một Parcel con bằng cách sử dụng 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.
    }
}

Hãy chia nhỏ những gì đang xảy ra trong _parcel:

  • Chúng tôi gọi public_receiver (& mut warehouse.id, parcel_ticket). Bởi vì Parcel có khả năng lưu trữ, cuộc gọi này được cho phép mặc dù chúng tôi không ở trong mô-đun bưu kiện. Dưới mui xe, tính năng này thực hiện kiểm tra và trích xuất tương tự như nhận, nhưng nó được phép chênh lệch mô-đun vì cửa hàng cho biết nó an toàn để làm như vậy. 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
  • Sau đó chúng tôi ngay lập tức chuyển Bưu kiện nhận được đến địa chỉ của người gọi (tx_context: :send (ctx)). Bước này đảm bảo bưu kiện rời khỏi kho và chuyển đến người dùng đã bắt đầu rút tiền. Chúng ta cũng có thể vừa trả lại Parcel từ hàm và Sui sẽ coi nó như một đầu ra thuộc sở hữu của địa chỉ người gọi (vì đó là đầu ra của hàm nhập). Thực hiện chuyển giao rõ ràng sẽ nhiều chi tiết hơn, nhưng làm rõ điều gì đang xảy ra (và cho phép chúng tôi thực hiện bất kỳ kiểm tra nào trước khi giải phóng đối tượng).

Tại sao phải bao gồm cửa hàng trong Bưu kiện? Nếu Parcel thiếu khả năng lưu trữ (tức là chỉ có khóa), lệnh gọi public_receiver trong demo: :warehouse sẽ không biên dịch - Sui thực thi rằng T có kho lưu trữ cho public_receiver. Trong trường hợp đó, chúng tôi sẽ buộc phải lấy bưu kiện bằng cách sử dụng nhận trong chính mô-đun bưu kiện (hoặc sử dụng một số mối quan hệ bạn bè), điều này làm phức tạp thiết kế đa mô-đun. Bằng cách thêm cửa hàng vào Parcel, chúng tôi nói một cách hiệu quả “đối tượng này có thể được di chuyển và nhận tự do bởi các mô-đun bên ngoài”, đó là những gì chúng tôi muốn cho một mẫu container chung.

Mẫu gọi hàm: Để sử dụng những thứ này trong giao dịch, luồng sẽ là:

1.**Đặt cọc (chuyển sang đối tượng) :**Gọi chuyển: :public_transfer (parcel_obj, @warehouse_id) để gửi Bưu kiện vào Kho. Điều này đánh dấu chủ sở hữu của bưu kiện là nhà kho. (Chúng tôi sử dụng public_transfer ở đây vì nó nằm bên ngoài mô-đun Parcel và Parcel có cửa hàng. Bên trong mô-đun của bưu kiện, việc chuyển tiền đơn giản cũng sẽ hoạt động.) 2. **Rút tiền (nhận lại) :**Sau đó, gọi _parcel (warehouse_obj, Receiving (parcel_id,...)). Bộ nhận có thể được SDK lấy bằng cách tham khảo ID của bưu kiện và phiên bản mới nhất. Hàm sẽ gọi public_receiver và sau đó chuyển bưu kiện cho bạn.

Sau cuộc gọi _parcel, chủ sở hữu của bưu kiện quay trở lại một địa chỉ (của bạn), vì vậy nó lại là một đối tượng thuộc sở hữu địa chỉ bình thường. Nhà kho không còn sở hữu nó nữa.

Cân nhắc giữa các mô-đun: Lưu ý rằng mô-đun Warehouse cần biết về loại Bưu kiện (chúng tôi sử dụng demo: :parcel: :Parcel). Điều này là do chúng tôi gõ rõ ràng Nhận là Nhận. Nếu bạn muốn một vùng chứa thực sự chung chung có thể nhận bất kỳ loại đối tượng nào, bạn sẽ phải sử dụng generics hoặc một cách tiếp cận khác (có thể là trường động với kiểu xóa). Nhưng đối với hầu hết các trường hợp sử dụng, bạn sẽ biết những loại trẻ em mà bạn mong đợi.

Tại sao public_receiver thay vì chỉ gọi Receive? Nếu chúng ta thử transfer: :receiver (&mut warehouse.id, parcel_ticket) trong mô-đun warehouse, trình xác minh Move sẽ từ chối vì Parcel không được định nghĩa trong demo: :warehouse. Sui cung cấp public_receiver như một cách may mắn để thực hiện việc này với một kiểm tra khả năng bổ sung (yêu cầu cửa hàng). Tương tự, Sui có transfer vs public_transfer, freeze object vs public_freeze object, v.v., theo cùng một mẫu: các phiên bản public_ được sử dụng bên ngoài mô-đun xác định và yêu cầu lưu trữ.

Đừng quên sự cho phép của phụ huynh: Ngay cả với public_receiver, bạn vẫn cần &mut warehouse.id đó. Chúng tôi nhận được nó vì _parcel nằm trong mô-đun của Warehouse và chấp nhận &mut Warehouse. Do đó, chỉ ai đó có thể gọi cho người đó (chủ kho) mới có thể rút bưu kiện. Nếu mô-đun kho không cung cấp một hàm như vậy một cách công khai, thì cũng không ai có thể gọi public_receiver bên ngoài trên con của nó. Vì vậy, cross-module không bỏ qua sự kiểm soát của cha mẹ; nó chỉ cho phép mã của cha mẹ hoạt động với các kiểu con mà nó không xác định.

Lưu ý về khả năng lưu trữ: Việc cung cấp một kho đối tượng làm cho nó linh hoạt hơn nhưng ít bị hạn chế hơn một chút - bất kỳ mô-đun nào có tham chiếu gốc đều có thể kéo nó ra bằng cách sử dụng public_receiver. Nếu bạn muốnhạn hạncách truy xuất một đối tượng (ví dụ: thực thi logic tùy chỉnh hoặc ngăn chặn việc trích xuất dễ dàng), bạn có thể cố tình biến nó thành chỉ có khóa. Chúng ta sẽ thấy một ví dụ về điều đó với các đối tượng bị ràng buộc với linh hồn. Trong những trường hợp đó, bạn có thể triển khai một hàm nhận tùy chỉnh thay vì dựa vào public_receiver.

Tóm lại phần này: public_receiver là bạn của bạn để quản lý các đối tượng con được xác định trong các mô-đun khác, miễn là các đối tượng đó có khả năng lưu trữ. Nó cho phép bạn xây dựng các hệ thống đa mô-đun (như Kho khó/Bưu kiện của chúng tôi) trong khi vẫn tôn trọng quyền sở hữu và kiểm soát truy cập. Chỉ cần nhớ bao gồm store trên các loại con và sử dụng public_transfer khi gửi chúng cho cha mẹ từ bên ngoài mô-đun của họ.

  • Sui
  • Architecture
5
Chia sẻ
Bình luận
.

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

259Bài viết368Câu trả lời
Sui.X.Peera.

Kiếm phần của bạn từ 1000 Sui

Tích lũy điểm danh tiếng và nhận phần thưởng khi giúp cộng đồng Sui phát triển.

Chiến dịch phần thưởngTháng Năm
      Chúng tôi sử dụng cookie để đảm bảo bạn có trải nghiệm tốt nhất trên trang web của chúng tôi.
      Thêm thông tin