伯克利女程序员硬核详解 Libra 编程语言 Move 的主要特征及基本语法
原文标题:《Whitepaper Deep Dive — Move: Facebook Libra Blockchain’s New Programming Language》
作者:Lee Ting Ting,加州大学伯克利分校区块链实验室研究学者,Turing Chain 联合创始人兼首席技术官
编译:Guoxi
责编:Aholiab
Lee Ting Ting,加州大学伯克利分校区块链实验室研究学者,Turing Chain 联合创始人兼首席技术官
自去年矿难以来,业界充满了对区块链唱衰的声音,链圈有很多人都开始对区块链的价值产生怀疑。而 Facebook 不断爆出的区块链项目的消息可谓是给链圈打了一剂强心针,盼望着,盼望着,今年 6 月,Facebook 发布了加密货币 Libra 的白皮书并上线了官网。可以预见, Libra 离落地不远了。
Libra 以非营利组织的形式管理,其创始成员包括大名鼎鼎的银行卡巨头 Visa,万事达,以及传统在线支付巨头 PayPal,颇有挑战当下全球金融货币体系的趋势。
Libra 最近越来越火。不少开发人员已经开始跟进 Libra 项目,希望在这个全球性的区块链项目中抢占先机。而 Libra 的编程语言 Move 则是开发人员眼中的重中之重。
为了帮助开发人员做好入门工作,加州大学伯克利分校区块链实验室研究学者,Turing Chain 联合创始人兼首席技术官 Lee Ting Ting 从开发人员的角度分析了 Move 语言的典型特征以及它与以太坊 Solidity 的异同,为我们带来了这篇 Move 语言的入门指南。
Move 语言的白皮书长达 26 页,本篇文章是对该白皮书的精华介绍,文中也会直接放出一些白皮书上的原文。Move 是 Facebook 公司为其加密货币产品 Libra 开发的全新编程语言。
作为开发人员和区块链社区爱好者,希望通过这篇文章帮助你快速入门 Move 语言。
Move 是一种用于实现 Libra 自定义交易和智能合约的可执行的字节码语言。
它与以太坊 Solidity 语言有以下两个区别:
Move 语言的一个关键特性是它能够定义受线性逻辑启发的带语义的自定义资源类型。在这种情况下资源永远不会被复制或被隐式丢弃,它只能在程序的存储位置之间转移。
这是一个与 Rust 语言类似的特性。Rust 语言中的数值一次只能分配给一个命名。如果你将某个数值分配给其他命名则将无法再使用之前的命名访问到该数值。
就比如说,下面这段代码将会输出一个错误:使用转移了的值「x」(Use of moved value ‘x’)。
这是因为 Rust 语言没有垃圾回收机制。当变量超出范围时,变量引用的内存也会被释放。
这样解释有点麻烦,为了简单起见,我们可以这样来理解,每个数据在同一时间内只能有一个「所有者」。在这个例子中,x 是数据初始的所有者,后来 y 也成了数据的所有者,所以程序会报错,如下面代码所示。
将现实世界中的物理资产编码成区块链上的数字资产,主要存在两大难题:
这也是所有数字资产都需要实现的两个关键特性,这些特性被认为是现实世界中物理资产的自然表征。就比如说,现实世界中稀有金属就是很稀缺的,在现实世界中你只能花属于自己的钱,换句话说就是自己有访问权限的钱。
为了更好地阐述这两个关键特性是如何实现的,让我们先从以下三个示例说起:
示例 1:不考虑稀缺性和访问控制的最简单的规则
左边为交易脚本的格式,右边为区块链状态的评估规则
上述的实现方式存在两个很严重的问题:
示例 2:在数字资产中加入稀缺性
左边为交易脚本的格式,右边为区块链状态的评估规则
现在我们强制要求在交易发起时发起方 Ka 的账户余额至少为交易的金额 n 。
虽然这种实现方式可以解决稀缺性的问题,但是现在还存在一个问题,就是你可以将任何人的加密货币余额转给自己,这是因为我们还没有加入对谁能发起交易的检查,也就是对加密货币所有权的检查。
示例 3:在数字资产中同时加入稀缺性和访问控制
左边为交易脚本的格式,右边为区块链状态的评估规则
为了实现加密货币的访问控制,我们可以在稀缺性检查之前使用数字签名机制 verify_sig 来检查所交易加密货币的所有者,这意味着 Alice 可以使用她的私钥来签署交易并证明她是所交易加密货币的所有者。
现有的区块链编程语言往往都会被以下问题所困扰,令人欣慰的是,Move 语言完美地解决了所有这些问题。主要体现在以下两方面。
相信你可能已经看出来了,这些正是以太坊智能合约中存在的问题。ERC-20 通证等自定义资产使用整数来表示资产和总供应量。每当生成新的通证时,智能合约代码必须手动检查交易是否满足稀缺性(在这种情况下为是否超过总供应量)。
此外,资产的间接表示会给区块链带来很多的问题,就比如说资产复制、资产重复使用、资产意外丢失等漏洞。
当然,除了上面两点以外,还包括:访问控制不够灵活。
这些区块链强制执行的访问控制策略只有基于公钥的数字签名方案。与稀缺性保护一样,访问控制策略也被深深嵌入到语言语义中。
如果开发人员想要设置自定义的访问控制策略,那么他还是会陷入重复造轮子的困境。
以太坊也存在这样的问题。以太坊智能合约缺乏对使用公钥私钥密码学实现访问控制的本地语言支持。面对这种需求。开发人员不得不手动编写访问控制,就比如说使用 OnlyOwner 函数。
尽管我是以太坊的忠实粉丝,但我坚持认为以太坊在这些资产属性方面存在欠缺。从安全方面考虑,这些资产属性本应得到原生的语言支持。
特别是,将以太坊转移到智能合约中需要用到动态分派 (dynamic dispatch,处理编程语言的语言方法调用的一种计算机制),这又会带来一类新的漏洞:可重入性漏洞( re-entrancy vulnerabilities )。
这里的动态分派意味着代码的执行逻辑将在代码运行时(动态)确定,而不是在代码编译时(静态)确定。
因此,在 Solidity 语言中,当智能合约 A 调用智能合约 B 的函数时,智能合约 B 可能会运行智能合约 A 的设计者从未预料到的代码,这可能会导致可重入性的漏洞(智能合约 A 意外执行智能合约 B 的函数,从而在实际更新账户余额之前提取到资金)。
一流的资源
从较高的层次上来说,Move 语言中模块 / 资源 / 程序之间的关系类似于面向对象语言中的类 / 对象 / 方法之间的关系。
Move 语言中的模块类似于其他区块链语言中的智能合约。模块声明资源类型和程序,而这些资源类型和程序编码用于创建,销毁和更新所声明资源的规则。
模块 / 资源 / 程序只是 Move 语言中的一些术语。下文中我们将会用一个例子来介绍它们。
灵活性
Move 通过交易脚本为 Libra 增加了很多灵活性。每笔 Libra 交易都包含一个交易脚本,该脚本实际上是交易的核心。
交易脚本可以用来执行一次性的行为(例如给一组特定的收款人付款),也可以用来执行可重用的行为(通过调用一个封装了可重用逻辑的程序)。
从上面我们可以看出,Move 的交易脚本通过同时支持一次性的行为和可重用的行为为 Libra 引入了更多的灵活性,而以太坊只能执行可重用的行为(即调用单个智能合约方法)。
以太坊被称为「可重用」的原因是智能合约中的函数可以被多次执行。
安全性
Move 的可执行格式是一种类型化的字节码,它比汇编语言更高级但比源语言更低级。在区块链上字节码验证器会检查字节码的资源,类型以及内存安全性,然后字节码解释器会直接执行字节码。这种设定使得 Move 在提供与源语言相关联的安全保证的同时,省去了将源编译器添加到可信计算基础 ( Trusted Computing Base,TCB) 以及编译到交易执行的关键路径的成本。
将 Move 构建成一种字节码语言确实是一种非常简洁的设计。由于它不需要像 Solidity 一样从源代码编译成字节码,因此不必担心编译器中可能出现的故障或漏洞。
可验证性
我们的方法是尽可能多地在区块链上执行核心安全属性的轻量级验证,但同时我们也在 Move 语言中加入了对链下高级静态验证工具的支持。
从这里我们可以看出 Move 更倾向于执行静态验证而不是在区块链上执行验证工作。尽管如此,正如白皮书末尾所述,Libra 团队未来将会开发完善验证工具。
模块化
Move 模块强制执行数据抽象并本地化执行资源的关键性操作。模块启用的封装与 Move 类型系统强制执行的保护相结合,强强联手可以确保模块外部的代码不能违反模块类型规定的属性。
这是一个非常好的数据抽象设计!这意味着智能合约中的数据只能在智能合约范围内修改,而不能在外部修改。
这个交易脚本的示例说明了模块外部的恶意开发人员或粗心的开发人员不可能违反模块资源的关键安全不变性。
这一部分中我们将讨论在进行 Move 语言开发时,实际使用到的模块、资源和程序分别是什么东西。
点对点支付交易脚本
Move 语言的点对点支付交易脚本,如下面代码所示:
amount (金额)表示所交易加密货币的金额,这些加密货币将从交易的发起方转移给接收方 payee。
代码中有几个新的符号,其中红色的小字是我记的笔记:
代码功能解读:
在第一步中,发送方从存储在 0x0.Currency 的模块中调用了名为 withdraw_from_sender 的程序。
在第二步中,发送方通过将加密货币的资源值转移到 0x0.Currency 模块的存款程序中从而将资金转移给收款人。
以下是三种会报错的代码示例:
1. 通过将转移加密货币 move(coin) 替换为复制加密货币 copy(coin) 来复制加密货币。
资源值只能被转移。尝试复制资源值(就比如说示例中使用的复制加密货币 copy(coin) )将在字节码验证时引起错误。
因为 coin 是一个资源值,所以它只能被转移。
2. 通过两次转移加密货币 move(coin) 来重复使用加密货币(双重支付)。
在上述的示例代码中加入一行:
0x0.Currency.deposit (copy (some_other_payee),move (coin))
就可以让发送方两次花费同一笔加密货币,第一次交易的收款人是 payee ,第二次交易的收款人是 some_other_payee。现实生活中的物理资产可以完全杜绝双重支付,幸运的是,Move 也可以做到。
3. 忘记执行转移加密货币 move(coin) 导致加密货币丢失。
忘记转移资源(就比如说删除上述代码示例中转移加密货币 move(coin) 所在的行)将触发字节码验证错误。这种机制可以保护 Move 开发人员不会有意或无意地丢失资源。
货币 Currency 模块
模块入门:Move 语言的执行模型
三个账户的区块链全局状态示例
每个帐户可以拥有零个或多个模块(上图中的矩形)和一个或多个资源值(上图中的圆柱体)。就比如说,地址 0x0 处的帐户拥有一个名为 0x0.Currency 的模块和一个 0x0.Currency.Coin 类型的资源值。地址 0x1 处的帐户拥有两个资源值和一个模块;地址 0x2 处的帐户拥有两个模块和一个资源值。
需要注意的是:
声明加密货币资源:
在名为 Currency (货币)的模块中定义一个由模块管理的名为 Coin (加密货币)的资源类型。
需要注意的是:
实现存款操作
这段程序将 Coin (加密货币)资源作为输入,并将其与存储在收款人 payee 帐户中的 Coin 资源组合,具体的步骤如下:
需要注意的是:
实现撤销存款 withdraw_from_sender:
这个程序分为三步:
需要注意的是:
写在最后
现在你已经了解了 Move 语言的主要特征,基本语法以及它与以太坊的差异。
最后,如果你想从事 Move 语言开发,我强烈建议你阅读 Move 语言原始的白皮书。白皮书中包含许多 Move 语言的设计原则以及许多很好的参考资料。