
从Aptos与Sui 含着金钥匙出场的那一刻起,人们就一直想知道为什么开发人员对 Move 语言如此兴奋。
更有意思的是,开发人员如此喜欢把 Move 语言与Rust 做比较,Aptos 与Solana 做比较。 本篇文章深入研究了 Move,一种用于智能合约开发的新颖编程语言,以及它与 Solana 上使用的现有基于 Rust 的模型的比较。
以下为MoveChina 翻译的全文《能合约开发——Move vs. Rust》。深入研究 Move,一种用于智能合约开发的新颖编程语言,以及它与 Solana 上使用的现有基于 Rust 的模型的比较。
介绍
最近几周和几个月,围绕 Aptos 和 Sui、新兴的高性能 L1 以及这些新链不可或缺的 Move 智能合约编程语言引起了很多关注。一些开发人员已经开始积极转向 Move,他们发誓这是智能合约开发的未来,而另一些开发人员则有点谨慎,他们认为 Move 只是另一种智能合约编程语言,从根本上说,它并没有提供比现有编程模型更多的功能。加密货币投资者还想知道这些新的 L1 有什么特别之处,以及它们如何与 Solana 相抗衡,后者目前是高性能 L1 类别的主要参与者,尤其是使用 Rust 作为智能合约编程的基础。
但是到目前为止,我们所看到的讨论并没有深入到充分理解这项新技术给我们带来的必要深度。这在讨论的两端都是正确的——Move 怀疑论者淡化了 Move 而没有充分理解它的一些更微妙(但非常重要)的方面,而 Move 粉丝则称赞 Move 没有尽头,但未能充分阐明究竟是什么让它如此 伟大的。这留下了一个很大的中间立场和很多模棱两可的地方,因此一直在关注这个讨论的外部旁观者、加密货币开发人员和投资者无法自信地形成他们的意见。
我最近对 Move 进行了深入研究,并在 Solana 上拥有智能合约开发经验 在这个讨论之外。
在本文中,我将深入探讨 Move、其新颖的编程模型、Sui 区块链以及它如何利用 Move 的功能,以及它与 Solana 及其编程模型的比较。 为了突出 Move 的特性,我将 Solana/Rust 与 Sui/Move 进行比较。我这样做的主要原因是,当您将某件事与您已经熟悉的另一件事进行比较而不是尝试自己理解它时,更容易理解它。值得注意的是,Move 还有其他版本,例如 Aptos Move,它们在某些事情上略有不同。本文的重点不是讨论 Move 不同版本之间的细微差别,而是展示 Move 的一般优势以及它与 Solana 编程模型的比较,因此为简单起见,我决定只使用一种版本( Sui Move) 通篇。因此,我在本文中介绍的某些 Move 概念(即对象和相关功能)仅适用于 Move 的 Sui 版本,而不适用于其他版本。虽然 Move 的其他版本不一定具有这些概念,但它们使用不同的机制(例如全局存储)实现相同的功能。但即便如此,本文中讨论的所有主要 Move 优势都适用于所有 Move 集成(原生支持 Move 字节码),包括 Aptos。我特别选择 Sui 的原因很简单,因为我对它更熟悉,而且我觉得它更直观,更容易以文章的形式呈现。 再一次,尽管我在本文中对 Solana 和 Sui 进行了比较,但我的意图并不是要给任何人或任何事情蒙上阴影,而只是强调这些技术的不同方面以及它们的各种好处和权衡,以便于理解。
solana 编程模型 为了充分理解本文中的要点,需要对 Solana 编程模型有一定的了解。如果您不熟悉 Solana 的编程模型,我建议您阅读我关于 Solana 智能合约编程的文章,其中涵盖了遵循此模型所需的所有概念。 为了快速复习,我也将在这里做一个简短的总结。如果您已经熟悉 Solana 的编程模型,则可以跳过本章。 在 Solana 上,程序(智能合约)是无状态的,因为它们不能自行访问(读取或写入)在整个交易过程中持续存在的任何状态。要访问或保持状态,程序需要使用帐户。每个账户都有一个唯一的地址(Ed25519 密钥对的公钥),可以存储任意数据。 我们可以将 Solana 的帐户空间视为全局键值存储,其中键是帐户地址(公钥),值是帐户数据。然后程序通过读取和修改其值在此键值存储之上运行。 账户有所有权的概念。每个帐户由一个(并且只有一个)程序拥有。当帐户归程序所有时,程序可以更改其数据。程序不允许改变他们不拥有的帐户(但允许从中读取)。这些检查是由运行时动态完成的,通过比较程序执行前后的帐户状态,如果发生非法突变,则交易失败。 每个账户还有一个与之关联的私钥(对应的公钥是它的地址),有权访问该私钥的用户可以用它签署交易。使用这种机制,我们在 Solana 智能合约中实现了权限和所有权功能——例如 为了访问某些资金,智能合约可以要求用户提供必要的签名。 在其他进行程序调用时,客户端需要指定该程序在调用期间将访问哪些帐户。这样交易处理运行时可以调度非重叠交易并行执行,同时保证数据一致性。这是 Solana 实现高吞吐量的设计特点之一。 程序可以通过 CPI 调用来调用其他程序。这些调用的工作方式与来自客户端的调用几乎相同——调用程序需要指定被调用程序将访问的帐户,并且被调用程序将执行所有相同的输入检查,就好像它是从客户端调用的一样( 它不信任调用程序)。 PDA 帐户是一种特殊的帐户,它使程序能够在不拥有或存储私钥的情况下提供帐户签名。PDA 保证只有为其生成 PDA 的程序才能为其创建签名(而不是其他用户和程序)。当一个程序需要通过 CPI 调用与另一个程序交互并提供权限(例如,实现一个保管库)时,这很有用。PDA 保证除了程序之外没有人可以直接访问程序的资源。PDA 也可用于在确定的地址创建帐户。 这些是 Solana 上安全智能合约编程的基本构建块。同样,如果您觉得这些概念中的任何一个不清楚,我强烈建议您阅读我关于 Solana 智能合约编程的文章,该文章更深入地介绍了这些(和其他内容)中的每一个。 在某种程度上,您可以将 Solana 程序视为操作系统中的程序,将帐户视为文件,任何人都可以在其中自由执行任何程序,甚至部署自己的程序。当程序(智能合约)运行时,它们将读取和写入文件(帐户)。所有文件都可供所有程序读取,但只有对文件具有所有权权限的程序才能写入。程序也可以执行其他程序,但它们不以任何方式相互信任——无论谁执行程序,它都需要假设输入是潜在的对抗性的。由于任何人都可以在全球范围内访问此操作系统,因此将本机签名验证支持添加到程序中,以便为用户启用权限和所有权功能……这不是一个完美的类比,但它是一个有趣的类比。
Move 编程模型 在 Move 中,智能合约作为模块发布。模块由函数和自定义类型(结构)组成。结构由可以是原始类型(u8、u64、bool…)或其他结构的字段组成。函数可以调用其他函数——可以在同一个模块中,也可以在其他模块中(如果它们是公开的)。 将其放在 Solana 的上下文中,就好像所有智能合约都作为模块发布在单个程序中。这意味着所有智能合约(模块)都包含在同一类型系统中,并且可以直接相互调用,而无需通过中间 API 或接口。这是非常重要的,其含义将在整篇文章中详细讨论。 3.1 对象 在我们继续之前,重要的是要注意以下对象概念特定于 Move 的 Sui 变体,并且在 Move 的其他集成中可能会略有不同(例如 Aptos 或 Diem/core Move)。即便如此,在其他 Move 版本中,也有类似的解决方案可以实现相同的目标(状态持久性),但并不太相似。我在这里介绍 Sui 对象的主要原因是因为本文后面的代码示例基于 Move 的 Sui 版本,还因为对象比例如对象更易于理解。核心 Move 中的全局存储机制。但重要的是,本文讨论的 Move 的所有主要优点都适用于所有 Move 集成(原生支持 Move 字节码),包括 Aptos。要了解 Sui 和 Aptos 版本的不同之处,我们为什么创建 Sui Move文章和 Move on Aptos 文档页面是一个好的开始。 对象是由运行时存储的结构实例,并跨交易保持状态。 存在三种不同类型的对象(在 Sui 中): 拥有者对象
共享对象
不可更改对象
拥有者对象是属于用户的对象。只有拥有该对象的用户才能在交易中使用它。所有权元数据是完全透明的并由运行时处理。它是使用公钥加密实现的——每个拥有的对象都与一个公钥相关联(存储在运行时对象的元数据中),并且任何时候你想在交易中使用一个对象,你都需要提供相应的签名(现在支持 Ed25519 ECDSA 和 K-of-N 多重签名支持即将推出)。 共享对象类似于拥有对象,但它们没有与之关联的所有者。因此,您不必拥有任何私钥就可以在交易中使用它们(任何人都可以使用它们)。任何拥有者对象都可以(由其所有者)共享,并且一旦共享了一个对象,它将永远保持共享状态——它永远不会被转移或再次成为拥有的对象。 不可更改对象:一旦一个对象被标记为不可变,它的字段就不能再被修改。与共享对象类似,它们没有所有者,任何人都可以使用。
Move 编程模型非常直观和简单。每个智能合约都是一个模块,由函数和结构定义组成。结构在函数中实例化,可以通过函数调用传递给其他模块。为了使结构在交易中持久化,我们将其转换为可以拥有者、共享或不更改的对象(特定于 Sui,这在其他 Move 变体中略有不同)。而已!
4. 安全的Move
所以我们已经在 Move 中看到了: 您可以将您拥有(或共享)的任何对象传递给任何模块中的任何函数
任何人都可以发布(可能是对抗性的)模块
没有模块拥有结构的概念,这将使所有者模块拥有对其进行更改的唯一权限,就像 Solana 的帐户一样——结构可以流入其他模块并嵌入到其他结构中
现在的问题是,是什么让它安全?是什么阻止了某人发布对抗模块、获取共享对象(如 AMM 池)并将其发送到对抗模块,然后该模块将继续耗尽资金?
在 Solana 中,有一个帐户所有权的概念,其中只有拥有帐户的程序才被允许对其进行更改。但是在 Move 中,没有模块拥有对象的概念,您可以将对象发送到任意模块中——不仅是对对象的引用,而且是整个对象本身,按值的传递方式。并且运行时没有进行特定检查以确保该对象在通过不受信任的模块时没有被非法修改。那么是什么保证了这个对象的安全呢?来自哪里的不可信代码不会滥用该对象的保证?
好吧,这就是 Move 的新颖性……让我们谈谈资源。 4.1. Structs 定义一个结构类型几乎是你所期望的: struct Foo { x: u64, y: bool }
到目前为止一切顺利——这也是你在 Rust 中定义结构的方式。但是 Move 中的结构有一些独特之处,那就是在 Move 中,与传统编程语言相比,模块对如何使用和不能使用它们的类型有更多的控制。如上面代码片段中定义的结构将具有以下限制:
它只能在定义结构的模块内实例化(“打包”)和销毁(“解包”) - 即您不能从任何其他模块的任何函数内部实例化或销毁结构实例
结构实例的字段只能从其模块访问(并因此发生更改)
您不能在其模块之外克隆或复制结构实例
您不能将结构实例存储在其他结构实例的字段中
这意味着如果您在另一个模块的函数中处理此结构的实例,您将无法改变其字段、克隆它、将其存储在另一个结构的字段中或删除它(您将 必须通过函数调用将其传递给其他地方)。可能结构体的模块实现了一些函数,这些函数可以从我们的模块中调用,但除此之外,我们不能直接为外部类型做任何这些事情。这使模块可以完全控制它们的类型如何使用和不能使用。
现在,似乎有了这些限制,我们失去了很多灵活性。这是真的——在传统编程中处理这样的结构会非常麻烦,但事实上,这正是我们在智能合约中想要的。毕竟,智能合约开发是关于对数字资产(资源)进行编程。如果你看一下上面描述的结构,这正是它的本质——它是一种资源。不能凭空随意创造,不能复制,不能随意破坏。所以我们确实在这里失去了一些灵活性,但我们失去的灵活性正是我们想要失去的那种灵活性。这使得使用资源变得直观且安全。
此外,Move 允许我们通过向结构添加功能来放松其中的一些限制。有四种功能:key、store、copy 和 drop。您可以将这些功能的任意组合添加到结构中 struct Foo has key, store, copy, drop { id: UID, x: u64, y: bool }
他们是这样做的:
key -- 允许结构成为对象(特定于 Sui,核心 Move 略有不同)。如前所述,对象是持久化的,并且在拥有对象的情况下,需要在智能合约调用中使用用户签名。使用 key 能力时,struct 的第一个字段必须是 UID 类型的对象的 ID。这将为其提供一个全局唯一 ID,可用于引用它。
store -- 允许将结构作为字段嵌入到另一个结构中
copy -- 允许从任何地方任意复制/克隆结构
drop -- 允许从任何地方任意销毁结构
本质上,Move 中的每个结构都是默认的资源。功能使我们能够细化地放松这些限制,并使它们的行为更像传统的结构。
4.2 Coin
为了更好地说明这一点,让我们以 Coin 类型为例。Coin 在 Sui 中实现了类似 ERC20 / SPL Token 的功能,并且是 Sui Move Library 的一部分。它是这样定义的: // coin.move struct Coin has key, store { id: UID, balance: Balance } // balance.move struct Balance has store { value: u64 } 您可以在 Sui 代码库中找到完整的模块实现。 Coin 类型具有密钥和存储功能。键意味着它可以用作对象。这允许用户直接拥有Coin(作为顶级对象)。当你拥有一个Coin时,除了你之外没有人可以在交易中引用它(更不用说使用它了)。Store 意味着 Coin 可以作为字段嵌入到另一个结构中。这对于可组合性很有用。 由于没有丢弃功能,因此 Coin 不会在函数中意外丢弃(销毁)。这是一个非常好的功能——这意味着你不会意外丢失Coin。如果你正在实现一个接收 Coin 作为参数的函数,那么在函数结束时,你需要明确地对它做一些事情——要么将其传输给用户,将其嵌入到另一个对象中,要么将其发送到另一个函数中 一个电话(这又需要对它做些什么)。当然可以通过调用 coin 模块中的 coin::burn 函数来销毁 Coin,但是你需要有目的地这样做(你不会意外地这样做)。 没有克隆能力意味着没有人可以复制代币,从而凭空创造新的供应。创建新供应可以通过 coin::mint 函数完成,并且只能由该币的国库能力对象的所有者调用(该对象最初转移给货币创建者)。 另外,请注意,多亏了泛型,每个不同的硬币都会有自己独特的类型。而且由于两个硬币只能通过 coin::join 函数相加(而不是直接访问它们的字段),这意味着根本不可能添加不同类型硬币的值(硬币 A + 硬币 B) - 没有 该签名的功能。类型系统在这里保护我们免于做坏账。 在 Move 中,资源的资源安全性由其类型定义。考虑到 Move 具有全局类型系统,这可以实现更自然和更安全的编程模型,其中资源可以直接传入和传出不受信任的代码,同时保持其安全性。乍一看,这似乎没什么大不了的,但实际上,这对智能合约的可组合性、人体工程学和安全性有很大的好处。这将在第 5 章中更深入地讨论。 4.3. 字节码验证 如前所述,Move 智能合约是作为模块发布的。并且任何人都可以创建任意模块并将其上传到区块链以供任何人执行。我们还看到 Move 对如何使用结构有一定的规则。 那么是什么保证了这些规则被任意模块所遵守呢?是什么阻止某人上传带有特制字节码的模块,例如 接收一个 Coin 对象,然后通过直接改变其内部字段来绕过这些规则?通过这样做,您可以非法夸大您拥有的硬币数量。仅字节码语法就可以做到这一点。 字节码验证可以防止这种滥用。Move 验证器是一个静态分析工具,它分析 Move 字节码并确定它是否遵守所需的类型、内存和资源安全规则。所有上传到链上的代码都需要通过验证者。当您尝试将 Move 模块上传到链上时,节点和验证器将首先通过验证器运行它,然后才能被允许提交。如果任何模块试图绕过 Move 的安全规则,它将被验证者拒绝并且不会被发布。这就是为什么用特制的字节码不可能破坏类型或资源安全规则的原因——验证会阻止你将这样的模块上传到链上! Move 字节码和验证器是 Move 的核心新颖性。这使得以资源为中心的直观编程模型成为可能,这是不可能的。关键是它允许结构化类型跨越信任边界传递而不会失去其完整性。 在 Solana 上,智能合约是程序,而在 Move 中,它们是模块。这似乎只是语义上的差异,但事实并非如此,它具有巨大的意义。不同之处在于,在 Solana 上没有跨程序边界的类型安全——每个程序通过从原始帐户数据手动解码来加载实例,这涉及手动进行关键安全检查。也没有本地资源安全。相反,资源安全必须由每个智能合约单独实施。这确实允许足够的可编程性,但与 Move 的模型相比,它极大地阻碍了可组合性和人机工程学,Move 的模型对资源有原生支持,并且它们可以安全地流入和流出不受信任的代码。 在 Move 中,类型确实存在于模块之间——类型系统是全局的。这意味着不需要 CPI 调用、帐户编码/解码、帐户所有权检查等——您只需使用参数直接调用另一个模块中的函数。跨智能合约的类型和资源安全由编译/发布时的字节码验证来保证,不需要在智能合约级别实现,然后像在 Solana 上那样在运行时检查。 5. Solana vs. Move 现在我们已经了解了 Move 编程的工作原理以及它从根本上安全的原因,让我们从可组合性、人机工程学和安全性的角度更深入地了解它对智能合约编程的影响。在这里,我将比较 Move/Sui 开发与 EVM 和 Rust/Solana/Anchor,以帮助了解 Move 的编程模型带来的好处。 5.1. 闪电贷 让我们从闪贷示例开始。闪电贷是 DeFi 中的一种贷款,借出的金额必须在借入的同一笔交易中偿还。这样做的主要好处是,由于交易是原子的,贷款可以完全无抵押。这可以用于例如 无需本金即可在资产之间进行套利。 实现这一点的主要困难是——你如何从闪贷智能合约中保证借出的金额将在同一笔交易中得到偿还?为了使贷款能够无抵押,交易需要是原子的——即如果借出的金额没有在同一笔交易中归还,整个交易需要失败。 EVM 具有动态调度,因此可以使用可重入实现,如下所示: 闪贷用户创建并上传自定义智能合约,调用时将通过调用将控制权传递给闪贷智能合约 闪贷智能合约然后将请求的贷款金额发送到自定义智能合约并调用自定义智能合约中的executeOperation()回调函数 然后,自定义智能合约将使用收到的借出金额来执行其所需的操作(例如套利) 自定义智能合约完成操作后,需要将借出的金额返还给闪贷智能合约 至此,自定义智能合约的 executionOperation() 将完成,控制权将返回到闪贷智能合约,该合约将检查借出金额是否已正确归还 如果自定义智能合约没有正确返回借出的金额,整个交易将失败 这很好地实现了所需的功能,但问题是它依赖于可重入性,我们非常希望禁止它在智能合约编程中成为可能。这是因为重入本质上是非常危险的,并且是许多漏洞的根本原因,包括臭名昭著的 DAO hack。 Solana 在这里做得更好,因为它不允许重入。但是如果没有可重入性和闪贷智能合约回调自定义智能合约的可能性,您如何在 Solana 上实施闪贷?好吧,这要归功于指令内省。在 Solana 上,每笔交易都包含多条指令(智能合约调用),您可以从任何指令中检查同一交易中存在的其他指令(它们的程序 ID、指令数据和账户)。这使得实施闪电贷款成为可能,如下所示: 闪贷智能合约实现借款和还款指令 用户通过在同一笔交易中将借入和还款指令调用堆叠在一起来创建闪贷交易。借入指令在执行时将使用指令自省检查是否在同一交易中稍后安排还款指令。如果还款指令调用不存在或无效,则此阶段交易将失败 在借入和还款调用之间,借入的资金可以被介于两者之间的任何其他指令任意使用 在交易结束时,repay 指令调用会将资金返还给 flash Lender 智能合约(在借入指令中使用自省检查该指令的存在) 出于好奇,这里有一个原型实现(链接) 该解决方案运行良好,但仍不理想。指令自省在某种程度上是一种特殊情况,而不是在 Solana 中常用的东西,因此它的使用在开发人员需要掌握的概念数量和实现本身的技术方面都有开销,因为有一些 需要适当考虑的细微差别。还有一个技术限制——因为repay指令需要在交易中静态存在,所以不可能在交易执行期间通过CPI调用动态调用repay。这几乎不会破坏交易,但在将其与其他智能合约集成时,它在一定程度上限制了代码的灵活性,并且还将更多的复杂性推到了客户端。 Move 还禁止动态调度和重入,但与 Solana 不同,它有一个非常简单和自然的闪贷解决方案。Move 的线性类型系统允许创建保证在交易执行期间仅使用一次的结构。这就是所谓的“烫手山芋”模式——一种没有键、存储、删除或克隆功能的结构。实现这种模式的模块通常具有一个实例化结构的函数和另一个破坏它的函数。由于“烫手山芋”结构没有删除、键或存储功能,因此保证将调用其“销毁”函数以使用它。即使我们可以将它传递给任何模块中的任意数量的其他函数,最终它还是需要在“销毁”函数中结束。这仅仅是因为没有其他方法可以摆脱它,并且验证者要求在交易结束时对其进行处理(它不能随意丢弃,因为没有丢弃能力)。 让我们看看如何利用它来实施闪电贷: 闪贷智能合约实现“烫手山芋” Receipt 结构 当通过调用贷款函数进行贷款时,它将向调用者发送两个对象——请求的资金(一个硬币)和一个收据,它是要偿还的贷款金额的记录 然后借款人可以将收到的资金用于其所需的操作(例如套利) 借款人完成其预期操作后,需要调用 repay 函数,该函数将接收借入资金和收据作为参数。这个函数保证在同一个交易中被调用,因为调用者没有其他方法可以摆脱 Receipt 实例(它不允许被丢弃或嵌入到另一个对象中,这是由验证者断言的) 还款函数通过读取收据中嵌入的贷款信息来检查是否退回了正确的金额。 可以在这里找到一个示例实现。 Move 的资源安全功能使 Move 中的闪电贷成为可能,而无需使用重入或内省。他们保证收据不能被不受信任的代码修改,并且需要在交易结束时返回到还款函数。有了这个,我们可以保证在同一笔交易中返回正确数量的资金。 该功能是使用基本语言原语完全实现的,Move 实现不会像 Solana 实现需要特殊处理的交易那样受到集成开销的影响。此外,没有把复杂性被推到客户端。 5.2. 铸币 权限锁 为了进一步突出 Move 编程模型的优势,我在 Solana (Anchor) 和 Sui Move 中都实现了“Mint Authority Lock”智能合约进行比较。 “铸币权限锁”智能合约所做的是将代币铸币的功能扩展为允许多个白名单方(机构)铸币,而不仅仅是一个铸币厂。智能合约所需的功能如下(适用于 Solana 和 Sui 实现): 原始代币铸币机构创建了一个“铸币锁”,这将使我们的智能合约能够规范代币的铸币。调用者成为”铸币锁“的管理员。 然后,管理员可以为可以提供给其他方的锁创建额外的铸币权限,并允许他们在需要时使用锁铸币。 每个铸币权限都有每日可铸币数量的限制。 管理员可以随时禁止(和取消禁止)任何铸币权限。 管理员权限可以转移给另一方。 该智能合约可用于例如 在原始铸币权限(管理员)仍保留铸币厂控制权的情况下,将代币的铸币功能提供给其他用户或智能合约。如果没有这个,我们必须将铸币的完全控制权交给另一方,这并不理想,因为我们必须相信它不会滥用它。并且不可能向多方授予许可 可以在此处 (Solana) 和此处 (Sui) 找到这些智能合约的完整实施。 现在让我们看一下代码,看看实现有何不同。以下是此智能合约的完整 Solana 和 Sui 实现的并排代码屏幕截图: 图片 立即引人注目的是,对于相同的功能,Solana 实现的大小是 Sui 的两倍多(230 LOC 对 104)。这已经很重要了,因为更少的代码通常意味着更少的错误和更短的开发时间。 那么这些额外的线路是从哪里来的 Solana 呢?如果我们仔细查看 Solana 代码,我们可以将其分为两部分——指令实现(智能合约逻辑)和账户检查。指令实现与我们在 Sui 上的实现有点接近——136 LOC 与 Sui 上的 104。额外的行可归因于两个 CPI 调用的样板文件(每个约 10 LOC)。最显着的区别是由于帐户检查(上面屏幕截图中标记为红色的部分)在 Solana 上是必需的(实际上很关键),但在 Move 中不需要。账户支票占该智能合约(91 LOC)的约 40%。 Move 不需要帐户检查。这不仅是有益的,因为 LOC 减少了。消除进行帐户检查的必要性非常重要,因为事实证明正确实施这些检查非常棘手,而且如果您在那里犯了一个错误,通常会导致严重的漏洞和用户资金的损失。事实上,一些最大的(就用户资金损失而言)Solana 智能合约漏洞是由不正确的账户检查引起的账户替换攻击: Wormhole ($336M) Cashio($48M) Crema Finance ($8.8M) 显然,摆脱这些支票将是一件大事。 8. 总结 本文深入探讨了 Solana 和 Sui 的编程模型、它们的比较方式以及 Move 编程语言。 第 2 章是对 Solana 编程模型的总结,第 3 章介绍了 Sui Move 及其编程模型。第 4 章接着解释类型和资源安全如何在 Move 中工作。Move 的特性对智能合约开发的意义并不是很明显,所以在第 5 章中,使用现实生活中的示例对 Solana 和 Sui Move 进行了更彻底的比较。第 6 章讨论了 eBPF/SBF,并说明让 Move 功能或 Move 本身在 Solana 上工作并非易事。第 7 章涉及 Sui 的一些与 Move 相关的功能。 智能合约编程是关于对数字资产进行编程。可以肯定地说,这是一种新的编程类型,不同于我们迄今为止看到的其他类型的编程(例如系统、后端……)。因此,现有的编程语言和编程模型不能很好地适应这个用例也就不足为奇了。 问题的症结在于,我们想要一个自然地用于处理资源的编程模型,但同时我们正在与不受信任的代码进行交互。Solana 在这里做了一个妥协,它确实在不受信任的环境中启用了具有必要可编程性的智能合约,但它的编程模型对于使用资源进行编程并不是很自然。字节码验证使两者都成为可能。在某种程度上,它将不受信任的代码变成了受信任的代码。 Move 是一种用于智能合约开发的新颖编程语言。它的核心新颖之处在于它的字节码,它被有意设计为易于验证。虽然字节码验证本身并不是一个新概念,但 Move 所做的验证却是。通过其字节码和验证,Move 实现了一种智能合约编程模型,该模型对资源具有一流的支持,并且可以在不受信任的环境中保证安全的可编程性。 乍一看,这对智能合约开发的影响并不明显,但如第 5 章所述,它们在智能合约的人体工程学、可组合性和安全性方面确实非常重要。Move 比 Solana 的基于 Rust 的编程模型有了显着的进步。 如此之多,以至于我认为 Move 对智能合约开发的作用就像 React 对前端开发所做的那样,并且说“你可以用 Move 做什么,你可以用 Rust 做什么”类似于说“你可以用 React 做什么” 可以用 jQuery 做”。当然,可以实现一个与 React 应用程序等效的基于 jQuery 的应用程序,但这并不实用。React 引入了虚拟 DOM 的概念,它对开发人员完全透明,但允许更快、可扩展和简单的前端开发。以类似的方式,Move 的字节码验证是一种底层技术,对开发人员也是完全透明的,但它提供了更符合人体工程学、可组合和更安全的智能合约开发。由于其安全性和更直观的编程模型,Move 还大大降低了智能合约开发人员的入门门槛。 如果 Move 设法获得牵引力(有早期迹象表明它会),它可能会对 Solana 构成相当大的威胁。这是因为两个原因。 首先是 Move 智能合约的开发时间要快得多。看来,在 Move 中从头开始开发智能合约的速度可能比在 Rust 中快 2-5 倍。在编写在 Move 中微不足道但在 Solana 上可能很复杂的智能合约时尤其如此。因此,Move 生态系统的发展可能会超过 Solana。由于区块链的开放性和无许可性,没有强大的锁定效应,流动性可以轻松流动。Solana 开发人员可能会因为经济原因而被迫采用 Move——您可以切换到 Move 或被更快地开发更安全智能合约的 Move 开发人员赶超。如果您正在雇用智能合约开发人员,您可以雇用将构建一个智能合约的 Rust 开发人员或将在相同时间内构建两个更安全的 Move 开发人员。这类似于 React 对前端开发的影响。 第二个原因是 Move 的门槛远低于 Rust 或 Solidity。因为 Move 语法更简单,编程模型更直观,所以有一整类开发人员无法在 Rust 或 Solidity 中进行智能合约开发,但可能能够在 Move 中进行。而且由于要学习的概念更少,让非智能合约开发人员加入 Move 比让他们加入 Rust 容易得多(Rust 本身就是一种复杂的语言,加上像 PDA 这样的 Solana 概念会给初学者带来很多困惑)或 Solidity(您需要熟悉语言的非常微妙的细节,例如可重入性,以便能够开发安全的智能合约)。即使现有的 Solana 和 Solidity 开发人员不切换到 Move,尚未进入该领域的开发人员市场也比该领域现有开发人员的数量要大几个数量级。由于 Move 的进入门槛较低并允许更快的开发,因此与 Rust 或 Solidity 相比,它具有更好的产品市场契合度,并且可以占据更大的份额。如果新开发人员开始大量涌入,我希望他们从 Move 而不是 Rust 或 Solidity 开始。这再次类似于 Web 行业中 React 所发生的情况。 因此,我完全期望在中长期内将一流的 Move 支持添加到 Solana。但这不是一件容易的事。为了获得 Move 的主要好处,Move 字节码需要得到原生支持。这意味着简单地编译 Move 到 eBPF/SBF 并不会削减它(参见第 6.3 节)。为了保持现有的生态系统,两个运行时都需要得到支持。主要的技术挑战是在运行时之间实现适当的互操作性。这需要对 Move 和 Solana 有深入的了解,因此我希望 Solana 团队在 Move 团队的某个人的支持下直接推动这一点。 Move 起源于 Diem 项目的 Meta(née Facebook)。由 Sam Blackshear 领导的 Move 团队的任务是弄清楚如何处理智能合约。在更仔细地研究了这个问题后,他们发现智能合约编程就是对数字资产(资源)进行编程,但现有的语言都没有原生支持该用例。决定从头开始构建一种新的编程语言。 我想强调的是,创建一门新语言的决定并不是一个显而易见的决定,因为它需要多年的工程努力才能让它起步,而且在大多数情况下,最好使用现有的解决方案。Move 团队正确地预见到了一种安全、对资源具有一流支持、同时又足够灵活的智能合约语言可以构建,仅此一项就显示出高度的专业性。这是团队和支持该项目的 Novi/Meta 工程领导层的一个大胆举措(将有许多董事和副总裁参与这样的决定)。Meta 自从关闭了他们的 Diem 工作后,最终未能获得其对 Move 的投资的结果,但它仍然对更广泛的加密社区做出了巨大贡献。 总而言之,Move 是一项了不起的技术,我相信它将对我们开发智能合约的方式产生巨大影响。干得好Move团队!

从Aptos与Sui 含着金钥匙出场的那一刻起,人们就一直想知道为什么开发人员对 Move 语言如此兴奋。
更有意思的是,开发人员如此喜欢把 Move 语言与Rust 做比较,Aptos 与Solana 做比较。 本篇文章深入研究了 Move,一种用于智能合约开发的新颖编程语言,以及它与 Solana 上使用的现有基于 Rust 的模型的比较。
以下为MoveChina 翻译的全文《能合约开发——Move vs. Rust》。深入研究 Move,一种用于智能合约开发的新颖编程语言,以及它与 Solana 上使用的现有基于 Rust 的模型的比较。
介绍
最近几周和几个月,围绕 Aptos 和 Sui、新兴的高性能 L1 以及这些新链不可或缺的 Move 智能合约编程语言引起了很多关注。一些开发人员已经开始积极转向 Move,他们发誓这是智能合约开发的未来,而另一些开发人员则有点谨慎,他们认为 Move 只是另一种智能合约编程语言,从根本上说,它并没有提供比现有编程模型更多的功能。加密货币投资者还想知道这些新的 L1 有什么特别之处,以及它们如何与 Solana 相抗衡,后者目前是高性能 L1 类别的主要参与者,尤其是使用 Rust 作为智能合约编程的基础。
但是到目前为止,我们所看到的讨论并没有深入到充分理解这项新技术给我们带来的必要深度。这在讨论的两端都是正确的——Move 怀疑论者淡化了 Move 而没有充分理解它的一些更微妙(但非常重要)的方面,而 Move 粉丝则称赞 Move 没有尽头,但未能充分阐明究竟是什么让它如此 伟大的。这留下了一个很大的中间立场和很多模棱两可的地方,因此一直在关注这个讨论的外部旁观者、加密货币开发人员和投资者无法自信地形成他们的意见。
我最近对 Move 进行了深入研究,并在 Solana 上拥有智能合约开发经验 在这个讨论之外。
在本文中,我将深入探讨 Move、其新颖的编程模型、Sui 区块链以及它如何利用 Move 的功能,以及它与 Solana 及其编程模型的比较。 为了突出 Move 的特性,我将 Solana/Rust 与 Sui/Move 进行比较。我这样做的主要原因是,当您将某件事与您已经熟悉的另一件事进行比较而不是尝试自己理解它时,更容易理解它。值得注意的是,Move 还有其他版本,例如 Aptos Move,它们在某些事情上略有不同。本文的重点不是讨论 Move 不同版本之间的细微差别,而是展示 Move 的一般优势以及它与 Solana 编程模型的比较,因此为简单起见,我决定只使用一种版本( Sui Move) 通篇。因此,我在本文中介绍的某些 Move 概念(即对象和相关功能)仅适用于 Move 的 Sui 版本,而不适用于其他版本。虽然 Move 的其他版本不一定具有这些概念,但它们使用不同的机制(例如全局存储)实现相同的功能。但即便如此,本文中讨论的所有主要 Move 优势都适用于所有 Move 集成(原生支持 Move 字节码),包括 Aptos。我特别选择 Sui 的原因很简单,因为我对它更熟悉,而且我觉得它更直观,更容易以文章的形式呈现。 再一次,尽管我在本文中对 Solana 和 Sui 进行了比较,但我的意图并不是要给任何人或任何事情蒙上阴影,而只是强调这些技术的不同方面以及它们的各种好处和权衡,以便于理解。
solana 编程模型 为了充分理解本文中的要点,需要对 Solana 编程模型有一定的了解。如果您不熟悉 Solana 的编程模型,我建议您阅读我关于 Solana 智能合约编程的文章,其中涵盖了遵循此模型所需的所有概念。 为了快速复习,我也将在这里做一个简短的总结。如果您已经熟悉 Solana 的编程模型,则可以跳过本章。 在 Solana 上,程序(智能合约)是无状态的,因为它们不能自行访问(读取或写入)在整个交易过程中持续存在的任何状态。要访问或保持状态,程序需要使用帐户。每个账户都有一个唯一的地址(Ed25519 密钥对的公钥),可以存储任意数据。 我们可以将 Solana 的帐户空间视为全局键值存储,其中键是帐户地址(公钥),值是帐户数据。然后程序通过读取和修改其值在此键值存储之上运行。 账户有所有权的概念。每个帐户由一个(并且只有一个)程序拥有。当帐户归程序所有时,程序可以更改其数据。程序不允许改变他们不拥有的帐户(但允许从中读取)。这些检查是由运行时动态完成的,通过比较程序执行前后的帐户状态,如果发生非法突变,则交易失败。 每个账户还有一个与之关联的私钥(对应的公钥是它的地址),有权访问该私钥的用户可以用它签署交易。使用这种机制,我们在 Solana 智能合约中实现了权限和所有权功能——例如 为了访问某些资金,智能合约可以要求用户提供必要的签名。 在其他进行程序调用时,客户端需要指定该程序在调用期间将访问哪些帐户。这样交易处理运行时可以调度非重叠交易并行执行,同时保证数据一致性。这是 Solana 实现高吞吐量的设计特点之一。 程序可以通过 CPI 调用来调用其他程序。这些调用的工作方式与来自客户端的调用几乎相同——调用程序需要指定被调用程序将访问的帐户,并且被调用程序将执行所有相同的输入检查,就好像它是从客户端调用的一样( 它不信任调用程序)。 PDA 帐户是一种特殊的帐户,它使程序能够在不拥有或存储私钥的情况下提供帐户签名。PDA 保证只有为其生成 PDA 的程序才能为其创建签名(而不是其他用户和程序)。当一个程序需要通过 CPI 调用与另一个程序交互并提供权限(例如,实现一个保管库)时,这很有用。PDA 保证除了程序之外没有人可以直接访问程序的资源。PDA 也可用于在确定的地址创建帐户。 这些是 Solana 上安全智能合约编程的基本构建块。同样,如果您觉得这些概念中的任何一个不清楚,我强烈建议您阅读我关于 Solana 智能合约编程的文章,该文章更深入地介绍了这些(和其他内容)中的每一个。 在某种程度上,您可以将 Solana 程序视为操作系统中的程序,将帐户视为文件,任何人都可以在其中自由执行任何程序,甚至部署自己的程序。当程序(智能合约)运行时,它们将读取和写入文件(帐户)。所有文件都可供所有程序读取,但只有对文件具有所有权权限的程序才能写入。程序也可以执行其他程序,但它们不以任何方式相互信任——无论谁执行程序,它都需要假设输入是潜在的对抗性的。由于任何人都可以在全球范围内访问此操作系统,因此将本机签名验证支持添加到程序中,以便为用户启用权限和所有权功能……这不是一个完美的类比,但它是一个有趣的类比。
Move 编程模型 在 Move 中,智能合约作为模块发布。模块由函数和自定义类型(结构)组成。结构由可以是原始类型(u8、u64、bool…)或其他结构的字段组成。函数可以调用其他函数——可以在同一个模块中,也可以在其他模块中(如果它们是公开的)。 将其放在 Solana 的上下文中,就好像所有智能合约都作为模块发布在单个程序中。这意味着所有智能合约(模块)都包含在同一类型系统中,并且可以直接相互调用,而无需通过中间 API 或接口。这是非常重要的,其含义将在整篇文章中详细讨论。 3.1 对象 在我们继续之前,重要的是要注意以下对象概念特定于 Move 的 Sui 变体,并且在 Move 的其他集成中可能会略有不同(例如 Aptos 或 Diem/core Move)。即便如此,在其他 Move 版本中,也有类似的解决方案可以实现相同的目标(状态持久性),但并不太相似。我在这里介绍 Sui 对象的主要原因是因为本文后面的代码示例基于 Move 的 Sui 版本,还因为对象比例如对象更易于理解。核心 Move 中的全局存储机制。但重要的是,本文讨论的 Move 的所有主要优点都适用于所有 Move 集成(原生支持 Move 字节码),包括 Aptos。要了解 Sui 和 Aptos 版本的不同之处,我们为什么创建 Sui Move文章和 Move on Aptos 文档页面是一个好的开始。 对象是由运行时存储的结构实例,并跨交易保持状态。 存在三种不同类型的对象(在 Sui 中): 拥有者对象
共享对象
不可更改对象
拥有者对象是属于用户的对象。只有拥有该对象的用户才能在交易中使用它。所有权元数据是完全透明的并由运行时处理。它是使用公钥加密实现的——每个拥有的对象都与一个公钥相关联(存储在运行时对象的元数据中),并且任何时候你想在交易中使用一个对象,你都需要提供相应的签名(现在支持 Ed25519 ECDSA 和 K-of-N 多重签名支持即将推出)。 共享对象类似于拥有对象,但它们没有与之关联的所有者。因此,您不必拥有任何私钥就可以在交易中使用它们(任何人都可以使用它们)。任何拥有者对象都可以(由其所有者)共享,并且一旦共享了一个对象,它将永远保持共享状态——它永远不会被转移或再次成为拥有的对象。 不可更改对象:一旦一个对象被标记为不可变,它的字段就不能再被修改。与共享对象类似,它们没有所有者,任何人都可以使用。
Move 编程模型非常直观和简单。每个智能合约都是一个模块,由函数和结构定义组成。结构在函数中实例化,可以通过函数调用传递给其他模块。为了使结构在交易中持久化,我们将其转换为可以拥有者、共享或不更改的对象(特定于 Sui,这在其他 Move 变体中略有不同)。而已!
4. 安全的Move
所以我们已经在 Move 中看到了: 您可以将您拥有(或共享)的任何对象传递给任何模块中的任何函数
任何人都可以发布(可能是对抗性的)模块
没有模块拥有结构的概念,这将使所有者模块拥有对其进行更改的唯一权限,就像 Solana 的帐户一样——结构可以流入其他模块并嵌入到其他结构中
现在的问题是,是什么让它安全?是什么阻止了某人发布对抗模块、获取共享对象(如 AMM 池)并将其发送到对抗模块,然后该模块将继续耗尽资金?
在 Solana 中,有一个帐户所有权的概念,其中只有拥有帐户的程序才被允许对其进行更改。但是在 Move 中,没有模块拥有对象的概念,您可以将对象发送到任意模块中——不仅是对对象的引用,而且是整个对象本身,按值的传递方式。并且运行时没有进行特定检查以确保该对象在通过不受信任的模块时没有被非法修改。那么是什么保证了这个对象的安全呢?来自哪里的不可信代码不会滥用该对象的保证?
好吧,这就是 Move 的新颖性……让我们谈谈资源。 4.1. Structs 定义一个结构类型几乎是你所期望的: struct Foo { x: u64, y: bool }
到目前为止一切顺利——这也是你在 Rust 中定义结构的方式。但是 Move 中的结构有一些独特之处,那就是在 Move 中,与传统编程语言相比,模块对如何使用和不能使用它们的类型有更多的控制。如上面代码片段中定义的结构将具有以下限制:
它只能在定义结构的模块内实例化(“打包”)和销毁(“解包”) - 即您不能从任何其他模块的任何函数内部实例化或销毁结构实例
结构实例的字段只能从其模块访问(并因此发生更改)
您不能在其模块之外克隆或复制结构实例
您不能将结构实例存储在其他结构实例的字段中
这意味着如果您在另一个模块的函数中处理此结构的实例,您将无法改变其字段、克隆它、将其存储在另一个结构的字段中或删除它(您将 必须通过函数调用将其传递给其他地方)。可能结构体的模块实现了一些函数,这些函数可以从我们的模块中调用,但除此之外,我们不能直接为外部类型做任何这些事情。这使模块可以完全控制它们的类型如何使用和不能使用。
现在,似乎有了这些限制,我们失去了很多灵活性。这是真的——在传统编程中处理这样的结构会非常麻烦,但事实上,这正是我们在智能合约中想要的。毕竟,智能合约开发是关于对数字资产(资源)进行编程。如果你看一下上面描述的结构,这正是它的本质——它是一种资源。不能凭空随意创造,不能复制,不能随意破坏。所以我们确实在这里失去了一些灵活性,但我们失去的灵活性正是我们想要失去的那种灵活性。这使得使用资源变得直观且安全。
此外,Move 允许我们通过向结构添加功能来放松其中的一些限制。有四种功能:key、store、copy 和 drop。您可以将这些功能的任意组合添加到结构中 struct Foo has key, store, copy, drop { id: UID, x: u64, y: bool }
他们是这样做的:
key -- 允许结构成为对象(特定于 Sui,核心 Move 略有不同)。如前所述,对象是持久化的,并且在拥有对象的情况下,需要在智能合约调用中使用用户签名。使用 key 能力时,struct 的第一个字段必须是 UID 类型的对象的 ID。这将为其提供一个全局唯一 ID,可用于引用它。
store -- 允许将结构作为字段嵌入到另一个结构中
copy -- 允许从任何地方任意复制/克隆结构
drop -- 允许从任何地方任意销毁结构
本质上,Move 中的每个结构都是默认的资源。功能使我们能够细化地放松这些限制,并使它们的行为更像传统的结构。
4.2 Coin
为了更好地说明这一点,让我们以 Coin 类型为例。Coin 在 Sui 中实现了类似 ERC20 / SPL Token 的功能,并且是 Sui Move Library 的一部分。它是这样定义的: // coin.move struct Coin has key, store { id: UID, balance: Balance } // balance.move struct Balance has store { value: u64 } 您可以在 Sui 代码库中找到完整的模块实现。 Coin 类型具有密钥和存储功能。键意味着它可以用作对象。这允许用户直接拥有Coin(作为顶级对象)。当你拥有一个Coin时,除了你之外没有人可以在交易中引用它(更不用说使用它了)。Store 意味着 Coin 可以作为字段嵌入到另一个结构中。这对于可组合性很有用。 由于没有丢弃功能,因此 Coin 不会在函数中意外丢弃(销毁)。这是一个非常好的功能——这意味着你不会意外丢失Coin。如果你正在实现一个接收 Coin 作为参数的函数,那么在函数结束时,你需要明确地对它做一些事情——要么将其传输给用户,将其嵌入到另一个对象中,要么将其发送到另一个函数中 一个电话(这又需要对它做些什么)。当然可以通过调用 coin 模块中的 coin::burn 函数来销毁 Coin,但是你需要有目的地这样做(你不会意外地这样做)。 没有克隆能力意味着没有人可以复制代币,从而凭空创造新的供应。创建新供应可以通过 coin::mint 函数完成,并且只能由该币的国库能力对象的所有者调用(该对象最初转移给货币创建者)。 另外,请注意,多亏了泛型,每个不同的硬币都会有自己独特的类型。而且由于两个硬币只能通过 coin::join 函数相加(而不是直接访问它们的字段),这意味着根本不可能添加不同类型硬币的值(硬币 A + 硬币 B) - 没有 该签名的功能。类型系统在这里保护我们免于做坏账。 在 Move 中,资源的资源安全性由其类型定义。考虑到 Move 具有全局类型系统,这可以实现更自然和更安全的编程模型,其中资源可以直接传入和传出不受信任的代码,同时保持其安全性。乍一看,这似乎没什么大不了的,但实际上,这对智能合约的可组合性、人体工程学和安全性有很大的好处。这将在第 5 章中更深入地讨论。 4.3. 字节码验证 如前所述,Move 智能合约是作为模块发布的。并且任何人都可以创建任意模块并将其上传到区块链以供任何人执行。我们还看到 Move 对如何使用结构有一定的规则。 那么是什么保证了这些规则被任意模块所遵守呢?是什么阻止某人上传带有特制字节码的模块,例如 接收一个 Coin 对象,然后通过直接改变其内部字段来绕过这些规则?通过这样做,您可以非法夸大您拥有的硬币数量。仅字节码语法就可以做到这一点。 字节码验证可以防止这种滥用。Move 验证器是一个静态分析工具,它分析 Move 字节码并确定它是否遵守所需的类型、内存和资源安全规则。所有上传到链上的代码都需要通过验证者。当您尝试将 Move 模块上传到链上时,节点和验证器将首先通过验证器运行它,然后才能被允许提交。如果任何模块试图绕过 Move 的安全规则,它将被验证者拒绝并且不会被发布。这就是为什么用特制的字节码不可能破坏类型或资源安全规则的原因——验证会阻止你将这样的模块上传到链上! Move 字节码和验证器是 Move 的核心新颖性。这使得以资源为中心的直观编程模型成为可能,这是不可能的。关键是它允许结构化类型跨越信任边界传递而不会失去其完整性。 在 Solana 上,智能合约是程序,而在 Move 中,它们是模块。这似乎只是语义上的差异,但事实并非如此,它具有巨大的意义。不同之处在于,在 Solana 上没有跨程序边界的类型安全——每个程序通过从原始帐户数据手动解码来加载实例,这涉及手动进行关键安全检查。也没有本地资源安全。相反,资源安全必须由每个智能合约单独实施。这确实允许足够的可编程性,但与 Move 的模型相比,它极大地阻碍了可组合性和人机工程学,Move 的模型对资源有原生支持,并且它们可以安全地流入和流出不受信任的代码。 在 Move 中,类型确实存在于模块之间——类型系统是全局的。这意味着不需要 CPI 调用、帐户编码/解码、帐户所有权检查等——您只需使用参数直接调用另一个模块中的函数。跨智能合约的类型和资源安全由编译/发布时的字节码验证来保证,不需要在智能合约级别实现,然后像在 Solana 上那样在运行时检查。 5. Solana vs. Move 现在我们已经了解了 Move 编程的工作原理以及它从根本上安全的原因,让我们从可组合性、人机工程学和安全性的角度更深入地了解它对智能合约编程的影响。在这里,我将比较 Move/Sui 开发与 EVM 和 Rust/Solana/Anchor,以帮助了解 Move 的编程模型带来的好处。 5.1. 闪电贷 让我们从闪贷示例开始。闪电贷是 DeFi 中的一种贷款,借出的金额必须在借入的同一笔交易中偿还。这样做的主要好处是,由于交易是原子的,贷款可以完全无抵押。这可以用于例如 无需本金即可在资产之间进行套利。 实现这一点的主要困难是——你如何从闪贷智能合约中保证借出的金额将在同一笔交易中得到偿还?为了使贷款能够无抵押,交易需要是原子的——即如果借出的金额没有在同一笔交易中归还,整个交易需要失败。 EVM 具有动态调度,因此可以使用可重入实现,如下所示: 闪贷用户创建并上传自定义智能合约,调用时将通过调用将控制权传递给闪贷智能合约 闪贷智能合约然后将请求的贷款金额发送到自定义智能合约并调用自定义智能合约中的executeOperation()回调函数 然后,自定义智能合约将使用收到的借出金额来执行其所需的操作(例如套利) 自定义智能合约完成操作后,需要将借出的金额返还给闪贷智能合约 至此,自定义智能合约的 executionOperation() 将完成,控制权将返回到闪贷智能合约,该合约将检查借出金额是否已正确归还 如果自定义智能合约没有正确返回借出的金额,整个交易将失败 这很好地实现了所需的功能,但问题是它依赖于可重入性,我们非常希望禁止它在智能合约编程中成为可能。这是因为重入本质上是非常危险的,并且是许多漏洞的根本原因,包括臭名昭著的 DAO hack。 Solana 在这里做得更好,因为它不允许重入。但是如果没有可重入性和闪贷智能合约回调自定义智能合约的可能性,您如何在 Solana 上实施闪贷?好吧,这要归功于指令内省。在 Solana 上,每笔交易都包含多条指令(智能合约调用),您可以从任何指令中检查同一交易中存在的其他指令(它们的程序 ID、指令数据和账户)。这使得实施闪电贷款成为可能,如下所示: 闪贷智能合约实现借款和还款指令 用户通过在同一笔交易中将借入和还款指令调用堆叠在一起来创建闪贷交易。借入指令在执行时将使用指令自省检查是否在同一交易中稍后安排还款指令。如果还款指令调用不存在或无效,则此阶段交易将失败 在借入和还款调用之间,借入的资金可以被介于两者之间的任何其他指令任意使用 在交易结束时,repay 指令调用会将资金返还给 flash Lender 智能合约(在借入指令中使用自省检查该指令的存在) 出于好奇,这里有一个原型实现(链接) 该解决方案运行良好,但仍不理想。指令自省在某种程度上是一种特殊情况,而不是在 Solana 中常用的东西,因此它的使用在开发人员需要掌握的概念数量和实现本身的技术方面都有开销,因为有一些 需要适当考虑的细微差别。还有一个技术限制——因为repay指令需要在交易中静态存在,所以不可能在交易执行期间通过CPI调用动态调用repay。这几乎不会破坏交易,但在将其与其他智能合约集成时,它在一定程度上限制了代码的灵活性,并且还将更多的复杂性推到了客户端。 Move 还禁止动态调度和重入,但与 Solana 不同,它有一个非常简单和自然的闪贷解决方案。Move 的线性类型系统允许创建保证在交易执行期间仅使用一次的结构。这就是所谓的“烫手山芋”模式——一种没有键、存储、删除或克隆功能的结构。实现这种模式的模块通常具有一个实例化结构的函数和另一个破坏它的函数。由于“烫手山芋”结构没有删除、键或存储功能,因此保证将调用其“销毁”函数以使用它。即使我们可以将它传递给任何模块中的任意数量的其他函数,最终它还是需要在“销毁”函数中结束。这仅仅是因为没有其他方法可以摆脱它,并且验证者要求在交易结束时对其进行处理(它不能随意丢弃,因为没有丢弃能力)。 让我们看看如何利用它来实施闪电贷: 闪贷智能合约实现“烫手山芋” Receipt 结构 当通过调用贷款函数进行贷款时,它将向调用者发送两个对象——请求的资金(一个硬币)和一个收据,它是要偿还的贷款金额的记录 然后借款人可以将收到的资金用于其所需的操作(例如套利) 借款人完成其预期操作后,需要调用 repay 函数,该函数将接收借入资金和收据作为参数。这个函数保证在同一个交易中被调用,因为调用者没有其他方法可以摆脱 Receipt 实例(它不允许被丢弃或嵌入到另一个对象中,这是由验证者断言的) 还款函数通过读取收据中嵌入的贷款信息来检查是否退回了正确的金额。 可以在这里找到一个示例实现。 Move 的资源安全功能使 Move 中的闪电贷成为可能,而无需使用重入或内省。他们保证收据不能被不受信任的代码修改,并且需要在交易结束时返回到还款函数。有了这个,我们可以保证在同一笔交易中返回正确数量的资金。 该功能是使用基本语言原语完全实现的,Move 实现不会像 Solana 实现需要特殊处理的交易那样受到集成开销的影响。此外,没有把复杂性被推到客户端。 5.2. 铸币 权限锁 为了进一步突出 Move 编程模型的优势,我在 Solana (Anchor) 和 Sui Move 中都实现了“Mint Authority Lock”智能合约进行比较。 “铸币权限锁”智能合约所做的是将代币铸币的功能扩展为允许多个白名单方(机构)铸币,而不仅仅是一个铸币厂。智能合约所需的功能如下(适用于 Solana 和 Sui 实现): 原始代币铸币机构创建了一个“铸币锁”,这将使我们的智能合约能够规范代币的铸币。调用者成为”铸币锁“的管理员。 然后,管理员可以为可以提供给其他方的锁创建额外的铸币权限,并允许他们在需要时使用锁铸币。 每个铸币权限都有每日可铸币数量的限制。 管理员可以随时禁止(和取消禁止)任何铸币权限。 管理员权限可以转移给另一方。 该智能合约可用于例如 在原始铸币权限(管理员)仍保留铸币厂控制权的情况下,将代币的铸币功能提供给其他用户或智能合约。如果没有这个,我们必须将铸币的完全控制权交给另一方,这并不理想,因为我们必须相信它不会滥用它。并且不可能向多方授予许可 可以在此处 (Solana) 和此处 (Sui) 找到这些智能合约的完整实施。 现在让我们看一下代码,看看实现有何不同。以下是此智能合约的完整 Solana 和 Sui 实现的并排代码屏幕截图: 图片 立即引人注目的是,对于相同的功能,Solana 实现的大小是 Sui 的两倍多(230 LOC 对 104)。这已经很重要了,因为更少的代码通常意味着更少的错误和更短的开发时间。 那么这些额外的线路是从哪里来的 Solana 呢?如果我们仔细查看 Solana 代码,我们可以将其分为两部分——指令实现(智能合约逻辑)和账户检查。指令实现与我们在 Sui 上的实现有点接近——136 LOC 与 Sui 上的 104。额外的行可归因于两个 CPI 调用的样板文件(每个约 10 LOC)。最显着的区别是由于帐户检查(上面屏幕截图中标记为红色的部分)在 Solana 上是必需的(实际上很关键),但在 Move 中不需要。账户支票占该智能合约(91 LOC)的约 40%。 Move 不需要帐户检查。这不仅是有益的,因为 LOC 减少了。消除进行帐户检查的必要性非常重要,因为事实证明正确实施这些检查非常棘手,而且如果您在那里犯了一个错误,通常会导致严重的漏洞和用户资金的损失。事实上,一些最大的(就用户资金损失而言)Solana 智能合约漏洞是由不正确的账户检查引起的账户替换攻击: Wormhole ($336M) Cashio($48M) Crema Finance ($8.8M) 显然,摆脱这些支票将是一件大事。 8. 总结 本文深入探讨了 Solana 和 Sui 的编程模型、它们的比较方式以及 Move 编程语言。 第 2 章是对 Solana 编程模型的总结,第 3 章介绍了 Sui Move 及其编程模型。第 4 章接着解释类型和资源安全如何在 Move 中工作。Move 的特性对智能合约开发的意义并不是很明显,所以在第 5 章中,使用现实生活中的示例对 Solana 和 Sui Move 进行了更彻底的比较。第 6 章讨论了 eBPF/SBF,并说明让 Move 功能或 Move 本身在 Solana 上工作并非易事。第 7 章涉及 Sui 的一些与 Move 相关的功能。 智能合约编程是关于对数字资产进行编程。可以肯定地说,这是一种新的编程类型,不同于我们迄今为止看到的其他类型的编程(例如系统、后端……)。因此,现有的编程语言和编程模型不能很好地适应这个用例也就不足为奇了。 问题的症结在于,我们想要一个自然地用于处理资源的编程模型,但同时我们正在与不受信任的代码进行交互。Solana 在这里做了一个妥协,它确实在不受信任的环境中启用了具有必要可编程性的智能合约,但它的编程模型对于使用资源进行编程并不是很自然。字节码验证使两者都成为可能。在某种程度上,它将不受信任的代码变成了受信任的代码。 Move 是一种用于智能合约开发的新颖编程语言。它的核心新颖之处在于它的字节码,它被有意设计为易于验证。虽然字节码验证本身并不是一个新概念,但 Move 所做的验证却是。通过其字节码和验证,Move 实现了一种智能合约编程模型,该模型对资源具有一流的支持,并且可以在不受信任的环境中保证安全的可编程性。 乍一看,这对智能合约开发的影响并不明显,但如第 5 章所述,它们在智能合约的人体工程学、可组合性和安全性方面确实非常重要。Move 比 Solana 的基于 Rust 的编程模型有了显着的进步。 如此之多,以至于我认为 Move 对智能合约开发的作用就像 React 对前端开发所做的那样,并且说“你可以用 Move 做什么,你可以用 Rust 做什么”类似于说“你可以用 React 做什么” 可以用 jQuery 做”。当然,可以实现一个与 React 应用程序等效的基于 jQuery 的应用程序,但这并不实用。React 引入了虚拟 DOM 的概念,它对开发人员完全透明,但允许更快、可扩展和简单的前端开发。以类似的方式,Move 的字节码验证是一种底层技术,对开发人员也是完全透明的,但它提供了更符合人体工程学、可组合和更安全的智能合约开发。由于其安全性和更直观的编程模型,Move 还大大降低了智能合约开发人员的入门门槛。 如果 Move 设法获得牵引力(有早期迹象表明它会),它可能会对 Solana 构成相当大的威胁。这是因为两个原因。 首先是 Move 智能合约的开发时间要快得多。看来,在 Move 中从头开始开发智能合约的速度可能比在 Rust 中快 2-5 倍。在编写在 Move 中微不足道但在 Solana 上可能很复杂的智能合约时尤其如此。因此,Move 生态系统的发展可能会超过 Solana。由于区块链的开放性和无许可性,没有强大的锁定效应,流动性可以轻松流动。Solana 开发人员可能会因为经济原因而被迫采用 Move——您可以切换到 Move 或被更快地开发更安全智能合约的 Move 开发人员赶超。如果您正在雇用智能合约开发人员,您可以雇用将构建一个智能合约的 Rust 开发人员或将在相同时间内构建两个更安全的 Move 开发人员。这类似于 React 对前端开发的影响。 第二个原因是 Move 的门槛远低于 Rust 或 Solidity。因为 Move 语法更简单,编程模型更直观,所以有一整类开发人员无法在 Rust 或 Solidity 中进行智能合约开发,但可能能够在 Move 中进行。而且由于要学习的概念更少,让非智能合约开发人员加入 Move 比让他们加入 Rust 容易得多(Rust 本身就是一种复杂的语言,加上像 PDA 这样的 Solana 概念会给初学者带来很多困惑)或 Solidity(您需要熟悉语言的非常微妙的细节,例如可重入性,以便能够开发安全的智能合约)。即使现有的 Solana 和 Solidity 开发人员不切换到 Move,尚未进入该领域的开发人员市场也比该领域现有开发人员的数量要大几个数量级。由于 Move 的进入门槛较低并允许更快的开发,因此与 Rust 或 Solidity 相比,它具有更好的产品市场契合度,并且可以占据更大的份额。如果新开发人员开始大量涌入,我希望他们从 Move 而不是 Rust 或 Solidity 开始。这再次类似于 Web 行业中 React 所发生的情况。 因此,我完全期望在中长期内将一流的 Move 支持添加到 Solana。但这不是一件容易的事。为了获得 Move 的主要好处,Move 字节码需要得到原生支持。这意味着简单地编译 Move 到 eBPF/SBF 并不会削减它(参见第 6.3 节)。为了保持现有的生态系统,两个运行时都需要得到支持。主要的技术挑战是在运行时之间实现适当的互操作性。这需要对 Move 和 Solana 有深入的了解,因此我希望 Solana 团队在 Move 团队的某个人的支持下直接推动这一点。 Move 起源于 Diem 项目的 Meta(née Facebook)。由 Sam Blackshear 领导的 Move 团队的任务是弄清楚如何处理智能合约。在更仔细地研究了这个问题后,他们发现智能合约编程就是对数字资产(资源)进行编程,但现有的语言都没有原生支持该用例。决定从头开始构建一种新的编程语言。 我想强调的是,创建一门新语言的决定并不是一个显而易见的决定,因为它需要多年的工程努力才能让它起步,而且在大多数情况下,最好使用现有的解决方案。Move 团队正确地预见到了一种安全、对资源具有一流支持、同时又足够灵活的智能合约语言可以构建,仅此一项就显示出高度的专业性。这是团队和支持该项目的 Novi/Meta 工程领导层的一个大胆举措(将有许多董事和副总裁参与这样的决定)。Meta 自从关闭了他们的 Diem 工作后,最终未能获得其对 Move 的投资的结果,但它仍然对更广泛的加密社区做出了巨大贡献。 总而言之,Move 是一项了不起的技术,我相信它将对我们开发智能合约的方式产生巨大影响。干得好Move团队!
The first Aptos APAC Community since May, 2022. Member of Move Accelerator. We're committed to building in Aptos ecosystem.
The first Aptos APAC Community since May, 2022. Member of Move Accelerator. We're committed to building in Aptos ecosystem.

Subscribe to AptosGlobal

Subscribe to AptosGlobal
Share Dialog
Share Dialog
<100 subscribers
<100 subscribers
No activity yet