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

以太坊今日将完成柏林硬分叉升级,这些知识点你需要了解-区块链315巴比特区块链作者,团队,专栏,公众号,头条· 18 分钟前 ·阅读约 7 分钟

以太坊今日将完成柏林硬分叉升级,这些知识点你需要了解-区块链315

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

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

以太坊今日将完成柏林硬分叉升级,这些知识点你需要了解-区块链315

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

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

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

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

1

柏林硬分叉前的 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。

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

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

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

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

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

2

实施 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。如果未访问,则成本为 2100 gas,如果已访问,则成本为 100 gas。因此,如果 slot 在已访问的存储密钥列表中,则一次 SLOAD 的成本会降低 2000 gas。

柏林硬分叉之后的 SSTORE

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

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

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

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

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

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

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

以太坊今日将完成柏林硬分叉升级,这些知识点你需要了解-区块链315

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

3

EIP-2930

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

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

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

以太坊今日将完成柏林硬分叉升级,这些知识点你需要了解-区块链315

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

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

已访问地址

以上,我们只讨论了 SLOAD 和 SSTORE 操作码,但这些并不是柏林硬分叉之后唯一改变的操作码。例如,原先调用操作码的固定成本为 700 gas。但是在实施 EIP-2929 之后,如果地址不在访问列表中,则开销就是 2600 gas,但如果是在已访问列表中,则开销就是 100 gas。而且,与已访问存储密钥一样,之前访问该地址的操作码并不重要(例如,如果先调用 EXTCODESIZE,则该操作码将花费 2600 gas,使用相同地址的任何后续 EXTCODESIZE、CALL、STATICCALL 将花费 100 gas)。

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

以太坊今日将完成柏林硬分叉升级,这些知识点你需要了解-区块链315

我们必须支付 2400 gas 的费用才能将这个访问列表包含在交易中,但是第一个使用 B 地址的操作码将花费 100 gas (而不是 2600gas)。所以我们这样做就节省了 100 gas,如果 B 以某种方式使用它的存储,并且我们知道它将使用哪些密钥,那么我们还可以将它们包括在访问列表中,并为每个密钥节省 100/200 的 gas (取决于第一个操作码是 SLOAD 还是 SSTORE)。

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

以太坊今日将完成柏林硬分叉升级,这些知识点你需要了解-区块链315

我们可以这样做,但这是不值得的,因为 EIP-2929 指定了被调用的合约地址(即 tx.to)总是包含在 accessed_addresses 列表中,因此这只会白白浪费 2400 gas。

让我们再次分析上一节的示例:

以太坊今日将完成柏林硬分叉升级,这些知识点你需要了解-区块链315

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

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

4

eth_createAccessList RPC 方法

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

以太坊今日将完成柏林硬分叉升级,这些知识点你需要了解-区块链315

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

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

以太坊今日将完成柏林硬分叉升级,这些知识点你需要了解-区块链315

5

激活合约

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

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

这意味着,如果一个合约对执行某些操作的成本做出假设,那么 gas 成本的增加可能会导致它无法工作。例如,一个合约调用另一个合约(例如 someOtherContract.someFunction{gas: 34500}())因为它假设某个函数正好使用 34500 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 使用量变化的更复杂示例‌;

以太坊今日将完成柏林硬分叉升级,这些知识点你需要了解-区块链315

本公众号所载文章中观点仅代表原作者个人立场,不代表巴比特资讯立场。投资者不应将文中观点、结论为作出投资决策的惟一参考因素,亦不应认为文中观点可以取代自己的判断。在决定投资前,如有需要,投资者务必向专业人士咨询并谨慎决策。

以太坊今日将完成柏林硬分叉升级,这些知识点你需要了解-区块链315

免责声明:作为区块链信息平台,本站所发布文章仅代表作者个人观点,与链闻 ChainNews 立场无关。文章内的信息、意见等均仅供参考,并非作为或被视为实际投资建议。