六、Move Patterns

  • Ch1hiro
  • 发布于 1天前
  • 阅读 155

本片笔记继续学习Move 的相关模式

1、Entry 修饰符

1.1、Entry 修饰符

在 Move 中,entry 修饰符用于标记那些可以用于链下调用的函数,这些函数被称为入口函数。例如:

entry fun new() {
    // ...
}

1.2、Entry 特性

  1. 直接调用:带有 entry 修饰符的函数可以在链下(通过前端代码或 sui cli)直接调用,从而触发特定的操作,不能在模块内调用

  2. 参数要求:入口函数的参数必须是交易块的输入,不能是该块中前面交易的结果或修改过的值。这样设计的目的是确保每个入口函数调用都是独立的,避免复杂的依赖关系和潜在的安全风险。假设在一个交易块中有两个交易:交易 A,它调用一个普通函数,计算某个数值。交易 B,它调用入口函数。在这种情况下,交易 B 的入口函数参数不能是交易 A 的结果。也就是说,如果交易 A 修改了某个数值,交易 B 的入口函数不能依赖这个修改后的数值作为其参数。

  3. 返回值:入口函数通常不返回值,但它们可以有返回值,这些返回值必须具有 drop 能力,表示返回的值可以被安全地丢弃,不会在系统中存储或保留。这个限制确保了入口函数在修改状态后不会产生未预期的副作用。

2、可编程交易块(PTB)

2.1、可编程交易块

​ 可编程交易模块 (Programmable Transaction Block, PTB) 是 Sui 区块链的一项核心技术,旨在提高交易的灵活性和可编程性。PTB 允许用户在一个交易中组合多个操作,使得复杂的交易逻辑可以通过一次提交来实现,类似于批量提交交易。这不仅简化了操作流程,还提高了区块链系统的效率和可扩展性。

PTB 主要解决了区块链交易中的以下几个问题:

  1. 交易复杂度:传统区块链每个交易只能执行单一操作,复杂业务需要多次交易提交,操作繁琐且效率低下。PTB 允许将多个操作合并为一个交易,提高了业务处理的效率。
  2. 操作原子性:在多次交易中,如果中途某一步失败,需进行复杂的回滚操作。PTB 保证了所有操作的原子性,避免了部分成功导致的数据不一致问题。
  3. 网络性能:通过减少交易次数,降低了网络带宽消耗和交易确认时间,提升了整体性能。

2.2、PTB 命令介绍

我们可以使用 sui client ptb 命令来构建、预览和执行这些交易块:

sui client ptb [OPTIONS]

OPTIONS 参数:

  1. --assign <NAME> <VALUE>:为变量名称赋值,可以在后续的 PTB 中使用。

    --assign A 100   
    --assign B '[100, 5000]'
  2. --merge-coins <INTO_COIN> <[COIN OBJECTS]>:将多个 coin 合并到指定的 coin 中,这里对象的 id 需要以 @ 符号为前缀。如果是merge gas,则需要预留一个gas对象用来支付

    --merge-coins @into_coin_obj '[@coin_obj_1, @coin_obj_2]'
  3. --move-call <PACKAGE::MODULE::FUNCTION> <TYPE_ARGS> <FUNCTION_ARGS>:调用指定的 Move 函数。

    --move-call std::option::is_none <u64> none
    --assign a none
    --move-call std::option::is_none <u64> a
  4. --split-coins <COIN> <[AMOUNT]>:将 coin 拆分为多个指定金额的 coin。

    --split-coins gas '[1000, 5000, 75000]'
  5. --transfer-objects <[OBJECTS]> <TO_ADDRESS>:将对象转移到指定地址。

    --split-coins gas [1000, 5000, 75000]
    --assign new_coins
    --transfer-objects [new_coins.0, new_coins.1, new_coins.2] @to_address

3、模块初始化

3.1、简述

​ 在 Sui Move 中模块初始化器是一个特殊的函数:init 函数,它在模块首次发布到区块链上时自动执行,用于初始化模块的状态和配置,例如创建初始对象、配置全局参数等。并且这个init函数只会在发布模块时执行一次。

3.2、init函数的特点

  • 名称和访问权限:函数必须命名为init,并设置为私有,防止外部调用。
  • 参数:通常接受一个TxContext的可变引用作为参数,这是交易的上下文信息。
  • 一次性:这个函数只在你的模块首次发布时执行一次。

3.3、安全性和信任

​ 虽然init函数可以用来一次性创建重要的对象,但我们也需要注意,模块中的其他函数可能仍然可以创建相同的对象。因此,init函数是设置模块初始状态的好地方,但不能单独作为安全措施。

4、一次性见证(OTW)

4.1、简述

One Time Witness (OTW) 是 Sui Move 中的一个重要机制,旨在确保某些操作只能执行一次。它类似于其他编程语言中的单例模式,保证只能创建一个 OTW 类型的实例,并且使用完就销毁,不能再次创建。它的设计初衷是防止重复操作,保证系统的安全性和稳定性。

OTW 解决了这样一个问题:在某些场景下,我们希望某个操作只能执行一次。例如,代币的创建必须是一次性的,否则可能会造成系统中的代币重发,产生非预期行为。通过使用 OTW,我们可以强制这种唯一性约束。

4.2、特点

单次见证是一个特殊的数据类型,它有如下特点:

1.命名规则:其结构体名称通常是模块名称的大写,确保与其他类型名称的区分。

2.能力限制:OTW 类型仅具有 drop 能力,这意味着它不能被复制,只能被消耗。

3.字段限制:OTW 通常不包含任何字段,或者仅包含一个布尔字段。

4.唯一性:OTW 的实例在模块的生命周期中只会被创建一次,并且通常在模块的初始化函数中作为参数传递。

4.3、定义 OTW 类型

move 模块中定义一个与模块同名且全大写的结构体 MOVE 作为一次性见证的结构体类型,并赋予其 drop 能力。

module hello::move {
    use sui::types;

    const ENotOneTimeWitness: u64 = 0;

    // One Time Witness Struct
    public struct MOVE has drop {}

    fun init(otw: MOVE, ctx: &mut TxContext) {
        assert!(types::is_one_time_witness(&otw), ENotOneTimeWitness);
    }
}

init 函数的第一个参数为 MOVE 类型,该函数在合约发布时由虚拟机调用和实例化一个 MOVE 对象,并不需要我们手动传入,并且由虚拟机保证只有一个 MOVE 对象。如果再次初始化 MOVE 对象,则会提示:Invalid one-time witness construction. One-time witness types cannot be created manually, but are passed as an argument 'init’

4.4、示例代码

利用一次性见证者的特点创建代币

module hello::hiro {
    use sui::coin;

    public struct HIRO has drop {}

    fun init(otw: HIRO, ctx: &mut TxContext) {
        let (treasuryCap, metaData) = coin::create_currency(
            otw,
            9u8,
            b"HIRO",
            b"HIRO Token",
            b"A test coin",
            option::none(),
            ctx
        );
        transfer::public_freeze_object(metaData);
        transfer::public_transfer(treasuryCap, ctx.sender());
    }
}
============================================================================================================
module sui::coin {

    public fun create_currency<T: drop>(
        witness: T,
        decimals: u8,
        symbol: vector<u8>,
        name: vector<u8>,
        description: vector<u8>,
        icon_url: Option<Url>,
        ctx: &mut TxContext
    ): (TreasuryCap<T>, CoinMetadata<T>) {
        // Make sure there's only one instance of the type T
        assert!(sui::types::is_one_time_witness(&witness), EBadWitness);
        // ...
    }
}
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Ch1hiro
Ch1hiro
一名Web3初学者