{"content":{"title":"Move中的设计模式(1)——Capability","body":"> 一本关于 Move 编程语言设计模式的书。翻译并补充了[英文版](https://github.com/villesundell/move-patterns)\r\n原文链接：https://blog.chrisyy.top/move-patterns/\r\n仓库链接：https://github.com/chrisyy2003/move-patterns\r\n\r\n# 介绍\r\n\r\nMove是一种安全、沙盒式和形式化验证的下一代编程语言，它的第一个用例是[Diem](https://en.wikipedia.org/wiki/Diem_(digital_currency))区块链（当时名字叫Libra, 脸书团队开发的项目, 译者注）,Move为其实现提供了基础。 Move 允许开发人员编写灵活管理和转移数字资产的程序，同时提供安全保护，防止对那些链上资产的攻击。不仅如此，Move 也可用于区块链世界之外的开发场景。\r\n\r\nMove 的诞生从[Rust](https://www.rust-lang.org/)中吸取了灵感，Move也是因为使用具有移动（move）语义的资源类型作为数字资产（例如货币）的显式表示而得名。\r\n\r\n今天，Move 及其虚拟机正在为 [多个区块链](https://github.com/MystenLabs/awesome-move#move-powered-blockchains) 提供动力，其中大部分仍处于早期开发阶段。\r\n\r\n## 什么是Move Patterns\r\n\r\n本书旨在讨论面向资源的语言的软件设计模式和最佳实践，尤其是Move及其风格。这部分涵盖了 Move 中广泛使用的编程模式，为什么会存在Move设计模式，主要有以下三个方面。\r\n\r\n- 面向资源编程 \r\nMove是一种新的编程语言，其特点是面向资源编程，对于区块链最核心的 Token 资产进行了更为贴合的处理，实现了真正意义上的数字资产化。Move与传统面向对象的编程语言有着很大不同，所以一些面向对象的设计模式在Move中是不存在的（例如Uniswap中的工厂模式）。当然，这些模式中的一些很可能也可以在其他一些基于资源的语言和生态系统中实现。\r\n\r\n- 状态存储机制 \r\n在Solidity中，能够定义并保存自己的状态变量，变量的值放在全局储存上，在合约中可以直接通过全局变量直接读取或者修改它。\r\n\r\n  ```solidity\r\n  // A solidity examply\r\n  // set msg.sender to owner\r\n  contract A {\r\n      // 定义一个状态变量\r\n      address owner;\r\n      function setOwner() public {\r\n  \t// 通过变量名直接修改\r\n          owner = msg.sender;\r\n      }\r\n  }\r\n  ```\r\n\r\n  但是在Move中存储方式是完全不一样的，Move合约并不直接存储资源，代码中的每一个变量都是一个资源对象，是资源对象那么必须通过显示的接口去明确的调用。\r\n\r\n  Move中对资源的访问都是通过[全局存储操作](http://movebook.chrisyy.top/global-storage-operators.html)接口来访问的。操作函数包括`move_to`或者`move from`或者`borrow_global`，`borrow_global_mut`等函数。从全局储存里面取出资源，或者存放到账户下去，引用资源对象或者修改，都是需要**要开发者显示的去表示。**\r\n\r\n  ```solidity\r\n  module example::m {\r\n      // A Coin type\r\n      // 一种Coin类型的资源\r\n      struct Coin has key, store{\r\n          value: u64\r\n      }\r\n      // send sender a coin value of 100\r\n      // 在sender地址下存放100个coin\r\n      public entry fun mint(sender: &signer) {\r\n          move_to(sender, Coin {\r\n              value: 100\r\n          });\r\n      }\r\n  }\r\n  ```\r\n\r\n- Ability\r\n   Ability是 Move 语言中的一种类型特性，用于控制对给定类型的值允许哪些操作。\r\n\r\n  - [copy](<http://movebook.chrisyy.top/abilities.html#copy>)复制：允许此类型的值被复制\r\n  - [drop](<http://movebook.chrisyy.top/abilities.html#drop>) 丢弃：允许此类型的值被弹出/丢弃，没有话表示必须在函数结束之前将这个值销毁或者转移出去。\r\n  - [store](<http://movebook.chrisyy.top/abilities.html#store>) 存储：允许此类型的值存在于全局存储中或者某个结构体中\r\n  - [key](<http://movebook.chrisyy.top/abilities.html#key>) 键值：允许此类型作为全局存储中的键(具有 `key` 能力的类型才能保存到全局存储中)\r\n\r\n作为面向资源的编程语言，以上三个特点也是与其他语言非常不同的地方，基于资源编程的Move编程模式，也主要是围绕这些特性产生的。\r\n\r\n此外本书不是 Move 或任何其他面向资源的语言的指南。 有关 Move 本身的书籍，请参阅 [awesome-move#books](https://github.com/MystenLabs/awesome-move#books)。 另请参阅 [awesome-move](https://github.com/MystenLabs/awesome-move) 以获取来自 Move 编程语言社区的代码和内容的精选列表。\r\n\r\n# Capability\r\n\r\n|||\r\n|-|-|\r\n| **Name** | Capability |\r\n| **Origin** | [Libra Project](https://github.com/move-language/move/blob/5e034dde19a5320d7e2bdc9da25114e816b4454d/language/stdlib/modules/libra_coin.mvir#L8) / Unknown Author |\r\n| **Example** | [Sui Move by Example](https://examples.sui.io/patterns/capability.html) / [Damir Shamanaev](https://github.com/damirka) |\r\n| **Depends on** | None |\r\n| **Known to work on** | Move |\r\n\r\n## 概述\r\n\r\nCapability是一个能够证明**资源所有者**特定权限的资源（注意：它是一个资源也就是一个Move中的结构体），其作用主要是用来进行访问控制。\r\n\r\n例如当我们想限制某个资源的铸造权，管理权，函数调用权时，便可以采用Capability这种设计模式。这也是Move智能合约里面使用最广泛的一个设计模式，例如sui-framework中的[TreasuryCap](https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/sources/coin.move#L32-L35)。这是也是已知最古老的 Move 设计模式，可追溯到 Libra 项目及其代币智能合约，其中功能用于授权铸币。\r\n\r\n## 如何使用\r\n\r\nCapability本质是一个资源对象，只是被可信任的用户持有。通常在合约中我们可以定义一个`AdminCap`来代表本模块的控制权限，如果某个用户持有就可以用户可信，其中资源对象内不需要任何的字段。\r\n\r\n```move\r\nstruct AdminCap has key, store {}\r\n```\r\n\r\n一般Capability生成在模块初始化的时候，例如Sui中的init函数，就可以赋予部署者一个Capability的资源，然后通过`move_to`然后储存到它的账户下。\r\n\r\n然后当需要使用到有访问权限的函数时，此时函数就会检查调用者地址下是否存在这个Capability资源，如果存在那么说明调用者拥有正确的访问权限。\r\n\r\n### Aptos\r\n\r\n```move\r\nmodule example::capability {\r\n    use std::signer;\r\n\r\n    // 定义一个OwnerCapability类型\r\n    struct OwnerCapability has key, store {}\r\n\r\n    // 向管理者地址下存放一个OwnerCapability资源\r\n    public entry fun init(sender: signer) {\r\n        assert!(signer::address_of(&sender) == @example, 0);\r\n        move_to(&sender, OwnerCapability {})\r\n    }\r\n\r\n    // Only user with OwnerCapability can call this function\r\n    // 只有具有OwnerCapability的用户才能调用此函数\r\n    public entry fun admin_fun(sender: &signer) acquires OwnerCapability {\r\n        assert!(exists<OwnerCapability>(signer::address_of(sender)), 1);\r\n        let _cap = borrow_global<OwnerCapability>(signer::address_of(sender));\r\n        // do something with the cap.\r\n    }\r\n}\r\n```\r\n\r\n### Sui\r\n\r\nsui中的Move与Aptos或者starcoin中的Core Move有所不同，sui封装了全局操作函数，具体可以查看sui的[官方文档](https://docs.sui.io/learn/sui-move-diffs)。\r\n\r\n```move\r\nmodule capability::m {\r\n    use sui::transfer;\r\n    use sui::object::{Self, UID};\r\n    use sui::tx_context::{Self, TxContext};\r\n\r\n    struct OwnerCapability has key { id: UID }\r\n\r\n    /// A Coin Type\r\n    struct Coin has key, store {\r\n        id: UID,\r\n        value: u64\r\n    }\r\n\r\n    /// Module initializer is called once on module publish.\r\n    /// Here we create only one instance of `OwnerCapability` and send it to the publisher.\r\n    fun init(ctx: &mut TxContext) {\r\n        transfer::transfer(OwnerCapability {\r\n            id: object::new(ctx)\r\n        }, tx_context::sender(ctx))\r\n    }\r\n\r\n    /// The entry function can not be called if `OwnerCapability` is not passed as\r\n    /// the first argument. Hence only owner of the `OwnerCapability` can perform\r\n    /// this action.\r\n    public entry fun mint_and_transfer(\r\n        _: &OwnerCapability, to: address, ctx: &mut TxContext\r\n    ) {\r\n        transfer::transfer(Coin {\r\n            id: object::new(ctx),\r\n            value: 100,\r\n        }, to)\r\n    }\r\n}\r\n```\r\n\r\n## 总结\r\n\r\n相较于其他语言的访问控制（例如Solidity中定一个`address owner`即可，或者定义一个`mapping`），Move中的访问控制实现上是复杂的，主要由于Move中独特的存储架构，模组不存储状态变量，需要将资源存储到一个账户下面。\r\n\r\n但是单单对于对于访问控制来说，实现方式有很多种，例如在Move中可以使用`VecMap`等数据结构达到同样的效果，但是Capability这种模式跟面向资源编程的概念更加契合且容易使用。\r\n\r\n> 需要注意的是，既然Capability作为一个凭证，显然是不能`copy`的能力的，如果别人拿到`copy`之后，它可以通过复制从而获得更多的Capability。同样在正常的业务逻辑下，Capability也是不能随意的丢弃也就是不能有`drop`能力，因为丢弃的话显然会造成一些不可逆的影响。"},"author":{"user":"https://learnblockchain.cn/people/6415","address":"0xd69a77bE38f2Fb35De1aCC3AC3626E6d6c8d6928"},"history":"QmQwPWMgBLc4abNhV8bcmuraoG7DS9bT8TRMRQK5BZz3kM","timestamp":1668066326,"version":1}