想象你是大型仓储中心的调度员。仓库里没有普通货物,只有一箱箱绝版限量球鞋——每一箱都独一无二,你的系统不允许“复印”出一模一样的第二箱,也不允许任何人悄悄把某箱扔进垃圾桶。你的全部工作,就是对照货单,把球鞋从 A 区搬到 B 区、从 B 区交到客户手里。一旦系统松口,允许复制或销毁任意一箱,整个仓库的账目就会在几分钟内崩塌。区块链世界的数字资产,恰好面临同样的风险:以太坊上臭名昭著的“重入攻击”,本质就是黑客趁账目没更新,“复印”走了本该只属于自己的一份钱。Move 语言(Aptos 和 Sui 两大公链的合约语言)正是为堵住这类漏洞而生——它把数字资产设计成“不可复制、不可丢弃”的资源,再配上所有权机制、形式化验证等一整套制度,让安全不再依赖程序员的小心翼翼,而是刻进语言本身的基因里。这篇教程,就用仓库管理员的视角,带你拆解这套制度。
对 AI 从业者而言,Move 展示了“安全内建于语言”的设计哲学,也为 AI 生成内容确权、可信数据市场等场景提供了天然的资产底座;掌握它,你就拿到了下一代 Web3 基础设施的入场券。
目录
可视化演示
展示Move资源取向编程、VM形式化验证与Aptos/Sui并行执行模型,交互切换理解安全合约核心技术。
本节配有交互动效(进入视口后自动加载,约 3KB)。
'+modeData.resource.caption+'
1. 资源模型:仓库里搬不动的黄金
仓库管理员小美接到一项新任务:设计一套系统,保证任何员工都不能复印限量球鞋,也不能把球鞋丢进碎纸机。她很快就发现,靠“员工自觉”是行不通的——总有人手滑或使坏。唯一可靠的办法,是从制度上规定:球鞋这类物品,天生没有“复印”和“销毁”两个按钮。Move 语言的设计者做了同样的事情。
在传统以太坊合约里,代币余额只是账本映射里的一个数字,合约代码稍有不严谨,就可能被反复提款——这正是“重入攻击”的根源。Move 换了个思路:把资产定义成一种特殊类型,叫“资源”(resource)。资源像实体货物一样,只能被整体搬运,不能被复制(因为没有 copy 能力),也不能被丢弃(因为没有 drop 能力)。定义资源的语法如下:
这正是给球鞋箱贴封条:“本箱不可复印、不可销毁。”Move 编译器扮演铁面无私的安检员——任何违反封条规则的代码,在编译阶段就被红牌罚下,根本走不到链上。你看,安全不是靠运气,而是靠制度。
has 后面列出的“能力”(abilities)决定了这种资源能做什么、不能做什么——就像球鞋箱上贴的封条。- struct Coin声明一种新的数据类型 Coin,它是资源货物的“箱型”——全仓库只认这种规格的箱子。
- has store, key为类型赋予两种能力,
store表示它可以存放在全局存储中,key表示它能作为账户状态的入口键;注意这里没有copy和drop,复制与销毁因此被禁止。 - 字段定义花括号内是类型的字段,
value是u64(无符号 64 位整数),用来记录金额。
2. 语言基础:仓库的规章制度手册
一栋大仓库不可能靠口头约定运转,必须有一本规章制度手册,写清楚“谁负责收货、谁负责发货、货物怎么打包”。在 Move 里,这本手册就是模块(module)。一个模块把相关的类型和函数收拢在一起,发布到链上后,所有人都能按手册办事,但没人能偷改手册内容。
模块里,每个函数都是一条“操作流程”(fun),每个结构体都是一类“货物规格”。Move 还提供了copy、drop、store、key四种能力,相当于给货物盖四种印章:盖了“可复制章”才能复制,盖了“可销毁章”才能丢弃。类型系统会在编译期检查每一枚印章,一旦发现操作越权,立即报错。一个典型的铸造(mint)流程长这样:
有了这本手册,任何开发者都能照着流程操作,却无法篡改规则。Move 的模块机制,让“规章制度”本身也成为了不可变的基础设施,这比靠某个人自觉遵守规则可靠得多。
my_addr::coin 里定义了一个公开函数 mint,输入签名者引用,输出一个全新的 Coin 资源。- module 声明
module是 Move 代码的容器,相当于仓库的规章制度手册,规定了所有操作流程。 - 双冒号命名
发布者地址::模块名构成全局唯一标识,就像手册的编号,全网不重样。 - public fun
public表示该函数公开可调用,fun是函数关键字,mint接受一个签名者引用&signer(代表交易发起人)。 - 返回类型
: Coin表示函数返回一个 Coin 资源——铸造新币的过程,就是生产出一箱新的“限量球鞋”。
3. 所有权机制:谁的东西谁做主
球鞋箱在仓库里不会乱跑:每箱都登记在某个客户的货单名下,搬运必须“货单合一”。Move 用一套全局存储操作来管理这种归属关系——move_to 相当于把货登记到你名下,move_from 相当于从某个货架上取走,borrow_global 则像是隔着玻璃看一眼货物清单,可以读,但不能伸手拿。
这套机制与账户体系深度绑定:只有交易的签名者(&signer)能在自己名下存入资源;想读取别人的资产,只能用引用;想转走别人的资产,必须由对方配合签名。所有权转移的语义一目了然——资源从 A 的存储中消失,出现在 B 的存储中,中间不存在任何“临时副本”。
小美在仓库里巡视时感慨:以前最怕两箱球鞋被“顺手牵羊”或“狸猫换太子”,现在每箱球鞋都有明确的货主和唯一的搬运路径。所有权机制的真正价值,在于让“谁的东西谁做主”这句话,从口号变成了编译器强制执行的规则。
- move_to将资源存入账户名下,成功后该资源就归这个账户“保管”。
- 签名者引用 &signer
&signer是交易的签名凭证,只有持有它才能在自己的存储中存放资源,防止别人往你名下乱塞货。 - borrow_global按地址读取某个账户下的资源,返回的是引用而非所有权。
- 返回 &T
&T表示“借阅”——可以看、可以算,但不能拿走、不能销毁,读完之后原物归位。
4. 安全特性:铁打的保险柜
仓库最值钱的是安全感。小美在仓库里加了三道保险:第一道是“传送带单向通行”——货物只能沿固定方向搬运,无法原路返回偷偷再搬一次(对应防重入);第二道是“会计数学证明”——不管怎么搬,全库总库存永远不变(对应资产守恒);第三道是“审计员的全程验算”——用形式化验证对关键性质给出数学级证明。
Move 的安全性根植于线性逻辑:每个资源在交易结束时必须被“妥善处理”(转移、销毁或存入),不能凭空消失,也不能凭空多出来。这让“重入攻击”失去了土壤——攻击者想重复提款,就必须凭空复制资源,而这是类型系统不允许的。更妙的是,Move 的资产守恒可以用公式精确刻画:
Move 生态还配套了形式化验证工具(如 Move Prover),它能把“没有重入漏洞”“不会有人饿死(资金不足必失败)”等性质写成数学规范,再用证明器自动验证。这就好比给仓库请了一位永不疲倦的审计员,把 “账目永远平衡” 变成了一条可证明的定理,而不是一句美好的愿望。
- ∀t对任意时刻 t 都成立,不是“大概守恒”,而是“永远守恒”。
- 账户集合 A$\mathcal{A}$ 代表链上所有账户的集合,求和范围是全网。
- 左边求和时刻 t 的全网总余额,也就是交易发生前仓库的总库存。
- 右边求和下一时刻 t+1 的总余额,交易结束后库存必须分毫不差。
5. Aptos与Sui:同一标准的两家分店
把 Move 带到市场的有两家“连锁仓库”:Aptos 和 Sui。它们师出同门,都遵守“资源不可复制、不可丢弃”的核心制度,但装修风格和运营流程各有千秋。Aptos 更像原版旗舰店,保留 Move 的经典语义,并设计了 Block-STM 乐观并发机制来提升吞吐;Sui 则大刀阔斧地改成“对象模型”——每个资源都是一个带全局唯一 ID 的对象,所有权直接记录在对象上。
对象模型带来一个巨大红利:并行执行。传统区块链把交易排成一列纵队挨个执行,慢且拥堵;Sui 则把交易按依赖关系分组,互不干扰的交易可以同时办理。判断两笔交易能否并行的依据十分清晰:
Aptos 和 Sui 的竞争,本质上是在同一套安全制度下的两种效率探索。对开发者而言,学懂 Move 的底层资源模型,就能在两套生态间自由迁移——制度不变,只是换了排队的规则。
- Tx₁ 与 Tx₂两笔待执行的交易,可以理解为两位客户同时来仓库办事。
- 并行符号 ∥表示两笔交易可以同时执行,不需要排队等待。
- deps(Tx)一笔交易所依赖的对象集合,即它会读取或修改的资源清单。
- 交集为空当两份依赖清单没有任何重叠时,互不干扰,并行安全;一旦有重叠,就老老实实排队。
6. 合约实战:开工搭建自己的仓库
理论说了一堆,小美决定亲手搭建一个迷你仓库。Move 合约的开发流程像一个标准流水线:写模块 → 写测试 → 形式化验证 → 部署发布。先用 move new 初始化项目,再在 sources/ 里写模块文件,定义资源与函数,然后用 move test 跑单元测试,用 Move Prover 验证不变量,最后用 CLI 工具(Aptos CLI 或 Sui CLI)编译发布。
一个关键的实战细节是断言:合约里处处需要“条件不满足就回滚”的检查。比如转账前必须确认余额充足,否则整笔交易作废。Move 提供了 assert! 宏,其语义等价于一条逻辑蕴含:
搭建完成那天,小美把所有测试跑了一遍:复制被拒、丢弃被拒、余额守恒、重入失败——全部通过。她忽然明白,Move 的价值不在于让你少写几行代码,而在于把“安全”从开发者的自觉里解放出来,装进了语言的骨架。写 Solidity 像是在悬崖边开车,全凭车技;写 Move 则像在护栏完整的赛道上行驶,容错率天然高出一大截。
- assert! 宏Move 内置的断言函数,是合约里最常用的安全检查语句。
- 条件 b一个布尔表达式,比如“余额 ≥ 转账金额”。
- 错误码 e一个
u64错误编号,用来告诉调用者失败原因。 - 蕴含关系逻辑蕴含的意思是“若 b 为假,则执行 abort(e)”,即不满足条件就整体回滚。
AI 应用场景
链上 AI 模型资产确权
阶段:模型部署与分发阶段
解决的问题:AI 模型权重、API 额度或商用许可证都是高价值数字资产,传统方式用中心化数据库登记,极易被复制盗用、超量分发。Move 将许可证铸造成“不可复制、不可丢弃”的资源,买家的授权只能整体转移、无法二次复制,从源头杜绝盗版扩散。
具体案例:某 AI 公司把“商用许可证”发布为 Sui 上的资源对象,每家企业购买后获得一个专属许可资源,转授权给子公司时执行 move 转移,链上全程留痕,审计方随时可查。
DeFi 稳定币与借贷协议开发
阶段:智能合约开发与安全审计阶段
解决的问题:传统 DeFi 合约频繁遭遇重入攻击和“凭空铸币”漏洞,根因是资产在转账中可能被复制。Move 用资源类型 + 资产守恒公式,让“总资产不变”成为编译期与证明器可验证的性质。
具体案例:Aptos 生态的某稳定币协议用 Move 实现抵押铸造与清算,开发者用 Move Prover 验证“清算后总抵押物不减少”,审计报告因此大幅缩短。
链游道具与 NFT 市场
阶段:应用层合约开发阶段
解决的问题:游戏道具与 NFT 的核心诉求是“稀缺性”,传统合约却能通过漏洞复制道具。Move 把每件道具设计为资源对象(Sui 上每个对象有唯一 ID),复制被语言禁止,玩家间的交易只是所有权转移。
具体案例:某链游用 Move 发行武器皮肤,玩家交易走 move 转移;官方活动空投也是铸造新资源而非复制旧资源,刷道具漏洞从机制上消失。
Python 演示
"""简化模拟 Move 语言的资源语义:不可复制、不可丢弃、必须显式转移。""" class Resource: def __init__(self, name, value): self.name = name self.value = value def copy(self): raise TypeError("复制被禁止:资源是不可复制的") def drop(self): raise TypeError("丢弃被禁止:资源是不可丢弃的") class Account: def __init__(self, address, balance=0): self.address = address self.balance = balance self.resources = {} def move_to(self, resource): self.resources[resource.name] = resource self.balance += resource.value def move_from(self, name): if name not in self.resources: raise KeyError("资源不存在: %s" % name) asset = self.resources.pop(name) self.balance -= asset.value return asset def transfer(sender, receiver, name): """Move 风格转账:先从发送方移除,再存入接收方,中间无副本。""" asset = sender.move_from(name) receiver.move_to(asset) def main(): alice = Account("0xalice", 100) bob = Account("0xbob", 0) alice.move_to(Resource("Coin", 30)) print("转移前: alice=%d bob=%d total=%d" % ( alice.balance, bob.balance, alice.balance + bob.balance)) transfer(alice, bob, "Coin") print("转移后: alice=%d bob=%d total=%d" % ( alice.balance, bob.balance, alice.balance + bob.balance)) try: bob.resources["Coin"].copy() except TypeError as err: print("尝试复制 ->", err) try: alice.move_from("Coin") except KeyError as err: print("Alice 重复转出 ->", err) if __name__ == "__main__": main()
class Resource:定义资源类型,模拟 Move 的 struct,资源天生没有复制与丢弃许可。def copy(self): raise TypeError(...)模拟 Move 缺失 copy 能力时编译器的拒绝,运行时同样拦截。alice.move_to(Resource("Coin", 30))铸造并存入资源,余额同步增加。asset = sender.move_from(name)从发送方存储中移除资源;不存在则抛异常,防止凭空取货。transfer(alice, bob, "Coin")体现 Move 转移的完整语义:先移出、再移入,任何一步失败整单作废。
常见误区
误区我熟悉 Solidity,看看 Move 的语法差异就能上手了,它无非是把 `mapping` 换成了 `struct`,把函数名改一改。
正解Move 与 Solidity 是两种思维范式。Solidity 默认一切可复制、可随意操作,安全靠开发者“自律”;Move 默认资产不可复制、不可丢弃,安全靠编译器强制。好比开车:Solidity 是悬崖边没护栏的公路,Move 是有防撞栏的赛道。如果只用 Solidity 的思维写 Move,你会本能地去找“复制”的入口,然后发现处处碰壁。
为何常见:人的直觉是“新语言 = 旧语言换皮”,尤其当两者都叫“智能合约语言”时,容易忽略底层资源模型的根本差异。
误区既然 Coin 不能复制,那我岂不是连查个余额都不行?那 DeFi 应用怎么展示用户的资产?
正解Move 区分了“拥有”和“借用”。通过 borrow_global 返回的 &T 引用,你可以自由读取字段、参与计算,只是不能把别人的资产拿走或变出一份新的。就像逛仓库,你可以隔着玻璃清点球鞋、记下款式,但不能把鞋穿上就跑。读取不产生副本,所以不违反线性逻辑。
为何常见:把“不可复制”误解为“不可访问”,是因为我们习惯了普通编程语言里“读 = 复制一份到临时变量”的实现方式。
误区Move 把一切搞得这么严格,估计只能发币、做借贷,想做 NFT 或者复杂业务逻辑肯定力不从心。
正解Move 的“资源”抽象恰好是 NFT、链游道具、身份凭证、AI 模型许可等一切“稀缺数字物品”的天然载体。Sui 的对象模型甚至让每个 NFT 都是独立对象,可并行转移。不能复制不是限制,而是特性——只要你的业务里存在“不能随意复制的有价值事物”,Move 就是最贴切的工具。
为何常见:Move 最出圈的案例确实是稳定币,让人误以为它只擅长金融场景,忽略了“资源”这一更普适的抽象。
误区都叫 Move,那我在 Aptos 上写的合约直接复制到 Sui 上就能跑。
正解两家的 Move 已经从共同祖先分叉出了显著差异。Sui 将能力系统精简为 copy、drop、store、key 的重新诠释,引入全局唯一对象 ID 和“所有权在对象上”的模型;Aptos 则保留更接近原版的全局存储语义。跨链部署需要修改数据模型和存储访问方式,不能直接拷贝。学习时建议先吃透资源语义,再针对目标链补差异。
为何常见:两套生态都叫“Move“,官方文档又都强调同源,容易被”同名即同物”的错觉误导。
误区我用 Move Prover 验证过了,所有性质都证明通过,合约可以放心上线,不用再审计了。
正解形式化验证只保证“你写进规范的属性成立”,不保证“你没写进规范的属性成立”。比如你证明了“总余额守恒”,但忘了验证“只有合约 owner 能铸造”,那么后一个漏洞依然存在。形式化验证是强大的数学审计员,但审计标准需要人来制定;它让“已知风险”消失,却无法覆盖“未知风险”。最佳实践是:验证 + 人工审计 + 测试三重叠加。
为何常见:“数学证明”听起来绝对可靠,人们容易高估工具的覆盖面,把“证明了一些性质”误当成“证明了没有漏洞”。
面试题
Q1. Move 语言中“资源”最核心的特征是什么?
- A.可以被任意复制克隆
- B.不可复制、不可丢弃← 正确答案
- C.只能被管理员创建
- D.无需归属任何账户
答案:B。Move 资源像仓库里的实体球鞋,天然没有“复印”和“销毁”按钮——这通过缺失 copy/drop 能力实现。A 描述的是普通变量,C 是业务规则而非语言特性,D 与“资源必有归属”相悖。
Q2. 在 Move 中,要允许一种类型被复制,需要声明哪种能力?
- A.key 能力
- B.store 能力
- C.copy 能力← 正确答案
- D.drop 能力
答案:C。copy 能力就是“可复制印章”,盖了章的才允许复制。key 用于全局存储入口,store 用于存入存储,drop 用于允许销毁。四者含义不同,别把 “store“ 与 ”copy” 混为一谈。
Q3. Move 的 move_to 函数要求第一个参数是什么?
- A.一个普通地址地址
- B.签名者的引用对象← 正确答案
- C.可变资源的引用
- D.无符号的大整数
答案:B。move_to(s: &signer, r: T) 要求传入签名者引用,确保只有交易发起人能把资源存入自己名下。普通地址没有签名权,无法证明“是我本人”,因此不能用于 move_to。
Q4. 下列哪项是 Move 语言能够在语言层面防范的攻击?
- A.重入攻击← 正确答案
- B.SQL 注入
- C.钓鱼攻击
- D.DDoS 攻击
答案:A。重入攻击依赖“资产被复制”或“状态未更新可重复调用”,而 Move 资源不可复制、转移后原位置即失效,从类型系统上堵死了重入。其余三类是链下或网络层问题,不属于 Move 的职责范围。
Q5. 关于形式化验证,下列说法正确的是?
- A.用数学方法证明合约性质← 正确答案
- B.可以完全替代测试环节
- C.需要先编写形式化规范← 正确答案
- D.只能用于 Solidity 合约
答案:AC。形式化验证通过证明器验证规范(spec)中的性质,A、C 正确。它不能替代测试(B 错),因为规范之外的风险无法覆盖;Move 生态的 Prover 是形式化验证主力工具,D 明显错误。
Q6. Sui 对 Move 最重要的改造是什么?
- A.引入对象模型支持并行执行← 正确答案
- B.删除了核心的资源类型
- C.改用 JavaScript 的语法
- D.取消了所有权转移机制
答案:A。Sui 把每个资源变成带全局唯一 ID 的对象,所有权记录在对象上,依赖不重叠的交易可并行处理。B、D 违背 Move 根基,C 纯属虚构。
Q7. 关于 Move 资源遵循的“线性逻辑”,下列说法正确的是?
- A.每个资源恰好使用一次← 正确答案
- B.资源可以被无限复制
- C.使用后资源即被消费← 正确答案
- D.资源可以随意被销毁
答案:AC。线性逻辑规定资源在交易结束时必须被妥善处理且不会产生副本——A 的“恰好使用一次”和 C 的“使用后被消费”都正确。B 是普通变量的行为,D 需要 drop 能力,并非默认特性。
Q8. 下列哪些属于 Move 语言的能力(abilities)?
- A.copy 能力← 正确答案
- B.drop 能力← 正确答案
- C.store 能力← 正确答案
- D.mint 函数
答案:ABC。Move 的四种能力是 copy、drop、store、key,A、B、C 正确。mint 是常见的函数名(铸造函数),不是能力;能力是类型声明里的印章,函数是操作流程。
Q9. 在 Move 中,模块(module)的主要作用是什么?
- A.一个智能合约实例
- B.一组命名空间容器← 正确答案
- C.用户的链上账户
- D.网络中的挖矿节点
答案:B。模块是 Move 的代码组织单位,类似仓库的规章制度手册——把类型和函数收拢在一个命名空间下,供其他模块调用。它不是某个具体实例(A),不是账户(C),更不是节点(D)。
Q10. 关于 Sui 的对象模型,下列说法错误的是?
- A.每个对象都有全局唯一 ID
- B.对象可以被任意复制使用← 正确答案
- C.所有权转移改变对象拥有者
- D.独立对象可并行处理
答案:B。Sui 对象模型的核心是“对象 + 唯一 ID + 所有权”,对象是稀缺资源,不能任意复制,B 错误。A、C、D 都是对象模型的正确描述,因此本题选 B。