跳至内容
0%

Solidity 中的 selfdestruct 是什么?

发布于 2023年9月25日2 分钟阅读

更新:根据 EIP-6049 的要求,selfdestruct 已在 Ethereum 的 Shanghai 升级中被弃用。本文现已过时,仅反映该功能之前的运作方式。

SelfdestructSolidity 中的一个关键字,开发者用它来终止合约。在 2021 年的一篇研究论文《Why Do Smart Contracts Self-Destruct? Investigating the Selfdestruct Function on Ethereum》中,约有 800 份合约使用了 selfdestruct 关键字,这使其成为学习 Solidity 时需要理解的一个重要概念。

本文将定义 selfdestruct 是什么、说明其用途,并给出在 Solidity 智能合约中实现 selfdestruct 的示例。

什么是 selfdestruct?

Selfdestruct 是一个用于终止合约的关键字,它会从 Ethereum 区块链上移除合约字节码,并将合约中的资金发送到指定地址。

Selfdestruct 起源于 2016 年,当时 Ethereum 区块链和去中心化组织(DAO)都还处于早期阶段。最早的一个 DAO 曾因黑客攻击损失了 360 万 ETH。由于 Solidity 合约的不可变性,该攻击持续了数天。由于当时不存在销毁合约的方法,早期的 DAO 开发者只能分叉整条区块链以阻止进一步的攻击。

因此,selfdestruct 被创建出来,作为面对安全威胁时的一个退出方式。它最初被称为“suicide function”,从 Solidity v0.5.0 开始改名为 selfdestruct

开发者为什么使用 selfdestruct 函数?

开发者使用 selfdestruct 函数,主要是为了提高智能合约代码的安全性、清理不再使用的合约,以及快速转移 Ethereum 资产。

在需要升级智能合约时,selfdestruct 会很有用。例如,ERC-20 框架是 Ethereum 上所有同质化代币的标准实现,用以实现互操作性。任何不符合 ERC-20 标准接口的代币,都很难与其他合约交互。

在这种情况下,开发者会选择切换到新合约,而不是升级现有合约。因此,他们可以使用 selfdestruct 函数从当前合约中提取资金,再构建一个具备所需功能的新合约。

开发者使用 selfdestruct 函数还为了防范潜在的安全威胁。根据同一份 2021 年 selfdestruct 智能合约报告,当开发者发现合约存在安全漏洞时,selfdestruct 函数可以帮助他们立即终止有缺陷的合约,并替换为安全的合约。

使用 selfdestruct 有哪些缺点?

selfdestruct 函数的缺点在于它无法找回 ERC-20 代币,也无法在合约自毁后重新路由代币。

通常,调用 selfdestruct 函数的开发者未能及时就合约终止一事进行沟通。即便团队发布了更新通知,消息传播的速度也可能不够快。结果是,有些人可能仍会向已销毁的合约发送资金,误以为它仍在运行。在这种情况下,资金将永久丢失。

使用 selfdestruct 的第二个缺点是,它只能转移 Ether(ETH),无法转移其他 ERC-20 代币,例如山寨币,或遵循 ERC-721 代币标准的 NFT。一旦调用 selfdestruct,这些资产将永远无法找回。

为什么有人认为 selfdestruct 函数存在风险?

selfdestruct 函数使恶意开发者更容易实施 rug pull。

该函数使开发者能够调用 selfdestruct,并将资金导向自己的个人钱包。因此,一些协议用户和开发者对使用 selfdestruct 函数持复杂态度。

selfdestruct 是如何工作的?

Selfdestruct 的工作方式是:从链上清除合约字节码,将所有剩余资金发送到指定地址,然后向开发者返还一部分 Gas 费用。

一份合约由两个部分组成:状态和函数。这两部分定义了合约的行为和可调用的函数。移除字节码意味着该合约不再有任何定义其内容的组成部分,使该合约无法被调用。

Selfdestruct 函数有一个必需参数,用于指明调用者希望在合约销毁后,将合约中的资金发送到哪个地址。

什么是负 Gas,为何它很重要?

负 Gas 指的是当开发者使用 selfdestruct 函数时,Ethereum 链退还的那部分交易费用。

Ethereum 通过将移除链上数据所节省的 Gas 作为奖励返还给调用者,以此鼓励使用 selfdestruct 函数。当一份合约调用 selfdestruct 函数时,该笔交易所用总 Gas 的一半会退还给调用者。

selfdestruct 会移除该合约在 Ethereum 链上的历史记录吗?

不会。selfdestruct 函数不会移除合约在 Ethereum 链上的历史记录——它只会移除合约的字节码。

Ethereum 是一条区块链,是一个公共账本,由一个由多个互联节点组成的网络始终保存着该区块链状态的副本。因此,在调用 selfdestruct 函数之前完成的所有数据和交易,都会被永久记录在链上,无法更改。

调用 selfdestruct 函数后,资金是否会永久丢失?

既是又不是:现有的 ETH 会被发送到调用者在函数中指定的另一个地址,而 ERC-20 代币则会丢失。

此外,如果有人向已完成自毁(即已终止)的合约发送资金,这些资金不会被重新路由,将会丢失。

selfdestruct 函数示例

该程序的目标是允许铸造 NFT,直到合约中的 ETH 达到一定数量为止。一旦达到该数量,程序会将所有 Ether 作为奖励发送给最后一位铸造者的钱包。每个人可以用 1 Ether 铸造多个 NFT,但每次只能铸造一个。该合约并非用于生产环境,仅作示例用途。

该铸造合约的主要问题在于,它使用 “this.balance” 来验证合约资金是否足够以触发结束条款。因此,攻击者可以轻易发送一笔金额,将合约资金推高至超过设定限额,然后使用 selfdestruct 关键字将资金重定向到自己声明的构造函数中。

因此,在使用 selfdestruct 时,开发者必须谨慎选择用于满足特定条件的变量。应避免使用任何指向合约地址或合约资金的引用,因为这些数值可以被人为操纵。

如何使用 selfdestruct 函数

初始化一个 selfdestruct 函数很简单:你需要在函数内使用 selfdestruct 关键字,并指定一个 payable 地址,用于在 selfdestruct 函数被调用后接收合约资金。

以下是 Solidity 课程中通常会讲授的一个 selfdestruct 函数示例:

以下是这段代码所做的事情:

  • 该函数的名称是 destroy
  • 该参数将地址指定为 apocalypse
  • 当 destroy 函数被调用时,地址通过 apocalypse 变量指定
  • 该函数的可见性被声明为 public,因此其他合约可以访问它
  • 随后,我们使用 selfdestruct 关键字,在将 apocalypse 变量声明为 payable 后将其传入

由于该函数是 public 的,这本身就代表了一个潜在的安全隐患。为应对这一点,可以考虑添加 onlyOwner 修饰符,或使用 require 语句来确认只有所有者才能调用 destroy 函数。

通过 Alchemy 的 Solidity 开发者课程学习 Solidity 和 selfdestruct 函数

Selfdestruct 是一种在发生安全漏洞、开发者需要升级合约或需要移除旧合约时终止合约的保险机制。尽管 selfdestruct 的使用在 Solidity 开发者社区中仍存在争议,但该函数的引入已经帮助防范了许多潜在的黑客攻击。

想进一步了解 selfdestruct 等重要函数,成为更优秀的 Solidity 开发者,可以报名参加 Alchemy University 提供的免费、为期 7 周的 Ethereum Bootcamp。如果你刚开始接触开发,Alchemy 的 3 周 JavaScript 速成课程是学习 Ethereum bootcamp 前很好的先修课程。

Background gradient

构建区块链应用

Alchemy 将最强大的 Web3 开发者产品和工具与资源、社区及专业支持结合在一起。