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 Move | Sui Move |
|---|---|---|
| 存储模型 | 账户下存储资源 | 全局对象存储 |
| 资产标识 | 由账户地址 + 类型标识 | 全局唯一 UID |
| 转移机制 | 接收者需预先创建容器 | 直接转移,无需接收者准备 |
| 并行执行 | 受限(共享全局状态) | 天然支持(对象独立) |
| 入口函数 | entry fun + signer | entry 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 从语言设计上完全消除了重入攻击的可能性:
- 没有动态调度:Move 的函数调用在编译时完全确定,不存在 Solidity 中的
call机制 - 线性类型:资源在同一时刻只能有一个所有者,不可能在持有资源的同时将其传递给外部代码
- 借用检查: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>,
}
全面对比
| 维度 | Solidity | Move |
|---|---|---|
| 类型系统 | 弱类型,资产是 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(key、store、copy、drop)精确控制值的生命周期行为,从编译期保证资源安全。
模块系统与强封装
Move 的模块是类型和函数的命名空间。关键安全属性:只有定义类型的模块才能创建、销毁和访问该类型的内部字段。这种封装是不可绕过的。
动态字段(Dynamic Fields)
允许在运行时向对象添加任意类型的键值对,实现灵活的数据模型扩展,而不需要预先定义所有字段。
可编程交易块(PTBs)
允许在单笔交易中组合多个 Move 调用,前一个调用的返回值可以直接作为后一个调用的参数。这是 Sui 独有的强大特性,实现了无合约级别的可组合性。
小结
Move 语言从 2018 年 Facebook Libra 项目中诞生,经历了从 Diem 到 Sui 的重大演化。它的核心设计——线性类型、Ability 系统、模块封装——在 Sui 的对象模型中得到了最充分的发挥。与 Solidity 相比,Move 在类型安全、资产安全和重入防护方面具有本质性的优势。Move 2024 Edition 进一步提升了语言的表达力和开发者体验,使其成为当前最先进的智能合约语言之一。从下一章开始,我们将搭建开发环境,亲手编写第一个 Move 程序,开始真正的 Move on Sui 之旅。