可升级智能合约简介

  • QuickNode
  • 发布于 2025-01-18 17:11
  • 阅读 16

本文详细介绍了可升级智能合约的概念、工作原理及其在以太坊区块链中的应用。通过代理模式,开发者可以在不牺牲安全性和去中心化的情况下,修改已部署的智能合约功能。文章还讨论了使用可升级合约的原因,如修复漏洞和产品改进,并介绍了如何实现可升级智能合约,包括透明代理模式和通用升级代理标准。

概述

默认情况下,部署在以太坊区块链上的智能合约是不可变的。虽然这有助于实现去中心化和安全性,但它可能会降低智能合约的功能性。

解决这个问题需要在开发过程中使用可升级的智能合约模式。使用代理模式创建的可升级智能合约使开发者能够在部署后修改合约功能——而不会损害安全性或去中心化。

我们将做什么

  • 定义可升级智能合约并总结其使用场景
  • 理解可升级智能合约的工作原理
  • 学习创建可升级智能合约所需的要素

你需要具备的基础知识

对以下内容的基本理解:

  • 智能合约
  • 去中心化应用(dApps)
  • 以太坊虚拟机(EVM)
  • Solidity

什么是智能合约?

在深入探讨可升级智能合约之前,让我们回顾一下什么是智能合约。简单来说,智能合约是运行在区块链上特定地址的程序。智能合约是自执行的,因为只要调用正确的函数,它们就可以自动执行操作。例如,一旦你调用特定函数,智能合约就可以将代币发送到你的地址。

以太坊智能合约部署在以太坊虚拟机(EVM)中,这是一个图灵完备的虚拟机,能够在区块链上执行代码。EVM 环境的一个基本规则是,合约代码在部署后无法更改,这解释了为什么智能合约被标记为“不可变”。

什么是可升级智能合约?

与普遍看法相反,智能合约可以升级。然而,需要澄清的是,“可升级”和“可变”是两个不同的概念。

可升级智能合约使用一种称为“代理模式”的特殊功能,使开发者在部署后能够灵活地修改合约逻辑。以下是代理合约架构中的组件分解:

  1. 代理合约

  2. 执行或逻辑合约

第一个合约(代理合约)在开始时部署,包含合约存储和余额。与此同时,第二个合约(执行合约)存储用于执行函数的合约逻辑。

在代理模式中,代理合约存储执行合约的地址。当用户发送请求时,消息会通过代理合约,代理合约将其路由到执行合约。然后,代理合约从执行合约接收计算结果并将其返回给用户。

现在,代理合约本身无法修改。然而,我们可以创建具有更新后合约逻辑的额外执行合约,并将消息调用重新路由到新合约。通过代理模式,你并没有以任何方式改变智能合约。你所做的只是部署一个新的逻辑合约,并要求代理合约引用它而不是旧合约。这个新的逻辑合约可以具有额外的功能或修复旧错误。

为什么要使用可升级合约?

以太坊的部分吸引力在于智能合约的不可变性。用户相信开发者无法任意进行更改,这鼓励他们自由地与去中心化应用进行交互。

因此,可升级智能合约的变化性质可能会引发担忧——尤其是在去中心化和安全性方面。然而,在智能合约中实现代理模式可以带来显著的改进,例如:

修复漏洞

与每个程序一样,智能合约通常也存在缺陷和漏洞。但是,如果没有升级合约的选项,修复致命漏洞是困难的——如果不是完全不可能的话。

使用可升级智能合约使开发者更容易解决已识别的问题。你只需创建一个新的执行合约,并更新代理合约以指向新合约。

有了修复漏洞的选项,智能合约开发者可以向用户保证安全性。

改进产品

随着去中心化应用(dApps)的普及,它们将需要不断改进。为此,作为后端的智能合约必须允许修改。

可升级智能合约模式将能够添加新功能并改善整体用户体验。然而,重要的是升级的控制权是去中心化的,以避免恶意行为。

如何使智能合约可升级

由于大多数代理模式依赖于 DELEGATECALL EVM 操作码,我们将简要解释委托调用的工作原理:

在两个合约之间的常规消息调用( CALL)中,调用合约( 合约 A)将数据有效负载发送到被调用合约( 合约 B)。然后, 合约 B 执行合约逻辑并将结果返回给 合约 A。关于常规消息调用,你需要理解两件事:

  • 被调用合约( 合约 B)总是读取和写入其存储。
  • 关于消息调用的信息,例如 msg.sendermsg.value 在消息调用期间可能会发生变化。

在委托调用( DELEGATECALL)中,被调用合约( 合约 B)持有用于执行操作的代码。然而,实际的逻辑执行发生在调用合约( 合约 A)的上下文中。这具有以下含义:

  • 合约 A 可以执行存储在 合约 B 中的逻辑,就像调用内部函数一样。
  • 合约 B 中执行的读写操作仅影响 合约 A 的存储。
  • msg.sendermsg.value 在调用期间保持不变。

来自 Solidity 文档:

这意味着合约可以在运行时从不同的地址动态加载代码。存储、当前地址和余额仍然引用调用合约,只有代码取自被调用地址。

一旦你理解了委托调用,代理模式的概念就更容易掌握了。代理合约持有逻辑合约的地址,并将所有调用委托给它。由于代理使用 DELEGATECALL 函数,因此它可以使用逻辑合约中的代码进行操作。

除了委托调用之外,可升级合约还需要另一个函数来控制升级。此函数用于更改代理中引用的执行合约的地址。

一个简单的方法是将 upgrade 函数构建到代理合约中,并使用 onlyAdmin 来防止未经授权的升级。这确保只有具有管理员级别访问权限的实体才能将代理升级到新的逻辑合约。

开发者喜欢这种模式,因为代理存储了升级逻辑,而执行合约可以接受委托调用,而无需复杂的设计。然而,使用简单的代理模式会引入“函数选择器冲突”问题。这促使了一种新模式的开发,称为“透明代理”。

透明代理

如前所述,简单的代理合约使用 DELEGATECALL 函数将函数调用重定向到持有可执行代码的逻辑合约。代理还使用一个函数来更改逻辑合约的地址,以实现合约升级。

然而,如果代理的升级管理函数与逻辑合约中的另一个函数具有相同的标识符,则这种方法可能会遇到问题。在这种情况下,很难知道调用了哪个合约——代理合约还是逻辑合约?

函数选择器冲突可能导致错误,或者更糟糕的是,恶意利用。为了解决这个问题,Open Zeppelin 团队开发了透明代理模式。以下是透明代理模式的工作原理的分解。

透明代理模式根据消息调用的来源( msg.sender)解释消息调用:

  • 如果 msg.sender 指向外部地址,代理会自动将其委托给逻辑合约。即使调用的函数与代理的任何内部函数相似。
  • 如果 msg.sender 指向管理员,代理不会委托调用。相反,它将执行操作(前提是它可以理解该函数)。

这意味着只有管理员才能调用代理管理函数。如果外部地址调用冲突的逻辑合约函数,操作将简单地回滚。使用透明代理模式的唯一问题是你必须支付更高的 gas 费用。这是因为 EVM 需要额外的 gas 来为每次调用加载执行合约地址,并执行委托逻辑。此外,透明代理模式本身的部署成本也比普通模式更高。

通用升级代理标准

另一种不太流行的代理模式是通用升级代理标准(UUPS)。与透明代理模式一样,通用可升级代理使用 DELEGATECALL 函数。主要区别在于,逻辑合约(而不是代理)管理升级功能。

在这种模式中,逻辑合约仍然写入代理合约的存储,并将其地址存储在后者中。然而,每个逻辑合约都继承了一个 Proxiable 合约,该合约提供了升级功能。

使用存储在执行合约中的 Proxiable 合约,你可以更新代理合约中引用的合约地址。这样,只要所有新的执行合约都继承相同的 Proxiable 合约,你就可以继续使用它们。

通用可升级代理的好处在于它们减少了函数选择器冲突的可能性。虽然 Solidity 编译器无法检测两个合约之间的冲突函数,但当它们发生在同一合约中时,它可以拒绝它们。此外,在这种情况下,代理合约的存储占用空间较小,使其部署成本更低。

然而,使用通用可升级代理是有代价的。如果你将代理合约升级到一个未继承升级功能的逻辑合约,将来将无法升级代理。

结论

如果你已经阅读了本指南,恭喜!你现在了解了如何设计可升级智能合约,以及为什么使用它可能对你的 dApp 有益。

订阅我们的新闻通讯以获取更多关于以太坊的文章和指南。如果你有任何反馈,请随时通过Twitter与我们联系。你还可以在我们的Discord社区服务器上与我们聊天,那里有一些你会遇到的最酷的开发者 :)

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

0 条评论

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