以太坊状态清理
作者:李康
在 2016 年 9 月份,以太坊网络遭遇了 DDOS 攻击,由于以太坊协议的漏洞(可以使用 SUICIDE 操作码廉价地生成新账户),使得攻击者可以在很少的花费下创建大量的空账户 - empty account(zero balance、nonce、code),空账户功能性上与未存在账户 - non-existence account 相似,但是不同点在于空账户会存在于以太坊的 account state trie 中,所以这一次 DDOS 攻击导致以太坊节点存储空间爆炸(大概增加了 1900 万空账户,但是账户与 account state trie 中的节点不是 1:1 的关系,因为有中间节点,所以真正增加的存储空间大于 1900 万账户所需空间)。为了解决该问题,以太坊进行了硬分叉 spurious dragon,旨在清除空账户,减小以太坊节点所需存储空间,修补协议漏洞(SUICIDE 创建新账户时需要额外的 25000 gas),并且引进了新的协议规则:1). empty account 不再允许被创建(原先可以导致空账户产生的操作将会使账户变为 non-existence)。 2). 可以通过交易与空账户交互(touch)来删除它们。
一个在硬分叉 spurious dragon 之前可以产生大量空账户的合约例子:
pragma solidity ^0.4.15;
contract C {
function del(address s) {
selfdestruct(s);
}
}
contract A {
uint public a;
C cc;
event Log(address);
function() payable{
}
function set(address s) {
cc = C(s);
a = 5;
}
function haha() {
a = 3;
Log(msg.sender);
}
function des() {
for (uint i = 0; i < 100; ++i) {
cc.del(address(i + 6));
}
a = 100;
}
}
合约 A 调用 C 的 del 函数时,会调用 selfdestruct 函数,根据黄皮书对 SUICIDE 操作码的解释 "Halt execution and register account for later deletion",意味着 C 合约的执行会立即终止,但是在该交易执行完毕后,才会删除该账户,也就意味着可以多次调用 del 即 selfdestruct 函数,导致产生大量的空账户。
参考文献: