首页  >  要闻 >  区块链 >  柏林硬分叉今日将完成升级!这些知识点你需要了解
柏林硬分叉今日将完成升级!这些知识点你需要了解
 流动的沙    
2021年04月15日 07:36
  收藏
   

在北京时间4月15日下午18:00左右(具体是以太坊网络区块高度达到#12 244 000时),以太坊的柏林(Berlin)硬分叉升级将会发生,这次升级将纳入4个新的 EIP 改进提案,而其中两个(EIP-2929 和 EIP-2930)将会影响交易的 Gas 成本计算。

本文解释了在这次硬分叉升级前后的 Gas 成本计算,这将如何随 EIP-2929 而发生改变,以及如何使用 EIP-2930 引入的访问列表功能,原文作者是 Nomic Labs 软件开发者 Franco Victorio。

柏林硬分叉今日将完成升级!这些知识点你需要了解-1

注:文章篇幅较长,以下是其中的一些要点。

柏林硬分叉改变了一些 opcode 操作码的 Gas 成本。如果你在 DApp 或智能合约中有一个硬编码的 Gas 值,它们可能会停止工作。如果发生这种情况,并且智能合约是不可升级的,则用户将需要使用访问列表(EIP-2930)来启用它。

访问列表可用于稍稍降低 Gas 成本,但在某些情况下,它们实际上会增加 Gas 消耗总量。

geth 包含了一个新的 RPC 方法(eth\u createAccessList)来简化访问列表的创建。

柏林硬分叉前的 Gas 成本

EVM 执行的每个 opcode 操作码都有一个相关的 Gas 成本。对于大多数操作码而言,这个成本是固定的:PUSH1 总是消耗3个单位的 Gas,MUL 则消耗5个单位的 Gas,等等。而对于其他操作码来说,它是可变的:例如,SHA3 操作码的成本取决于其输入的大小。

我们将重点讨论 SLOAD 和 SSTORE 操作码,因为它们是受柏林硬分叉影响最大的操作码。我们稍后将讨论那些以地址为目标的操作码,就像所有的  EXT* 和 CALL* 操作码,因为它们的 Gas 成本也会发生变化。

柏林硬分叉之前的 SLOAD

如果没有 EIP-2929,SLOAD 的成本很简单:它总是会消耗800 Gas。

柏林硬分叉之前的 SSTORE

就 Gas 而言,SSTORE 可能是最复杂的操作码,因为它的成本取决于存储 slot 的当前值、新值及它是否以前被修改过。我们将只分析一些场景以获得基本的理解。如果你想了解更多,请阅读本文末尾链接的 eip。

如果 slot 的的值从0更改为1(或任何非零值),则成本为20 000;

如果 slot 的的值从1更改为2(或任何其他非零值),则成本为5 000;

如果 slot 的的值从1(或任何非零值)更改为0,则成本也为5000,但在交易结束时你将获得 Gas 退款。这篇文章中,我们不会详细讨论退款,因为它们不受柏林硬分叉的影响;

如果以前在同一事务中修改了该值,则所有后续 sstore 的成本为800;

这里的细节有些枯燥,重要的一点是,SSTORE是非常昂贵的,其成本取决于几个因素。

实施 EIP-2929 之后的 Gas 成本

EIP-2929 改变了所有这些值,但在此之前,我们需要先谈谈这个 EIP 引入的一个重要概念:已访问地址和已访问存储密钥。

如果地址或存储密钥以前在交易期间被“使用”,则该地址或存储密钥就被视为已访问。例如,当你调用另一个合约时,该合约的地址会被标记为已访问。

类似地,当你 SLOAD 或 SSTORE 某些 slot 时,它将被视为在交易的其余部分已被访问。不管是哪个操作码做的:如果一个 SLOAD 读取了一个 slot,那么它将被认为对接下来的 SLOAD 及 SSTORE 都是已访问的。

这里需要注意的一点是,存储密钥位于某个地址的“内部”。正如 EIP 所解释的:

执行事务时,维护一组 accessed_addresses: Set[Address] 和 accessed_storage_keys: Set[Tuple[Address, Bytes32]。

也就是说,当我们说一个存储 slot 被访问时,我们实际上是说一对(address, storageKey)被访问了。

话虽如此,我们还是来谈谈新的 Gas 成本吧。

柏林硬分叉之后的 SLOAD

在柏林硬分叉之前,SLOAD 的固定成本是800 Gas,现在,这取决于是否已访问了存储 slot。如果未访问,则成本为2 100 Gas,如果已访问,则成本为100 Gas。因此,如果 slot 在已访问的存储密钥列表中,则一次 SLOAD 的成本会降低2 000 Gas。

柏林硬分叉之后的 SSTORE

让我们在部署 EIP-2929 的环境下回顾一下之前的 SSTORE 示例:

如果 slot 的值从0更改为1(或任何非零值),则成本为:22 100(如果未访问存储密钥),20 000(如果已访问存储密钥);

如果 slot 的值从1更改为2(或任何其他非零值),则成本为:5 000(如果未访问存储密钥),2 900(如果已访问存储密钥);

如果 slot 的值从1(或任何非零值)更改为0,则成本与上一项相同,然后加上退款;

如果以前在同一交易中修改了该值,则所有后续 SSTORE 的成本为100;

如你所见,如果要修改的 slot 以前被访问过,那么第一次 SSTORE 的成本将降低2 100 Gas。

下面的表总结了目前为止所有改变的值:

柏林硬分叉今日将完成升级!这些知识点你需要了解-2

请注意,在最后一行中,谈论是否访问了 slot 是没有意义的,因为如果它以前被写入过,则表明其也被访问过。

EIP-2930

我们在文章开头提到的另一个 EIP 就是 EIP-2930,这个改进提案添加了一种新类型的事务,该事务可以在事务负载中包括访问列表。这意味着你可以在事务开始执行之前预先声明哪些地址和 slot 应被视为是已访问的。

例如,一个未访问 slot 的 SLOAD 成本为2 100,但是如果该 slot 包含在事务的访问列表中,则相同的操作码成本就为100。

但是,如果当地址或存储密钥已被访问时,Gas 成本变更低了,这是否意味着我们可以将所有内容添加到事务的访问列表中并降低 Gas 成本呢?不完全是这样,因为你还需要为添加的每个地址和每个存储密钥支付 Gas。

让我们看一个例子,假设我们正在向合约A发送一笔交易,访问列表可能如下所示:

柏林硬分叉今日将完成升级!这些知识点你需要了解-3

如果我们用这个访问列表发送了一笔交易,并且第一个使用 0x0 slot 的操作码是 SLOAD,则它将花费100 Gas(而不是2 100 Gas),这就降低了2 000 Gas的消耗量。但事务访问列表中包含的每个存储密钥的成本为1 900 Gas,所以我们只省了100 Gas。(如果访问该 slot 的第一个操作码是 SSTORE,那么我们将节省2 100 Gas,这意味着如果考虑到存储密钥的成本,我们总共将节省200 Gas。)

这是否意味着我们在使用带有访问列表的交易时总是能节省 Gas 消耗?并非如此,因为我们还要为访问列表中的地址支付 Gas 成本(在我们的示例中是“<address of A>”。)

已访问地址

以上,我们只讨论了 SLOAD 和 SSTORE 操作码,但这些并不是柏林硬分叉之后唯一改变的操作码。例如,原先调用操作码的固定成本为700 Gas。但是在实施 EIP-2929 之后,如果地址不在访问列表中,则开销就是2 600 Gas,但如果是在已访问列表中,则开销就是100 gas。

而且,与已访问存储密钥一样,之前访问该地址的操作码并不重要(例如,如果先调用 EXTCODESIZE,则该操作码将花费2 600 Gas,使用相同地址的任何后续 EXTCODESIZE、CALL、STATICCALL 将花费100 Gas)。

这是如何受到访问列表交易的影响的?例如,如果我们将一笔交易发送至合约 A,而该合约调用另一个合约 B,那么我们可以包含如下访问列表:

柏林硬分叉今日将完成升级!这些知识点你需要了解-4

我们必须支付2 400 Gas 的费用才能将这个访问列表包含在交易中,但是第一个使用 B 地址的操作码将花费100 Gas(而不是2 600 Gas)。

所以我们这样做就节省了100 Gas,如果 B 以某种方式使用它的存储,并且我们知道它将使用哪些密钥,那么我们还可以将它们包括在访问列表中,并为每个密钥节省100/200的 Gas(取决于第一个操作码是 SLOAD 还是 SSTORE)。

但我们为什么要谈另一个合约呢?我们调用的合约怎么了?我们为什么不这样做?

柏林硬分叉今日将完成升级!这些知识点你需要了解-5

我们可以这样做,但这是不值得的,因为 EIP-2929 指定了被调用的合约地址(即 tx.to)总是包含在 accessed_addresses 列表中,因此这只会白白浪费2 400 Gas。 让我们再次分析上一节的示例:

柏林硬分叉今日将完成升级!这些知识点你需要了解-6

这实际上是浪费,除非我们包含多个存储密钥。如果我们假设一个 SLOAD 总是首先使用一个存储密钥,那么我们至少需要24个存储密钥才能实现收支平衡。

显然,分析并创建这样的一个访问列表是没有意义的。幸运的是,我们有更好的方法。

eth_createAccessList RPC 方法

Geth(从1.10.2版本开始)包含了一个新的 eth\u createAccessList RPC 方法,其可以用来生成访问列表。它的用法类似于 eth_estimateGas,但它不是用于估算 Gas,而是返回如下内容:

柏林硬分叉今日将完成升级!这些知识点你需要了解-7

也就是说,它为你提供了该交易将使用的地址和存储密钥的列表,以及如果包含访问列表,则会消耗的 Gas。(而且,与 eth_estimateGas 一样,这是一种估计值,在实际进行交易时,列表可能会更改。)

我想,随着时间的推移,我们会发现执行此操作的正确方法是什么,而我的伪代码猜测是:

柏林硬分叉今日将完成升级!这些知识点你需要了解-8

激活合约

必须要指出的是,访问列表的主要目的不是使用 Gas,正如 EIP 所解释的:

EIP-2929所引入的是减轻合约破坏风险,因为交易可预先指定和支付交易计划访问的帐户和存储 slot。因此,在实际执行中,SLOAD 和 EXT* 操作码只需要100 Gas,这已经足够低了,它不仅可防止因该 EIP 而导致的破坏,还可以“激活”由于 EIP-1884 而卡住的任何合约。

这意味着,如果一个合约对执行某些操作的成本做出假设,那么 Gas 成本的增加可能会导致它无法工作。

例如,一个合约调用另一个合约(例如 someOtherContract.someFunction{gas: 34500}())因为它假设某个函数正好使用34 500 Gas,那么它就会中断,但如果在事务中包含适当的访问列表,那么合约将再次工作。

如果你想自己测试这些 EIP,你可以复制这个 repo,它有几个可使用 Hardhat 和 geth 执行的示例。有关说明,请查看 README 文件。

相关资料:

1、EIP-2929‌ 和 EIP-2930;

2、EIP-2930 依赖于柏林硬分叉的另一组成部分:EIP-2718‌;

3、EIP-2929 引用了大量 EIP-2200‌ 的内容,所以如果你想更深入地了解 Gas 成本,你应该从 EIP-2200 开始;

4、有关比较 Gas 使用量变化的更复杂示例‌。

编辑: 流动的沙
更多财经请关注 WX: Chaocaijing123456
币海财经: 全球财讯门儿清 https://www.bihai123.com/
声明: 本文由入驻币海编者上传,观点仅代表编者本人,不代表币海财经赞同其观点或证实其描述,请自行判断。

延伸阅读