摘要
继Solidity、Rust语言后,从折戟项目Libra中孕育的新一代编程语言Move因公链项目Aptos、Sui又重回聚光灯下。纵观区块链发展历程,每批新兴公链的崛起,往往意味着一定程度的发展范式的改变。Move的再度亮相似乎正暗示着,新语言的叙事正在成为公链竞争的新战场。
以太坊编程语言Solidity引发过不少触目惊心的安全事故。仅2021年区块链生态里被公开的安全事件就超过200例,损失金额超98亿美元,其中由智能合约出现漏洞所引发的安全问题占据绝大多数。由于Libra的愿景是成为全球化货币的金融基础设施,因此Move必须把保证资产安全性放到设计目标的首要位置。
为更好的实现资产安全,Move在语言设计、虚拟机和验证工具三个层面均作出了创新改变:Move为数字资产定义了新的Resource类型,并抽象出资源应满足的两种基本属性,稀缺性和访问权限;通过强数据抽象的Module系统实现账户的权限控制;继承Rust的Ownership系统来实现资产所有权的转移。除此之外,静态调用、形式化验证、字节码校验器等机制的引入也共同为数字资产的安全提供了多重保障。
从语言接受度上看,Move 对开发者非常友好,其宗旨就是降低开发者的安全门槛,使合约开发者可以专注于业务逻辑,上手容易,整体开发者迁移成本并不算高;从生态上看,Move的实际应用场景还处于早期,尚未大面积铺开应用生态。目前使用Move孵化出的公链项目只有Aptos、Sui以及国内的Starcoin。未来,由于Move为金融而生的特性和赛道成熟度等因素,DEX、DeFi、钱包这类金融基础设施会率先落地,紧接着是Socialfi、Gamefi等金融类相关应用。
概述
近日,公链赛道异常活跃:两条逆市狂揽近5亿美元融资的新公链Aptos和Sui引起了行业内的广泛关注。要说它们为何能有如此大的能耐,除了创始团队大部分来自于已然夭折的稳定币项目Libra(后改名Diem)的深厚背景外,更大可能是因为它们从Diem那继承了最核心的遗产 — — Move语言。
过去几年,区块链领域里的每一批新公链崛起的浪潮都包含一定程度的发展范式的转移。现在,以Move为首的Diem系公链似乎正在暗示,新编程语言的叙事已经成为公链竞争的战场之一。
1. Move诞生缘由
世界上的语言这么多了,为何Libra还要“多此一举”设计Move呢?我们知道,Libra的愿景是成为全球化货币的金融基础设施,赋能几十亿人。因此, Move就必须把保证资产安全性放到设计目标的首要位置。然而,过去的编程语言并不能很好地满足这个要求。
据SlowMist Hacked数据不完全统计,仅在2021年里区块链生态里被公开的安全事件就超过200例,损失金额超98亿美元。其中,绝大多数安全问题都出现在生态DApp或DeFi协议中。
更进一步,DApp事故可以分为前端事故、后端事故及合约事故。前端事故主要是DApp中涉及传统信息技术的客户端中出现了安全漏洞导致用户的账户信息、个人信息等被盗从而导致用户的加密资产被盗或损失;后端事故则是DApp中涉及传统信息技术的服务器端出现安全漏洞导致DApp的后台服务与链上交互过程被劫持从而导致用户的加密资产被盗或损失;至于合约事故,通常是协议的智能合约出现漏洞导致的资产损失。从案例占比来看,链上协议的安全事故绝大部分都属于合约安全事故。
典型的合约安全攻击事件包括闪电贷、重入攻击、双花攻击、数值溢出、交易重放、交易回执等。它们均反映出以Solidity为代表的老一代编程语言不仅在语言特性、合约运行和虚拟机设计等方面都或多或少存在着一些安全隐患。
因此,Libra毅然舍弃沿用旧的智能合约编程语言,开发了安全性更高的Move语言,本文也将主要阐述Move的各项安全特性。
2. Move如何保证资产安全性
Move在资产安全性上的表现优于以往的编程语言,很大程度上是因为它站在前人的肩膀上作出了创新型的改进,这些改进主要体现在三个方面,分别是语言设计、虚拟机和链下验证工具。
2.1 语言设计:为数字金融而生
Move在慢慢弱化“数字”属性,强调“资产”属性。为什么这么说?先来看看以往图灵完备的智能合约语言是如何定义数字资产的。
我们知道,以太坊采用的是账户模型,本身是一个巨大的交易状态机,每一笔交易都会改变以太坊的世界状态,而交易又是打包在区块里。所以,以太坊一方面可以看成是打包成一个个区块的交易而形成的账本链条,另一方面也可以看成是随着区块的产生,不断地从一个世界状态跃迁到另一个世界状态的状态机。
进一步来说,每一个账户的信息总和组成了每个时刻的世界状态。同时,对于每一个账户来说,他都是从地址到账户状态的一个映射。这样表达可能比较抽象,我们可以这样通俗来理解。打开你的钱包,你每一张银行卡都对应了一个银行账户。银行卡的卡号就相当于账户的地址,卡号中的资金余额、消费明细、理财产品持仓等等都是这个账户的状态。所以每一张卡在某一个时间点都对应了一个状态。你所有银行卡的集合,就对应了你的资金的世界状态。随着你使用银行卡进行交易、进行消费,你的银行卡的状态就会发生变更,你的资金的世界状态也就会发生跃迁。
这种映射关系主要通过以太坊账户关键字段里的balance来体现。直观上看,这是一个典型的KV键值对(mapping (address => uint256) public balances),Value体现为账户的某种Token余额。所以,Token在Solidity里是用整型数值变量(uint)来表示的,不同账户之间的代币转移过程是通过数字加减法操作的。
下面是使用Solidity实现的一个ERC20的转账逻辑,“from用户“(即发送方给“to用户”(接收方)转Value数量的token,其主要过程为:
i. 从发送方地址(from)映射出初始余额balance,赋值给oldFromVal变量;
ii. 要求oldFromVal大于value,即发送方余额足够;
iii. 从接收方地址(to)的映射出初始balance,赋值给oldToVal变量
iv. 将oldToVal + value并且赋值给newToVal;
v. 将oldFromVal — value并且赋值给newFromVal;
vi. 将newFromVal设置成发送方地址(from)的新balance;
vii. 将newToVal设置成接收方(to)的新balance;
用通俗的例子简化一下,Alice给Bob转账10块钱,调用智能合约在Alice的账户地址下减去10,给Bob的地址上加10。整个修改balance的过程,在中心化金融场景里是常见的扣款逻辑。
不过,链上世界就不一样了。比如说,会不会出现Bob的余额增加了10,但Alice的余额却没改的情况呢?答案是肯定的。我们知道,链上行为大多依赖智能合约,而智能合约都是按照提前设定好的规则自动执行的。回到上面的例子中,如果from和to地址相同,也就是向自己发送token时。按照代码执行的顺序,虽然先在发送方地址减去了转账value,但后面又将newToVal的值覆盖了newFromVal的值,导致的效果是该账户余额一直增加,但没有扣款。这就导致了代币无限增发漏洞。
从这个例子可以看出,用户间的交易过程就是依靠合约代码逻辑来强硬实现的,最终体现为地址下对应的数目更新。它的可靠性依赖合约的开发者,很难保证不会出现一些人为错误。归根结底,Solidity是面向区块链智能合约的语言,并非面向资产的语言。数字资产在Solidity里只是单纯可以被加减的数字,没有进行类型定义;而数字的表意性不够,资产理应有所区别。
a) First-class Resource — — 实现数字资产化
为解决上述问题,Move专门为数字资产定义了新的数据类型First-class Resource,直译为资源是一等公民。一等公民的意思很好理解,就是编程语言在编程时要将一等公民作为首要考虑的被编程对象;至于资源,简而言之,就是数量有限,并且能产生价值的事物即是资源。Move沿袭该思想抽象出了资源在编程时应遵循的两种约束,即稀缺性和访问权限。
●稀缺性
稀缺性是有价值的实物资产的重要属性,比如物理世界的金条,无论中间进行了多少次流转,这根金条都不会从1根变为2根,也不会突然消失;但是数字资产并不存在固有的物理稀缺性。因此,Move认为数字资产计算规则必须以编程方式强制执行这种稀缺性。它规定了系统内的资产的供应要有所限制,资产不能凭空消失,复制现有的资产应该被禁止,创造新的资产应该是一种特权操作。这里提到的编程方式是指Move定义的语法结构Ability。它包含Move为各种变量抽象出的4个属性,可复制(copy ) 、可索引( key )、可丢弃( drop )、可储存( store ),开发者可以相互组合使用这些字段来赋予变量不同的能力。需要注意的是,一旦该变量声明为Resource类型,它只能使用Key和Store属性,无法被添加Copy和Drop。这样一来,Move从编程语法结构上保证了资源类型的稀缺性;
● 访问权限
资产的所有者应该可以通过某种访问控制策略来保护自己的资产。我们知道,以太坊主要依靠的是公钥签名机制,而Move又在这个基础上提供了新的Module系统。我们后面还会详细阐述Module,这里先按下不表。
总的来说,Move利用Resource从底层将资产的概念进行了封装,使得数字资产真正成为了合约变量,不仅可以存储、赋值,还能作为函数/过程的参数和返回值;Ability结构也从语法上体现出Move对Resource变量做出的强制规则,保证了资产不能凭空消失、也不能任意复制,很好的避免了诸如上述无限复制增发漏洞等安全隐患。
b) Module — — 实现权限控制和可组合
Module即模块,和Rust中的Mod、Solitidy中的Contract类似,内部可以声明一系列数据类型和函数,包括Resource,Struct,Function;其中Struct和Resource都是用来定义新的数据结构类型,区别在于Resource无法被复制和丢弃;至于Function函数则和其它大多数语言类似,可以用来创建、销毁和更新Module里声明的类型。整体来看,Module具有以下特性:
●强数据抽象
之所以说Module是强数据抽象的,是因为它规定了数据类型在其声明的Module内部里是透明的,在外部是不透明的,且每个Resource对象都被封装在特定的Module里,由所有者的账户控制和更新,对外提供函数来按照详细的策略来创建、修改和销毁资产。如前文所述,这个特性也常被用于Move的访问权限控制。Module外部无法绕过Module直接对内部的Resource进行操作,必须通过提供的函数有所限制的使用Reource。体现在代码里,外部开发者只能调用Module里的Public类型的函数,根据模块内部的定义进行操作,避免了意外调用带来的安全隐患。
●灵活性与无状态性
Module在设计时仍然保留了对外提供模块的公开接口来进行合约间的相互组合和资源使用,这在功能上和Solidity使用的Interface标准并无太大差别,不同点在于Move语言的模块是无状态的,状态保存在全局存储中。具体来说,Solidity里的诸如ERC-20合约的Token实现更像是一个账本,通过账本的状态变更记录着每个用户与合约交互的完整数据;而Move通过Module将资产封装后分散存储在账户地址下,更像贴好标签的独立保险柜。
c) Ownership系统 — — 实现资产所有权
这个概念是从Rust传承下来的,它对所有权是这样定义的:
1. 每个值(value)都有对应的被称为所有者的变量;
2. 值在任一时刻有且只有一个所有者;
3. 当所有者离开作用域时,值将被丢弃。
简单来说,value只能有唯一所有者,这里的所有者可以是一个函数。当把一个value传递给新的函数时,该函数将成为新的所有者,这里将值传递给函数在语义上与给变量赋值相似。
那么,这个特性在Move里是如何体现的呢?首先,所有的Resource数据都必须存储在账户下面。因此只有分配了账户以后,才会存在对应的Resource资产;其次,每个Resource只要从账户里取出,就必须被“使用”:当使用内置的Move_from方法将资产从账户中取出后,要么将其作为返回值传递,即必须要流向新的账户,要么将其销毁,意味着资产取多少用多少。这也很好体现了交易的本质就是资产所有权的转移。
所以,回过头来看Resource的资源属性,虽然用户之间的Resource交易仍按照资产数值大小进行相应的加减和索引,但与Solidity中强行利用代码逻辑将一个地址的余额减少,另一个地址增加的方式有本质区分;Resource的转移过程更像是搬砖,把资源从一个账户搬到另一个账户,传输期间不会出现丢失和复制的情况,更好的保证了资产安全性。
总的来看,Move通过线性类型概念明确了数据的所有权,强调资源稀缺性、保护性和访问控制,并利用模块系统定义了每个资源的生命周期、存储和访问模式。这些特征共同确保了数字资产不会凭空产生和隐式丢弃,减少了诸如双花、无限增发等安全问题的产生。
2.2 虚拟机:Runtime Bytecode Verifier — — 执行时寻找漏洞
上文介绍了Move的语言设计特性,开发人员可以利用这些特性来编写特定需求的智能合约。不过,Move与Solidity都属于高级编程语言,计算机并不能直接读取和执行这些源代码,因此需要依赖专用的执行器来实现具体的合约。
目前,虚拟机是智能合约主流的实现方式之一,Move也不例外。它可以为程序提供一个完全对底层透明的执行环境,其目的是实现“一次编写,到处运行”的特性,而不是让程序开发人员为兼容每个不同的服务器编写不同版本的程序。这么设计的原因是智能合约是运行在需要进行拜占庭容错来达成共识的分布式系统环境里,所有节点必须按照智能合约约定的规则产生相同的计算结果,否则合约执行的结果无法被各个节点所共识;而虚拟机能屏蔽区块链节点自身执行环境的区别,在所有节点上的运行均一致,实现智能合约的确定性。
Move虚拟机是典型的基于栈的字节码解释器,输入为字节码,加上当前的世界状态,输出是对世界状态的改变;其内部有独立的指令集来执行和处理系统的所有状态变动,用户可以在Move提供的字节码对照表里找到命令对应的意义。
●工作流程
Move虚拟机具体的工作流程为:首先通过编译器将源代码变为低级的字节码Bytecode并传递给虚拟机;收到字节码后,虚拟机要先调用字节码校验器进行校验,验证器可以检查出Move源代码里的各种类型错误;最后,虚拟机解释器按照脚本顺序解释执行,从左到右遍历每个数据或者字节码。运行时基于堆栈执行,遇到数据就压入堆栈,遇到字节码就弹出相应数据的数据,利用字节码进行计算,算完之后再把计算结果压回堆栈,直到退出。
通过这个流程检查源代码可能出现的类型错误主要包括:
i. 栈的越界检查。检查每个函数访问操作数栈的范围以及栈高是否合法,这里的栈是由字节码校验器创建的各个字节码的指令块(basic block);
ii. Type&Kind检查。检查推导栈内的变量的具体类型是否正确;比如栈类无法对Resource类型变量使用CopyLoc字节码,这也是资产稀缺性的直接体现;
iii. 引用检查。防止出现悬空引用,每个引用必须指向被分配的存储位置。
●EVM与Move VM
EVM是链节点为智能合约创造的一个隔离的、可确定的沙盒环境,多个合约程序是运行在同一个进程内的不同的虚拟机沙箱中。这意味着以太坊合约之间的调用是同一个进程内不同的智能合约虚拟机之间的调用,安全依赖于智能合约虚拟机之间的隔离。
而Move虚拟机支持并行执行。合约之间的调用被集中放置在一个沙盒中,在这种架构下,合约的状态的安全性主要要通过编程语言内部的安全性进行隔离,而非依赖虚拟机进行隔离。
2.3 链下工具:形式化验证 — — 执行前寻找漏洞
理想的情况下,Move可以通过运行bytecode校验来发现合约运行时的安全漏洞。但是,这种链上校验的计算成本非常高,且这种计算往往会占用非常多的网络资源,影响链上TPS。因此,Move希望采取的策略是只对关键的安全属性进行轻量级的链上验证,其它的安全问题则是依赖链下的静态验证工具来进行检查。基于这种策略,Move开发了独有的Move Prover形式化验证器来防止人为编写错误,进而提高代码的安全性。
所谓形式化验证,即根据某些形式规范或属性来验证程序的可靠性,这些规范通常是依靠建立数学模型设立的,整个过程按照严格的逻辑推理来得到正确的结果,以此证明合约在正式部署前不存在bug。和传统的人工审计代码安全相比,形式化验证可以解决人工手段无法穷举可能的输入的弊端。
回到Move Prover上,Move为此建立了一套规范化语言Move specification language,通过前提条件、后置条件、不变式等来描述程序怎样才算是正确的运行;然后,通过Move boogie compiler来将Move程序规范转换成boogie程序。Boogie是微软开发的形式化验证中间语言,目的是为其它语言构建程序验证器,同时也是形成验证条件传递给证明求解器的工具。最后,通过求解器给出的结果来判断程序是否符合规范,若不规范则给出具体解决路径。
● 静态调用
值得一提的是,Move Prover是静态验证工具,这是因为Move从机制设计上是完全不支持动态调用的。动态调用指,如果一个程序在调用另一个程序时,必须在运行时才能确定被调用的目标,则称这种合约调用是动态的。从机制上看,它有点类似服务器的远程调用。但是,动态调用会引发循环调用时的并发问题。比如A合约调用B合约,而B合约又调用A合约,A合约的前一次执行未完成又进行下一次执行,导致后面的执行无法读取到前面的中间状态,最终引发安全漏洞。著名的The DAO事故就是由合约动态调用问题导致的。因此,在Move设计里每个函数调用都是静态的,开发者能够在程序运行之前,知道它以某种顺序调用了哪些函数。这种静态类型系统更适合通过形式化验证推理,将问题的暴露前置到了编译阶段,以降低在运行时出现bug的可能,很好的分担了安全检查的压力。
3. 总结与展望
如果说从比特币Script到以太坊Solidity是合约表达能力的变革,那从Solidity到Move就是合约安全能力的进化。因此,Move也被寄托了更高的市场期待。
从语言机制上,Move对资产安全的探索方向是十分激动人心的。它在语言设计、虚拟机和验证工具三个层面均作出了创新改变,这些设计是一个有机的整体:面向资产编程的设计,使得 Move 语言与支持去中心化金融应用的部署天然适配;线形逻辑、访问控制、静态调用、形式化验证等特性也为数字资产的安全提供了多重保障。
从语言接受度上看,Move 对开发者非常友好,其宗旨就是降低开发者的安全门槛,使合约开发者可以专注于业务逻辑;与此同时,Move由Rust和Solidity演进而来,舍弃了两者设计中不必要的“糟粕”,复杂度相对较低,因此整体开发者迁移成本并不算高。据Move实践者透露,对有Rust、Solidity编程经验的开发者而言,上手Move的时间大概只需花费1–2天;对无智能合约编程基础的开发者而言,从零学习Move也大致只需要1–2周。
从生态上看,Move的实际应用场景还处于早期,尚未大面积铺开应用生态。除了已经夭折的Libra外,目前使用Move孵化出的公链项目只有Aptos、Sui以及中国的StarCoin。其中Aptos与Sui均处于测试网阶段,二者均在Move的基础上做了不同方向上的探索:Sui引入了不可变状态,试图在Move中实现类似UTXO的编程模型,而Aptos 在探索Layer1上的交易并行执行以及更高性能;至于Starcoin,其主网已于2021年5月启动,目前在探索Layer2乃至Layer3的分层扩展模式。而随着这几个项目后续发展的推进,我们也会看到各自生态内一些更细分领域的项目落地。由于Move为金融而生的特性,可以预见到第一批落地的项目仍会是DEX、DeFi、钱包这类金融基础设施;随着发展成熟度的提高基础设施的完善,更多金融相关赛道如Socialfi、Gamefi等才会逐步引入到生态中。
本轮新公链叙事的最大焦点毫无疑问是Move,它能否借着这股东风在市场里大施拳脚,我们共同期待。
参考资料
1. https://diem-developers-components.netlify.app/papers/diem-move-a-language-with-programmable-resources/2020-05-26.pdf
2. https://move-book.com/cn/index.html
3. https://move-dao.github.io/move-book-zh/modules-and-scripts.html
4. https://jolestar.com/why-move-1/
5.https://mirror.xyz/0xbuidlerdao.eth/MePeSGYe63OX8xXb8IwIrXzGk_S606NG7SR879XMXRE
6. https://mp.weixin.qq.com/s/bSS9GAcVp6tuWjedpTysQw
7. https://starcoin.org/zh/developers/others/starcoin_ecology/
8. https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/docs/design/virtual_machine/evm.html
9. https://doc.rust-lang.org/book/
10. https://solidity-cn.readthedocs.io/zh/develop/
关于火币研究院
火币区块链应用研究院(简称“火币研究院”)成立于2016年4月,于2018年3月起致力于全面拓展区块链各领域的研究与探索,以泛区块链领域为研究对象,以加速区块链技术研究开发、推动区块链行业应用落地、促进区块链行业生态优化为研究目标,主要研究内容包括区块链领域的行业趋势、技术路径、应用创新、模式探索等。本着公益、严谨、创新的原则,火币研究院将通过多种形式与政府、企业、高校等机构开展广泛而深入的合作,搭建涵盖区块链完整产业链的研究平台,为区块链产业人士提供坚实的理论基础与趋势判断,推动整个区块链行业的健康、可持续发展。
联系我们:
咨询邮箱: research@huobi.com
官方网站: https://research.huobi.com/
Twitter: Huobi_Research
https://twitter.com/Huobi_Research
Medium: Huobi Research
https://medium.com/huobi-research
Telegram: Huobi Research
https://t.me/HuobiResearchOfficial
免责声明
1. 火币区块链研究院与本报告中所涉及的项目或其他第三方不存在任何影响 报告客观性、独立性、公正性的关联关系。
2. 本报告所引用的资料及数据均来自合规渠道,资料及数据的出处皆被火币区块链研究院认为可靠,且已对其真实性、准确性及完整性进行了必要的核查,但火币区块链研究院不对其真实性、准确性或完整性做出任何保证。
3. 报告的内容仅供参考,报告中的结论和观点不构成相关数字资产的任何投资建议。火币区块链研究院不对因使用本报告内容而导致的损失承担任何责任,除非法律法规有明确规定。读者不应仅依据本报告作出投资决策,也不应依据本报告丧失独立判断的能力。
4. 本报告所载资料、意见及推测仅反映研究人员于定稿本报告当日的判断,未来基于行业变化和数据信息的更新,存在观点与判断更新的可能性。
5. 本报告版权仅为火币区块链研究院所有,如需引用本报告内容,请注明出处。 如需大幅引用请事先告知,并在允许的范围内使用。在任何情况下不得对本 报告进行任何有悖原意的引用、删节和修改。