EIP2929, EIP2930 简介

EIP2929, EIP2930都已经被列入下一次的硬分岔(Berlin Hardfork),所以我就来写个学习笔记。虽然本人对EVM的理解没有很深,但也希望借着这个机会逼自己学习,如果有错误的话也希望懂的更多的各路大神可以不吝赐教。

Berlin without hardfork.

EIP2929: Gas cost increases for state access opcodes

乍看之下这是一个极为恐怖的Proposal。在Gas已经高到爆炸的2021年,理论上不应该再通过这种「加油」类的方案。不过不用紧张,其实这个EIP真正改变的是第一次access的价格,如果一笔交易内要执行一样Opcode动作两次,那么gas cost 将降低为100。

Increases gas cost for SLOAD*CALLBALANCEEXT* and SELFEDESTRUCT when used for the first time in a transaction.

大家都知道,合约最终会被Compile成一堆Opcode,这些Opcode也是用来计算最终交易手续费的依据:理论上越是花时间的的Opcode,应该要收越高的手续费。

但是一直以来,state access opcode 太便宜都是一个已知的问题:在2016年的上海DOS攻击中,其中几个攻击的手法就是透过恶意交易大量读取帐户资讯、大量的创造合约再销毁,或是不断用 EXTCODESIZE 来读合约大小等等,让Client必须花大量的IO资源处理交易(需要读写disk的动作特别慢),最终使Client程式Crash或是延长出块时间。尽管大部分的弱点已经透过EIP150中大量提升gas cost获得改善(还有其后的EIP1884),但在EIP2929中,也引用的这篇Paper的数据:现在replay所有以太坊上的交易,当时那些恶意交易中的worst case还会需要~80秒才能完成。这跟以太坊所定义的13秒出块时间有着很大的差距,也代表这个潜在的攻击是可行的。

透过增加这些opcode所需要的gas cost,可以降低每个区块最大可能的读取数。以下是偷抄Vitalik PPT 的数据:(12,500,000 为gas limit上限)

Pre-EIP 2929:

  • BALANCE spam: 12,500,000 / (400 cost + 320 address size + 50 boilerplate) = 16,233 accesses per block
  • CALL spam: 12,500,000 / (700 + 320 + 50) = 11,682 accesses per block
  • SLOAD spam: 12,500,000 gas / (800 + 25 boilerplate) = 15,151 accesses per block (but of a smaller tree)

Post-EIP 2929:

  • BALANCE spam: 12,500,000 / (2,600 + 320 + 50) = 4,280 accesses per block
  • CALL spam: 12,500,000 / (2,600 + 320 + 50) = 4,280 accesses per block
  • SLOAD spam: 12,500,000 / (2,100 + 25) = 5,882 accesses per block

说实在的这个数据的解释也很废话,就是把Opcode变得用贵,能Spam的数量越少。平均来说Gas cost 变高3倍,所以之前worst case的80秒执行时间可以被下降到大概~27秒。

SSTORE changes

在实作层,EVM会维系一个本笔交易读取过所有交易的Set。每次有尚未读取过的slot时,就会先收取一笔 CLOD_SLOAD_COST (2100),然后把这个slot加入这个set中,下次读写就会比较便宜。

对于已经读取过的Slot,再次写入的Opcode SSTORE 之gas cost为会降低为

5000 — COLD_SLOAD_COST (2100) = 2900

简单的说,单纯只操作一次 SSTORE 的总gas 会维持一样在 5000 。但如果这个slot是之前有读过的,则写入的gas cost就会降低。近一步来说,一个  x += 100,其实会变得更便宜:

Pre-EIP-2929: 800 SLOAD + 5000 SSTORE = 5800
Post-EIP-2929: 2100 SLOAD + 2900 warm SSTORE = 5000

其他Side effects

这个改动除了降低了最高能够spam的次数以外,也降低了以太坊想要做到stateless client,理论上最大的witness 大小。其实这里的原理跟前面很类似,下图的表格比较的是目前使用hexary tree所需要的witness大小:若12.5M的区块全部塞满该Opcode的witness,理论上最大会占多少空间。在EIP2929之后由于gas cost增加,就压缩了最大可能的witness size.

这裡单纯只比较增加gas cost后,对于max witness size的影响。影片中有提到其他许多方法旨在减少Witness bytes,包括使用binary tree而不是hexary tree,以及用Code Merklization等等。这些其他方法也能够降低最后的Max Witness size,但跟这个EIP没有直接相关。不过可以注意的一点是,这些其他在witness size上面的优化跟gas cost 所带来的优化的效果是可以相乘的,例如SLOAD,更改gas price已经能够让max size 缩小2.6倍,若是改用Binary tree可以将 Witness bytes降低到 288 bytes,就会是再3~倍的优化。

对用户的影响

依照Martin Swende 给出的数据,这个EIP对于一般交易的影响仅有提高0.3~0.4%。理由很简单,虽然第一次access storage变贵了,但是后面几次读写就会变得便宜。大部分应用的程式逻辑都是类似的几个变数进行读写,因此可能有不少的动作反而会变得更便宜。一个最简单的例子就是ERC20 Transfer,两个余额的 +=和 -= 都会变便宜,所以总共的花费也是变便宜的。

这其中也会对于Solidity的开发pattern有着一定程度的影响,我目前想到的影响可能有两个:

  1. 由于多次的storage access变便宜,永远cache state variables不再是一个最佳策略。以前我们会尽量想办法减少写入state storage的次数,现在可能会基于coding style考量减少一些的memory cache。
  2. 之前写合约都会尽量避免external call,甚至会写一些一次把所有variable都回传回来的笨函示,来避免多次的external calls。这有一部分原因是因为每次external call都会需要使用到  EXTCODESIZE 这个Opcode所以很贵。但如果 EXT 系列的Opcode也变得越call越便宜,那么这个一次全部call 回来cache 住的pattern也可能改变。

以上两个想法都还没有经过实证,如果之后看到更有证据的分析的话,也会来这里分享。

EIP2930: Optional access lists

EIP2929可能会影响一些链上的合约,因为有些合约有hardcode external call的gas 上限。对于这方面的问题,EIP2930提出一个新的交易类型,让交易中多带一个access list,即所有这笔交易即将读写的storage slot,并且先帮忙付掉第一次读写的gas,而真正交易读写该storage时,只会被要求付100 gas。

这不但可以避免这次EIP2929带来的副作用,也可以被使用在其他因为gas price 改变的硬分岔升级而坏掉的合约,例如在EIP1184 增加 SLOAD gas price 时影响到的 Aragon 和Kyber 等等。儘管当时升级前各大专案都有帮助用户提出migration 方案,但如果有人曾经卡钱在裡面,也可以Follow一下这次柏林Hardfork。

本文链接地址:https://www.wwsww.cn/ytf/10362.html
郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。