Move 包更新

Aptos链支持代码升级,这意味着已经部署的 Move 代码可以进行代码更新。如果发生了代码升级,所有被升级的代码的消费者在下次执行代码时将自动收到更新后的代码。

代码升级使代码所有者能够在一个稳定的、已知的账户地址下迭代他们的合同或框架,然后可以被链内或链外的其他应用程序引用。

代码升级基于一个由软件包的所有者所决定的升级策略。默认策略是(向后)兼容。这意味着,只有那些保证没有对现有公共 API(包括交易和公共函数)和/或现有资源存储有破坏的升级才会被接受。由于 Move 的强类型字节码语义,这种兼容性检查在技术上是完备且可能的。然而,即使是兼容的升级也可能会对应用程序产生危险的影响,所以在依赖链上可升级的代码时,应当具体问题,具体分析(也见下面关于安全方面的讨论)。

如何指定代码的升级策略

Aptos 链上的代码升级发生在 Move package 层面。在 Move.toml 文件中,开发者可以指定该 package 的升级策略。

[package]
name = "MyApp"
version = "0.0.1"
upgrade_policy = "compatible"
...

💡 兼容性检查

Aptos 在 Move package 发布时,通过一个专用的 Aptos 框架事务检查兼容性。如果兼容性不符合规定,该事务就会终止。

可用升级策略

目前,支持三种不同的升级策略。

  • 兼容:升级必须向后兼容,具体而言:

    • 对于存储,所有旧的结构声明在新代码中必须是相同的。这保证了新代码能正确解释存储的现有状态。然而,可以添加新的结构声明。

    • 对于 API,所有的公共函数必须具有与以前相同的签名。可以添加新的函数。

  • 不可更改:代码不可升级,保证永远不变。

  • 任意:代码可以任意升级,没有任何兼容性检查。

这些升级策略在兼容性方面的排序是:任意 < 兼容 < 不可更改。一个 package 在链上的升级策略只能变强,不能变弱。此外,一个包的所有依赖关系的策略必须强于或等于给定包的策略。例如,一个 不可更改 的包不能直接或间接引用一个 兼容 包。这给用户提供了保证,即在引擎盖下不会发生意外的更新。上述规则有一个例外:安装在地址 0x10xa 的框架包可以免于依赖性检查。这是必要的,因为标准库是 兼容 的升级策略,在免于上述依赖性检查后,我们可以在标准库的基础上,构建以 不可更改 作为升级策略的包。

除了上面的规则外,对于升级策略为 任意 的包还有一条规则:与升级策略为 任意 的包的依赖关系必须在同一个账户内(两个包必须在同一个地址)。这个限制的目的是为了保护用户通过共享这种包来防止误操作。一个账户可以不受限制地使用升级策略为 任意 包,但共享这种代码是不允许的。

依赖的安全性考虑

如前所述,即使是兼容的升级,也可能对依赖升级后的代码的合约产生危险影响。这些影响可能仅仅来自于错误,但也可能是恶意升级的结果。例如,升级后的依赖关系可以突然使所有的功能中止,破坏你的合约的运行,或者突然花费更多的 gas 来执行。因为你不能控制升级,所以对可升级包的依赖需要谨慎处理。

  • 最安全的依赖当然是对一个 不可更改 的包。它保证永远不会改变,包括它的横向依赖关系(根据 Aptos 框架中对其他依赖的要求)。为了升级一个 不可更改 的包,包 owner 必须引入一个新的主要版本,这实际上就像一个独立的新包。目前,主要版本必须通过命名来表达(例如,module feature_v1module feature_v2)。然而,并不是所有的包 owner 都喜欢将他们的代码发布为 不可更改 的,因为这剥夺了修复错误和升级代码的能力。

  • 如果你依赖了一个 兼容 包,强烈建议你知道并理解发布该依赖包的组织实体。最高级别的保证是,该软件包由一个 DAO 管理,没有一个用户可以发起升级,而是必须进行投票或类似的投票。例如, Aptos 框架就是这种情况。

  • 对升级策略为 任意 的软件包的依赖在技术上只允许在同一个账户中进行,所以升级的决定和确保兼容性的责任在账户所有者的身上。

在实际事务中升级代码

一般来说,Aptos 通过 Move 模块 aptos_framework::code,提供了从智能合约的任何地方发布代码的方法。然而,请注意,在当前交易中发布的代码在该事务结束前不能被执行。

Aptos 框架本身,包括所有的连锁管理逻辑,是一个程序化升级的例子。该框架被标记为 兼容。升级通过特定生成的治理脚本发生。更多细节请见Aptos治理

Last updated