6月18日,由Facebook发起的加密数字货币项目Libra正式亮相。Libra的白皮书与多个技术文档也同步发布本文从技术角度对Move语言进行解读,带领大家对这门语言做一个初步的了解。
数字资产管理
Move最吸引眼球的特性是它提出的一套完整的面向数字资产的编程体系。与现有的区块链编程语言相比,Move着重强化了数字资产的地位。使用Move语言,开发者能够更灵活、安全地在链上定义和管理数字资产。
面临的挑战
在区块链上定义数字资产具有挑战性。因为物理世界中资产的一些特性,难以在数字世界进行表达。
稀缺性:资产的供应应当受到管控。资产不能被复制,创建新的资产是特权行为。
访问控制:系统要能够确保参与者自身的资产受到访问控制策略的保护。
现有解决方案
现有的区块链编程语言都具备在区块链上定义数字资产的能力,但仍具有一些不足和局限。下面以比特币和以太坊为例,作一个简要的说明。
比特币通过UTXO的方式对其资产进行编码,并使用比特币脚本来定义资产的转移规则,确保稀缺性和访问控制。开发者可以使用比特币脚本来定义各种不同的访问控制策略。
然而,比特币脚本具有相当的局限性,扩展性很差。比特币脚本不是图灵完备的语言,开发者也不能在其中自定义数据类型和过程等。因此,想要在比特币区块链上定义一种新的数字资产,或者实现更复杂的访问控制策略,只能借助一些外部的手段,对开发者不友好。
以太坊使用数值代表以太币,并在系统层面保证了以太币的稀缺性和访问控制。开发者可以使用EVM字节码来编写智能合约,与链上的数字资产进行交互。EVM字节码是一种图灵完备的语言,具有很强的扩展性。开发者不仅可以使用它操作以太币,也可以自定义数字资产,编写复杂的访问控制策略。此外,也可以使用更高级的Solidity语言来编写智能合约,Solidity代码在运行之前会被编译为EVM字节码。
然而,在以太坊中,自定义的数字资产的级别要低于以太币。以太币的安全性有系统级别的保护,而自定义数字资产的安全性只能由开发者来保障。无论是底层的EVM字节码还是高级的Solidity,开发者都只能用数值来间接代表数字资产。由于在编程语言的层面没有对稀缺性和访问控制提供支持,开发者在编码过程中很容易发生错误,从而导致严重的后果,比如资产的复制、重用和丢失等。
一等资源
针对上述问题,Move提出了一等资源的概念。开发者不仅可以利用一等资源来实现安全的数字资产,也可为数字资产编写正确的业务逻辑。
Move将数字资产与其他数据类型(如整数、布尔等)区分开来,将其定义为资源类型。资源类型的语义受到了线性逻辑的启发:一个资源不能被复制,也不能被隐式的丢弃(即在程序结束时,资源处于无所属的状态),只能进行转移。除了上述区别以外,资源类型在其他方面与普通的数据类型一样,都可以保存在数据结构中,可以作为过程的参数等。
Move使用模块对资源进行管理。首先,资源的类型结构定义在模块中,每个资源都必然定义在某个模块中。其次,模块中还定义了能够操作该资源的过程,例如创建、修改、销毁等。开发者可以且只能通过模块提供的公开过程来对资源进行操作,不能自行编码修改资源。也就是说,当使用资源来定义一种数字资产时,其所有的业务逻辑,包括访问控制策略,均已经在模块中定义。而任何脱离、绕过该模块对数字资产进行的操作都是非法的、不被允许的。
在Libra区块链中,Libra代币也被定义为一个资源类型,与用户自定义的资源一致。这意味着自定义数字资产与Libra代币都受到了同样的保护。
不过,需要注意的是,Move语言对数字资产提供的安全性约束仅限于模块之外,模块内部的安全性约束仍然需要开发者自行保证。
灵活性
Move语言的另一个特点是其为Libra提供了更高的灵活性,主要包含以下两个方面。为便于理解,下文使用以太坊及Solidity语言作比较。
交易脚本
以太坊的交易中,包含了目标智能合约的地址,以及提供给目标智能合约的输入数据。如果使用Solidity语言,则该输入数据的内容为函数签名以及参数列表。即,以太坊中的一笔交易,实质上是调用了一个合约的一个接口(不考虑合约间的调用)。
在Libra的交易中,取而代之的是交易脚本。交易脚本是一段完整的、任意内容的Move程序。在交易脚本中,可以调用已经发布在账本状态中的模块的过程若干次,并基于调用的结果做一些额外的本地处理,例如简单的控制流等。
交易脚本为Libra提供了极大的灵活性。用户不仅可以像以太坊那样,调用某个模块的中的过程,还可以很轻易地执行一些一次性的行为。例如,一次性将某个代币转账给多个人,即使该代币的模块没有提供批量转账的过程。
模块系统
以太坊和Libra均使用了基于账户的账本状态。
在以太坊中,账户分为用户账户和合约账户。用户账户不包含数据(以太币除外)。合约账户中,既包含了代码,又包含了数据。从面向对象的角度来看,以太坊的智能合约就像是单例对象。由于数据无法直接保存在用户账户中,以太坊上的大多数数字资产,都实现了一个单独的ERC20 Token合约,在其中包含了所有用户的资产信息。
Libra中可以利用Move模块实现类似智能合约的功能。但Move模块与以太坊中的智能合约在设计上并不相同,要更为灵活。Move模块仅包含代码(包括资源结构定义和过程),而数据保存在资源中。虽然对资源的操作需要通过模块中的过程来进行,但二者并没有捆绑在一起。同一类型的资源可以有若干个,并且发布在不同的账户下。因此,在Libra中,数字资产保存在用户自己的账户下,而不是像以太坊一样集中保存。此外,可以在一个Move模块中定义多种资源类型结构。
虽然Move中模块、资源、过程的关系与面向对象编程中类、对象、方法的关系有些相像,但实际上有很大的区别。Move模块的设计更像是函数式编程的风格。
安全性
Move在设计时充分考虑了安全性,不符合安全性要求的Move程序将会被拒之门外。
类型化的字节码
Move必须拒绝不满足关键安全属性(如资源安全性、类型安全性、内存安全性等)的程序,因此需要在程序执行之前进行链上验证。为此,Move采用了类型化的字节码作为可执行程序的格式。
类型化的字节码介于高级语言和汇编语言之间。以以太坊为例,Solidity是一种高级语言,而EVM字节码可以认为是汇编语言。Solidity中虽然也做了各种各样的验证,能够保证编译出的EVM字节码具有一定的安全性。然而,由于EVM在执行时所使用的是EVM字节码,而Solidity对安全性的保证实际上发生在编译过程中,若要进行链上验证,则必须把编译过程放到链上。否则,攻击者完全可以绕过Solidity,直接编写具有漏洞的EVM字节码。一方面,编译过程在链上进行势必会对性能造成影响。另一方面,EVM字节码缺乏数据类型信息,难以进行验证。综上所述,采用类型化的字节码,既提供了安全保证,也避免了编译带来的性能损耗。
更利于静态验证的设计
出于计算成本的考虑,Move的链上验证仅包含了部分关键的安全属性。除了链上验证,Move也被设计为支持高级的链下静态验证。为此,Move做了以下设计,使其比大多数通用语言更适合静态验证。
首先,Move不支持动态调度。所有调用的目标都能够被静态确定。这使得Move不必构建复杂的调用图,就能精确地推断出程序的执行效果。
其次,有限的可变性。Move借鉴了Rust的“借用检查”机制,确保每个值在同一时刻最多有一个可变引用。
第三,模块性。Move模块对资源进行了强制的数据抽象和关键操作本地化。也就是说,对于模块以外的程序来说,每个资源都是一个黑盒。外部代码无法得知资源内部的细节,只能通过模块的公开过程对资源进行操作。
虚拟机
字节码解释器
Move字节码解释器是基于堆栈的,与CLR和JVM类似。指令使用堆栈中的操作数,并在执行后将结果推入堆栈。
Move支持六大类字节码指令:
· 用于将数据从局部变量复制、移动到堆栈的操作,如CopyLoc、MoveLoc等,以及用于将数据从堆栈移动到局部变量的指令,如StoreLoc。
· 对类型化堆栈值的操作,例如将常量推入堆栈,以及对堆栈操作数进行算术、逻辑运算。
· 模块相关的内建指令,例如:Pack和Unpack,用于创建、销毁模块的声明类型;MoveToSender、MoveFrom,用于在帐户下发布、取消发布模块的类型;以及BorrowField,用于获取对模块中某个类型的字段的引用。
· 引用相关的指令,例如:ReadRef用于读引用,WriteRef用于写引用,ReleaseRef用于释放引用,FreezeRef用于将可变引用转换为不可变引用。
· 控制流操作,例如条件分支,以及过程调用和返回。
· 区块链特定的内建操作,例如获取交易脚本的发送者地址、创建新帐户等。
除此之外,Move也提供了密码学原语,比如sha3。这些原语由标准库的模块实现,而不是字节码指令。
字节码验证器
字节码验证器为模块和交易脚本强制执行安全属性。如果不通过字节码验证程序,则无法发布模块或执行交易脚本。
字节码验证器主要进行三个方面的检查。
· 结构检查,确保字节码表的格式正确。通过结构检查,可以发现诸如非法表索引、重复表条目、非法类型签名等错误。
· 语义检查,用于发现非法参数、危险引用、资源复制等错误。
· 链接。通过将所用到的结构类型、过程签名与其声明模块关联,从而发现非法调用内部过程、过程与定义不匹配等错误。在此过程中,会访问全局账本状态。
结论
上文对Move语言做了简要的解读,希望能使读者对Move语言的主要特性、设计等有基本的了解。本文如有错误,请读者不吝指正。有关于Move语言的更多细节,请阅读Move语言的官方技术文档
评论
查看更多