编译:Alan|SophonLabs
Move诞生于2018年Libra项目的早期阶段 — Mysten的两位创始人(Evan和我)也是Libra的创始团队。在我们决定创建一种新的语言之前,早期的Libra团队密集地研究了现有的智能合约用例和语言,以了解开发人员想要做什么,以及现有语言在哪些方面没有提供。我们发现的关键问题是,智能合约都是关于资产和访问控制的,然而早期的智能合约语言缺乏对这两者的类型/价值表示。我们的假设是,如果我们为这些关键概念提供一流的抽象,我们就可以大大改善智能合约的安全性和智能合约程序员的生产力 — 拥有正确的词汇来完成手头的任务,就可以改变一切。多年来,许多人对Move的设计和实施做出了贡献,因为该语言从一个关键的想法演变成一个平台无关的智能合约语言,其大胆目标是成为 “web3的JavaScript”。
今天,我们很高兴地宣布Move与Sui整合的一个里程碑。Sui Move的功能是完整的,由先进的工具支持,并有大量的文档和例子,包括如下几部分:
关于使用Sui Move对象编程的系列教程
一本关于Sui Move基础知识、设计模式和样本的开发文档
由Mysten Move团队开发的 VSCode增强插件,支持代码解析和错误诊断,
将Move的构建、测试、包管理、文档生成以及Move验证器与sui CLI整合在一起
Move是一种跨平台的嵌入式语言。核心语法本身非常简单:它有通用的概念,如structs(结构体), integers(整型), and addresses(地址),但它没有区块链特有的概念,如accounts(账户)、transactions(交易)、time、cryptography等。这些功能必须由整合Move的区块链平台提供。重要的是,这些区块链不需要自己的Move分叉 — 每个平台都使用相同的Move虚拟机、字节码验证器、编译器、验证器、包管理器和CLI,但通过建立在这些核心组件之上的代码来增加区块链的特定功能。 Diem是第一个嵌入Move的区块链,随后基于Move的区块链(包括0L、StarCoin和Aptos)大多采用了Diem风格的方法。尽管Diem风格的Move有一些很好的品质,但Diem的许可性质和Diem区块链的某些实现细节(特别是存储模型)都使得一些基本的智能合约用例难以实现。特别是,Move和Diem的原始设计早于NFT的流行爆炸,并且有一些怪癖,使NFT相关用例的实施特别棘手。 在这篇文章中,我们将通过三个这样的例子,展示原始Diem风格的Move嵌入的问题,并描述我们如何在Sui Move中解决这个问题。我们假定对Move有一些基本的了解,但希望这些关键点对任何有编程背景的人来说都是可以理解的。
批量创建和分发资产的能力对于入职和吸引web3用户都是至关重要的。也许一个Twitch流媒体人想要分发纪念性的NFT,一个创造者想要为一个特别的活动发送门票,或者一个游戏开发者想要向所有的玩家空投新的物品。 这里是一个(失败的)尝试,为Diem-style Move中的资产大规模造币编写代码。这段代码将一个接收者地址的向量作为输入,为每个接收者生成一个asset,并尝试转移资产。
在Diem-style Move中,全局存储是由(地址,类型名称)对键入的 — 也就是说,每个地址最多可以存储一个特定类型的资产。因此,move_to(receiverient, CoolAsset { …}试图通过将CoolAsset存储在接收者的地址下来转移它。 然而,这段代码在move_to(receiverient, …)这一行会编译失败。关键的问题是,在Diem-style Move中,你不能发送一个CoolAsset类型的值到一个地址A,除非。 一个非A的地址发送一个交易,在A创建一个账户 A的所有者发送了一个事务,明确选择接收CoolAsset类型的对象。 这是两个交易,只是为了接收一个资产!这样做的决定对Diem来说是有意义的,它是一个许可系统,需要仔细限制账户的创建,并防止账户因存储系统的限制而持有过多的资产。但是,对于一个想要使用资产分配作为入职机制的开放系统,或者只是一般地允许资产在用户之间自由流动,就像他们在以太坊和类似的区块链上所做的那样,这是非常有限的。
而在Sui Move中实现相同功能代码代码如下:
Sui Move的全局存储以对象ID为键。每个具有键值对的的结构都是一个 “Sui对象”,它必须有一个全局唯一的id字段。Sui Move引入了一个可以在任何Sui对象上使用的transfer
原语,而不是使用限制性的move_to结构。在底层,这个基元将id映射到全局存储中的CoolAsset,并添加元数据以表明该值为接收方所拥有。 mass_mint的Sui版本的一个有趣的属性是,它与所有其他交易(包括调用mass_mint的其他交易!)进行交换。Sui运行时将注意到这一点,并通过不需要共识的拜占庭一致广播 "快速路径 "发送调用该函数的事务。这样的事务既可以被提交,也可以被并行执行。这不需要程序员做任何努力(他们只需写下上面的代码,其余的由runtime来处理。) 也许很微妙的是,这段代码的Diem变体并非如此--即使上面的代码有效,exists和guid::create的调用都会与其他生成GUID或触及账户资源的事务产生争论点。在某些情况下,有可能重写Diem风格的Move代码以避免争论点,但许多写Diem风格Move的习惯性方法会引入小阻碍,使并行执行时受阻。
让我们用一个可以实际编译和运行的变通方法来扩展Diem风格的Move代码。做到这一点的习惯方法是 “wrapper pattern(包装器模式)”:因为Bob不能直接将CoolAsset移动到Alice的地址,我们要求Alice “选择 “接收CoolAsset,首先发布一个包装器类型CoolAssetStore,里面有一个集合类型(表)。爱丽丝可以通过调用opt_in函数来做到这一点。然后,我们添加代码,允许Bob将CoolAsset从他的CoolAssetStore移到Alice的CoolAssetStore。 在这段代码中,让我们添加一个其他的问题:我们将只允许CoolAsset的转移,如果它们被创建后至少有30天。这种政策对于那些(例如)想要阻止投机者购买/炒作活动门票的创作者来说是很重要的,这样真正的粉丝就更容易以合理的价格获得这些门票。
这段代码是有效的。但这是一种相当复杂的方式来完成将资产从Alice转移到Bob的任务!再看一下Sui Move的另外一种实现方式
这段代码要短得多。这里需要注意的关键是,cool_transfer是一个入口函数(意味着它可以被Sui运行时通过事务直接调用),然而它有一个CoolAsset类型的参数作为输入。这又是Sui运行时的神奇之处一个事务包括它想操作的一组对象ID,而Sui Runtime时:
将ID解析为对象值(不需要上面Diem风格的代码中的borrow_global_mut和table_remove部分)。
检查该对象是否为交易的发送者所拥有(消除了对上述signer::address_of部分和相关代码的需要)。这一部分特别有意思,我们很快就会解释:在Sui中,安全的对象所有权检查是运行时的一部分
根据被调用函数cool_transfer的参数类型检查对象值的类型
将对象值和其他参数绑定到cool_transfer的参数上,并调用该函数
这使得Sui Move的程序员能够跳过逻辑中 “提款 “部分的模板,直接跳到有趣的部分:检查30天的到期政策。同样地,”存款 “部分也通过上文解释的Sui Move转账结构得到了极大的简化。最后,没有必要像CoolAssetStore那样引入一个具有内部集合的封装类型 — 以id为索引的Sui全局存储允许一个地址存储任意数量的具有给定类型的值。 另一个需要指出的区别是,Diem风格的cool_transfer有5种方式可以中止(即失败并在没有完成转移的情况下向用户收取汽油费),而Sui Move cool_transfer只有一种方式可以中止:当违反了30天的政策。 将对象所有权检查卸载到运行时是一个很大的成功,不仅在人机工程学方面,而且在安全性方面。在运行时层面上的安全实现可以防止在构造上实现这些检查(或完全忘记它们!)的错误。 最后,注意到Sui Move的入口点函数签名cool_transfer( asset: CoolAsset, …)给了我们很多关于这个函数要做什么的信息(与Diem风格的函数签名相比,它更加不透明)。我们可以认为这个函数是在请求转移CoolAsset的权限,而另一个函数f(asset: &mut CoolAsset, …)是在请求写入(但不是转移)CoolAsset的权限,而g(asset: &CoolAsset, …)只是在请求读取权限。
因为这些信息可以直接在函数签名中获得(不需要执行或静态分析!),它可以直接被钱包和其他客户端工具使用。在Sui钱包中,我们正在研究人类可读的签名请求,利用这些结构化的函数签名,向用户提供iOS/Android风格的权限提示。钱包可以这样说:”此交易要求允许读取你的CoolAsset,写入你的AssetCollection,并转移你的ConcertTicket。继续吗?”。
人类可读的签名请求解决了许多现有平台(包括使用Diem-style Move!的平台)上存在的大规模攻击载体,即钱包用户必须盲目地签署交易而不了解其可能产生的影响。我们认为使钱包体验不那么危险是促进加密货币钱包主流采用的关键步骤,并通过启用人们可读签名请求等功能来设计Sui Move以支持这一目标。
最后,让我们考虑一个关于捆绑不同类型的资产的例子。这是一个相当常见的用例:程序员可能想把不同类型的NFT打包成一个集合,把物品捆绑在一起在市场上出售,或者给现有的物品添加附件。假设我们有以下情况:
Alice定义了一个在游戏中使用的角色对象
Alice希望支持用后来创建的不同类型的第三方配饰来装饰她的角色。
任何人都应该能够创建一个配件,但是一个角色的所有者应该决定是否添加一个配饰。
转移一个角色应该自动转移其所有的配饰。
这一次,让我们从Sui Move的代码开始。我们将利用Sui运行时内置的对象所有权功能的另一个方面:一个对象可以被另一个对象所拥有。每个对象都有一个唯一的所有者,但一个父对象可以有任意数量的子对象。父/子对象关系是通过使用transfer_to_object函数创建的,它是上面介绍的transfer函数的关联对象。
在这段代码中,角色模块包括一个accessorize函数,让角色的拥有者添加一个具有任意类型的附件对象作为子对象。这允许Bob和Clarissa创建他们自己的饰品类型,具有不同的属性和功能,这是Alice没有的,但又建立在Alice已经完成的基础上。例如,Bob的衬衫只有在它是角色最喜欢的颜色时才能装备,而Clarissa的剑只有在角色足够强大时才能挥舞。 下面是在Diem-style Move 的实战演示,但都没有成功,也就是Diem-style的 Move是不能实现上述场景的。
由此可见,在Diem-style Move中的问题如下
只支持同类型的集合(正如第一次测试所展示的那样),但配饰从根本上来说不是一类
对象之间的关联只能通过 “wrapping(包装)”(即把一个对象存储在另一个对象中)来创建;但可以被包装的对象集合必须预先定义(如第二次尝试),或者以一种临时的方式添加,不支持附加对象组成(像第三次测试的那样)。
Sui是第一个在如何使用Move方面与最初的Diem设计大相径庭的平台。设计能充分利用Move和平台独特功能结合既是一门艺术,也是一门科学,需要对Move语言和底层区块链的能力有深刻的理解。我们对Sui Move所取得的进展和它将启用的新用例感到非常兴奋!”。 [1] 支持Diem式的Move政策的另一个论点是,”必须选择加入对应的资产类型后,才能接收特定类型的资产”,这是一个很好的防止垃圾邮件的机制。然而,我们认为垃圾邮件的预防属于应用层。与其要求用户发送花费真金白银的交易来选择接收资产,不如在(例如)钱包层面用丰富的用户定义的政策和自动垃圾邮件过滤器来轻松解决垃圾邮件问题。
— — — — — — — — — — — — -— — End — — — — — — — — — — — — — — —
SophonLabs(智子实验室)团队由密码极客,技术爱好者,分析师,区块链研究员等组成。专注区块链媒体与投研,为WEB3赋能。
公众号:SophonLabs
Twitter:https://twitter.com/SophonLabs
官方Blog:https://blog.sophonlabs.io/
Email:BD@sophonlabs.io