6 月 18 日,由 Facebook 发起的加密数字货币项目 Libra 正式亮相。Libra 的白皮书与多个技术文档也同步发布,其中着重介绍了新的区块链编程语言 Move 和共识协议 LibraBFT。本文从技术角度对 Move 语言进行解读,带领大家对这门语言做一个初步的了解。

原文标题:《Libra 区块链编程语言解读》
作者:王加楠

十分钟速览 Libra 区块链编程语言

数字资产管理

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 语言的官方技术文档

来源链接:mp.weixin.qq.com