{"author":{"address":null,"user":"https://learnblockchain.cn/people/21993"},"content":{"body":"# Siu 是如何处理资产所有权\r\n\r\n在Sui上，Object是数据储存的基本单位，通过定义，创建和管理这些代表用户资产级的可编程对象。\r\n\r\n## Object type (对象类型)\r\n\r\nSui 定义的每个对象都会用有key 和 UID。在区块链浏览器查看，可以看到五个基本参数 — owner，objectId, type, version, 最后一个交易摘要。 我们还可以通过其他字段来定义object 完善功能。\r\n\r\n## Object ownership **(对象所有权)**\r\n\r\nSui链上的，每个对象都有一个owner字段，它决定了对象在事务中的使用方式。\r\n\r\n## **Address-owned (**地址所有**)**\r\n\r\n地址所有对象限制了访问权限，只有对应地址的所有者才能对其进行操作。\r\n\r\n```rust\r\npublic fun transfer\u003cT: key\u003e(obj: T, recipient: address)\r\n\r\npublic fun public_transfer\u003cT: key + store\u003e(obj: T, recipient: address)\r\n```\r\n\r\n## **Dynamic fields  (动态字段)**\r\n\r\nSui的动态字段和对象是动态添加和删除的，动态字段允许我们根据需要调整对象的机构，按需消耗gas。\r\n\r\nFields 可以存储任务课存储的值，Object Fields 只能存具有 **key** 能力的对象\r\n\r\n## **Immutable 不可变**\r\n\r\n不能被更改，转移活着删除，这类对象没有所有者，可以被任何人使用。\r\n\r\n## **Shared  共享**\r\n\r\n`0x2::transfer::share_object`\r\n\r\n共享对象需要 **key** 能力，可被公开访问和使用。\r\n\r\n下面例子是创建一个销售甜甜圈的商店，每个人都需要访问商店菜才能购买。\r\n\r\n```rust\r\nmodule examples::donuts {\r\n    use sui::transfer;\r\n    use sui::sui::SUI;\r\n    use sui::coin::{Self, Coin};\r\n    use sui::object::{Self, UID};\r\n    use sui::balance::{Self, Balance};\r\n    use sui::tx_context::{Self, TxContext};\r\n\r\n    /// For when Coin balance is too low.\r\n    const ENotEnough: u64 = 0;\r\n\r\n    /// Capability that grants an owner the right to collect profits.\r\n    struct ShopOwnerCap has key { id: UID }\r\n\r\n    /// A purchasable Donut. For simplicity's sake we ignore implementation.\r\n    struct Donut has key { id: UID }\r\n\r\n    /// A shared object. `key` ability is required.\r\n    struct DonutShop has key {\r\n        id: UID,\r\n        price: u64,\r\n        balance: Balance\u003cSUI\u003e\r\n    }\r\n\r\n    /// Init function is often ideal place for initializing\r\n    /// a shared object as it is called only once.\r\n    fun init(ctx: \u0026mut TxContext) {\r\n        transfer::transfer(ShopOwnerCap {\r\n            id: object::new(ctx)\r\n        }, tx_context::sender(ctx));\r\n\r\n        // Share the object to make it accessible to everyone!\r\n        transfer::share_object(DonutShop {\r\n            id: object::new(ctx),\r\n            price: 1000,\r\n            balance: balance::zero()\r\n        })\r\n    }\r\n\r\n    /// Entry function available to everyone who owns a Coin.\r\n    public fun buy_donut(\r\n        shop: \u0026mut DonutShop, payment: \u0026mut Coin\u003cSUI\u003e, ctx: \u0026mut TxContext\r\n    ) {\r\n        assert!(coin::value(payment) \u003e= shop.price, ENotEnough);\r\n\r\n        // Take amount = `shop.price` from Coin\u003cSUI\u003e\r\n        let coin_balance = coin::balance_mut(payment);\r\n        let paid = balance::split(coin_balance, shop.price);\r\n\r\n        // Put the coin to the Shop's balance\r\n        balance::join(\u0026mut shop.balance, paid);\r\n\r\n        transfer::transfer(Donut {\r\n            id: object::new(ctx)\r\n        }, tx_context::sender(ctx))\r\n    }\r\n\r\n    /// Consume donut and get nothing...\r\n    public fun eat_donut(d: Donut) {\r\n        let Donut { id } = d;\r\n        object::delete(id);\r\n    }\r\n\r\n    /// Take coin from `DonutShop` and transfer it to tx sender.\r\n    /// Requires authorization with `ShopOwnerCap`.\r\n    public fun collect_profits(\r\n        _: \u0026ShopOwnerCap, shop: \u0026mut DonutShop, ctx: \u0026mut TxContext\r\n    ) {\r\n        let amount = balance::value(\u0026shop.balance);\r\n        let profits = coin::take(\u0026mut shop.balance, amount, ctx);\r\n\r\n        transfer::public_transfer(profits, tx_context::sender(ctx))\r\n    }\r\n}\r\n```\r\n\r\n## **Wrapped  包裹**\r\n\r\n在Move，可以通过将`结构`类型的字段放入另一个字段来组织数据结构。\r\n\r\n- 数据结构嵌套\r\n- Move 中的结构体嵌套\r\n- 需要 `store` 能力\r\n\r\n结构体的组合和使用十分重要。\r\n\r\n### 直接包装\r\n\r\n- 直接将 Sui 对象作为字段\r\n- 包装对象不能单独存在\r\n- 只能通过包装对象访问\r\n- 无法形成循环包装\r\n- 支持解包\r\n- 对象 ID 在包装与解包中保持不变\r\n\r\n```rust\r\nstruct Foo has key {\r\n    id: UID,\r\n    bar: Bar,\r\n}\r\n\r\nstruct Bar has store {\r\n    value: u64,\r\n}\r\n```\r\n\r\n在这两个结构题中，Bar是一个普通结构体，Fo是一个Sui对象(具有 `key` 能力)。\r\n\r\n```rust\r\nstruct Bar has key, store {\r\n    id: UID,\r\n    value: u64,\r\n}\r\n//添加了key 能力，就成为了Sui对象\r\n```\r\n\r\n如果将 `Bar` 类型的 Sui 对象放入 `Foo` 类型的 Sui 对象中，则对象类型 `Foo` 将包装对象类型 `Bar`。对象类型 `Foo` 是包装器或包装对象\r\n\r\n在 Sui 中，为了将一个结构体类型嵌入到另一个结构体类型中，被嵌入的类型必须具有 `store` 能力。而当结构体类型被嵌入到具有 `key` 能力的 Sui 对象结构体中时，它也成为一个 Sui 对象类型。\r\n\r\n## 通过 `Option` 包装\r\n\r\n- 灵活性\r\n- 可选的包装对象\r\n- 可更换的包装内容\r\n\r\n```rust\r\nstruct SimpleWarrior has key {\r\n    id: UID,\r\n    sword: Option\u003cSword\u003e,\r\n    shield: Option\u003cShield\u003e,\r\n}\r\n\r\nstruct Sword has key, store {\r\n    id: UID,\r\n    strength: u8,\r\n}\r\n\r\nstruct Shield has key, store {\r\n    id: UID,\r\n    armor: u8,\r\n}\r\n\r\npublic fun create_warrior(ctx: \u0026mut TxContext) {\r\n    let warrior = SimpleWarrior {\r\n        id: object::new(ctx),\r\n        sword: option::none(),\r\n        shield: option::none(),\r\n    };\r\n    transfer::transfer(warrior, tx_context::sender(ctx))\r\n}\r\npublic fun equip_sword(warrior: \u0026mut SimpleWarrior, sword: Sword, ctx: \u0026mut TxContext) {\r\n    if (option::is_some(\u0026warrior.sword)) {\r\n        let old_sword = option::extract(\u0026mut warrior.sword);\r\n        transfer::transfer(old_sword, tx_context::sender(ctx));\r\n    };\r\n    option::fill(\u0026mut warrior.sword, sword);\r\n}\r\n```\r\n\r\n## 通过 `vector` 包装\r\n\r\n- 同类型对象的集合包装\r\n- 类似于 `Option` 包装\r\n\r\n```rust\r\nstruct Pet has key, store {\r\n    id: UID,\r\n    cuteness: u64,\r\n}\r\n\r\nstruct Farm has key {\r\n    id: UID,\r\n    pets: vector\u003cPet\u003e,\r\n}\r\n```\r\n\r\nSui Move 中有四种能力控制特定类型的值如何使用（赋予的权限）。\r\n\r\n类型的值如何被使用，这些能力是：**copy**、**drop**、**store** 和 **key。**\r\n\r\n相当于游戏中的职业，法师，射手等，具有不同的技能，职责就不同。\r\n\r\n```rust\r\nenum MoveAbility {\r\n  COPY //允许复制值。\r\n  DROP //允许弹出/删除值。\r\n  KEY //允许将值直接保存在全局存储中。\r\n  STORE //允许将值保存在全局存储中的结构内。\r\n}\r\n```\r\n\r\n# drop\r\n\r\n允许在不再需要的某个结构题将其删除,没有drop能力是不允许被忽略的，防止被随意丢弃。这个能力也是为了防止资产泄漏或错误处理。而具有单个 `drop` 能力的结构称为 [*Witness*](https://move-book.com/programmability/witness-pattern.html)。\r\n\r\n```rust\r\nmodule book::drop_ability;\r\n\r\n/// This struct has the `drop` ability.\r\npublic struct IgnoreMe has drop {\r\n    a: u8,\r\n    b: u8,\r\n}\r\n\r\n/// This struct does not have the `drop` ability.\r\npublic struct NoDrop {}\r\n\r\n#[test]\r\n// Create an instance of the `IgnoreMe` struct and ignore it.\r\n// Even though we constructed the instance, we don't need to unpack it.\r\nfun test_ignore() {\r\n    let no_drop = NoDrop {};\r\n    let _ = IgnoreMe { a: 1, b: 2 }; // no need to unpack\r\n\r\n    // The value must be unpacked for the code to compile.\r\n    let NoDrop {} = no_drop; // OK\r\n}\r\n\r\n```\r\n\r\n# **copy**\r\n\r\n我们可以使用`copy`自定义类型来允许实例被复制。\r\n\r\n```rust\r\npublic struct Copyable has copy {}\r\n```\r\n\r\n`copy`在Move中允许类型的实例或值被复制，但Move的资源模型默认是不允许复制的。\r\n\r\n`copy`能力允许隐式和显式复制。隐式复制发生在赋值操作中，而显式复制可以通过解引用操作符实现。\r\n\r\ncopy能力通常搭配drop能力一起使用。\r\n\r\n```rust\r\n\r\nlet a = Copyable {};\r\nlet b = a;   // `a` is copied to `b`\r\nlet c = *\u0026b; // explicit copy via dereference operator\r\n\r\nlet Copyable {} = a; // doesn't have `drop`\r\nlet Copyable {} = b; // doesn't have `drop`\r\nlet Copyable {} = c; // doesn't have `drop`\r\n```\r\n\r\n在move中，所有的原生类型和标准库中定义的类型都具有 `copy` 和 `drop`能力，并且有Move编译器进行内存管理。\r\n\r\n# key\r\n\r\n允许结构体被存储，并且第一个字段为`UID`类型，确保对象的唯一性和非可丢弃性。可以直接由账户或者地址拥有。\r\n\r\n具有key能力，就不能有 `drop` 和`copy`。又因为`UID` 的存在，Move的原生类型和标准类型都没有`key` 能力，这能力仅适用于Sui框架和自定义的类型。\r\n\r\n```rust\r\npublic struct Object has key {\r\n    id: UID, // required\r\n    name: String,\r\n}\r\n\r\n/// Creates a new Object with a Unique ID\r\npublic fun new(name: String, ctx: \u0026mut TxContext): Object {\r\n    Object {\r\n        id: object::new(ctx), // creates a new UID\r\n        name,\r\n    }\r\n}\r\n\r\n```\r\n\r\n# Store\r\n\r\n`store`能力在 **Move** 中能将数据持久化存储在区块链上，它可以允许开发者在Move模块中定义存储和检索的数据结构。\r\n\r\n```rust\r\n/// This type has the `store` ability.\r\npublic struct Storable has store {}\r\n\r\n/// Config contains a `Storable` field which must have the `store` ability.\r\npublic struct Config has key, store {\r\n    id: UID,\r\n    stores: Storable,\r\n}\r\n\r\n/// MegaConfig contains a `Config` field which has the `store` ability.\r\npublic struct MegaConfig has key {\r\n    id: UID,\r\n    config: Config, // there it is!\r\n}\r\n\r\n```\r\n\r\n## **`store` 与 `key` 的关系**\r\n\r\n`store` 能力通常与 `key` 能力一起使用。`key` 能力用于访问和修改存储在区块链上的数据。在 Move 中，每个存储的数据项都对应一个唯一的 `key`。\r\n\r\n- `key` 用于指向存储在区块链上的数据项的位置。\r\n- `store` 能力通过 `key` 来读取和写入数据。\r\n\r\n因此，`store` 能力需要 `key` 来操作存储的数据，两者是紧密相关的。通过 `key`，程序可以定位到特定的数据项，并通过 `store` 能力对其进行读写操作。\r\n\r\n💧  [HOH水分子公众号](https://mp.weixin.qq.com/s/d0brr-ao6cZ5t8Z5OO1Mog?poc_token=HCUGjWej_3AyhF8KrYl9hmUdnOPak-ppJG2BCofI)\r\n\r\n🌊  [HOH水分子X账号](https://x.com/0xHOH)\r\n\r\n⭐️ [HOH水分子Github社区](https://github.com/hoh-zone)","title":"Sui Move 学习：Object \u0026 四种能力"},"history":null,"timestamp":1737295443,"version":1}