Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Move 语言的演进

Move 语言诞生于 2018 年 Facebook(现 Meta)的 Libra(后更名为 Diem)区块链项目,并于 2019 年随 Libra 白皮书首次公开亮相。它是第一种专为数字资产设计的智能合约语言,从一开始就将安全性资源导向形式化验证作为核心设计目标。如今,Move 已经从 Diem 的账户模型演化到 Sui 的对象模型,在保持语言内核安全特性的同时,获得了前所未有的表达力和性能。

Move 的诞生:Diem 时代

背景与设计动机

2018 年,Facebook 启动了雄心勃勃的 Libra 项目,目标是创建一个全球性的数字货币和金融基础设施。项目团队意识到,现有的智能合约语言(尤其是 Solidity)在安全性方面存在根本性的缺陷——重入攻击、整数溢出、权限管理混乱等问题导致了数十亿美元的资产损失。

为此,他们决定从零开始设计一门新语言,核心设计原则包括:

  • 资源安全:数字资产不能被复制或意外销毁,就像现实世界中的物理资产
  • 类型安全:通过强类型系统在编译期捕获尽可能多的错误
  • 模块封装:资源类型只能由定义它的模块创建和销毁
  • 形式化可验证:语言设计支持数学层面的安全性证明

线性类型与资源模型

Move 最核心的创新是引入了线性类型系统。在传统编程语言中,一个值可以被随意复制和丢弃。但在 Move 中,某些类型(资源类型)必须被恰好使用一次——不能复制,不能丢弃,只能移动(Move)。

module examples::linear_type_demo;

/// 一个不能被复制或丢弃的代币
/// 没有 copy 和 drop 能力
public struct PreciousToken has key, store {
    id: UID,
    value: u64,
}

/// 创建代币
public fun mint(value: u64, ctx: &mut TxContext): PreciousToken {
    PreciousToken {
        id: object::new(ctx),
        value,
    }
}

/// 销毁代币并取回值
public fun burn(token: PreciousToken): u64 {
    let PreciousToken { id, value } = token;
    id.delete();
    value
}

/// 以下代码将无法编译——Move 编译器阻止了不安全行为:
///
/// fun unsafe_copy(token: &PreciousToken): PreciousToken {
///     *token  // 错误! PreciousToken 没有 copy 能力
/// }
///
/// fun unsafe_drop(token: PreciousToken) {
///     // 什么都不做就返回
///     // 错误! PreciousToken 没有 drop 能力,必须被显式处理
/// }

这种设计从语言层面保证了数字资产的安全性——你的代币不可能因为编程错误而被凭空复制或消失。

Ability 系统

Move 使用**四种 Ability(能力)**来精确控制类型的行为:

Ability含义作用
copy可以复制允许值被复制
drop可以丢弃允许值在作用域结束时被自动销毁
store可以存储允许值被存储在其他对象的字段中
key可以作为对象允许值作为 Sui 对象使用(需要 id: UID 字段)
module examples::abilities_demo;

/// 普通数据——可以自由复制和丢弃
public struct Point has copy, drop {
    x: u64,
    y: u64,
}

/// 链上对象——可以存储和转移,但不能复制
public struct Sword has key, store {
    id: UID,
    damage: u64,
    level: u8,
}

/// 权限凭证——只能转移,不能复制或存储到其他对象中
public struct AdminCap has key {
    id: UID,
}

从 Diem Move 到 Sui Move

账户模型 vs 对象模型

Diem/Aptos Move 和 Sui Move 最大的区别在于存储模型

在 Diem Move 中,资源存储在账户之下。如果 Alice 想把一个 Token 转给 Bob,Bob 的账户下必须先有一个接收该类型 Token 的“容器“:

Diem Move(账户模型):

Alice 的账户 (0xAlice)
└── Token { value: 100 }

转移前提: Bob 的账户必须先执行 "accept<Token>()" 创建空容器
Bob 的账户 (0xBob)
└── Token { value: 0 }  ← 必须预先存在!

转移过程:
1. 从 Alice 账户取出 Token
2. 检查 Bob 账户是否有 Token 容器
3. 合并到 Bob 的 Token 中

Sui Move 彻底改变了这个模型。在 Sui 中,对象是一等公民,拥有全局唯一 ID 和独立的所有权。Alice 可以直接将对象转给 Bob,无需 Bob 做任何准备:

Sui Move(对象模型):

对象 0xAABB (Token)
├── id: 0xAABB
├── owner: Alice
└── value: 100

转移过程:
1. 更改对象 0xAABB 的 owner 为 Bob
就这么简单!Bob 不需要做任何操作。

代码对比

以下代码展示了同样的“转移代币“操作在两种模型中的差异:

// ===== Sui Move 风格 =====
module examples::sui_token;

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

/// 铸造并直接转给接收者
/// Bob 不需要任何前置操作!
public fun mint_and_send(
    value: u64,
    recipient: address,
    ctx: &mut TxContext,
) {
    let token = Token {
        id: object::new(ctx),
        value,
    };
    transfer::public_transfer(token, recipient);
}

在 Diem/Aptos Move 中,类似操作需要更多的样板代码——接收者需要先“注册“才能接收资源。Sui 的对象模型消除了这种摩擦,让数字资产的转移像发送电子邮件一样简单。

关键差异总结

特性Diem/Aptos MoveSui Move
存储模型账户下存储资源全局对象存储
资产标识由账户地址 + 类型标识全局唯一 UID
转移机制接收者需预先创建容器直接转移,无需接收者准备
并行执行受限(共享全局状态)天然支持(对象独立)
入口函数entry fun + signerentry fun + TxContext
模块初始化无原生支持init 函数在发布时自动执行

Move vs Solidity

对于从以太坊转来的开发者,理解 Move 和 Solidity 的区别尤为重要。

类型安全

Solidity 中的代币本质上是一个映射表中的数字,没有类型安全保障:

// Solidity — 代币只是一个数字
mapping(address => uint256) public balances;

function transfer(address to, uint256 amount) public {
    require(balances[msg.sender] >= amount);
    balances[msg.sender] -= amount;
    balances[to] += amount;
}

Move 中的代币是一个真正的类型对象,编译器保证它不能被凭空创造或复制:

module examples::move_token;

/// 代币是一个有 Ability 约束的结构体
/// 只能通过 mint 创建,通过 burn 销毁
public struct Token has key, store {
    id: UID,
    value: u64,
}

/// 唯一的创建途径
public fun mint(treasury: &mut Treasury, value: u64, ctx: &mut TxContext): Token {
    treasury.total_supply = treasury.total_supply + value;
    Token { id: object::new(ctx), value }
}

/// 唯一的销毁途径
public fun burn(treasury: &mut Treasury, token: Token) {
    let Token { id, value } = token;
    treasury.total_supply = treasury.total_supply - value;
    id.delete();
}

public struct Treasury has key {
    id: UID,
    total_supply: u64,
}

重入攻击

重入攻击是 Solidity 中最臭名昭著的安全漏洞,2016 年的 The DAO 事件导致了 6000 万美元的损失。

// Solidity — 存在重入风险
function withdraw(uint256 amount) public {
    require(balances[msg.sender] >= amount);
    // 危险!先转账,再更新余额
    (bool success,) = msg.sender.call{value: amount}("");
    require(success);
    balances[msg.sender] -= amount;  // 攻击者可以在这之前重复调用 withdraw
}

Move 从语言设计上完全消除了重入攻击的可能性:

  1. 没有动态调度:Move 的函数调用在编译时完全确定,不存在 Solidity 中的 call 机制
  2. 线性类型:资源在同一时刻只能有一个所有者,不可能在持有资源的同时将其传递给外部代码
  3. 借用检查:Move 的引用系统保证不会出现可变引用和不可变引用同时存在的情况
module examples::safe_withdraw;

use sui::coin::{Self, Coin};
use sui::sui::SUI;
use sui::balance::Balance;

const EInsufficientBalance: u64 = 0;

/// Move 中的提款——天然安全,无需特殊防护
public fun withdraw(vault: &mut Vault, amount: u64, ctx: &mut TxContext): Coin<SUI> {
    assert!(vault.balance >= amount, EInsufficientBalance);
    vault.balance = vault.balance - amount;
    // 返回 Coin 对象,调用者获得所有权
    // 不存在回调机制,不可能触发重入
    coin::take(&mut vault.coin_balance, amount, ctx)
}

public struct Vault has key {
    id: UID,
    balance: u64,
    coin_balance: Balance<SUI>,
}

全面对比

维度SolidityMove
类型系统弱类型,资产是 mapping 中的数字强类型,资产是一等类型对象
重入攻击需要 ReentrancyGuard 等模式防护语言层面不可能发生
整数安全需要 SafeMath(0.8.0 后内置检查)编译器内置溢出检查
资产安全依赖开发者正确实现逻辑编译器保证资产不被复制或丢弃
权限控制通常用 onlyOwner 修饰符Capability 模式 + 类型系统
升级机制代理模式(复杂且有风险)原生包升级机制
形式化验证有限支持Move Prover 提供数学级别验证

Move 的核心价值观

Move 语言的设计哲学可以概括为三个核心价值:

1. 默认安全(Secure by Default)

安全不是事后添加的特性,而是融入语言 DNA 的设计原则。Move 编译器会在你的代码运行之前就捕获绝大多数安全漏洞:

module examples::secure_by_default;

/// 编译器保证:
/// 1. NFT 不能被复制(没有 copy ability)
/// 2. NFT 不能被意外丢弃(没有 drop ability)
/// 3. 只有该模块能创建和销毁 NFT
public struct NFT has key, store {
    id: UID,
    name: vector<u8>,
}

/// 这是创建 NFT 的唯一入口
public fun mint(name: vector<u8>, ctx: &mut TxContext): NFT {
    NFT { id: object::new(ctx), name }
}

/// 这是销毁 NFT 的唯一入口
public fun burn(nft: NFT) {
    let NFT { id, name: _ } = nft;
    id.delete();
}

/// 模块外的代码无法绕过这些函数来创建或销毁 NFT
/// 这是由 Move 的模块封装机制在语言层面保证的

2. 天然表达力(Expressive by Nature)

Move 提供了丰富的原语来表达复杂的数字资产逻辑。可编程对象、动态字段、可编程交易块等特性让开发者能够构建高度灵活的应用:

module examples::expressive;

use sui::dynamic_field;

/// 一个可以动态扩展属性的角色
public struct Character has key {
    id: UID,
    name: vector<u8>,
    level: u64,
}

/// 装备武器——使用动态字段
public fun equip_weapon(character: &mut Character, weapon: Weapon) {
    dynamic_field::add(&mut character.id, b"weapon", weapon);
}

/// 武器本身也是一个结构体
public struct Weapon has store {
    name: vector<u8>,
    damage: u64,
}

/// 在 PTB 中,这些操作可以组合在一笔交易中:
/// 1. 铸造角色
/// 2. 铸造武器
/// 3. 装备武器
/// 4. 将角色转给玩家
/// 全部在一笔交易中原子性完成!

3. 对所有人直觉化(Intuitive for All)

Move 2024 Edition 引入了大量语法改进,让代码更加简洁和直观:

module examples::intuitive;

use std::string::String;

public struct Profile has key {
    id: UID,
    name: String,
    bio: String,
    score: u64,
}

public fun create_profile(
    name: String,
    bio: String,
    ctx: &mut TxContext,
): Profile {
    Profile {
        id: object::new(ctx),
        name,
        bio,
        score: 0,
    }
}

/// Move 2024 方法语法——像调用对象方法一样
public fun update_score(profile: &mut Profile, delta: u64) {
    profile.score = profile.score + delta;
}

/// 使用示例(在测试或 PTB 中):
/// let profile = create_profile(name, bio, ctx);
/// profile.update_score(10);  // 方法语法:直观!

Move 语言时间线

2018 年
├── Facebook 启动 Libra 项目
└── Move 语言开始设计和开发

2019 年
├── 6 月: Libra 白皮书发布,Move 首次公开亮相
└── Move 技术论文发表

2020 年
├── Libra 更名为 Diem
├── 第一个 Move 网络启动
└── Move Prover(形式化验证工具)发布

2021 年
├── Mysten Labs 成立(由前 Meta/Novi 核心成员创建)
└── Sui 项目启动,开始设计对象模型

2022 年
├── Meta 宣布关闭 Diem 项目
├── Sui 测试网上线
├── Move 社区分化:Aptos Move(账户模型)vs Sui Move(对象模型)
└── Sui 获得大规模融资

2023 年
├── 5 月: Sui 主网正式上线
├── Move 2024 Edition 预览
└── zkLogin、DeepBook 等创新功能发布

2024 年
├── Move 2024 Edition 正式发布
├── 枚举类型、方法语法等新特性落地
├── Walrus 去中心化存储发布
└── Sui 生态快速扩张

2025–2026 年
├── Move 语言持续演进
├── Sui 性能进一步优化
└── 生态项目全面开花

Move 的核心技术要素

可编程对象(Programmable Objects)

每个 Sui 对象都有唯一 ID、明确的所有权和类型化的数据。对象是 Move 在 Sui 上最核心的抽象单元。

Ability 系统(线性类型)

四种 Ability(keystorecopydrop)精确控制值的生命周期行为,从编译期保证资源安全。

模块系统与强封装

Move 的模块是类型和函数的命名空间。关键安全属性:只有定义类型的模块才能创建、销毁和访问该类型的内部字段。这种封装是不可绕过的。

动态字段(Dynamic Fields)

允许在运行时向对象添加任意类型的键值对,实现灵活的数据模型扩展,而不需要预先定义所有字段。

可编程交易块(PTBs)

允许在单笔交易中组合多个 Move 调用,前一个调用的返回值可以直接作为后一个调用的参数。这是 Sui 独有的强大特性,实现了无合约级别的可组合性。

小结

Move 语言从 2018 年 Facebook Libra 项目中诞生,经历了从 Diem 到 Sui 的重大演化。它的核心设计——线性类型、Ability 系统、模块封装——在 Sui 的对象模型中得到了最充分的发挥。与 Solidity 相比,Move 在类型安全、资产安全和重入防护方面具有本质性的优势。Move 2024 Edition 进一步提升了语言的表达力和开发者体验,使其成为当前最先进的智能合约语言之一。从下一章开始,我们将搭建开发环境,亲手编写第一个 Move 程序,开始真正的 Move on Sui 之旅。