1. 北竹林首页
  2. 资讯
  3. 技术指南

美女程序员通读Libra 26页的白皮书后 找出了这些Move编程精华

一文读懂Move编程语言

自去年矿难以来,业界充满了对区块链唱衰的声音,链圈有很多人都开始对区块链的价值产生怀疑。而 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

它与以太坊 Solidity 语言有以下两个区别

  1. Move 是一种字节码语言,它可以直接在 Move 虚拟机中运行,而以太坊的 Solidity 语言是一种更高级别的语言,它需要先编译成字节码再加入到以太坊虚拟机中运行。

  2. Move 语言不仅可以用来实现智能合约,还可以用来实现自定义交易(别着急,接下来我们会详细介绍自定义交易),而 Solidity 语言只能用来实现以太坊中的智能合约。

这是一个与 Rust 语言类似的特性。Rust 语言中的数值一次只能分配给一个命名。如果你将某个数值分配给其他命名则将无法再使用之前的命名访问到该数值。

就比如说,下面这段代码将会输出一个错误:使用转移了的值“ x ”(Use of moved value ‘x’)。

这是因为 Rust 语言没有垃圾回收机制。当变量超出范围时,变量引用的内存也会被释放。

这样解释有点麻烦,为了简单起见,我们可以这样来理解,每个数据在同一时间内只能有一个“所有者”。在这个例子中,x 是数据初始的所有者,后来 y 也成了数据的所有者,所以程序会报错,如下面代码所示。

在开放系统中编码数字资产

将现实世界中的物理资产编码成区块链上的数字资产,主要存在两大难题:

  • 稀缺性:区块链系统中资产的供应应该受到严格管控。应该禁止复制现有的资产,同时也应该禁止普通用户随意创建新资产。

  • 访问控制:区块链系统中的参与者应该能够使用访问控制策略保护自己的资产。

这也是所有数字资产都需要实现的两个关键特性,这些特性被认为是现实世界中物理资产的自然表征。就比如说,现实世界中稀有金属就是很稀缺的,在现实世界中你只能花属于自己的钱,换句话说就是自己有访问权限的钱。

为了更好地阐述这两个关键特性是如何实现的,让我们先从以下三个示例说起:

示例1:不考虑稀缺性和访问控制的最简单的规则

左边为交易脚本的格式,右边为区块链状态的评估规则

  • G [K]:= n 表示使用加密货币数额 n 来更新账户 K 在区块链全局状态中存储的加密货币余额。

  • transaction⟨Alice,100⟩ 表示将 Alice 的账户余额设置为 100 。

上述的实现方式存在两个很严重的问题:

  • Alice 可以通过不断发起交易 transaction⟨Alice,100⟩ 让自己拥有无限多的加密货币。

  • Alice 与 Bob 之间的加密货币转账也变得毫无意义,因为 Bob 也可以使用相同的手段向自己发送无限多的加密货币。

示例2:在数字资产中加入稀缺性

左边为交易脚本的格式,右边为区块链状态的评估规则

现在我们强制要求在交易发起时发起方 Ka 的账户余额至少为交易的金额 n 。

虽然这种实现方式可以解决稀缺性的问题,但是现在还存在一个问题,就是你可以将任何人的加密货币余额转给自己,这是因为我们还没有加入对谁能发起交易的检查,也就是对加密货币所有权的检查。

示例3:在数字资产中同时加入稀缺性和访问控制

左边为交易脚本的格式,右边为区块链状态的评估规则

为了实现加密货币的访问控制,我们可以在稀缺性检查之前使用数字签名机制 verify_sig 来检查所交易加密货币的所有者,这意味着 Alice 可以使用她的私钥来签署交易并证明她是所交易加密货币的所有者。

现有的区块链编程语言

现有的区块链编程语言往往都会被以下问题所困扰,令人欣慰的是,Move 语言完美地解决了所有这些问题。主要体现在以下两方面。

  1. 间接地表示资产有些区块链编程语言使用整数对数字资产进行编码。这种编码方式十分牵强,因为这些整数值与数字资产根本就不是一回事。事实上,这些区块链中并没有任何类型或数值来表示比特币/以太币/山寨币!这使得编写与数字资产交互的智能合约变得十分笨拙且容易出错。在这些区块链中实现诸如资产转入/转出以及将资产存储在数据结构中这样的操作都需要特殊的语言支持。

  2. 稀缺性是不可扩展的这些语言往往只能表示一种稀缺资产。除此之外,稀缺性保护直接在语言语义中进行硬编码。开发人员如果想要创建一个自定义的资产,遗憾的是他得不到该语言的一丝帮助,他将不得不重复造轮子,重新实现资产的稀缺性。

相信你可能已经看出来了,这些正是以太坊智能合约中存在的问题。ERC-20 通证等自定义资产使用整数来表示资产和总供应量。每当生成新的通证时,智能合约代码必须手动检查交易是否满足稀缺性(在这种情况下为是否超过总供应量)。

此外,资产的间接表示会给区块链带来很多的问题,就比如说资产复制、资产重复使用、资产意外丢失等漏洞。

当然,除了上面两点以外,还包括:访问控制不够灵活

这些区块链强制执行的访问控制策略只有基于公钥的数字签名方案。与稀缺性保护一样,访问控制策略也被深深嵌入到语言语义中。

如果开发人员想要设置自定义的访问控制策略,那么他还是会陷入重复造轮子的困境。

以太坊也存在这样的问题。以太坊智能合约缺乏对使用公钥私钥密码学实现访问控制的本地语言支持。面对这种需求。开发人员不得不手动编写访问控制,就比如说使用 OnlyOwner函数。

尽管我是以太坊的忠实粉丝,但我坚持认为以太坊在这些资产属性方面存在欠缺。从安全方面考虑,这些资产属性本应得到原生的语言支持。

这里的动态分派意味着代码的执行逻辑将在代码运行时(动态)确定,而不是在代码编译时(静态)确定。

因此,在 Solidity 语言中,当智能合约 A 调用智能合约 B 的函数时,智能合约 B 可能会运行智能合约 A 的设计者从未预料到的代码,这可能会导致可重入性的漏洞(智能合约 A 意外执行智能合约 B 的函数,从而在实际更新账户余额之前提取到资金)。

Move 语言的设计目标

一流的资源

模块/资源/程序只是 Move 语言中的一些术语。下文中我们将会用一个例子来介绍它们。

灵活性

从上面我们可以看出,Move 的交易脚本通过同时支持一次性的行为和可重用的行为为 Libra 引入了更多的灵活性,而以太坊只能执行可重用的行为(即调用单个智能合约方法)。

以太坊被称为“可重用”的原因是智能合约中的函数可以被多次执行。

安全性

将 Move 构建成一种字节码语言确实是一种非常简洁的设计。由于它不需要像 Solidity 一样从源代码编译成字节码,因此不必担心编译器中可能出现的故障或漏洞。

可验证性

从这里我们可以看出 Move 更倾向于执行静态验证而不是在区块链上执行验证工作。尽管如此,正如白皮书末尾所述,Libra 团队未来将会开发完善验证工具。

模块化

这是一个非常好的数据抽象设计!这意味着智能合约中的数据只能在智能合约范围内修改,而不能在外部修改。

Move语言实操

这一部分中我们将讨论在进行 Move 语言开发时,实际使用到的模块、资源和程序分别是什么东西。

点对点支付交易脚本

Move语言的点对点支付交易脚本,如下面代码所示:

amount(金额)表示所交易加密货币的金额,这些加密货币将从交易的发起方转移给接收方 payee。

代码中有几个新的符号,其中红色的小字是我记的笔记:

  • 0x0:存储模块的帐户地址

  • currency:模块的名称

  • coin:资源类型

  • 程序返回的 coin 值是一个类型为 0x0.Currency.Coin 的资源值

  • move():该值不能再次使用

  • copy():该值可以再次使用

代码功能解读:

以下是三种会报错的代码示例:

1. 通过将转移加密货币 move(coin) 替换为复制加密货币 copy(coin) 来复制加密货币。

因为 coin 是一个资源值,所以它只能被转移。

2. 通过两次转移加密货币 move(coin) 来重复使用加密货币(双重支付)。

3. 忘记执行转移加密货币 move(coin) 导致加密货币丢失。

货币 Currency 模块

模块入门:Move 语言的执行模型

三个账户的区块链全局状态示例

需要注意的是:

  • 交易脚本的执行只有两种结果:成功或是失败,不会存在中间的状态。

  • 模块是在区块链全局状态中发布的长期存在的代码。

  • 区块链全局状态的结构为从帐户地址到帐户的映射。

  • 帐户最多只能包含一个给定类型的资源值,并且最多只能包含一个具有给定名称的模块(就比如说,上图中地址 0x0 处的帐户不能再拥有一个额外的 0x0.Currency.Coin 资源或另一个名为 Currency 的模块)。

  • 所声明模块的地址是类型的一部分(就比如说,0x0.Currency.Coin 和 0x1.Currency.Coin 是不能互换使用的不同类型)。

  • 开发人员仍然可以通过自定义的包装器( wrapper )资源来实现一个帐户拥有多个给定资源类型的实例。(resource TwoCoins { c1: 0x0.Currency.Coin, c2: 0x0.Currency.Coin })

  • 开发人员仍然可以通过名称引用资源而不会产生任何冲突,就比如说,你可以使用 TwoCoins.c1 和 TwoCoins.c2 这两个名称引用这两个资源。

声明加密货币资源:

在名为 Currency(货币)的模块中定义一个由模块管理的名为 Coin(加密货币)的资源类型。

需要注意的是:

  • Coin(加密货币)是一种结构类型,其字段容许的值类型为 u64 (64位无符号整数)。

  • 只有 Currency (货币)模块的程序能够创建或销毁 coin(加密货币)类型的值。

  • 其他模块和交易脚本只能通过模块提供的公共可访问的程序来写入或引用值字段。

实现存款操作

这段程序将 Coin(加密货币)资源作为输入,并将其与存储在收款人 payee 帐户中的 Coin 资源组合,具体的步骤如下:

  1. 销毁输入的加密货币并记录其数值。

  2. 获取对存储在收款人帐户下的 Coin 资源的唯一引用。

  3. 将程序传递过来的加密货币的数值加到收款人账户余额中,并更新收款人账户余额。

需要注意的是:

  • Unpack,BorrowGlobal 是内置程序。

  • Unpack <T> 是唯一一种删除类型为 T 的资源的方法。它将类型为 T 的资源作为输入,删除它,并返回绑定到资源字段的数值。

  • BorrowGlobal <T> 将地址作为输入,并返回对该地址下唯一的 T 实例的引用。

  • &mut Coin 是对 Coin 资源的可变引用,而不是对 Coin 。

实现撤销存款 withdraw_from_sender:

这个程序分为三步:

  1. 获取对发送方帐户下唯一的 Coin 类型资源的引用。

  2. 用输入的数额减少引用的 Coin 的数值。

  3. 创建并返回值为更新后金额的新加密货币。

需要注意的是:

  • 任何人都可以调用存款函数 deposit ,但撤销存款函数 withdraw_from_sender 具有访问控制策略,因而只能被加密货币的所有者调用。

  • 获取交易发起人账户函数 GetTxnSenderAddress 类似于 Solidity 语言中的 msg.sender 。

  • 除非满足什么条件否则就拒绝函数 RejectUnless 类似于 Solidity 语言中的 require 。如果此项检查失败,则当前交易脚本会停止执行,并且它执行的任何操作都不会更新区块链全局状态。

  • Pack <T> 也是一个内置程序,它主要用来创建一个 T 类型的新资源。

  • 与 Unpack <T> 一样, Pack <T> 只能在资源 T 的声明模块中调用。

写在最后

现在你已经了解了 Move 语言的主要特征,基本语法以及它与以太坊的差异。

最后,如果你想从事 Move 语言开发,我强烈建议你阅读 Move 语言原始的白皮书。白皮书中包含许多 Move 语言的设计原则以及许多很好的参考资料。

声明:登载此文出于传递更多信息之目的,观点仅代表作者本人,绝不代表北竹林赞同其观点或证实其描述。

提示:投资有风险,入市须谨慎。本资讯不作为投资理财建议。

联系我们

QQ:

1739447883

邮箱:

1739447883@qq.com