Move智能合约初体验 - 第1部分

本文介绍了Sui Move语言及其特性,Sui Move是基于Rust的智能合约语言,强调资源管理和所有权,具有对象中心存储的特点,通过严格的类型系统和能力(如copy、drop、store和key)确保数据完整性和安全性。同时,文章还介绍了Sui Move中的数据类型、Object对象,以及函数和参数。

我最近尝试了一种新的智能合约语言,MOVE

这种语言的灵感来自 Rust,因此对于 Solidity 开发者来说并不直观。

然而,用它来构建非常有趣。

在本系列文章中,我旨在介绍 Move 语言及其特性。

让我们直接开始吧。

快速历史

MOVE 是一种编程语言,最初由 Facebook(现在的 Meta)开发,作为 Libra(后来的 Diem)区块链项目的一部分。

在 Diem 关闭后,MOVE 在 AptosSui 等区块链生态系统中找到了新的生命。它被设计为一种安全、灵活且面向资源的语言,专门用于区块链用例。

目前,我们有不同的 Move 变体,如 Aptos Move 和 Sui Move。

💡

加粗SUI-Move加粗 可能与原始 Move 存在显着差异,以支持 Sui 的以对象为中心的架构和并行事务处理。诸如 dynamic fields(动态字段) object-first storage(对象优先存储) 之类的术语是 SUI-MOVE 中的根本性变化,它似乎更针对以资产为中心、高吞吐量和对象优先的执行进行了优化。

在本文中,我们将专门解读 Sui Move。

关于 Move

Move 是一种编译型的、严格类型的语言。严格类型意味着每个变量、结构体和函数都必须在编译时明确知道其类型。

没有隐式强制转换,没有鸭子类型。虽然 Move 是严格类型的,但它允许通过使用类型参数实现泛型类型,从而在保持严格类型的同时实现一定程度的灵活性。

它也深受 Rust 的启发。例如所有权系统、借用检查器、移动语义、无垃圾收集器的内存安全等概念。

需要记住的术语:

术语 含义
Package(包) 部署单元;一组一起发布在链上的模块。
Module(模块) 包内的编译单元;定义结构体、函数和常量。
Struct(结构体) 用户定义的类型,可以有字段和 abilities(能力);可以表示对象或内存中的值。
Entry Function(入口函数) 用户可在交易中调用的特殊公共函数;具有严格的参数规则。
Abilities(能力) 编译时标签(copy(复制), drop(丢弃), store(存储), key(键)),控制类型的行为方式。
Object(对象) 具有 key(键) 能力和 id: UID 的结构体;持久存储在链上并由 Sui 跟踪。
UID 表示对象标识的特殊内置类型。Key(键) 结构体必需。
Move Semantics(移动语义) 默认行为,值在赋值时被移动(消耗),而不是被复制。
Reference(引用) (&, &mut) 值的借用——不可变 (&) 或可变 (&mut)——强制执行严格的别名使用。
Type Abilities(类型能力) copy(复制), drop(丢弃), store(存储), key(键) – 启用值复制、销毁、存储和对象 ID 使用。

包是发布在链上一个或多个模块的集合。单个包可以包含一个或多个模块。模块是包的构建块。

有趣的是,模块或包不处理存储,因此我们不能只定义不同的字符串或整数,并期望它们存储在链上。

相反,包和模块只处理数据(对象)应该如何表现。

💡 虽然我们可以定义一个常量不变的值,但它们会直接嵌入到字节码中。这些只能为原始类型指定,并且只能用作只读常量。

包在发布时会变成不可变的对象,并被分配一个唯一的 ID,就像 Ethereum 中的智能合约地址一样。

包对象可以升级,但本文不讨论。

由于存储不是由包处理的,因此我们在 Move 中有一个命令式字符来处理存储,即对象

包定义逻辑和结构,而对象保存数据和状态。

加粗我们将在本文后面学习有关对象的内容。加粗

数据类型

在我们研究对象之前,我们应该了解 Sui Move 中的所有数据类型。

以下是有关数据类型的一些简要说明。

我们可以通过三种方式访问 Sui-Move 提供的类型。

内置类型

  1. 第一种是 Move 语言中的内置类型,包括 bool、无符号整数(u8u64 等)和 address
  2. vector<T>: 这仅仅是多个值的集合,基本上是一个数组。它是 Move 中唯一的原始集合类型。关于 vector 有趣的部分是,编译器默认只支持它们的声明。这意味着,要对 vector 使用 push_backlengthpop_back 等方法,我们需要标准库中的 vector 模块。

注意: 虽然 vector 本身是原始类型,但其他类似集合的功能(例如 Maps(映射)Sets(集合))并没有作为原始类型内置到语言中。 相反,它们是在 Move 标准库中作为库实现的。

Sui 框架

  1. 另一方面,Sui 框架提供了 Sui 特定的类型,例如 UIDIDTxContext 以及与对象、包升级等相关的操作。它还包含作为依赖项的标准库。 默认情况下,此框架会添加到我们项目的 move.toml 依赖项中。

标准库:

  1. 标准库为我们提供了 stringBitVectorOption&lt;T> 等类型。此外,它还提供了大量功能来处理所有类型,包括内置类型。

Sui 框架和标准库都有一些隐式导入。 这表明我们不需要显式导入某些内容,例如标准库中的 std::optionstd::vector 以及 Sui 框架中的 tx_contextobjecttransfer 等。

用户自定义类型

我们还有用户定义的类型,例如 结构体和枚举 -(solidity 开发者熟悉的术语)。

结构体:

  • 这些是具有命名字段的固定容器——每个实例都具有相同的字段和结构,例如具有名称、年龄和 id 的用户。在 Sui Move 中,一个结构体需要 key(键) 能力和 UID 类型的字段才能被视为对象。存储能力不是作为对象的严格要求,但如果对象被存储或移动,则是必需的。更多关于 abilities(能力) 的信息稍后介绍。
  • 例如:
public struct StructExample has key, store, copy, drop {
        value: u64,
    }

枚举

  • 另一方面,枚举是灵活的类型,可以是几种变体之一,例如 Result,它可以是 Ok(value)Err(message)。你可以在创建时选择一种变体。枚举对于表达选项或结果很有用,但它们不能用作 Sui 对象
  • 枚举可以有空值、只有值或命名值。 例如:
/// 定义各种字符串段。
public enum Segment has copy, drop {
    /// 空变体,无值。
    Empty,
    /// 具有值的变体(位置样式)。
    String(String),
    /// 具有命名字段的变体。
    Special {
        content: vector&lt;u8>,
        encoding: u8, // 编码标签。
    },
}

/// 实例化

/// 构造一个 `Empty` 段。
public fun new_empty(): Segment { Segment::Empty }

/// 构造一个带有 `str` 值的 `String` 段。
public fun new_string(str: String): Segment { Segment::String(str) }

/// 构造一个带有 `content` 和 `encoding` 值的 `Special` 段。
public fun new_special(content: vector&lt;u8>, encoding: u8): Segment {
    Segment::Special {
        content,
        encoding,
    }
}

如果其中一些内容让你感到陌生,请不要担心——稍后我会对其进行分解。

Abilities(能力)

Move 类型可以分配有 4 种 abilities(能力)。

简而言之,move 中的 abilities(能力),决定了特定类型允许执行的操作。

  1. copy(复制)
  2. drop(丢弃)
  3. store(存储)
  4. key(键)

让我们简要地谈谈每一个。

copy(复制)

  • 允许将值复制到内存。

sui 函数中的所有内容都保留在内存中,除非写入存储。 与 solidity 不同,没有 calldata 的概念。在 Sui Move 中,我们不会在存储或内存之间进行选择。

  • 例如,如果没有 copy(复制) 能力,我们将无法执行以下操作 👇🏾
let student = StudentID { ... };
let a = student;
let b = student; // 错误:已移动

drop(丢弃)

  • 允许弹出/丢弃具有此能力的类型的值。具有 drop(丢弃) 的类型可以被忽略或销毁。
  • 例如:
module example::drop_demo;

public struct TempData has drop {
    value: u64,
}

public fun make_and_discard() {
    let _ = TempData { value: 42 };
    // 没有错误 — TempData 具有 `drop(丢弃)`,因此可以被忽略
}

store(存储)

  • 允许此能力的类型的值存在于存储中的值内。
  • 对于 Sui,store(存储) 确定一个值是否可以存在于全局存储中。 这在处理从函数传输或返回对象时非常重要。
  • 例如,当你创建一个对象并从函数返回它,然后将该对象分配给一个帐户时。 此对象正在模块范围之外传输。 此时你将需要 store(存储)
  • 简而言之,任何期望离开函数范围的结构体——无论是通过返回、传输还是嵌入到另一个存储的值中——都必须具有 store(存储) 能力。

让我们考虑一个例子来清楚地理解这一点:

  1. SimpleData 具有 store(存储) 能力,因为我们将其嵌入到 object(对象)(具有 key(键) 能力的 DataHolder)中。
  2. 在第二种情况下,Outer 结构体具有 store(存储),这意味着它的所有字段都应该具有 store(存储),因此 inner 结构体也需要具有 store(存储) 能力。
 /// 具有存储能力的结构体
    public struct SimpleData has store {
        value: u64,
    }

    /// 包含 SimpleData 的对象
    public struct DataHolder has key {
        id: object::UID,
        data: SimpleData, //  由于 SimpleData 存储在一个对象中,因此它必须具有 `store(存储)` 能力
    }

 /// 类似情况
    public struct Inner has store {
        value: u64,
    }

    public struct Outer has store {
        nested: Inner,
    }
  • 当你从公共入口函数返回一个结构体时,你正在将其交给用户帐户(跨模块边界),也就是需要存储能力。
   // 如果从模块中返回,则结构体需要存储
    public struct ReturnableData has store {
        value: u64,
    }

    public entry fun create(): ReturnableData {
        ReturnableData { value: 10 }
    }

key(键)

  • Key(键) 用于将结构体指定为对象。 它说明这个特定的结构体应该被视为一个对象,独立地存储在链上 (顶层),具有一个 唯一 ID ( UID),并且由 Sui 运行时跟踪所有权、版本控制等。
  • 结构体中的所有字段都应该具有 store(存储),但不必一定具有 key(键) 能力。
  • u64string 这样的内置类型默认已经具有 copy(复制)drop(丢弃)store(存储) 能力,这就是为什么我们没有明确地写任何东西。 但是任何应该位于具有 key(键) 的另一个结构体中的结构体都应该具有 store(存储) 能力。

💡 原始类型默认具有此处提到的所有四种 abilities(能力),因此我们不会明确提及它们。 b. 但是,结构体/枚举需要开发人员明确提及 abilities(能力)。

为了编写可重用、灵活的模块,我们经常需要类型参数

让我们来谈谈 Move 如何允许我们使用泛型和 phantom types(幽灵类型) 来做到这一点。

高级类型 - 泛型和幽灵

在跳到代码之前,还有一个有趣的功能需要学习,即“泛型”

你将在我们在下一部分中谈到的 move 包中看到泛型的使用。 如果你看到诸如“Type Parameters(类型参数)”或“Type Arguments(类型实参)”之类的术语,请不要感到困惑。 这些是泛型的其他名称。

将泛型视为未知类型的占位符。 意味着你可以使用泛型定义一个函数,并且顾名思义,这将成为一个适用于任何 type(类型) 的泛型函数。

或者你可以定义一个泛型结构体,这意味着该结构体可以具有你分配的任何 type(类型)。 让我们用一个小例子来理解一下。

泛型结构体

这定义了一个可以容纳任何类型 T 的容器。

public struct Container&lt;T> has drop {
    value: T,
}

泛型函数

这从 T 类型的值创建一个新的 Container<T>。

  • &lt;T> 是某种类型的占位符(例如,u64String 等)。
  • 使用时,T 会被你传入的实际类型替换。
public fun new&lt;T>(value: T): Container&lt;T> {
    Container { value }
}

泛型的好处在于它们可以不使用。 以下是上述函数和结构体的示例用法。

let c1 = new(10);               // 推断为 Container&lt;u8>
let c2 = new&lt;u64>(100);         // 显式类型
let c3: Container&lt;String> = new("hi".to_string());

请注意,我们可以将 u8、u64 以及字符串传递给同一个函数和结构体。

Phantom Types(幽灵类型)

有时,你想用一种类型标记一个结构体以用于识别或逻辑,而无需实际存储该类型的任何数据。 这就是 phantom types(幽灵类型) 的用武之地。

以下是 Coin 系统中的一个示例:

public struct TreasuryCap&lt;phantom T> has key, store {
    id: UID,
    total_supply: Supply&lt;T>,
}
  • phantom T 告诉编译器:“我正在跟踪类型 T,但我没有存储该类型的值。”
  • 它用于将逻辑绑定到 coin 类型 (T),而无需在内存中携带它。
  • Move 使用此方法来强制执行类型安全的铸造TreasuryCap&lt;USDC> 不会错误地铸造 DAI

现在我们了解了类型和 abilities(能力),现在是研究对象的时候了。

对象 - 一等公民

在 Sui 中,对象是一等公民。 与帐户或余额等传统术语不同,SUI 使用对象来存储任何东西。

对象具有:

  • 一个唯一的 ObjectID - 从事务上下文中派生的对象的唯一标识符。
  • 一个 version(版本) - 对象被修改或写入区块链的次数。
  • 一个 owner(所有者) (帐户地址、共享或另一个对象) - 我们将在稍后讨论。

我们使用 struct(结构体) 类型定义对象。 需要注意的是,这个结构体应该具有 key(键) 能力,并且 id: UID 作为第一个字段。

示例:

public struct MyCounter has key {
    id: UID,
    value: u64,
}

在上面的例子中:

  • MyCounter 是一个自定义对象类型。
  • id: UID 赋予它一个唯一的身份。
  • value: u64 是我们要存储的数据。

对象的所有权

进一步扩展对象的所有权。 每个对象都有所有者,这是 Sui 链的内置功能。 让我们讨论三种类型:

  1. 由地址拥有:
    1. 简单来说,帐户地址是所有者。 这表明只有所有者才能在入口函数中传递此对象并对其进行修改。
    2. 例如,你在 Sui 链上拥有 100 USDT,这意味着你拥有一个或多个组合在一起的 USDT coin 对象,并且所有这些对象都将你的帐户地址作为所有者。
    3. 这意味着,可以有一个余额为 100 的 USDT 对象,也可以有 10 个余额为 10 的 USDT coin 对象,依此类推。 有趣的是,你可以合并、拆分或单独传输这些对象。
  2. 共享对象:
    1. 共享对象不属于任何人 - 任何人都可以根据我们定义的访问逻辑对其进行读取或写入。
    2. 简而言之,共享对象不一定“无人拥有”,而是可以由多个用户同时访问
    3. 此上下文中的所有权指的是共享所有权,其中访问控制和可变性由对象的共享性质定义。 开发人员可以根据逻辑设置可访问性检查,例如白名单等。
    4. 这也引出了另一个重要的一点。 当共享对象被更改时,需要进行共识执行,因为它们不能并行执行。 另一方面,拥有的对象不会与其他对象混淆,因此可以并行执行,而无需共识。
  3. 由另一个对象拥有(像具有元数据的 NFT 一样的可组合性):
    1. 一个对象可以拥有另一个对象。 这可用于嵌套组合,构建丰富的层次结构。
    2. 例如,想象一个会议通行证系统——你持有一个 master pass(主通行证) 对象,并且根据你的票务等级,它拥有几个 sub-event passes(子活动通行证)(也是对象)。 这些子通行证不能独立存在 - 它们与父对象相关联。
  4. 冻结状态: 对象也可以是不可变的,这意味着它们被永久冻结,并且不能再被移动/修改。

到目前为止,我们已经讨论了 类型、对象和 abilities(能力) 等。 为了使一切协同工作,我们需要执行逻辑,即函数。

让我们看看函数是如何工作的,以及它们如何驱动 Move 模块中的逻辑。

函数和参数

函数是任何 Sui Move 模块的构建块。 每个操作——创建对象、更新状态、转移所有权、铸造Token——都是在函数内部完成的。

函数使用 fun 关键字声明:

fun add(x: u64, y: u64): u64

{
    x + y
}

函数可见性

关键字 谁可以调用它? 用于
fun 仅在同一个模块内 内部逻辑
public fun 任何模块 链上模块到模块的调用
public entry fun 最终用户(钱包、前端、CLI) 事务入口点
public(package) fun 同一个包中的任何模块 跨包模块的内部 API

函数语法

&lt;visibility>? &lt;entry>? fun &lt;name>&lt;type_params>(&lt;params>): &lt;return_type> {
    // 函数体
}
  • visibility:可选(public, public(package))

  • entry:可选 (只有最终用户可调用的函数才能使用 entry)

  • &lt;type_params>:可选的泛型类型参数,如 &lt;T>

  • params: 逗号分隔的参数

  • return_type:单个类型、元组或 () 表示无返回

    • *

具有所有特性的示例

public entry fun transfer_coin&lt;T>(
    coin: Coin&lt;T>,
    recipient: address,
    ctx: &mut TxContext
) {
    transfer::public_transfer(coin, recipient);
}
  • entry → 可以在事务中调用
  • public → 从模块外部可见
  • T → 泛型类型参数
  • ctx → 任何改变状态的函数始终需要

参数说明

&T 值的不可变引用——无法修改它。
&mut T 可变引用——允许更改传递的对象。
T (按值传递) 仅对具有复制/丢弃能力的原始类型或小型结构体有效。
vector&lt;T> 如果 T 是有效类型(例如 vector&lt;u64>),则可以直接传递。
TxContext 必须是 &mut TxContext,始终最后传递。 状态更改所必需。

入口函数规则

  • 必须只接受某些参数类型
    • 对象引用
    • 纯值(u64booladdress 等)
    • TxContext
  • 最后一个参数必须是 &mut TxContext

有效:

public entry fun create(ctx: &mut TxContext) { ... }

无效:

public entry fun bad(ctx: TxContext) { ... } // 不是引用

多个返回值

函数可以返回元组:

fun get_name_and_age(): (string::String, u8) {
    (string::utf8(b"John"), 25)
}

let (name, age) = get_name_and_age();

总结

好的,我们在本文中介绍了很多细节。

我们了解到 Move 提供了一种独特而强大的方法来构建区块链应用程序,它结合了 Rust 启发式功能的稳健性以及区块链特定的资源管理和所有权重点。

Sui-Move 的突出特点之一是它强调以对象为中心的存储,其中对象是一等公民。 这种从传统的基于帐户的模型的范式转变使得可以更动态地处理资产和数据。 此外,严格的类型系统和 copy(复制)drop(丢弃)store(存储)key(键) 等 abilities(能力) 确保开发人员保持对数据完整性和安全性的细粒度控制。

此外,通过利用泛型和 phantom types(幽灵类型),开发人员可以编写灵活且可重用的模块,从而进一步提高智能合约开发的效率。 灵活性和安全性之间的谨慎平衡使 Sui Move 特别适合构建下一代区块链应用程序。

通过对这些概念的扎实掌握,你可以自信地探索更高级的主题,例如对象所有权和多版本并发控制。

我对 Sui Move 的探索才刚刚开始,但到目前为止所涵盖的基础知识已经揭示了该语言在构建可扩展、安全和高性能区块链解决方案方面的巨大潜力。

我们将在本系列文章的下一部分中再见。

使用的参考资料

The Move Book - https://move-book.com/index.html

Sui Docs - https://docs.sui.io/


  • 原文链接: decipherclub.com/my-firs...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
decipherclub
decipherclub
江湖只有他的大名,没有他的介绍。