一次性见证(One Time Witness)

一次性见证(One Time Witness, OTW)是一种特殊类型的实例,仅在模块初始化器中创建,并保证是唯一的,并且只有一个实例。在需要确保仅执行一次见证授权操作的情况下非常重要(例如-创建新的Coin)。在 Sui Move中,如果一个类型的定义具有以下属性,则被视为 OTW:

  • 以模块的名字命名,但是所有字母大写
  • 只拥有 drop 修饰符

可以使用sui::types::is_one_time_witness(witness)来检查一个实例是不是OTW。

English Version

One Time Witness (OTW) is a special instance of a type which is created only in the module initializer and is guaranteed to be unique and have only one instance. It is important for cases where we need to make sure that a witness-authorized action was performed only once (for example - creating a new Coin). In Sui Move a type is considered an OTW if its definition has the following properties:

  • Named after the module but uppercased
  • Has only drop ability

To check whether an instance is an OTW, sui::types::is_one_time_witness(witness) should be used.

为了得到这个类型的实例,我们需要把它作为第一个参数传入到 visibilityinit() 函数: Sui 运行时自动提供两个初始化参数。

module examples::mycoin {

    /// 以模块的名字命名
    /// Name matches the module name
    struct MYCOIN has drop {}

    /// 将OTW作为第一个参数传入`init` 函数
    /// The instance is received as the first argument
    fun init(witness: MYCOIN, ctx: &mut TxContext) {
        /* ... */
    }
}
English Version

To get an instance of this type, you need to add it as the first argument to the init() function: Sui runtime supplies both initializer arguments automatically.

module examples::mycoin {

    /// Name matches the module name
    struct MYCOIN has drop {}

    /// The instance is received as the first argument
    fun init(witness: MYCOIN, ctx: &mut TxContext) {
        /* ... */
    }
}

通过以下例子我们可以更好地了解如何使用OTW:

/// 在这个例子中我们将解释OTW如何工作
/// This example illustrates how One Time Witness works.
/// 一次性见证(OTW)是一个在整个系统中唯一的实例。它具有以下属性:
/// One Time Witness (OTW) is an instance of a type which is guaranteed to
/// be unique across the system. It has the following properties:
///
/// - created only in module initializer | 只可以在`init`函数中创建
/// - named after the module (uppercased) | 使用模块名命名(大写)
/// - cannot be packed manually | 无法手动构造
/// - has a `drop` ability | 拥有`drop`属性
module examples::one_time_witness_registry {
    use sui::tx_context::TxContext;
    use sui::object::{Self, UID};
    use std::string::String;
    use sui::transfer;

    // 通过使用`sui::types`检查对象类型是否为OTW
    // This dependency allows us to check whether type
    // is a one-time witness (OTW)
    use sui::types;

    /// 错误码0: 当传入对象非OTW时触发
    /// For when someone tries to send a non OTW struct
    const ENotOneTimeWitness: u64 = 0;

    /// 此类型的对象将标记存在一种类型,每种类型只能有一条记录。
    /// An object of this type will mark that there's a type,
    /// and there can be only one record per type.
    struct UniqueTypeRecord<phantom T> has key {
        id: UID,
        name: String
    }

    /// 提供一个公共函数用于注册`UniqueTypeRecord`对象
    /// `is_one_time_witness`将确保每个泛型(T)只可使用这个函数一次
    /// Expose a public function to allow registering new types with
    /// custom names. With a `is_one_time_witness` call we make sure
    /// that for a single `T` this function can be called only once.
    public fun add_record<T: drop>(
        witness: T,
        name: String,
        ctx: &mut TxContext
    ) {
        // 这里将检查传入值是否为OTW
        // This call allows us to check whether type is an OTW;
        assert!(types::is_one_time_witness(&witness), ENotOneTimeWitness);

        // :)
        // Share the record for the world to see. :)
        transfer::share_object(UniqueTypeRecord<T> {
            id: object::new(ctx),
            name
        });
    }
}

/// 创建一个OTW的例子
/// Example of spawning an OTW.
module examples::my_otw {
    use std::string;
    use sui::tx_context::TxContext;
    use examples::one_time_witness_registry as registry;

    /// 使用模块名命名但是全部大写
    /// Type is named after the module but uppercased
    struct MY_OTW has drop {}

    /// 通过`init`函数的一个参数获取`MY_OTW`, 注意这并不是一个引用类型
    /// To get it, use the first argument of the module initializer.
    /// It is a full instance and not a reference type.
    fun init(witness: MY_OTW, ctx: &mut TxContext) {
        registry::add_record(
            witness, // here it goes <= 在这里使用
            string::utf8(b"My awesome record"),
            ctx
        )
    }
}