构建于Sui的EVM开发者心理模型

本文是为以太坊开发者准备的Sui入门指南,旨在帮助他们理解Sui的核心概念,如对象模型、资源即资产、所有权、并行处理等,并介绍Sui中如何通过将状态存储在对象中、显式所有权以及Move语言的资源概念来实现更高的安全性和效率。通过对比Solidity,揭示了Sui在安全、执行和开发体验上的差异。

如果你已经在以太坊或其他 EVM 链上构建了一段时间,那么你很可能已经越来越多地听到 Sui 的消息。这是一个具有快速执行、并行性、强大的安全保证和一种名为 Move 的新语言的生态系统。 乍一看,Move 可能会让人感到陌生。它看起来不像 Solidity,它的行为不像 Solidity,而且你多年来内化的许多假设突然不再适用。这可能会让人感到害怕,但它也可能会让人感到耳目一新。 这篇博文不是作为参考手册,而是作为从 Solidity 精神模型到 Sui 思维方式的 引导式演练。我们将从 大的概念 开始,然后逐渐加入细节,以便一切都有落脚之处。

我们将介绍在 Sui 中开发的基础知识,从对象模型到作为资源的资产、安全性、并行性、开发人员工具等等。

OpenZeppelin 和 Sui 正在构建智能合约库和入门应用程序,作为未来 Sui 应用程序的基础。本指南提供了在使用新的智能合约库等进行构建之前的出发点。

1. 第一个重大转变:状态的存储位置

当你编写 Solidity 时,存在一个非常基本的假设,你很少质疑它:

状态存储在合约内部。

余额、所有权、权限。一切最终都是合约存储中的一些变量。用户实际上并没有直接 拥有 资产;合约拥有,并且它们会跟踪谁拥有什么。

从账户到对象模型

在 Sui 上,状态存储在对象中

与其询问合约“Alice 的余额是多少?”,不如说 Alice 实际上 拥有一个对象,该对象代表她的余额。如果她想转移它,她会转移对象本身。

没有可供窥视的全局存储。虽然 Sui 维护着一组全局对象,但 Move 代码无法动态发现或获取它们。交易可能读取或修改的每个对象都必须作为输入显式传递,这让 验证者预先知道交易将影响哪些状态片段。这可以实现更安全的执行、更少的隐藏依赖关系和更好的并行性,所有这些都不会牺牲可组合性。

一旦理解了这一点,大多数 Move 和 Sui 突然就会变得更加连贯。

2. 对象:一切的构建块

一旦你接受状态存储在对象中,下一个问题就很明显了:

Sui 上的对象到底是什么?

从高层次来看,对象只是区块链知道如何跟踪、版本控制和分配所有权的结构化数据片段。但与 Solidity 中 任何内容 在合约存储中都可以像状态一样运行不同,Sui 对什么是有,什么不是对象更加明确。

key 权限:是什么让事物成为对象

在 Move 中,默认情况下并非每个结构体都会成为链上对象。要在 Sui 上存储、拥有和转移,一个结构体必须声明 key 权限。

Move 中的权限是编译时标记,用于描述类型允许执行的操作。在这种情况下,key 告诉系统此类型的值可以作为具有身份和所有权的链上对象存在。

Copy

添加 has key 告诉系统:此结构体的实例是真实的链上对象。目前,知道 store 权限允许将此对象 嵌入为其他对象内部的字段,而不是仅存在于顶层就足够了。

这里有两条重要的规则:

  • 对象必须具有 key 权限
  • 对象必须包含一个 UID 字段作为其第一个成员,命名为 id,它唯一地标识链上的对象

UID 不是你自己发明的。它是由网络使用交易上下文创建的。你无需担心语法的具体细节。重要的是,对象转移由 Sui 框架提供的 transfer 助手处理,并且交易上下文是显式传递的。

这就是为什么对象创建始终需要 TxContext:它允许 Sui 生成唯一的对象 ID 并跟踪所有权变更。与 Solidity 的 msg.sender 或其他隐式全局变量不同,Move 使交易上下文成为交易的显式输入。我们稍后会回到细节。

Copy

起初这可能会感觉很冗长,但这是故意的:没有任何东西会凭空出现

对象不是存储槽

一个细微但重要的区别:定义一个 key 结构体本身并 创建存储。

该结构体定义只是一个 类型。存储只有在创建了一个实际的对象并由某人拥有后才存在。这强化了存储不是环境或全局的想法 — 它始终与独立于任何单个模块存在的具体对象相关联。

有了这个基础,我们就可以转移到 Sui 对象最重要属性:所有权

3. 所有权:谁可以触碰什么(以及为什么重要)

现在我们知道什么是对象,下一个自然的问题是:

谁有权使用它们?

在 Sui 上,所有权不是一个抽象的概念或由代码强制执行的惯例。这是一个 一等公民概念,系统本身理解并强制执行。Sui 上的每个对象都有一个明确的所有权模型,该模型决定了谁可以在交易中包含该对象、涉及该对象的交易是否会与其他交易冲突,以及系统在执行期间可以安全地利用多少并行性。

从高层次来看,Sui 对象分为 三个基本的所有权类别

  • 拥有的对象: 由单个地址控制。只有所有者才能在交易中使用它们。
  • 共享对象: 任何人都可以访问,更新经过协调以确保并发下的正确性。
  • 不可变对象: 永远冻结并且所有人都可以读取,没有更改的可能性。

除此之外,你可能会遇到一些 派生的所有权模式

  • 包装的对象: 另一个 对象 的成员的对象,允许将复杂状态组合并作为一个单元移动。
  • 动态字段(子)对象: 逻辑上附加到父对象并通过它而不是直接访问的对象。
  • 参与方对象: 一种共享对象,其访问权限仅限于预定义的一组参与者,从而无需向所有人开放对象即可实现协调更新。

这些模式建立在相同的所有权原语之上。这与 Solidity 非常不同,在 Solidity 中,所有权通常作为合约内部的逻辑来实现。

为简单起见,本指南侧重于三个核心的所有权类型,这足以理解大多数 Sui 程序和性能特征。

关于 Sui 中地址的快速说明

在进一步讨论之前,澄清 地址 在 Sui 上意味着什么是很有帮助的。如果你来自以太坊,你可以主要以相同的方式考虑 账户地址:它们代表用户或账户,它们由私钥控制,并且钱包管理它们。这些是 拥有对象 并授权交易的地址。

但是,Sui 还使用相同的地址格式来表示 对象 ID。换句话说,你看到的并非每个地址都对应于用户账户;有些地址唯一地标识链上的对象。在本节中,当我们谈论 地址拥有对象 时,我们专门指 账户地址,而不是对象 ID。

这种区别在概念上很重要,但在实践中,它强化了相同的想法:Sui 上的所有权始终是明确且明确的。对象要么由地址拥有,要么是共享的,要么是不可变的,并且系统直接强制执行这一点。

还值得注意的是,因为对象 ID 是地址,所以对象地址可以 通过转移到对象(TTO)接收其他对象。这些对象稍后通过 transfer::receive 接口显式访问。这是一种强大的组合机制,但它 超出了本指南的范围,本指南侧重于核心所有权模型。

拥有的对象:默认情况

Sui 上最常见的所有权形式是 地址拥有的对象。拥有的对象恰好属于一个地址,并且仅允许该地址将其用作交易的输入。

Copy

如果 Alice 拥有一把 Sword,则只有 Alice 可以:

  • 将其包含在交易中
  • 将其转移给其他人
  • 改变它(如果规则允许)

此时代码中不需要访问检查。如果 Alice 不拥有该对象,则交易根本无法通过验证。

此模型简单、易于推理且速度极快。仅触及拥有的对象的交易不需要全局排序,并且可以独立且并行地进行验证和执行。

对于许多用例,例如用户余额、NFT、库存项目,这就是你所需要的。

共享对象:当多个用户需要访问时

有时,单个所有者是不够的。你可能希望多个用户与同一状态片段进行交互:流动性池、游戏板或注册表。对于这些情况,Sui 提供了 共享对象

共享对象被显式标记为共享,并且可以由任何人读取或修改。共享对象是不可逆转的;一旦共享,它就无法恢复为单所有者对象。

Copy

共享对象:

  • 可以由任何人读取和修改,
  • 当多个交易尝试更新它们时,可能需要协调,
  • 如果被大量访问,可能会变得拥挤。

重要的是要注意,共享对象 本质上并不比拥有的对象更昂贵。但是,因为它们可以被任何人访问,所以它们更有可能经历 拥塞。当许多交易尝试触及同一共享对象时,排序变得必要,并且成本可能会因此增加。

因此,应有意识地使用共享对象,仅用于系统中真正需要多个参与者共享访问的部分。

不可变对象:写入一次,永久读取

最后一个核心所有权模型是 不可变对象

一旦对象变为不可变,就永远无法再次修改。

Copy

不可变对象:

  • 没有所有者
  • 可以由任何人读取
  • 保证永远不会改变

它们非常适合配置数据、规则、常量或参考信息。因为不可变对象无法修改,所以它们与地址拥有的对象一样快速且无冲突,而且通常更容易推理。

简要说明:Sui 上的“共识”意味着什么

如果你来自以太坊,即使是一个简单的 ETH 转移 也会与其他所有内容竞争相同的全局执行顺序。 Sui 采取了不同的方法。只有当交易触及相同的对象时,共识才需要对交易进行排序

这是关键的想法:

  • 每个交易都显式声明它将读取或修改的对象。
  • 如果两个交易 在输入上没有重叠,则它们不需要彼此相对排序。
  • 验证者可以 独立且并行地 验证和执行这些交易,即使它们是同一共识流程的一部分。

所有权使这成为可能。

仅触及 地址拥有或不可变对象 的交易在构造上是无冲突的。关于谁有权更改对象以及没有竞争更新的风险没有任何歧义。因此,可以在整个网络中并行验证和执行这些交易。

当交易涉及 共享对象 时,排序可能变得必要,因为多个参与方可能尝试更新相同的状态。在这些情况下,共识建立单一的、商定的更新顺序以确保正确性。

早期版本的 Sui 为拥有的对象交易描述了一个单独的“快速通道”。这种区别不再存在。相反,所有交易都通过共识,并且仍然利用所有权和输入可见性来最大化并行性并消除整个类别的冲突,例如对象挪用。

关键的结论是:

在 Sui 上,共识是普遍的,但排序是有条件的。

作为开发人员,这为你提供了一个强大的杠杆。通过选择如何对所有权进行建模,你可以塑造交易何时可以独立进行,以及你的应用程序可以解锁多少并行性。

4. 资产和资源:通过构造实现安全

到目前为止,我们已经看到在 Sui 上:

  • 状态存储在对象中
  • 对象具有明确的所有权
  • 所有权决定了谁可以触碰什么,以及何时需要在共识中进行排序

下一个自然的问题可能是:

好的,但是资产如何运作?余额、代币和 NFT 如何适应此模型?

这就是 Move 的设计真正开始发挥作用的地方。

资产通常如何在 Solidity 中建模

在 Solidity 中,资产几乎总是间接表示的。

典型的 ERC-20 代币的余额如下所示:

Copy

从编译器的角度来看,这只是数据:

  • 余额是存储中的数字
  • 转移是算术更新
  • 正确性完全取决于周围的逻辑

随着时间的推移,生态系统已经开发出强大的惯例和模式来确保这一点安全。这些包括检查-效果-交互、SafeMath(历史上)、仔细的审计等等,但语言本身实际上并 知道这些数字代表稀缺资产。

它们只是整数。

Move 中作为资源的资产

Move 采用不同的方法。

在 Sui 上,资产被建模为 资源:具有由类型系统本身强制执行的特殊规则的值。因此,Sui 资产:

  • 无法复制
  • 无法隐式销毁
  • 必须始终移动到某个地方

这些规则不是惯例或最佳实践。它们在编译时和 VM 级别强制执行。如果你忘记处理资源,代码就根本无法编译。

这是一个细微的转变,但很重要:

  • 资产不再是 被计算的,而是 **它们是 直接处理的

Coin 是你拥有的对象

让我们看看 coin 在 Sui 上是如何运作的。

不是合约保留余额的内部映射,而是 每个 coin 都是一个对象,由地址拥有。如果你有余额,那是因为你拥有一个或多个 coin 对象。

铸造新的 coin 如下所示:

Copy

这里发生了一些重要的事情:

  • 没有 全局余额映射 被更新
  • 系统创建了一个 真实的 coin 对象
  • 该对象的所有权 已显式转移 给接收者

在撰写本文时,Sui 正在积极探索一种更符合人体工程学的模型,称为 地址余额,该模型引入了每个 (地址, 货币) 对的规范余额,同时保留了 Sui 以对象为中心的执行模型和并行性。

有关更多详细信息,请参阅 SIP-58: Sui 地址余额

合约 拥有用户余额。用户拥有。

合约控制的唯一事情是 谁被允许铸造,这是通过拥有 TreasuryCap 来强制执行的。这是一个称为 capability 的特殊对象,它表示代币的 铸造权限。 谁拥有此对象谁就被允许铸造该类型的代币。

稍后我们将更深入地讨论 capability。目前,将它们识别为 Sui 安全模型背后的核心构建块之一就足够了。

为什么这是一个大问题

将资产建模为资源有一些非常实际的后果:

  • 资产不能被静默删除或丢失
  • 转移必须是明确且完整的
  • 不变量更容易推理

许多在 Solidity 中常见的错误在这里根本没有存在的空间。不是因为开发人员更小心,而是因为该语言拒绝表达这些错误。

对于 Solidity 开发人员来说,起初这可能会让人感到限制。但是一旦你习惯了,你就会意识到你已经内化的许多“最佳实践”现在 默认情况下是有保证的

一个微妙但重要的转变

在 EVM 世界中,合约负责:

  • 跟踪余额
  • 强制执行不变量
  • 防止滥用

在 Sui 上,合约(或更确切地说,模块)主要定义 对象上的规则

  • 如何创建它们
  • 如何转换它们
  • 谁被允许执行某些操作

数据本身独立存在,由用户拥有或显式共享。

一旦你以这种方式看待资产,将其视为对象和资源而不是映射中的条目,那么很多 Sui 设计开始感觉不那么“不同”而更 不可避免

使用 Capability 代替权限

现在让我们回到 capability。如果你习惯了 Solidity,访问控制可能看起来像这样:

Copy

在这里,权限 是从地址推断出来的,并通过合约内部的有条件逻辑强制执行。

Move 采用不同的方法。

在 Move 和 Sui 中,特权操作通常使用 capability 来保护。Capability 是一个特殊的对象,它表示执行特定操作的权利。如果你有 capability,你可以执行该操作。如果你没有,那你就不能。没有回退逻辑或替代路径。

我们之前看到的 TreasuryCap 是这个想法的一个具体例子。

要铸造代币,你必须拥有相应的 TreasuryCap 对象。如果你没有它,你根本无法调用铸造函数。有:

  • 没有要忘记的地址检查
  • 没有要绕过的修饰符
  • 没有“伪造”授权的方法

交易将在任何代码执行之前失败。

此模式称为 基于 capability 的访问控制,它在整个 Sui 中都会显示:

  • 铸造和销毁资产
  • 管理员或升级权限
  • 协议配置
  • 一次性或有限权限

系统不是检查 你是谁,而是检查 你拥有什么

对于 Solidity 开发人员来说,一个有用的思考方式是:

Capability 就像表示为对象的不可伪造的密钥。

它们可以被转移、共享或有意销毁,但它们无法被复制或猜测。

在某些情况下, alongside 基于 capability 的门控,依旧会进行地址检查是有意义的。

虽然对象所有权通常足以进行访问控制,但某些设计需要额外的保证。例如,当存在多个 capability 实例并且每个实例都控制对不同资源的访问时,capability 可能携带元数据(例如地址或 ID),应显式验证这些 metadata。

是否需要进行此类检查取决于预期的业务逻辑。仅拥有 capability 并不总是完整的安全边界。

现在有了以对象和所有权模型为基础的资产,你现在就有了理解 Sui 开发的模样所需要的基础;一个以其速度、安全性和开发人员体验而闻名的增长型区块链。

接下来,我们将通过涵盖安全注意事项、显式上下文而不是隐式全局变量、“严格”的 Move 的意义等等来完善在 Sui 生态系统中构建的这个前身。

5. 安全副作用:消失了什么,留下了什么

至此,我们没有直接谈论太多关于“安全”的信息,这是有意的。

在 Solidity 中,安全通常是你 添加到 设计之上的东西:修饰符、模式、审计、防御性编码。在 Sui 上,许多安全属性自然而然地从你已经看到的的对象和所有权模型中产生。

让我们看看这在实践中意味着什么。

重入:为什么 EVM 威胁模型不适用

如果你编写 Solidity 有一段时间了,那么你几乎肯定会担心重入。通常当我们谈论它时,问题来自于你可以最终得到一个“ 夹在中间的” 调用堆栈,其中:

  • 一个合约执行一些检查,
  • 然后进行外部调用或Hook,使这些检查无效,
  • 并在稍后恢复执行,假设这些检查仍然有效。

该模式依赖于非常具体的东西:能够 将外部调用引入你无法控制的代码 中,该代码可以执行任意逻辑并 回调 到原始函数中,在第一个执行框架完成之前。这是经典 EVM 重入的核心。

在 Sui 上,无法表达这种执行形状。

Move 不支持像 Solidity 那样进行动态调度。调用在编译时静态解析为已知的模块和函数。没有调用用户提供的代码、函数指针或任意合约地址作为回调的机制。

你可能会注意到,Move 还限制了哪些模块可以互相调用:一个模块只能调用自身或其显式声明的依赖项,并且包依赖关系图是无环的。虽然这增强了可预测性,但它 不是 避免重入的主要原因。即使使用无环调用图,在允许运行时选择的回调的语言中仍然可以进行重入。

真正的约束更简单也更强大:

没有一般的方法可以在执行过程中将控制权交给不受信任的代码。

这也排除了你最初可能担心的更微妙的形状,例如通过不同的包 ID 回调到同一包的旧版本。

所有调用目标都是显式的、静态已知的,并在执行之前解析。

更少的隐藏副作用

如果重入作为默认威胁消失,那么下一个问题就变成了:仍然可能出现哪些类型的错误? Solidity 中错误的另一个常见来源是 隐式状态访问

一个函数可能:

  • 从你忘记的映射中读取
  • 以意想不到的方式更新共享存储
  • 影响你没有预料到的用户

在 Sui 上,意外地做到这一点要困难得多。

因为交易可能触及的所有对象都必须显式传入:

  • 没有隐藏的读取
  • 没有令人惊讶的写入
  • 依赖关系是预先可见的

这使得代码更容易推理、更容易审计也更容易审查。

批准、许可以及为什么它们不太重要

在 Solidity 中,批准和许可的存在是因为用户不直接控制资产;合约控制。

在 Sui 上,用户直接拥有资产。要授予其他人代表资产行事的权利,你通常会:

  • 转移 capability
  • 转移对象的所有权
  • 或按设计与共享对象交互

这并不意味着批准完全消失,但它们不再是大多数工作流程所需的核心原语。EVM 世界中依赖于许可的许多模式都被 显式的所有权转移capability 委托 所取代。

仍然需要注意什么

所有这些并不意味着安全性问题已经解决了。Sui 上发生改变的不是需要仔细推理,而是 你正在推理什么

一旦重入和隐藏的回调被排除,剩下的风险就来自于 你可以通过代码看到和跟踪的显式状态转换

在 Sui 上,如果你将任何对象作为可变引用传递,那么你调用的任何函数都可以更改该对象。仅所有权并不能在此处保护你。如果一个值作为 &mut(可变引用)传递,那么无论它是拥有的、共享的还是嵌套在另一个对象中,都可以更改它。

这意味着你仍然需要审慎地对待:

  • 业务逻辑正确性 确保状态转换与应用程序的预期规则相匹配。
  • 跨调用的不变量管理 如果你检查一个条件,调用另一个函数,然后继续执行,你必须假定传递为 &mut 的任何对象都可能在该调用期间已更改。
  • 排序和组合 涉及多个对象突变的复杂流程必须经过设计,以使中间状态要么有效,要么不可观察。
  • 权限和升级边界 Capability、管理对象和升级权限仍然是强大的工具,滥用仍然可能导致严重的失败。
  • 通过竞争造成的拒绝服务 虽然不是正确性问题,但将过多的操作集中在相同的对象上可能会降低性能或可用性。

重要的是,这些风险是 显式且本地的

没有不可见的读取,没有意外的写入,也没有仅在运行时才出现的执行路径。当某些东西可以改变时,它在函数签名中是可见的。当权限存在时,它表示为一个对象。当排序很重要时,它直接编码在调用顺序中。

换句话说,剩下的挑战不是防御未知行为,而是关于 正确建模已知行为

作为模型属性的安全性

总结这种转变的一个有用的方法是:

  • 在 Solidity 中,安全性主要在于 防御性编码
  • 在 Sui 中,安全性主要在于 选择正确的所有权和对象模型

一旦正确地做出该选择,许多整个类别的错误就会变得不可能。这并不是因为开发人员是完美的,而是因为语言和运行时不允许你表达它们。

以这种方式构建安全性后,我们现在可以缩小范围并查看更大的图景:所有这些如何影响系统级别的 执行、性能和并行性

6. 作为设计工具的执行和并行性

到目前为止,你可能已经注意到一个反复出现的主题:

在 Sui 上,EVM 中许多隐式的属性都变得显式。

执行 方面尤其如此。

EVM 世界:一切都与一切竞争

在 EVM 中,所有交易都在争夺相同的全局执行通道。即使两个交易触及完全无关的合约、对不同的用户进行操作并且根本没有逻辑交互,它们仍然需要完全排序并一个接一个地执行。

从验证者的角度来看,没有安全的方法可以提前假设独立性。

存储 是全局且隐式共享的,因此只能在执行期间发现依赖关系。

这就是基于 EVM 的系统中的并行执行如此困难的原因。系统无法自信地并行执行交易,因为它无法提前知道哪些状态片段会受到影响。

Sui 的方法:使依赖关系显而易见

Sui 通过从一开始就显式地说明依赖关系来颠覆此模型。状态存储在对象中,对象必须作为交易输入传入,并且所有权是明确的。因此,验证者可以预先准确地看到交易将触及哪些状态片段。

这使得在 EVM 中很大程度上无法实现的事情成为可能。对不相交的拥有的或不可变对象集进行操作的交易不需要彼此相对排序。它们可以独立地验证、执行和最终确定,从而实现安全地并行执行而不会牺牲正确性。

共识和有条件排序

Sui 不会消除共识。相反,它缩小了它的作用。

所有交易都在单个共识系统下处理,但 仅当交易实际上争用相同的状态时,才需要排序。由于交易必须预先声明它们读取或修改的对象,因此系统可以区分排序重要的情况和不重要的情况。

当交易对不相交的拥有的或不可变对象集进行操作时,不存在歧义。这些交易不需要彼此相对排序,并且可以独立验证和执行。

仅当多个交易尝试更改相同的对象时,排序才成为必要。在这些情况下,共识建立一个单一的、商定的序列,以确保正确性。

并行性不是一种优化,而是一种设计选择

在大多数区块链系统中,性能调整发生在设计已经设置好之后。开发人员会寻找 gas 优化、批处理技巧或链下解决方法,以使系统在瓶颈开始出现时进行扩展。

在 Sui 上,性能从更早的时候开始,从数据模型本身开始。

当你决定如何表示状态时,例如哪些对象被拥有、哪些对象被共享以及哪些对象是不可变的,你同时也在决定系统将如何执行。这些选择决定了可能有多少并行性、自然会出现在哪里争用以及随着负载增加应用程序如何运行。

这是一个微妙但强大的转变。你不再使用越来越复杂的优化来对抗执行模型,而是与它一起工作。并行性不再是你希望运行时可以在事后恢复的东西。它是你从一开始就设计到系统中的东西。

以争用为导向的思考

在 Sui 上进行设计时,一个有用的思维习惯是问一个简单的问题:

哪些对象是许多用户想要同时触及的?

这些对象自然地定义了系统的压力点。它们是争用首先出现的地方、排序约束变得必要的地方以及性能可能在负载下降低的地方。尽早识别它们可以更容易地推理你的应用程序将如何扩展。

在许多情况下,正确的响应不是更加努力地进行优化,而是更改所有权边界。将每个用户的状态移动到拥有的对象中、保持共享对象的体积小且专注性强,以及将不可变数据推出热路径通常比任何低级别的优化都产生更大的影响。

在全局存储模型中,这种思考方式要困难得多,在全局存储模型中,一切都是隐式共享的,并且争用仅在运行时才可见。在 Sui 上,所有权使那些边界变得显式,这会将争用从一个惊喜变成一个设计选择。

作为一项功能的执行可预测性

Sui 的执行模型的一个不太明显的优势是可预测性。

在 EVM 中,通常很难提前知道哪些交易会发生冲突、负载将如何影响延迟或性能悬崖可能出现在哪里。许多这些影响仅在系统受到实际使用时才变得可见,即使那样也很难单独从代码中推理出它们。

在 Sui 上,这些问题基本上由设计决定。在拥有的对象上进行操作的交易自然可以扩展,因为它们不会与其他用户争用。读取不可变对象始终很廉价,并且永远不会引入冲突。当确实存在争用时,它是显式且本地化的,通常围绕一小组共享对象。

这使得不仅更容易推理正确性,而且更容易推理系统在真实条件下的运行方式。

现在有了以对象和所有权模型为基础的执行和并行性,下一步是查看交易本身的结构方式,以及 Sui 如何在可以调用和不可以调用的内容周围绘制清晰的边界。

7. 交易、入口函数和显式上下文

到目前为止,我们已经谈了很多关于 什么 代码可以在 Sui 上执行,从对象、所有权、资产和执行方面来看。

下一个主题是关于 如何进入执行,以及 Sui 如何让你显式地说明哪些函数旨在由交易直接触发。

入口函数:显式交易边界

如果你来自 Solidity,你已经习惯了一个非常灵活的模型。任何 publicexternal 函数都可以被调用:

  • 直接由用户调用,
  • 由其他合约调用,
  • 由包装器、代理或中介调用。

这种灵活性很强大,但它也模糊了一个重要的区别:一个函数是旨在成为合约内部逻辑的一部分,还是旨在成为面向用户的交易入口点?

在 Sui 上,可以显式地进行这种区分。

Move 支持 entry 函数,这些函数被显式标记为可以直接从交易中调用。

Copy

一个 entry 函数:

  • 可以直接从交易(钱包、CLI、前端)调用,
  • 不能被其他 Move 模块调用,
  • 为交易执行的开始位置定义了一个清晰的边界。

重要的是,entry 不是强制性的。 Move 模块仍然可以公开 public 函数,并且这些函数在可组合性和重用方面更接近 Solidity 的 publicexternal 函数。

关键的区别在于 entry 让你在想要的时候 选择加入 一个更严格的模型。

为什么这很重要

对于 Solidity 开发人员来说,起初这可能会让人感到限制。毕竟,灵活性和可组合性是熟悉的,并且通常是所需的。

entry 的价值不在于它取代了 public 函数,而在于它为你提供了一个工具来清楚地分离:

  • 用户旨在直接调用的面向交易的 API,与
  • 旨在被其他模块重用的内部或可组合逻辑。

这使得更容易推理:

  • 哪些操作可以直接由用户触发,
  • 哪些流程是纯粹内部的,
  • 交易执行从何处开始和结束。

这对于你 希望其他合约以编程方式调用函数的情况尤其有用,例如随机数消耗、特权操作或假定单笔交易边界的流程。

在 Solidity 中,强制执行这种分离通常需要额外的保护或谨慎的约定。在 Sui 上,可以在函数签名中直接表达这种区别,并由语言强制执行。

结果不是更少的灵活性,而是 更明确的意图。 你可以选择一个函数何时是公共交易表面的一部分,以及它何时只是可重用逻辑。

显式上下文而不是隐式全局变量

你可能注意到的另一个区别是不存在像 msg.sender 这样的东西。

在 Move 中,你的代码中 没有可用的隐式全局变量。 如果一个函数需要与交易相关的信息,它必须显式接收该信息。

这就是 TxContext 的用途。

Copy

交易上下文包含以下内容:

  • 发送者的地址
  • 创建新对象 ID 所需的信息
  • 交易元数据

但至关重要的是,这不是你能神奇地访问的东西。你必须在函数签名中请求它。

这种设计强化了你在整个 Sui 中看到的相同原则:

所有输入和依赖项都是显式的。

为什么显式上下文是一个特性

你可以查看函数签名并立即看到:

  • 它是否创建对象
  • 它是否依赖于发送者
  • 它是否旨在作为入口点

没有任何东西是隐藏的。

一个更清晰的心智模型

一个有用的思考方式是:

  • 入口函数 定义了用户在单个交易中允许做什么
  • 非入口函数 定义了模块中的可重用逻辑
  • TxContext 是一个明确的证明,证明一个函数依赖于交易级别的信息

这种清晰的分离减少了歧义,并消除了 Solidity 开发者已经学会解决的许多边缘情况。

现在交易边界已经明确定义,下一个难题是合约如何初始化和引导。这一点尤其重要,因为 Sui 没有 Solidity 意义上的构造函数。

8. 没有构造函数的初始化

如果你来自 Solidity,你已经习惯了:

  • 构造函数运行
  • 初始状态作为部署的一部分进行设置

Sui 在这种意义上没有构造函数。相反,Sui Move 支持一个名为 init模块初始化器

什么是 init

init 函数是一个特殊的函数,它在包发布时精确运行一次,自动运行。Sui 运行时在发布过程中为每个模块调用 init 函数。

它的作用不是“在合约中设置变量”,而是:

  • 创建初始对象
  • 铸造和分发 capability 对象
  • 可选择共享或冻结对象作为设置的一部分

这是一个简单的例子:

这表面上看起来类似于 Solidity 构造函数,但语义却大不相同。

没有任意构造函数参数

与 Solidity 的一个重要区别是,init 不接受任意用户提供的参数

你不能像使用 Solidity 构造函数那样传递自定义参数。初始化被有意地约束为确定性和自包含的。

存在 参数化初始化的高级模式,最值得注意的是通过一次性见证人(one-time witnesses),但这些超出了本指南的范围。目前,知道 init 旨在用于引导对象和 capability,而不是用于通用配置就足够了。

一个有用的心智模型

一个有用的思考方式是:

  • Solidity 构造函数:使用参数初始化合约存储
  • Sui init创建第一个对象并分发权限

一旦初始化完成,其他一切都遵循你已经看到的相同规则:所有权是显式的,访问由 capability 强制执行,状态存在于对象中。

在介绍了初始化之后,我们现在已经完成了 Sui 应用程序从发布到执行的生命周期。剩下的主题更多的是关于 用 Move 构建的感受,而不是机制。

9. Move 的类型系统:为什么它感觉很严格(以及为什么这是重点)

如果你来自 Solidity,你对 Move 的第一个反应可能类似于:

为什么这种语言如此严格?为什么我必须对一切都如此明确?

这种反应是完全正常的。

Move 经过刻意设计,使某些错误无法表达,即使这意味着编写更多的代码或预先进行更多的思考。

Solidity 感觉很灵活,直到它不是

Solidity 给你很大的自由:

  • 你可以自由复制值
  • 你可以隐式地丢弃东西
  • 你几乎可以在任何地方读取和写入存储

这种灵活性很强大,但也意味着编译器对_意图_的理解很少。 从它的角度来看,余额、计数器和随机数都只是整数。

因此,许多重要的规则,例如“不要重复花费”,“不要忘记更新双方”,“不要丢失资金”,仅存在于开发者的自律和审查流程中。

Move 对稀缺性有明确的看法

Move 从一个不同的前提开始:

有些值代表稀缺资源,语言应该区别对待它们。

这就是为什么 Move 具有以下概念:

  • ability (copy,drop,store,key)
  • 线性资源
  • 显式所有权和转移

Move 没有假设一切都可以被复制和丢弃,而是强迫你声明一个类型允许做什么

例如:

  • coin 不能被复制
  • capability 不能被静默丢弃
  • 除非你拥有一个对象,否则不能对其进行修改

这些规则由编译器执行,而不是由约定执行。

Abilities 作为意图,而不是语法

早些时候,你看到了像 keystore 这样的 Ability。虽然它们乍一看可能像语法噪音,但它们有一个重要的用途:

Ability 告诉编译器一个值应该如何使用。

它们将意图直接编码到类型系统中:

  • 此值是否旨在作为链上对象?
  • 它可以存储在其他对象中吗?
  • 复制是否安全?
  • 是否允许它消失?

一旦你理解了这一点,Move 代码开始看起来不像“仪式”,更像是由编译器强制执行的文档

显式性是一种特性

同样的理念体现在各个方面:

  • 交易上下文是显式的
  • 对象所有权是显式的
  • 状态依赖是显式的
  • 入口点是显式的

起初这可能会让人觉得冗长,特别是如果你习惯了 Solidity 的隐式全局变量。 但是,它有所回报,当你阅读函数签名时,通常可以准确地知道它做什么以及它依赖什么。

很少有事情“偶然”发生。

一种有用的思考 Move 的方式不是“更严格的 Solidity”,而是更接近于:

一种用于数字资产的系统语言。

正如 Rust 迫使你考虑内存所有权以避免一整类的错误一样,Move 迫使你考虑资产所有权以避免一整类的财务和安全错误。

学习曲线是真实的,但它是预先加载的。 一旦心智模型就位,许多模式就会变得更简单,而不是更难。

总的来说,我们已经介绍了完整的概念弧线:从状态的存储位置,到对象和所有权,到资产、安全性、执行、交易和初始化如何组合在一起,最后是连接一切的语言哲学。

剩下的就是放大并简要地谈谈开发者体验和工具,然后结束。

10. 开发者体验和工具

在这一点上,许多 Solidity 开发者自然会关心的不再是概念性的问题,而是实际的问题:

工具是否已经到位? 在 Sui 上构建的实际感受如何?

诚然,以太坊生态系统已经发展了很多年,Solidity 周围的工具的广度很难匹敌。 但是 Sui 的工具刻意专注于对其执行模型最重要的事物。

以 CLI 为中心的工作流程

Sui 非常重视其命令行工具。 sui CLI 是大多数开发工作流程的主要入口点,它涵盖了令人惊讶的广泛领域:

  • 创建和管理帐户
  • 运行本地网络
  • 发布和升级包
  • 检查对象和所有权
  • 提交和模拟交易

由于状态存在于对象中,因此能够直接从 CLI 检查对象是非常有价值的。 你不仅仅是在查询存储; 你正在查看具体的*状态片段,具有明确的所有权和历史记录。

本地开发感觉很熟悉

运行本地 Sui 网络并在 Move 代码上进行迭代感觉比你想象的更接近传统开发:

  • 发布一个包
  • 通过交易与之交互
  • 检查结果对象
  • 迭代和升级

无需设置复杂的索引器或自定义脚本来了解发生了什么。 对象模型使状态可见且有形,这在基于 EVM 的系统中通常更难实现。

更少的工具,更多有意的工具

Sui 生态系统还没有像 Solidity 那样拥有大量的第三方工具,但它确实将许多基本功能直接纳入平台及其标准工具中。

许多问题没有过多地依赖外部框架来掩盖语言限制,而是通过以下方式直接解决:

  • Move 类型系统
  • 显式所有权和状态访问
  • 内置交易模拟
  • 明确的执行边界

对于许多开发人员来说,这最终感觉像是整体上减少了工具,但也减少了粘合代码和更少的尖锐边缘

一种不同的生产力

当你在适应心智模型时,在 Sui 上构建的最初阶段通常感觉更慢。 但是一旦进行了调整,生产力就会趋于提高。 这不是因为你编写的代码更少,而是因为你花在调试不可见状态、访问控制错误或意外交互上的时间更少。

从这个意义上讲,Sui 的开发者体验更多的是关于信心速度,而不是编写代码的速度。

在介绍了工具之后,我们现在可以退一步来总结从 Solidity 到 Sui 的整体转变; 不是在功能方面,而是在思维模式方面。

这就是我们将完成的地方。

11. 总结:思维方式的转变,而不仅仅是一个新的技术栈

从 Solidity 迁移到 Sui 实际上不是要学习新的语法或记住新的 API。 而是要采用一种关于如何构建区块链应用程序的不同思维方式

在 EVM 世界中,你习惯于:

  • 合约拥有状态
  • 全局存储是隐式可用的
  • 通过模式和纪律来强制执行安全性
  • 性能是你围绕其进行优化的东西

在 Sui 上,这些假设被颠倒了:

  • 状态存在于对象中
  • 所有权是显式的,并由系统强制执行
  • 资产是资源,而不是数字
  • 共识和排序是由所有权和争用决定的,而不是持续的税收
  • 并行性是你为之设计的,而不是你希望的

最初感觉具有限制性的东西:显式上下文、显式所有权、显式输入,结果证明是使系统更容易推理、更容易扩展且更难破坏的东西。

对于 Solidity 开发者来说,这可能是一个令人耳目一新的转变。 你多年来内化的许多“最佳实践”并没有消失,它们变成了模型本身的属性。 你不必防御一整类的错误,而是根本无法首先表达它们。

Sui 并不试图成为“以太坊,但速度更快”。 它做出了不同的权衡,这些权衡体现在各个方面:在语言、执行模型、安全保证和开发者体验中。

如果你对这条道路通向何方感到好奇,那么最好的下一步就是进行实验:

  • 构建一个小模块
  • 创建和移动一些对象
  • 有意地对所有权进行建模

一旦心智模型就位,Sui 就不再感到陌生,而是开始感到非常自然。

12. 接下来是什么?

Sui 正在获得动力,成为所有团队都想要的速度、安全性和开发者体验的首选区块链。 请务必关注 Sui 和 OpenZeppelin,了解未来几个月将发布的关于库和入门应用程序的更多开发,这将使开发人员更容易为金融的未来构建链上应用程序。

  • 原文链接: openzeppelin.com/news/a-...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
OpenZeppelin
OpenZeppelin
江湖只有他的大名,没有他的介绍。