Capability

NameCapability
OriginLibra Project / Unknown Author
ExampleSui Move by Example / Damir Shamanaev
Depends onNone
Known to work onMove

概述

Capability是一个能够证明资源所有者特定权限的资源(注意:它是一个资源也就是一个Move中的结构体),其作用主要是用来进行访问控制。

例如当我们想限制某个资源的铸造权,管理权,函数调用权时,便可以采用Capability这种设计模式。这也是Move智能合约里面使用最广泛的一个设计模式,例如sui-framework中的TreasuryCap。这是也是已知最古老的 Move 设计模式,可追溯到 Libra 项目及其代币智能合约,其中功能用于授权铸币。

如何使用

Capability本质是一个资源对象,只是被可信任的用户持有。通常在合约中我们可以定义一个AdminCap来代表本模块的控制权限,如果某个用户持有就可以用户可信,其中资源对象内不需要任何的字段。

struct AdminCap has key, store {}

一般Capability生成在模块初始化的时候,例如Sui中的init函数,就可以赋予部署者一个Capability的资源,然后通过move_to然后储存到它的账户下。

然后当需要使用到有访问权限的函数时,此时函数就会检查调用者地址下是否存在这个Capability资源,如果存在那么说明调用者拥有正确的访问权限。

Aptos

module example::capability {
    use std::signer;

    // 定义一个OwnerCapability类型
    struct OwnerCapability has key, store {}

    // 向管理者地址下存放一个OwnerCapability资源
    public entry fun init(sender: signer) {
        assert!(signer::address_of(&sender) == @example, 0);
        move_to(&sender, OwnerCapability {})
    }

    // Only user with OwnerCapability can call this function
    // 只有具有OwnerCapability的用户才能调用此函数
    public entry fun admin_fun(sender: &signer) acquires OwnerCapability {
        assert!(exists<OwnerCapability>(signer::address_of(sender)), 1);
        let _cap = borrow_global<OwnerCapability>(signer::address_of(sender));
        // do something with the cap.
    }
}

Sui

sui中的Move与Aptos或者starcoin中的Core Move有所不同,sui封装了全局操作函数,具体可以查看sui的官方文档

module capability::m {
    use sui::transfer;
    use sui::object::{Self, UID};
    use sui::tx_context::{Self, TxContext};

    struct OwnerCapability has key { id: UID }

    /// A Coin Type
    struct Coin has key, store {
        id: UID,
        value: u64
    }

    /// Module initializer is called once on module publish.
    /// Here we create only one instance of `OwnerCapability` and send it to the publisher.
    fun init(ctx: &mut TxContext) {
        transfer::transfer(OwnerCapability {
            id: object::new(ctx)
        }, tx_context::sender(ctx))
    }

    /// The entry function can not be called if `OwnerCapability` is not passed as
    /// the first argument. Hence only owner of the `OwnerCapability` can perform
    /// this action.
    public entry fun mint_and_transfer(
        _: &OwnerCapability, to: address, ctx: &mut TxContext
    ) {
        transfer::transfer(Coin {
            id: object::new(ctx),
            value: 100,
        }, to)
    }
}

总结

相较于其他语言的访问控制(例如Solidity中定一个address owner即可,或者定义一个mapping),Move中的访问控制实现上是复杂的,主要由于Move中独特的存储架构,模组不存储状态变量,需要将资源存储到一个账户下面。

但是单单对于对于访问控制来说,实现方式有很多种,例如在Move中可以使用VecMap等数据结构达到同样的效果,但是Capability这种模式跟面向资源编程的概念更加契合且容易使用。

需要注意的是,既然Capability作为一个凭证,显然是不能copy的能力的,如果别人拿到copy之后,它可以通过复制从而获得更多的Capability。同样在正常的业务逻辑下,Capability也是不能随意的丢弃也就是不能有drop能力,因为丢弃的话显然会造成一些不可逆的影响。