在北京时间4月15日下午18:00左右(具体是以太坊网络区块高度达到#12244000时),以太坊的柏林(Berlin)硬分叉晋级将会产生,这次晋级将归入4个新的EIP改善提案,而其中两个(EIP-2929和EIP-2930)将会影响买卖的gas本钱计算。
本文解说了在这次硬分叉晋级前后的gas本钱计算,这将怎么随EIP-2929而产生改变,以及怎么运用EIP-2930引进的拜访列表功能,原文作者是Nomic Labs软件开发者Franco Victorio。
注:文章篇幅较长,以下是其中的一些要点:
柏林硬分叉改变了一些opcode操作码的gas本钱。假如你在dapp或智能合约中有一个硬编码的gas值,它们或许会停止工作。假如产生这种状况,并且智能合约是不行晋级的,则用户将需要运用拜访列表(EIP-2930)来启用它。
拜访列表可用于稍稍下降gas本钱,但在某些状况下,它们实际上会添加gas耗费总量。
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。
假如slot的的值从0更改为1(或任何非零值),则本钱为20000;
假如slot的的值从1更改为2(或任何其他非零值),则本钱为5000;
假如slot的的值从1(或任何非零值)更改为0,则本钱也为5000,但在买卖结束时你将取得gas退款。这篇文章中,咱们不会具体评论退款,因为它们不受柏林硬分叉的影响;
假如曾经在同一业务中修改了该值,则一切后续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示例:
假如slot的值从0更改为1(或任何非零值),则本钱为:22100(假如未拜访存储密钥),20000(假如已拜访存储密钥);
假如slot的值从1更改为2(或任何其他非零值),则本钱为:5000(假如未拜访存储密钥),2900(假如已拜访存储密钥);
假如slot的值从1(或任何非零值)更改为0,则本钱与上一项相同,然后加上退款;
假如曾经在同一买卖中修改了该值,则一切后续SSTORE的本钱为100;
如你所见,假如要修改的slot曾经被拜访过,那么第一次SSTORE的本钱将下降2100 gas。
下面的表总结了目前为止一切改变的值:
请留意,在最终一行中,议论是否拜访了slot是没有意义的,因为假如它曾经被写入过,则标明其也被拜访过。
3
EIP-2930
咱们在文章开头提到的另一个EIP便是EIP-2930,这个改善提案添加了一种新类型的业务,该业务能够在业务负载中包括拜访列表。这意味着你能够在业务开端履行之前预先声明哪些地址和slot应被视为是已拜访的。例如,一个未拜访slot的SLOAD本钱为2100,可是假如该slot包括在业务的拜访列表中,则相同的操作码本钱就为100。
可是,假如当地址或存储密钥已被拜访时,gas本钱改变低了,这是否意味着咱们能够将一切内容添加到业务的拜访列表中并下降gas本钱呢?不完全是这样,因为你还需要为添加的每个地址和每个存储密钥付出gas。
让咱们看一个例子,假定咱们正在向合约A发送一笔买卖,拜访列表或许如下所示:
假如咱们用这个拜访列表发送了一笔买卖,并且第一个运用0x0 slot的操作码是SLOAD,则它将花费100 gas(而不是2100 gas),这就下降了2000 gas的耗费量。但业务拜访列表中包括的每个存储密钥的本钱为1900 gas,所以咱们只省了100 gas。(假如拜访该slot的第一个操作码是SSTORE,那么咱们将节约2100 gas,这意味着假如考虑到存储密钥的本钱,咱们总共将节约200 gas。)
这是否意味着咱们在运用带有拜访列表的买卖时总是能节约gas耗费?并非如此,因为咱们还要为拜访列表中的地址付出gas本钱(在咱们的示例中是"<address of A>")
已拜访地址
以上,咱们只评论了SLOAD和SSTORE操作码,但这些并不是柏林硬分叉之后仅有改变的操作码。例如,原先调用操作码的固定本钱为700 gas。可是在实施EIP-2929之后,假如地址不在拜访列表中,则开销便是2600 gas,但假如是在已拜访列表中,则开销便是100 gas。并且,与已拜访存储密钥相同,之前拜访该地址的操作码并不重要(例如,假如先调用EXTCODESIZE,则该操作码将花费2600 gas,运用相同地址的任何后续EXTCODESIZE、CALL、STATICCALL将花费100 gas)。
这是怎么受到拜访列表买卖的影响的?例如,假如咱们将一笔买卖发送至合约A,而该合约调用另一个合约B,那么咱们能够包括如下拜访列表:
咱们有必要付出2400 gas的费用才干将这个拜访列表包括在买卖中,可是第一个运用B地址的操作码将花费100 gas(而不是2600gas)。所以咱们这样做就节约了100 gas,假如B以某种办法运用它的存储,并且咱们知道它将运用哪些密钥,那么咱们还能够将它们包括在拜访列表中,并为每个密钥节约100/200的gas(取决于第一个操作码是SLOAD还是SSTORE)。
但咱们为什么要谈另一个合约呢?咱们调用的合约怎么了?咱们为什么不这样做?
咱们能够这样做,但这是不值得的,因为EIP-2929指定了被调用的合约地址(即tx.to)总是包括在accessed_addresses列表中,因而这只会白白浪费2400 gas。
让咱们再次剖析上一节的示例:
这实际上是浪费,除非咱们包括多个存储密钥。假如咱们假定一个SLOAD总是首先运用一个存储密钥,那么咱们至少需要24个存储密钥才干完成收支平衡。
明显,剖析并创立这样的一个拜访列表是没有意义的。走运的是,咱们有更好的办法。
4
eth_createAccessList RPC办法
Geth(从1.10.2版别开端)包括了一个新的eth\u createAccessList RPC办法,其能够用来生成拜访列表。它的用法类似于eth_estimateGas,但它不是用于估算gas,而是回来如下内容:
也便是说,它为你供给了该买卖将运用的地址和存储密钥的列表,以及假如包括拜访列表,则会耗费的gas。(并且,与eth_estimateGas相同,这是一种估计值,在实际进行买卖时,列表或许会更改。)
我想,随着时间的推移,咱们会发现履行此操作的正确办法是什么,而我的伪代码猜测是:
5
激活合约
有必要要指出的是,拜访列表的主要意图不是运用gas,正如EIP所解说的:
“EIP-2929所引进的是减轻合约损坏危险,因为买卖可预先指定和付出买卖方案拜访的帐户和存储slot。因而,在实际履行中,SLOAD和EXT*操作码只需要100 gas,这现已足够低了,它不仅可防止因该EIP而导致的损坏,还能够“激活”因为EIP 1884而卡住的任何合约。”
这意味着,假如一个合约对履行某些操作的本钱做出假定,那么gas本钱的添加或许会导致它无法工作。例如,一个合约调用另一个合约(例如someOtherContract.someFunctionhttps://bicoin8.com/wp-content/uploads/2023/04/202304211cHpE0.jpggas: 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运用量变化的更复杂示例;
视野开拓
阶级斗争的状态是确定劳动力价值的因素之一。劳动力可以通过阶级斗争来提高工资和改善生活条件。相反,有组织的资产阶级反击可能会降低劳动力的价值。-《马克思与《资本论》》