原文:《Web3.0底层语言:Move弥补了Solidity哪些不足?》
作者:宋嘉吉、任鹤义
为什么基于 Solidity 语言的以太坊生态如此庞大,市场依然对新公链有着新期待?Move 出自大厂(Meta),被行业普遍看好,前期一些基于 Move 语言开发的公链得到了市场的青睐和资本的追捧。面向 Web3 更为丰富的应用,底层语言的进化是基础,Move 有哪些优势,弥补了 Solidity 哪些不足?基于这些特点,Move 生态有可能诞生新模式和新应用。
针对已有的编程语言如 Solidity,Move 语言在很多细节设计考虑的比较周到,如将库与应用逻辑分离开来;但最为突出的特点是资源类型方面,即面向资源的编程。在 Dapp 应用支持方面,吸收了比特币 script 和以太坊的 smart contract 的优点,因此行业普遍对该编程语言比较看好。而针对 Solidity 被外界诟病的安全问题,move 也在尝试解决。
Move 是面向资源 (resources) 的编程语言,资源在 Move 的世界里是「第一等公民」(first-class resource),其关键特性是自定义资源类型:resources 永远不能被复制或隐式丢弃,只能在程序存储位置之间移动。Solidity 并不是面向资源的,用户的账户拥有某个 Token 资产,只是该 Token 合约分配给用户的一个数值。而 Move 创建的 Token 账户资产是独一无二的资源类型,比如账户 A 中的资产是保存在 A 账户中的,虽然也是数值,但不能复制、丢弃或重用,可以被安全地存储和转移。同时,账户资产只能由定义该资源的模块进行创建和销毁,这使得同质化的数值类型的资产可能产生的重入、类似双花或者账户余额出现不平衡的状况得以避免。在这一点上,Move 账户资产有些类似比特币的 UTXO 机制,Token 不再是简单的同质化数值,而是可区分的。
为了可以实现更灵活的业务,Move 另外定义了 4 种权限属性:可复制 (copy)、可丢弃 (drop)、可存储 (store)、可检索 (key)。这 4 种属性可以任意组合,来定义资源的属性,方便用户灵活操作。如 drop+store+key 的组合,定义的资源是不可以复制的,可以避免复制引发的代币增发以及双花的问题,这一点类似 NFT 以及比特币的 UTXO 机制。
对于模块化和合约组合性方面,Moe 使用了模块和脚本设计,通过传递资源实现合约交互。Solidity(如以太坊)上面的 Contract 合约通过 library(相当于静态库)进行消息的传递,从而实现 Contract 合约之间的调用、交互。而 Move 语言使用了模块 (module) 和脚本 (script) 的设计,前者类似于 Contract 合约,Move 语言的合约组合性则是模块之间的组合,通过传递资源(即前文提到的 resources)。关于组合性方面,Solidity 和 Move 的区别非常明显。
在交易执行方面,Move 的并行处理相交 Solidity 带来区块链性能的极大提升。并行执行(PE)通过识别独立交易并同时执行,这极大提升了区块链的扩展性。Solidity 并不支持并行处理,如以太坊上的交易按顺序执行,其他交易置于暂停(排序)状态——因此产生了 mempool(内存池)和 MEV 市场。如基于 Move 的公链 Aptos,利用 Block-STM(Software Transactional Memory)引擎实现并行处理,带来性能的明显提升。
Move 出自大厂(Meta),被行业普遍看好,期间一些基于 Move 语言开发的公链得到了市场的青睐和资本的追捧。为什么基于 Solidity 语言的以太坊生态如此庞大,市场依然对新公链有着新期待?Move 所拥有的优势,弥补了 Solidity 哪些不足?基于这些特点,Move 生态有可能诞生新模式和新应用。
本文对比 Solidity 和 EVM 存在的不足,分析了 Move 的优势与特点。
Move 是 Meta(原 Facebook)公司为其 Diem 项目(最初是全球稳定币项目 Libra)开发的一种安全可靠的智能合约语言,Aptos、Sui 等新公链使用的正是 move 编程语言,这些公链正是看中了 Move 的优势及其并行处理特性,可拓展单片链的局限。Move 是基于 Rust 的编程语言,但是 Move 专门针对智能合约进行开发优化,主要用于操作资源,因此入门门槛是低于 rust 的。因为主要针对智能合约,因此砍掉了许多 Rust 多余的操作,更加简洁。为了弥补 Solidity 和 EVM 出现的一些不足,Move 做了一些优化,使得基于 Move 的 Dapp 应用有着更多灵活玩法。
针对已有的编程语言如 Solidity,Move 语言在很多细节设计考虑的比较周到,如将库与应用逻辑分离开来;但最为突出的特点是资源类型方面,即面向资源的编程。在 Dapp 应用支持方面,吸收了比特币 script 和以太坊的 smart contract 的优点,因此行业普遍对该编程语言比较看好。而针对 Solidity 被外界诟病的安全问题,move 也在尝试解决。
与其出现的背景相匹配,Move 是面向资源 (resources) 的编程语言,资源在 Move 的世界里是「第一等公民」(first-class resource),其关键特性是自定义资源类型:resources 永远不能被复制或隐式丢弃,只能在程序存储位置之间移动。它可以像传统的类型一样,可以存储在数据结构中,也可以作为参数传递。简单的说,他就是传统编程语言中一个不可随意销毁的新数据类型。对比 Solidity 定义的资产,如以太坊上的某种 Token 账户,资产只是一个数值,两个账户之间发生转账后,账户资产数值相应的发生变化,不同账户资产的区别就是数值余额,并无本质区别(也就是说资产是同质的)。同时需要注意,例如以太坊上 ERC20 代币 TokenA,其是一个独立的合约账户,这个合约为用户(账户地址)分配一个数值,代表用户拥有的 Token A 的数量。从这一点可以看出,Solidity 并不是面向资源的,用户的账户拥有某个 Token 资产,只是该 Token 合约分配给用户的一个数值。
而 Move 创建的 Token 账户资产是独一无二的资源类型,比如账户 A 中的资产是保存在 A 账户中的,虽然也是数值,但不能复制、丢弃或重用,可以被安全地存储和转移,用并不完全准确的比喻,可以认为 A 账户中的资产与其他账户资产在某种意义上是不完全同质的。同时,账户资产只能由定义该资源的模块进行创建和销毁,这使得同质化的数值类型的资产可能产生的重入、类似双花或者账户余额出现不平衡的状况得以避免。在这一点上,Move 账户资产有些类似比特币的 UTXO 机制,Token 不再是简单的同质化数值,而是可区分的。为了可以实现更灵活的业务,Move 另外定义了 4 种权限属性:可复制 (copy)、可丢弃 (drop)、可存储 (store)、可检索 (key)。这 4 种属性可以任意组合,来定义资源的属性,方便用户灵活操作。如 drop+store+key 的组合,定义的资源是不可以复制的,可以避免复制引发的代币增发以及双花的问题,这一点类似 NFT 以及比特币的 UTXO 机制。
可以这样理解,以太坊(Solidity)的资产是由相应的合约控制,如果把 Token A 合约比喻为保险箱,保险箱会给所有用户分配一个数值余额,来表达用户所有拥有的 Token A 资产数量,但资产本身还是放在 Token A 合约的保险箱内。而 Move 用户账户本身就是一个单独的大保险箱,由用户自己控制,所有的 Token 资产都放在这个保险箱内。且这些 Token 并不是以数字的形式存在,而是不可复制的、权限受用户控制的资源(类型)。
Move 语言中的资源定义与权限是分离的,资源的权限属于用户。Solidity 中账户资源权限归属于合约,比如以太坊上某个 erc20 Token 属于相应的合约,如用户在 DEX 如 Uniswap 合约进行 Token A(权限属于 Token A 的合约)兑换为 Tokend B(权限属于 Token B 的合约)交易时,无法在 Uniswap 合约里直接提取自己的 A 资产换为 B 资产——因为 Uniswap 里资产的权限属于其合约。实际的流程至少三步交易操作:i)首先对 Uniswap 合约进行授权(approve),授权 Uniswap 合约代用户提取 A 合约的资产;ii)进入 Uniswap 合约进行兑换,提取 A 后将 B 存入账户;iii)取消授权(revoke)。但用户一般不会在完成兑换后立刻取消授权(为后续可能还会进行兑换行为节省 gas 费用),一旦 Uniswap 合约受到攻击或者出现漏洞,就为用的 A Token 账户带来风险。需要注意的是,授权 / 取消授权都需要在以太坊上执行合约操作,从而产生 gas 费。从这里我们可以清晰看到,Token A、Token B、Uniswap 里的 LP 资产权限分属于各自的合约,用户无法通过一个账户在三个合约之间自由切换。而 Move 的资产大账户则不需要跨合约授权,权限是属于用户的,用户直接在 DEX 里提取 A、兑换为 B 存储到账户里,这个过程可以在一个交易操作里完成,无需授权 / 取消授权操作,一定程度上提高了安全性。
此前我们的深度报告《Web3.0 时代:开放、隐私、共建》中提出了 Web3.0 与 Web2.0 很大的区别在于开放性、可组合性。那么这种开放调用从底层上是如何实现的呢?Move 语言又将提供哪些便利?
对于模块化和合约组合性方面,Solidity(如以太坊)上面的 Contract 合约通过 library(相当于静态库)进行消息的传递,从而实现 Contract 合约之间的调用、交互。而 Move 语言使用了模块 (module) 和脚本 (script) 的设计,前者类似于 Contract 合约,Move 语言的合约组合性则是模块之间的组合,通过传递资源(即前文提到的 resources)。关于组合性方面,Solidity 和 Move 的区别非常明显。
以部署 Token 合约为例,Solidity 的 Token 作为一种服务存在,可以查询余额,而 Move 的 Token 则是一种资源,也就是上文提到的「永远不能被复制或隐式丢弃,只能在程序存储位置之间移动」。而这两者之间的区别,可以这样比喻:基于 Solidiy 的合约之间调用是通过消息服务,如各类接口的调用,Solidity 上面的合约交互就好比是两个原始部落之间的贸易交流,为了方便两个部落之间的往来,需要统一生产工具和制作方法等标准信息——即两合约之间的状态同步,实现交互。A 部落发明了石斧头,于是将这个石斧头的用材标准、制作方法等信息告知 B 部落,由 B 部落自行生产(这就好比 Token A 和 Token B 分别由各自的合约控制)。注意这里为了安全,合约要保持隔离状态,只能传递消息服务,但消息服务显然是可以复制或被丢弃(将石刻信息擦除)。如果一个合约出现升级,如以太坊 NFT 接口标准 ERC 721、ERC 721A 和 ERC4907 等系列优化升级,就好比 A 部落发明了铁器,因此需要通过消息服务告知对方部落更新生产配置。一个合约的升级,需要调用过该合约的其他合约进行状态同步,跟随升级。这个工作流程无疑会增加复杂性,以太坊合约的升级迭代也是同样复杂的,且会带来 EVM 的字节代码膨胀。
Move 的世界,合约交互则更具备灵活组合性。还是上面的喻例,作为 Move 的模块 (module) 之间交互(即部落之间的交易)是通过传递资源来实现的,这种优化相当于科技升级,A 部落并不是告知 B 部落生产工具的配置信息,而是根据需求,将生产工具(无论是石斧头还是升级后的铁器工具)封装在一辆型号合适的标准运输车中,对方不必需升级生产配置,而是每次只需要接收车辆开走就行。换句话说,Move 的模块之间交互传递的不是消息,而是干脆传递了运输车辆(这是一种资源,永远不能被复制或隐式丢弃,只能在程序存储位置之间移动的资源)。无疑这种模式更具有灵活组合性,接收方收到车辆可以存储,也可以转移给其他方(其他合约),甚至可以将车辆货物卸下来后分装在不同的车辆里。也就是说,一个 Move 模块的升级,其他使用过该模块的合约自动会升级到最新状态。
Move 语言带来的安全改进是多方面的。
Move 语言的资源有四个属性:可复制、可索引、可丢弃、可存储,通过这四个属性的不同组合,用户可以方便的定义出任何类型的资源。Solidity 的资产是由代币合约赋予用户账户的数值余额,相比较,Move 无疑增加了资产的安全性。
Solidity 的资产是由相应的 Token 合约赋予用户的数值。而 Move 规定资源会存储在由所有者的帐户控制的模块里,资源的所有者具有最高决定权,只有所有者能够决定资源(Resource)的存储和转移。操作权限分离,使得不同场景可以定义不同的权限,这也是安全的一面。
Move 资源的设计让数字资产转移不是账户间余额数值的简单加减,而是存储位置间的移动(不可复制、不可丢弃),避免重入和双花攻击。重入指的是黑客抓住代码漏洞,制造恶意合约,在用户转账的同时再次调用转账函数(重入),在不改变账户余额的情况下不断提走资金。对于 Solidity 语言 Token 合约的赋值方案,重入攻击和双花的风险都很大。
另外,Move 的模块工作模式也大大降低了系统风险——如前文所述,Solidity 合约升级需要其他合约作出相应的升级,否则将带来安全隐患,而 Move 的合约升级非常简单,只需相应合约自身升级,并不需要其他合约作出更新,这在一定程度上规避了合约升级不及时带来的安全风险。
在交易执行方面,Move 的并行处理相交 Solidity 带来区块链扩展性的极大提升。并行执行(PE)通过识别独立交易并同时执行,这极大提升了区块链的扩展性。Solidity 并不支持并行处理,如以太坊上的交易按顺序执行,其他交易置于暂停(排序)状态——因此产生了 mempool(内存池)和 MEV 市场。对于两个不关联的交易,如果能够并行处理,则高效且可扩展。
如基于 Move 的公链 Aptos,利用 Block-STM(Software Transactional Memory)引擎实现并行处理,带来性能的明显提升。其工作理念类似以太坊二层网络的 Optimistic Rollup(乐观汇总),交易在区块内是预先排序的,先假设交易之间是没有依赖关系,乐观底执行并行交易。执行后验证所有交易结果,如果发现一个交易访问了由先前交易修改的内存位置,则该交易无效——因为很明显两个交易是相关的。刷新交易的结果,然后重新执行交易。重复该过程,直到区块中的所有交易都被执行。Block-STM 的特点是支持比较复杂的事物,适合多种应用负载工况。
如下图,将 Block-STM 与区块按交易顺序执行进行了比较。每个区块包含一万笔交易,账户数量决定了区块处理的交易的竞争复杂程度。在低竞争和高竞争情况下,Block-STM 比顺序执行的方案实现了 8-16 倍的加速。当交易任务是顺序的情况下,Block-STM 的消耗也更小。由此可见,Move 带来的并发性能是非常突出的。
由此可见,在 L2(二层网络)之前,主链的并行处理能力亦是公链扩容积极考虑的方案。这为 Move 生态带来更多的可能性。