自去年矿难以来,业界充满了对区块链唱衰的声音,链圈有很多人都开始对区块链的价值产生怀疑。而Facebook不断爆出的区块链项目的消息可谓是给链圈打了一剂强心针,盼望着,盼望着,今年6月,Facebook发布了加密货币Libra的白皮书并上线了官网。可以预见,Libra离落地不远了。
Libra以非营利组织的形式管理,其创始成员包括大名鼎鼎的银行卡巨头Visa,万事达,以及传统在线支付巨头PayPal,颇有挑战当下全球金融货币体系的趋势。
Libra最近越来越火。不少开发人员已经开始跟进Libra项目,希望在这个全球性的区块链项目中抢占先机。而Libra的编程语言Move则是开发人员眼中的重中之重。
为了帮助开发人员做好入门工作,加州大学伯克利分校区块链实验室研究学者,TuringChain联合创始人兼首席技术官LeeTingTing从开发人员的角度分析了Move语言的典型特征以及它与以太坊Solidity的异同,为我们带来了这篇Move语言的入门指南。
Move语言的白皮书长达26页,本篇文章是对该白皮书的精华介绍,文中也会直接放出一些白皮书上的原文。Move是Facebook公司为其加密货币产品Libra开发的全新编程语言。
作为开发人员和区块链社区爱好者,希望通过这篇文章帮助你快速入门Move语言。
关于Move
Move是一种用于实现Libra自定义交易和智能合约的可执行的字节码语言。
它与以太坊Solidity语言有以下两个区别:
Move是一种字节码语言,它可以直接在Move虚拟机中运行,而以太坊的Solidity语言是一种更高级别的语言,它需要先编译成字节码再加入到以太坊虚拟机中运行。
Move语言不仅可以用来实现智能合约,还可以用来实现自定义交易,而Solidity语言只能用来实现以太坊中的智能合约。
Move语言的一个关键特性是它能够定义受线性逻辑启发的带语义的自定义资源类型。在这种情况下资源永远不会被复制或被隐式丢弃,它只能在程序的存储位置之间转移。
Polygon向两名白帽黑客支付约346万美元赏金,PoS创始合约因漏洞损失约80万枚MATIC:12月29日,官方消息,Polygon公布关于最近网络升级的相关内容。Polygon表示,12月3日和4日,两名白帽黑客分别在漏洞赏金平台Immunefi报告Polygon PoS创世合约中的漏洞,Polygon确认该漏洞后决定升级主网,并在12月5日区块高度22156660完成90%的验证节点主网升级。但黑客还是在升级之前利用该漏洞窃取801,601 MATIC,基金会将承担损失。此外,Polygon向两名白帽黑客支付总计约346万美元的赏金。[2021/12/29 8:12:49]
这是一个与Rust语言类似的特性。Rust语言中的数值一次只能分配给一个命名。如果你将某个数值分配给其他命名则将无法再使用之前的命名访问到该数值。
就比如说,下面这段代码将会输出一个错误:使用转移了的值“x”。
这是因为Rust语言没有垃圾回收机制。当变量超出范围时,变量引用的内存也会被释放。
这样解释有点麻烦,为了简单起见,我们可以这样来理解,每个数据在同一时间内只能有一个“所有者”。在这个例子中,x是数据初始的所有者,后来y也成了数据的所有者,所以程序会报错,如下面代码所示。
在开放系统中编码数字资产
将现实世界中的物理资产编码成区块链上的数字资产,主要存在两大难题:
稀缺性:区块链系统中资产的供应应该受到严格管控。应该禁止复制现有的资产,同时也应该禁止普通用户随意创建新资产。
访问控制:区块链系统中的参与者应该能够使用访问控制策略保护自己的资产。
这也是所有数字资产都需要实现的两个关键特性,这些特性被认为是现实世界中物理资产的自然表征。就比如说,现实世界中稀有金属就是很稀缺的,在现实世界中你只能花属于自己的钱,换句话说就是自己有访问权限的钱。
为了更好地阐述这两个关键特性是如何实现的,让我们先从以下三个示例说起:
示例1:不考虑稀缺性和访问控制的最简单的规则
Composable Finance:退款单将在众贷结束后1周内可用:10月18日消息,Composable Finance发推称,退款单将在众贷结束后(昨天)1周内可用,之后官方将在退款单结束后24小时内归还用户的稳定币。对于那些直接以KSM参与的用户,其资金已经自动返回到polkadot.js钱包中。友情提示,如果用户参与了前一轮,那么将在下一轮贡献相同或更多的情况下获得5%的奖金。[2021/10/18 20:35:59]
左边为交易脚本的格式,右边为区块链状态的评估规则
G:=n表示使用加密货币数额n来更新账户K在区块链全局状态中存储的加密货币余额。
transaction?Alice,100?表示将Alice的账户余额设置为100。
上述的实现方式存在两个很严重的问题:
Alice可以通过不断发起交易transaction?Alice,100?让自己拥有无限多的加密货币。
Alice与Bob之间的加密货币转账也变得毫无意义,因为Bob也可以使用相同的手段向自己发送无限多的加密货币。
示例2:在数字资产中加入稀缺性
左边为交易脚本的格式,右边为区块链状态的评估规则
现在我们强制要求在交易发起时发起方Ka的账户余额至少为交易的金额n。
虽然这种实现方式可以解决稀缺性的问题,但是现在还存在一个问题,就是你可以将任何人的加密货币余额转给自己,这是因为我们还没有加入对谁能发起交易的检查,也就是对加密货币所有权的检查。
示例3:在数字资产中同时加入稀缺性和访问控制
左边为交易脚本的格式,右边为区块链状态的评估规则
为了实现加密货币的访问控制,我们可以在稀缺性检查之前使用数字签名机制verify_sig来检查所交易加密货币的所有者,这意味着Alice可以使用她的私钥来签署交易并证明她是所交易加密货币的所有者。
现有的区块链编程语言
现有的区块链编程语言往往都会被以下问题所困扰,令人欣慰的是,Move语言完美地解决了所有这些问题。主要体现在以下两方面。
动态 | 元界推出支持DNA支付的加密POS机设备:元界DNA正式推出全球POS机硬件设备,该设备基于元界DNA通证设计,顾客可使用元界DNA等支付方式,通过POS机完成支付购买商品。每笔付款都以元界DNA作为兑换媒介,并实现结算。
元界DNA的POS机解决方案瞄准万亿美元规模的POS机支付市场,打通全球海量的传统线下门店,推动传统用户进入数字资产世界的大门,建立起数字货币行业与传统商户消费市场的桥梁。元界DNA将构建基于区块链技术的加密数字货币开放金融生态系统,有望与多家行业领先公司和商业保险机构达成合作,未来旗下金融服务产品将落地并覆盖全球多个国家用户。[2020/2/10]
间接地表示资产。有些区块链编程语言使用整数对数字资产进行编码。这种编码方式十分牵强,因为这些整数值与数字资产根本就不是一回事。事实上,这些区块链中并没有任何类型或数值来表示比特币/以太币/山寨币!这使得编写与数字资产交互的智能合约变得十分笨拙且容易出错。在这些区块链中实现诸如资产转入/转出以及将资产存储在数据结构中这样的操作都需要特殊的语言支持。
稀缺性是不可扩展的。这些语言往往只能表示一种稀缺资产。除此之外,稀缺性保护直接在语言语义中进行硬编码。开发人员如果想要创建一个自定义的资产,遗憾的是他得不到该语言的一丝帮助,他将不得不重复造轮子,重新实现资产的稀缺性。
相信你可能已经看出来了,这些正是以太坊智能合约中存在的问题。ERC-20通证等自定义资产使用整数来表示资产和总供应量。每当生成新的通证时,智能合约代码必须手动检查交易是否满足稀缺性。
此外,资产的间接表示会给区块链带来很多的问题,就比如说资产复制、资产重复使用、资产意外丢失等漏洞。
当然,除了上面两点以外,还包括:访问控制不够灵活。
这些区块链强制执行的访问控制策略只有基于公钥的数字签名方案。与稀缺性保护一样,访问控制策略也被深深嵌入到语言语义中。
如果开发人员想要设置自定义的访问控制策略,那么他还是会陷入重复造轮子的困境。
以太坊也存在这样的问题。以太坊智能合约缺乏对使用公钥私钥密码学实现访问控制的本地语言支持。面对这种需求。开发人员不得不手动编写访问控制,就比如说使用OnlyOwner函数。
尽管我是以太坊的忠实粉丝,但我坚持认为以太坊在这些资产属性方面存在欠缺。从安全方面考虑,这些资产属性本应得到原生的语言支持。
特别是,将以太坊转移到智能合约中需要用到动态分派(dynamicdispatch,处理编程语言的语言方法调用的一种计算机制),这又会带来一类新的漏洞:可重入性漏洞。
这里的动态分派意味着代码的执行逻辑将在代码运行时确定,而不是在代码编译时确定。
因此,在Solidity语言中,当智能合约A调用智能合约B的函数时,智能合约B可能会运行智能合约A的设计者从未预料到的代码,这可能会导致可重入性的漏洞。
Move语言的设计目标
一流的资源
从较高的层次上来说,Move语言中模块/资源/程序之间的关系类似于面向对象语言中的类/对象/方法之间的关系。
Move语言中的模块类似于其他区块链语言中的智能合约。模块声明资源类型和程序,而这些资源类型和程序编码用于创建,销毁和更新所声明资源的规则。
模块/资源/程序只是Move语言中的一些术语。下文中我们将会用一个例子来介绍它们。
灵活性
Move通过交易脚本为Libra增加了很多灵活性。每笔Libra交易都包含一个交易脚本,该脚本实际上是交易的核心。
交易脚本可以用来执行一次性的行为,也可以用来执行可重用的行为。
从上面我们可以看出,Move的交易脚本通过同时支持一次性的行为和可重用的行为为Libra引入了更多的灵活性,而以太坊只能执行可重用的行为。
以太坊被称为“可重用”的原因是智能合约中的函数可以被多次执行。
安全性
Move的可执行格式是一种类型化的字节码,它比汇编语言更高级但比源语言更低级。在区块链上字节码验证器会检查字节码的资源,类型以及内存安全性,然后字节码解释器会直接执行字节码。这种设定使得Move在提供与源语言相关联的安全保证的同时,省去了将源编译器添加到可信计算基础(TrustedComputingBase,TCB)以及编译到交易执行的关键路径的成本。
将Move构建成一种字节码语言确实是一种非常简洁的设计。由于它不需要像Solidity一样从源代码编译成字节码,因此不必担心编译器中可能出现的故障或漏洞。
可验证性
我们的方法是尽可能多地在区块链上执行核心安全属性的轻量级验证,但同时我们也在Move语言中加入了对链下高级静态验证工具的支持。
从这里我们可以看出Move更倾向于执行静态验证而不是在区块链上执行验证工作。尽管如此,正如白皮书末尾所述,Libra团队未来将会开发完善验证工具。
模块化
Move模块强制执行数据抽象并本地化执行资源的关键性操作。模块启用的封装与Move类型系统强制执行的保护相结合,强强联手可以确保模块外部的代码不能违反模块类型规定的属性。
这是一个非常好的数据抽象设计!这意味着智能合约中的数据只能在智能合约范围内修改,而不能在外部修改。
Move语言实操
这个交易脚本的示例说明了模块外部的恶意开发人员或粗心的开发人员不可能违反模块资源的关键安全不变性。
这一部分中我们将讨论在进行Move语言开发时,实际使用到的模块、资源和程序分别是什么东西。
点对点支付交易脚本
Move语言的点对点支付交易脚本,如下面代码所示:
amount表示所交易加密货币的金额,这些加密货币将从交易的发起方转移给接收方payee。
代码中有几个新的符号,其中红色的小字是我记的笔记:
0x0:存储模块的帐户地址
currency:模块的名称
coin:资源类型
程序返回的coin值是一个类型为0x0.Currency.Coin的资源值
move:该值不能再次使用
copy:该值可以再次使用
代码功能解读:
在第一步中,发送方从存储在0x0.Currency的模块中调用了名为withdraw_from_sender的程序。
在第二步中,发送方通过将加密货币的资源值转移到0x0.Currency模块的存款程序中从而将资金转移给收款人。
以下是三种会报错的代码示例:
1.通过将转移加密货币move(coin)替换为复制加密货币copy(coin)来复制加密货币。
资源值只能被转移。尝试复制资源值将在字节码验证时引起错误。
因为coin是一个资源值,所以它只能被转移。
2.通过两次转移加密货币move(coin)来重复使用加密货币。
在上述的示例代码中加入一行:
0x0.Currency.deposit,move)
就可以让发送方两次花费同一笔加密货币,第一次交易的收款人是payee,第二次交易的收款人是some_other_payee。现实生活中的物理资产可以完全杜绝双重支付,幸运的是,Move也可以做到。3.忘记执行转移加密货币move(coin)导致加密货币丢失。
忘记转移资源将触发字节码验证错误。这种机制可以保护Move开发人员不会有意或无意地丢失资源。
货币Currency模块
模块入门:Move语言的执行模型
三个账户的区块链全局状态示例
每个帐户可以拥有零个或多个模块和一个或多个资源值。就比如说,地址0x0处的帐户拥有一个名为0x0.Currency的模块和一个0x0.Currency.Coin类型的资源值。地址0x1处的帐户拥有两个资源值和一个模块;地址0x2处的帐户拥有两个模块和一个资源值。
需要注意的是:
交易脚本的执行只有两种结果:成功或是失败,不会存在中间的状态。
模块是在区块链全局状态中发布的长期存在的代码。
区块链全局状态的结构为从帐户地址到帐户的映射。
帐户最多只能包含一个给定类型的资源值,并且最多只能包含一个具有给定名称的模块。
所声明模块的地址是类型的一部分。
开发人员仍然可以通过自定义的包装器资源来实现一个帐户拥有多个给定资源类型的实例。(resourceTwoCoins{c1:0x0.Currency.Coin,c2:0x0.Currency.Coin})
开发人员仍然可以通过名称引用资源而不会产生任何冲突,就比如说,你可以使用TwoCoins.c1和TwoCoins.c2这两个名称引用这两个资源。
声明加密货币资源:
在名为Currency的模块中定义一个由模块管理的名为Coin的资源类型。
需要注意的是:
Coin是一种结构类型,其字段容许的值类型为u64。
只有Currency模块的程序能够创建或销毁coin类型的值。
其他模块和交易脚本只能通过模块提供的公共可访问的程序来写入或引用值字段。
实现存款操作
这段程序将Coin资源作为输入,并将其与存储在收款人payee帐户中的Coin资源组合,具体的步骤如下:
销毁输入的加密货币并记录其数值。
获取对存储在收款人帐户下的Coin资源的唯一引用。
将程序传递过来的加密货币的数值加到收款人账户余额中,并更新收款人账户余额。
需要注意的是:
Unpack,BorrowGlobal是内置程序。
Unpack是唯一一种删除类型为T的资源的方法。它将类型为T的资源作为输入,删除它,并返回绑定到资源字段的数值。
BorrowGlobal将地址作为输入,并返回对该地址下唯一的T实例的引用。
&mutCoin是对Coin资源的可变引用,而不是对Coin。
实现撤销存款withdraw_from_sender:
这个程序分为三步:
获取对发送方帐户下唯一的Coin类型资源的引用。
用输入的数额减少引用的Coin的数值。
创建并返回值为更新后金额的新加密货币。
需要注意的是:
任何人都可以调用存款函数deposit,但撤销存款函数withdraw_from_sender具有访问控制策略,因而只能被加密货币的所有者调用。
获取交易发起人账户函数GetTxnSenderAddress类似于Solidity语言中的msg.sender。
除非满足什么条件否则就拒绝函数RejectUnless类似于Solidity语言中的require。如果此项检查失败,则当前交易脚本会停止执行,并且它执行的任何操作都不会更新区块链全局状态。
Pack也是一个内置程序,它主要用来创建一个T类型的新资源。
与Unpack一样,Pack只能在资源T的声明模块中调用。
写在最后
现在你已经了解了Move语言的主要特征,基本语法以及它与以太坊的差异。
最后,如果你想从事Move语言开发,我强烈建议你阅读Move语言原始的白皮书。白皮书中包含许多Move语言的设计原则以及许多很好的参考资料。
郑重声明: 本文版权归原作者所有, 转载文章仅为传播更多信息之目的, 如作者信息标记有误, 请第一时间联系我们修改或删除, 多谢。