以太坊史话之The DAO与ETH硬分叉
阿牛哥 Lv4

历史上,比特币出现过几次硬分叉,新产生了BCH、BSV等山寨比特币。同样,以太坊在2016年也出现过硬分叉。我过去觉得分叉都是某些矿工想获得额外收益搞出来的,但看完了ETH分叉的整个事件后,才发现分叉事件的背后也许另有故事。

这件事要从The DAO说起。

DAO和The DAO

DAO是Decentralised Autonomous Organization的缩写,代表“去中心化的自治组织”,实际上是一个可以投票的基金。

首先,DAO是一个基金,持有ETH的人将手上的币存入这个基金,成为基金的投资者。相应的,投资者获得DAO的代币,投的ETH越多获得的代币也就越多。

获得代币后,投资者就有了投票权,接下来DAO如何投资就由这些投资者投票决定。

The DAO就是这样一个DAO项目,它于2016年中开放投资,一时间受到了极大的关注,很快筹集到了1270万枚ETH。这么多ETH已经占到当时以太坊发行总量的三分之一,可见其规模之大。

当某个投资者想要从The DAO赎回份额时,操作略显复杂。投资者首先要执行The DAO的智能合约,The DDAO会将代币收回,并将等值的ETH转入一个新产生的子基金,接下来ETH要在子基金内锁仓28天,然后投资者才可将子基金里的ETH投向自己账户,从而达到赎回的目的。这是一套略显复杂的赎回逻辑。

黑客攻击

可是没人注意到,The DAO智能合约中存在一个代码漏洞,正式它引发了一系列事件,导致ETH最终分叉。

漏洞代码如下:

1
2
3
4
5
6
7
// Burn DAO Tokens
Transfer(msg.sender, 0, balances[msg.sender]);
withdrawRewardFor(msg.sender); // be nice, and get his rewards
totalSupply -= balances[msg.sender];
balances[msg.sender] = 0;
paidOut[msg.sender] = 0;
return true;

这段代码是用来赎回ETH的,代码的逻辑是:先转账给投资者(合约的调用者),然后把调用者在合约中资金清零,问题就出在balances[msg.sender] = 0;的位置上,开发者没有注意到应该把这条语句放在最前面,应该先清零再转账,留下了一个“重入攻击”漏洞。

只要黑客设法让代码在执行的过程中停在该语句之前,msg.sender中的资金就不会清零,然后多次调用该合约就能重复提款。

关于重入攻击,这篇文章有比较详细的解释。

另外,智能合约自身有一个限制,就是一旦发布就不能再更改,而要保证合约完全不存在瑕疵和漏洞是实分困难的,一旦包含漏洞的合约代码被广泛使用,潜在的损失也是不可估量的。

很快,黑客提走了360万枚ETH,只要黑客愿意,他可以把剩下的ETH都提走。

以太坊社区的反应

攻击发生后,关于官方是否应该出手挽回投资者的损失,在以太坊社区产生了激烈辩论。支持者和反对者都有比较合理的理由:

支持者:

首先,The DAO的投资者蒙受了巨大的损失。

其次,The DAO的智能合约代码存在漏洞,智能合约一旦上线,代码问题就不能更新和补救。而当前质押在The DAO的ETH占到了已发行ETH总量的三分之一。只要黑客愿意,完全可以再次利用该漏洞把所有ETH转走。

也就是说世界上三分之一的ETH损失掉了,这对于以太坊的前途会是一个巨大冲击。可以说,The DAO项目已经“大到不能倒(Too big to fail)”。所以,必须救!

反对者:

反对者主要从数字货币的理念出发,认为“去中心化”和“不可篡改性”是数字货币的本质特点。如果官方可以随意干预区块链,这是和数字货币的理念相悖的。

另外,在区块链世界里,“代码即法律(Code is law)”,黑客利用的是The DAO的代码漏洞,产生问题的根源在于The DAO的代码,而不是以太坊的基础代码,两者并不是同一个层次的问题。因为The DAO代码不完善,就要去改底层的以太坊区块链代码,显然是不合适的。

而且从长远看,救The DAO开了个“坏”头,难道以后大项目一旦出问题都要官方兜底吗?

可见不管是哪一方,对于救还是不救都有充分的理由。以太坊开发团队的立场是救,我也比较赞成,毕竟当下最迫切的问题是让以太坊能平稳地活下去。

第一次补救

黑客盗走的ETH有28天的锁定期,在此之前,黑客是取不走币的。以太坊开发团队在这段时间内开发了升级程序,作用是锁住黑客的钱包,让其无法转出。等以后再想办法将所有ETH从The DAO退给投资者。

可是这次补救引入了一个新bug。因为以太坊开发者仅仅在代码里设置了不允许黑客账户的资金转出,没有要求发起转账的gas fee。结果就是很多黑客发现这一漏洞后,不停地向矿工发送无效地转账请求,因为不需要gas fee,发这种请求是零成本的。使得矿工们遭受到了DoS攻击。

于是很多矿工不得不退回到了旧版本,也就是没有限制黑客转账的版本——所以,这次补救失败了。

第二次补救

第一次补救失败后,对于要不要再次补救产生了激烈的争论。最终以太坊官方决定以投票的方式结束这场争论。

官方设立了两个智能合约,一个表示支持,一个表示反对。投票者将自己的ETH投向其中一个表达支持或反对,投票结束后,ETH会从智能合约退还给投票者。

最终的投票结果是支持补救,这也和官方的态度一致。

距离28天的期限已经所剩无几,这一次只能强行修改区块链了,也就是只能硬分叉了。官方的方案是,当挖到第192万个区块时,自动将The DAO的资金强制转入新的智能合约并最终退给投资者。

事情并没有结束

对于The DAO导致的硬分叉事件,争论并没有就此结束,对于官方发起的投票是否合理也很有争议。这些争议问题没有得到大家都满意的处理结果,所以有些矿工继续在旧版本的链上挖矿,于是以太坊出现了两条链,一条是主流的官方支持的新链,依然叫ETH;一条是旧链,叫Ethereum Classic,简称ETC。

参考资源:

  1. Understanding the DAO Attack
  2. 《区块链技术与应用》公开课-TheDAO