信标代理(Beacon Proxy)模式解释

  • RareSkills
  • 发布于 2024-07-24 16:54
  • 阅读 1708

Beacon Proxy Pattern

Beacon Proxy Pattern Banner by RareSkills

Beacon Proxy(信标代理)是一种智能合约升级模式,其中多个代理使用相同的实现合约,并且所有代理可以在单个交易中升级。本文解释了这种代理模式的工作原理。

前提条件

我们假设你已经了解了最小代理的工作原理,甚至可能了解 UUPS 或 透明代理。

Beacon Proxies 的动机

通常,代理模式使用单个实现合约和单个代理合约。然而,多个代理也可以使用相同的实现。

多个代理指向单个实现的流程图

为了理解为什么我们需要这样做,让我们想象一个完全在链上的游戏。这个游戏希望将每个用户账户存储为一个单独的合约,以便账户可以轻松转移到不同的钱包,并且一个钱包可以拥有多个账户。每个代理在其各自的存储变量中存储账户信息。

你可以通过以下几种方式实现这一点:

  1. 使用最小代理标准(EIP1167)并将每个账户部署为克隆

  2. 使用 UUPS 或透明代理模式并为每个账户部署一个代理

在大多数情况下,任何一种选择都可以工作,但如果你想为账户添加新功能怎么办?

在最小代理标准的情况下,你将不得不重新部署整个系统并进行社会迁移,因为克隆是不可升级的。

传统代理是可升级的,但你必须一个一个地升级每个代理。对于更多账户来说,这将是昂贵的。

当有很多克隆和代理时,升级它们都是一件麻烦事。

Beacon 模式旨在解决这个问题:它允许你部署一个新的实现合约并同时升级所有代理。

这意味着 Beacon 模式将允许你部署一个新的账户实现并一次性升级所有代理。

从高层次来看,这个标准允许你为每个实现合约创建无限数量的代理,并且仍然能够轻松升级。

Beacon 的工作原理

顾名思义,这个标准需要一个 Beacon,OpenZeppelin 称之为“UpgradeableBeacon”,并在 UpgradeableBeacon.sol 中实现。

Beacon 是一个智能合约,通过公共函数向代理提供当前的实现地址。 Beacon 是代理关于当前实现地址的真实来源,这就是为什么它被称为“Beacon”。

当代理收到一个传入交易时,代理首先调用 Beacon 上的 view 函数 implementation() 以获取当前的实现地址,然后代理 delegatecalls 到该地址。这就是 Beacon 作为实现来源的工作原理。

Beacon Proxy 逐步 delegatecall 架构

任何额外的代理将遵循相同的模式:它们首先使用 implementation() 从 Beacon 获取实现地址,然后 delegatecall 到该地址。

注意:代理知道在哪里调用 implementation() 是因为它们在一个不可变变量中存储了 Beacon 的地址。我们稍后会详细解释这个机制。

这种模式具有高度可扩展性,因为每个额外的代理只需从 Beacon 读取实现地址,然后使用 delegatecall

Beacon proxy getImplementation() 函数可视化

尽管 Beacon Proxy 模式涉及更多的合约,但代理本身比 UUPS 或透明可升级代理更简单。

Beacon 代理总是调用相同的 Beacon 地址以获取当前的实现地址,因此它们不需要关心诸如管理员是谁或如何更改实现地址等细节。

同时升级多个代理

由于所有代理从 Beacon 的存储中获取实现地址,更改存储槽中的地址会导致所有代理 delegatecall 到新地址,立即“重新路由”它们。

要同时升级所有代理:

  1. 部署一个新的实现合约

  2. 在 Beacon 的存储中设置新的实现地址

设置新的实现地址是通过调用 Beacon 上的 upgradeTo(address newImplementation) 并传递新地址作为参数来完成的。upgradeTo()UpgradeableBeacon.sol(Beacon)上的两个公共函数之一。另一个公共(视图)函数是我们之前提到的 implementation()

注意:upgradeTo() 具有一个 onlyOwner 修饰符,该修饰符在 UpgradeableBeacon.sol(Beacon)的构造函数中设置。

Beacon proxy upgradeTo() 函数代码片段

upgradeTo() 调用一个内部函数 _setImplementation(address newImplementation)(也在 Beacon 上),该函数检查新的实现地址是否是一个合约,然后将 Beacon 中的地址存储变量 _implementation 设置为新的实现地址。

Beacon proxy _setImplementation() 函数代码片段

现在 Beacon 存储中的实现地址已更改,所有代理将读取 Beacon 中的新地址并将其 delegatecall 路由到新的实现...

剩余50%的内容订阅专栏后可查看

点赞 2
收藏 2
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
RareSkills
RareSkills
https://www.rareskills.io/