一次性见证(One Time Witness)

尽管常规的见证(Witness)是一种静态证明类型拥有权的好方法,但在某些情况下,我们需要确保见证仅被实例化一次。这就是一次性见证(One Time Witness,简称 OTW)的目的。

定义

一次性见证(OTW)是一种特殊类型的见证,只能使用一次。它不能手动创建,且每个模块中拥有唯一的实例。Sui 适配器将类型视为 OTW,如果满足以下规则:

  1. 只具有 drop 能力。
  2. 没有字段。
  3. 不是泛型类型。
  4. 模块名称为大写字母。

以下是 OTW 的示例:

module book::one_time {
    /// The OTW for the `book::one_time` module.
    /// Only `drop`, no fields, no generics, all uppercase.
    public struct ONE_TIME has drop {}

    /// Receive the instance of `ONE_TIME` as the first argument.
    fun init(otw: ONE_TIME, ctx: &mut TxContext) {
        // do something with the OTW
    }
}

OTW 不能手动构造,任何试图这样做的代码都会导致编译错误。OTW 可以作为模块初始化器的第一个参数进行接收。由于 init 函数每个模块只调用一次,因此 OTW 保证只被实例化一次。

强制使用 OTW

要检查一个类型是否为 OTW,可以使用Sui 框架sui::types 模块提供的特殊函数 is_one_time_witness

use sui::types;

const ENotOneTimeWitness: u64 = 1;

/// Takes an OTW as an argument, aborts if the type is not OTW.
public fun takes_witness<T: drop>(otw: T) {
    assert!(types::is_one_time_witness(&otw), ENotOneTimeWitness);
}

总结

OTW 模式是确保类型仅使用一次的强大工具。大多数开发者应该理解如何定义和接收 OTW,而 OTW 的检查和强制主要在库和框架中需要。例如,sui::coin 模块要求在 coin::create_currency 方法中使用 OTW,从而确保只创建一个 coin::TreasuryCap

OTW 是为接下来我们将要介绍的发布者(Publisher)对象打下基础的强大工具。