柏林硬分叉已于 4 月 14 日在主网上线,引进了四份 EIP 。其中的两份 (EIP-2929 和 EIP-2930)对买卖的 gas 本钱有影响。本文将解说部分 gas 本钱在柏林前是怎么核算的,参加了 EIP-2929 后会怎么改变,以及怎么运用 EIP-2930 引进的拜访列表。

要点速览

这篇文章很长,这是它的概要:

  • 柏林硬分叉改变一些操作码的 gas 本钱。假定在一个 dapp 或一个智能合约里 gas 费的值是硬编码的,它们或许会中止运转。假定这种状况发生了,且智能合约是不可更新的,消费者将需求用 EIP-2930 的拜访列表才能运用那部分的操作码。

  • 拜访列表能够用作减少少量的 gas 本钱,但实践上它们在一些状况下是会增加总 gas 耗费量的。

  • geth 增加了一个叫 eth_createAccessList 的新 RPC办法,用以简化拜访列表的创建。

柏林硬分叉前的 gas 本钱

EVM 履行的每个操作码都有一笔相关的 gas 本钱。它们大多数的本钱是固定的:PUSH1 总是耗费 3 个单位的 gas,MUL 耗费 5 个,等等。其他一些是会改变的:比如 SHA3 的操作码本钱依赖于它的输入大小。

咱们首要评论操作码 SLOAD 和 SSTORE,由于它们是最受柏林硬分叉影响的。咱们以后会评论针对地址的操作码,比如一切的 EXT* 和 CALL* ,由于它们的 gas 本钱也改变了。

柏林前 SLOAD 的 gas 本钱

在没有 EIP-2929 之前,SLOAD 的 gas 耗费很简单:它总是耗费 800 gas。所以(目前)没有什么可说的。

柏林前 SSTORE 的 gas 本钱

在 gas 耗费方面,SSTORE 或许是最复杂的操作码了,由于它的本钱取决于像存储 slot 的当时值、新值、以及它是否之前被修改正。咱们仅对一些状况进行剖析以取得一个基本了解;假定你想了解更多,请阅读文末的 EIP 链接。

  • 假定存储 slot 的值从0 变成 1 (或任何非 0 的值),gas 耗费量是 20000。

  • 假定存储 slot 的值从1 变成2 (或任何其他非 0 的值),gas 耗费量是 5000。

  • 假定存储 slot 的值从 1 (或任何非 0 的值) 变成 0,gas 耗费量也是 5000,但在买卖的终究你会取得 1 笔 gas 费返还。本文不会评论 gas 费返还,由于它们在柏林硬分叉中不受影响。

  • 假定存储 slot 的值在之前相同的买卖中被修改了,往后一切 SSTORE 的 gas 耗费量都是 800。

这部分的细节并不有趣,重要的是 SSTORE 很贵,而它的耗费取决于几个要素。

EIP-2929 后的 gas 耗费

EIP-2929 对上述一切操作码的 gas 耗费都有影响。但在深化这些改变前,咱们需求先谈谈这份 EIP 引进的一个重要概念:拜访过的地址 (accessed addresses)与拜访过的存储密钥 (accessed storage keys)。

假定一个地址或一个存储密钥在之前的买卖中被“运用”过,那么它们就会被视为“拜访过的”。例如,当你 CALL(调用)一个其他合约,该合约的地址就会被标为“ accessed (拜访过的)”。同样地,当你 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 是否被拜访过。假定它没有被拜访过,gas 耗费是 2100;假定被拜访过了,则是 100。因而,假定该 slot 是在拜访过的存储密钥列表里的,SLOAD 的 gas 耗费会少于 2000。

柏林后的 SSTORE

让咱们在 EIP-2929 语境下重温前面的 SSTORE 例子:

  • 假定存储 slot 的值从0 变成 1 (或任何非 0 的值),gas 耗费量是:

    • 假定存储密钥没有被拜访过,22100

    • 假定被拜访过了,20000

  • 假定存储 slot 的值从1 变成2 (或任何其他非 0 的值),gas 耗费量是:

    • 假定存储密钥没有被拜访过,5000

    • 假定被拜访过了,2900

  • 假定存储 slot 的值从 1 (或任何非 0 的值) 变成 0,gas 耗费与上一种状况相同,再加上返还。

  • 假定存储 slot 的值在之前相同的买卖中被修改了,往后一切 SSTORE 的 gas 耗费量都是100。

如你所见,假定 SSTORE 正在修改的 slot 是之前被拜访过的,第一个SSTORE 耗费少于 2100 gas。

总结

下表对上述的值进行了比较:

柏林硬分叉对 Gas 影响几何?

请注意,在终究一行没有必要议论 slot 是否现已被拜访过,由于假定它之前就被写入,那它就被拜访过了。

EIP-2930: 可选拜访列表买卖

咱们一开端提及的其他 EIP 便是 EIP-2930。这份 EIP 增加了一种新的买卖类型,它能够在买卖里参加一个拜访列表。这意味着你能够在买卖履行开端前,事先声明哪些地址和 slot 应被视为拜访过的。例如,一个未被拜访过的 slot 的一个 SLOAD 需求耗费 2100 gas,但假定该 slot 被参加到买卖拜访列表里,同一个操作码只需耗费 100 gas。

但假定现已被拜访过的地址或存储密钥会耗费更少 gas,这是否意味着咱们能够把一切东西都增加到买卖拜访列表来降低 gas 耗费了?棒!不必给 gas 费了! 然而,不尽然是这样,由于你每次增加地址和存储密钥的时分仍是需求付出 gas 费的。

咱们来看一个例子。假定咱们正在向合约 A 发送一笔买卖,拜访列表或许如下:

柏林硬分叉对 Gas 影响几何?

假定咱们发送一笔附有这个拜访列表的买卖,运用 slot 0x0 的第一个操作码是SLOAD,它耗费的是 100 而不是 2100 gas。这减少了 2000 gas。但每次把存储密钥增加到买卖的拜访列表中都需求耗费 1900 gas。因而咱们只省了100 gas。(假定拜访该 slot 的第一个操作码是 SSTORE而不是 SLOAD,咱们能够省 2100 gas,也便是说假定咱们考虑的是存储密钥的耗费的话,咱们总共节约 200 gas。 )

这是否代表只需咱们运用买卖拜访列表就能节约 gas?不是的,由于咱们还需求付出增加地址到拜访列表 (即咱们的例子中的 "<address of A>" ) 的 gas。

拜访过的地址

到目前为止,咱们只评论了操作码 SLOAD 和 SSTORE,但柏林升级后不是只要这些操作码有改变。例如,操作码 CALL 之前的固定耗费量是 700。但 EIP-2929 后,假定地址不在拜访列表里,它的耗费质变成了 2600,假定在,则是 100。还有,像拜访过的存储密钥,无论之前拜访的是什么操作码 (例如,假定EXTCODESIZE 是第一次被调用,那么该操作码将耗费 2600 gas,而往后任何运用同一个地址的 EXTCODESIZE、 CALL 仍是STATICCALL都只耗费 100 gas)。

这是怎么影响有拜访列表的买卖的呢?例如,假定咱们给合约 A 发送一笔买卖,而该合约调用另一个合约 B,那么咱们能够参加这样一个列表:

柏林硬分叉对 Gas 影响几何?

咱们将需求付出 2400 gas 以把这个拜访列表参加到买卖里,但之后运用 B 地址的第一个操作码只耗费 100 gas,而不是2600。因而,咱们经过这样做节约了 100 gas。假定 B 以某种方式运用它的存储,且咱们知道运用的是哪个密钥,那么咱们也能够把它们参加到拜访列表里,这样能够为每个密钥节约 100~200 gas (取决于你的第一个操作码是 SLOAD 仍是 SSTORE )。

可是为什么咱们要议论另一个合约?咱们正在调用的合约呢?为什么不对这个合约进行这些操作?

柏林硬分叉对 Gas 影响几何?咱们能够这样做,但这样不划算,由于 EIP-2929 明确规定正在被调用的合约 (即tx.to) 地址会默认参加到 accessed_addresses 列表里。因而咱们无须付出多余的 2400 gas。

让咱们再对之前的例子进行剖析:

柏林硬分叉对 Gas 影响几何?

除非咱们要参加多几个存储密钥,不然这其实很浪费。假定咱们预设 SLOAD 总是首先运用存储密钥,那么咱们起码需求24 个存储密钥能保本。

你能够想象一下,做剖析与手动创建一个拜访列表并不那么有趣。幸运的是,其实有更好的办法。

eth_createAccessList RPC 办法

Geth (从 1.10.2 版本开端 ) 参加了一个新的 eth_createAccessList RPC 办法,你能够用它来生成拜访列表。它的运用与 eth_estimateGas 类似,但它返回的不是 gas 估值,而是像下面这样的成果:

柏林硬分叉对 Gas 影响几何?

也便是它给你该买卖会用到的地址与存储密钥的列表,加上拜访列表被参加状况下所耗费的 gas。(像 eth_estimateGas,这是一个估值,当买卖实践上被挖的时分,这个列表或许会改变。)但,这并不代表 gas 耗费量会低于在没有拜访列表状况下发送同一笔买卖所耗费的!

我想咱们会跟着时间推移发现运用它的正确办法,但我猜的伪代码如下:

柏林硬分叉对 Gas 影响几何?

给合约松绑

值得一提的是,拜访列表的首要意图不在于运用 gas。如 EIP 所解说:

减轻由 EIP-2929 引进的合约断裂风险,由于买卖能够提前指定买卖计划拜访的账户和存储 slot 并提前付出;终究在实践履行中,操作码 SLOAD 和 EXT*只耗费 100 gas:这个低 gas 耗费不仅能够避免由该 EIP 引起的断裂,还能够“松开”任何因 EIP-1884 而受限的合约。

这意味着假定一个合约对履行某事务的本钱做了假定,gas 本钱的增加就或许使它停止运作。例如,一个合约调用另一个合约,像这样someOtherContract.someFunctionhttps://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpggas: 34500}(),由于它假定someFunction 会准确耗费 34500 gas,这样它会出问题。但假定你增加了一个合理的拜访列表,那么合约会再次运作。

自己做检验

假定你像自己去测验,复制这个代码库,里面由多个能够用 Hardhat 和 geth 履行的实例。在 README 查看阐明。

参阅文献

  • EIP-2929 和 EIP-2930 是与本文相关的两个柏林硬分叉 EIP。

  • EIP-2930 依赖于柏林硬分叉的另一部分: EIP-2718,它又叫类型买卖。

  • EIP-2929 参阅了许多 EIP-2200,因而假定你想深化了解 gas 本钱,你能够从那里开端。

视野开拓

他对于新兴市场投资有不少高见,比如说,“强龙压不过地头蛇”——到头来做得最好的总是本国投资者。在政治事件引发的大恐慌之后,地头蛇们高高兴兴地从外国人手中接过价格已经触底的股票。可你要指望以后股票升值时还能卖给他们,那就是做梦。要想在新兴市场上获得利润,唯一的办法就是以更高的价格卖给比自己更傻的外国投资者。-《对冲基金风云录》

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注