数据库工程里有个经典难题叫缓存穿透:请求的东西根本不存在,缓存放不下”不存在”这种答案,于是每次请求都直穿到磁盘。以太坊的状态树对这个问题的暴露程度比一般数据库更高,EIP-2583在2020年2月就是冲着它来的:给查询”树里不存在的账户”这条路径加收一笔Gas罚金,建议值是2000 Gas。提案停留在Stagnant,但它描述的攻击模型至今仍是节点研究者解释冷状态定价时会引用的案例。
不存在的地址为什么贵
节点查一个账户,要沿状态树逐层往下走。真实存在的账户被反复访问后会进入缓存,后续查找便宜;而不存在的地址每次都是全新一轮树下行,而且攻击者可以永远换新地址——不存在地址的组合近乎无限,把”不存在”缓存起来反而会被撑爆。提案点出要害:缓存命中时代价很低,缓存失效时代价极高,这个不对称就是攻击面。对合约侧的 BALANCE、EXTCODEHASH、EXTCODESIZE、EXTCODECOPY 和各种 CALL 变体来说,目标地址不存在时节点付出的磁盘操作明显多于目标存在时。
罚金怎么落地
方案本身不复杂:规范定义一个罚金常数(当时建议2000 Gas,定稿值标为待定),凡是合约指令以”树中不存在的账户”为目标做查找,就从可用Gas里额外扣罚金。表格细分了各种情形:带转账价值的 CALL 无论目标存在与否都不加罚金,因为以太坊给陌生地址打钱是合法操作;零价值调用到不存在地址才罚。SELFDESTRUCT 的目标地址也单列讨论。理由部分给过一笔账:一笔一千万Gas的交易在旧规则下最多能触发约一万四千次树查找,罚金设为2000可以把这个数字压到约三千七百次,也就是原来的百分之二十六——攻击成本与收益的天平就此掰回来。
后来发生了什么
账户访问的冷热度定价这条线,社区最终选择了更系统的路线:柏林升级的EIP-2929给 SLOAD、BALANCE、EXTCODEHASH 等统一引入冷热两档价格(冷访问两千六百Gas),把2583想单点修补的不对称顺手覆盖了大半。专用罚金机制因此失去必要性,2583安静地停在停滞名单里,没有正式关闭,也没有后继。读懂它仍然有用:它解释了为什么今天”查一个从没见过的地址”就是比”查热地址”贵——思想的血统在这里。
快速问答
问:普通转账给新地址会被罚金波及吗? 答:不会。协议层面的价值转移不属于合约指令查找罚没范围,2583也明确普通以太转账不受影响。 问:状态树查找慢会不会拖慢整个网络? 答:它影响的是单节点在验证交易时的磁盘压力;罚金机制的目的正是让一笔交易无法廉价地制造海量磁盘查找。 问:现在防这类攻击靠什么? 答:靠冷热定价、客户端缓存与并发优化,以及节点对 Gas 上限共同设定的最坏执行预算。
一次查找的完整旅程
顺着一次指令执行走一遍就能明白贵在哪。合约调 EXTCODEHASH 查一个地址:客户端先算出这个地址在树中的哈希键,从根节点开始逐层读盘、比对路径。目标存在时,路径上的节点被反复访问后大概率躺在缓存里,整趟旅程可能只碰一两次磁盘。目标不存在时更糟也更快——不是快,是”看起来”快:树在某一层发现该分支是空槽,查找立刻得出”不存在”,但攻击者换一个新地址再来,整棵树的冷路径读盘就要重来一遍,而且这样的地址永远用不完。两种结局的磁盘操作次数差距,就是提案要收进罚金的差价。顺便说一句,这也是为什么”随便找个乱敲的字符串当地址测一下”在链上不是零成本实验——全网节点都为这次不存在付出了查询。
风险提示:本文涉及攻击原理仅用于防御性理解,不构成任何攻击方法指导,也不构成投资建议。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。