本文探讨了以太坊升级对智能合约的影响,特别是从The Merge后的升级开始,包括Paris、Shapella和Dencun,详细介绍了每个升级中引入的新特性,例如PREVRANDAO、PUSH0、Danksharding相关操作码、MCOPY、TSTORE/TLOAD以及SELFDESTRUCT的修改,并展望了未来可能的EIP 3074/7702和EOF升级。
以太坊升级对智能合约的影响
以太坊大约每年都会进行一次升级。这是一个包含新功能的协调硬分叉。这些升级大多影响区块链层,侧重于扩展和共识加强等方面。然而,这些硬分叉也经常影响应用层,这时新的功能会暴露给智能合约。并非所有这些新功能都是向后兼容的,某些假设可能会被打破。因此,在创建不可变的智能合约时,了解这些新功能至关重要。
我们将从最近的以太坊硬分叉(从 The Merge 开始)进行讲解,并探讨它们对智能合约层的影响,重点关注安全性。最后,我们将展望未来,讨论潜在的未来升级。
Paris,或称“The Merge”,是以太坊从工作量证明过渡到权益证明的重大分叉。整个过程无缝进行,因为这种过渡经过精心设计,对智能合约及其用户的影响非常小,但仍然存在一些细微的变化:
DIFFICULTY
变为 PREVRANDAO
。
在 PoW 链上,DIFFICULTY
操作码经常被用作(不好)随机性的来源。然而,随着切换到 PoS,difficulty 已经不再具有意义,因为不再进行挖矿。这导致 DIFFICULTY
操作码变得无用。为了保持与使用 DIFFICULTY
进行随机性的智能合约的兼容性,决定将 DIFFICULTY
重命名为 PREVRANDAO
,并使其返回前一个区块的 RANDAO
混合。为了实现这一点,Solidity 在 0.8.18 版本 中弃用了 block.difficulty
并引入了 block.prevrandao
。注意:在底层仍然调用相同的操作码(0x44
),因此 0.8.18 之前的 Solidity 中的 block.difficulty
将产生与 0.8.18 及更高版本中的 block.prevrandao
相同的值。与 DIFFICULTY
一样,PREVRANDAO
也不是真正随机性的来源。它可以在一定程度上被预测和偏置,因此在使用它时必须非常小心。有关 PREVRANDAO
的安全假设 的更准确和完整的描述,请参阅 EIP。
此外,并非所有网络都以相同的方式实现 PREVRANDAO
。例如,Optimism 允许 sequencer 每隔一个块设置 PREVRANDAO
的值,从而降低了安全保证。在 Arbitrum 上,PREVRANDAO
将始终返回 '1',这显然不应该用于随机性。
block.timestamp
可能只增加 12 的倍数。这可能会影响依赖 block.timestamp
的合约。当然,这仅适用于以太坊主网,其他网络具有不同的区块时间。Shapella 的主要功能是提款,但它也引入了其他好东西,例如 PUSH0
:
PUSH0
是一个新的操作码,它将“0”推送到堆栈。
即使在纯 Solidity 中编写或审查智能合约时,你不会直接与 PUSH0
交互,但了解它的存在和用法仍然非常重要。从 Solidity 0.8.20 版本开始,Solidity 将默认使用 PUSH0
,这意味着任何编译后的合约都可能包含 PUSH0
。部署编译后的合约时,重要的是检查目标网络是否支持 PUSH0
,否则在遇到 PUSH0
指令时执行将恢复。幸运的是,Solidity 会在部署代码中生成 PUSH0
指令。这意味着如果目标网络不支持 PUSH0
,则会在部署时发生恢复,而不是在稍后的阶段,届时资金可能会被冻结。
如果你的目标网络不支持 PUSH0
,建议将 Solidity 版本降低到 0.8.19 或更早版本,或者将目标 EVM 版本降低到 Paris 或更早版本。
Dencun 实现了 danksharding 以及几个较小的功能:
BLOBBASEFEE
和 BLOBHASH
分别通过 blobbasefee()
和 blobhash()
在 Solidity 中公开。正如你所期望的那样,blobbasefee()
返回此块中 blob 的基本费用。blobhash()
返回此交易 blob 中具有给定索引的 KZG 承诺的版本化哈希。如果给定的索引不存在,则返回零字节,类似于 blockhash()
的行为。新的“点评估”预编译允许你验证 blob 中的一个点。此外,预编译返回 blob 中的元素数和 BLS 模数。目前,Solidity 没有提供在高层调用此预编译的方法,因此必须使用对预编译地址(0x0a
)的低层调用。执行此操作时,请确保预编译已部署 在目标网络上,否则该调用将默认成功。
EIP 5656:MCOPY
是引入的一个新操作码,用于优化某些操作,类似于 PUSH0
。Solidity 将从 0.8.25 版本开始默认使用它。因此,请确保目标网络支持它,否则在遇到 MCOPY
时执行将恢复。与 PUSH0
相比,MCOPY
默认不用于 Solidity 的部署代码中。这意味着部署可能会成功,但其他操作可能会失败,这可能会导致资金被冻结。
EIP 1153:TSTORE
/ TLOAD
是提供对瞬态存储访问的新操作码。这些目前只能在 Solidity 中使用内联汇编访问。使用 tstore()
或 tload()
时,请确保目标网络支持它们。此外,重要的是要理解所涉及的生存期:瞬态存储仅在交易结束时清除。将瞬态存储用于重入锁时,这意味着你仍然需要在调用结束时手动清除它们。否则,用户将无法在交易中与你的合约进行多次交互。
EIP 6780:SELFDESTRUCT
进行了修改,如果它不是在与创建合约相同的交易中执行,则合约不会被删除,但所有 ETH 余额仍然会被转移。如果 SELFDESTRUCT
是在与创建合约相同的交易中执行的,则行为保持不变,除了转移所有 ETH 余额外,合约还会被删除。此更改可能会破坏当前依赖 SELFDESTRUCT
的合约,并且在创建使用 SELFDESTRUCT
的合约时,需要注意这一点。无论如何,SELFDESTRUCT
仍然已弃用,不应使用。
这些功能尚未生效,但可能很快就会实现。因此,了解它们很有用。
EIP 3074 或 7702 通过赋予 EOA 智能合约功能(即执行代码的能力)来增强 EOA 的 UX。这打破了如果 msg.sender
等于 tx.origin
,则调用者必须是 EOA 而不是智能合约的假设。因此,不能再依赖此检查。
EOF 是一项重大更新,它为 EVM 字节码添加了结构。这些更改中的大多数应由底层 Solidity 处理。但是,某些方面对于开发人员仍然可见:
gas()
不可用,并且无法为调用指定 gas 限制。EF00
一样,例如,EOF 合约的 EXTCODEHASH
返回 keccak256(0xEF00)
。delegatecalls
,并且会恢复。允许相反的方向,从旧代码到 EOF 代码。如果你的应用程序依赖于此功能,你仍然可以使用旧(非 EOF)合约,因为这些合约保持其当前行为。
请务必查看 EOF 规范 以了解更多详细信息。不过,你需要准备一份零食,因为它很大!
以太坊的升级经常带来令人兴奋的新功能和可能性。不幸的是,它们有时会改变行为并打破假设,而且通常以不明显的方式进行。作为智能合约开发人员和安全工程师,了解这些变化对于创建强大而安全的应用程序非常重要。
- 原文链接: blog.sigmaprime.io/ether...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!