以太坊的快速出块带来的问题
以太坊将出块速度提高了十几秒,相当于比特币十分钟的四十倍。但是这也带来一些问题,比特币和以太坊都是运行在应用层的共识协议,底层是一个P2P的Overlay Network,它本身的传输时间是比较长的,因为它Flooding的时候没有考虑实际的拓扑结构。所以发布一个新区块传输到其他节点本身可能就需要十几秒的时间,对于比特币600s的出块时间来说这个时间还算挺快的,但是对于以太坊来说这个时间就太长了。
但即使是对比特币来说,因为挖矿是一个概率的过程,所以还是有可能两个矿工同时发布一个区块到网上,这种情况会带来临时性的分叉。因此对十几秒出块的以太坊来说,这种临时性的分叉就会形成常态。这对于共识协议就形成了挑战。
在比特币里,只有最长合法链是被认可的,那些区块的出块奖励才是真正有用的,其他分叉的链的奖励是作废的(那些区块被称为orphan block或者stale block)。对于比特币来说,因为临时性分叉不是很多,所以这样规定还是可以接受的。但是对于以太坊来说,如果也这样规定的话,就会导致矿工挖到的区块有很大概率都是白挖的。这样对矿工不太公平,尤其是对个体矿工而言。
为什么?挖矿的两个趋势:
- 挖矿设备的专业化:很多都不再用个人电脑,而是用ASIC芯片,或者像以太坊用GPU
- 大型矿池(mining pool)的出现,资本的运作导致很多算力集中在大型矿池
为什么大型矿池的出现不利于个体矿工?因为虽然挖矿概率和算力是按比例的,但是因为大型矿池的算力多,如果出现分叉的时候,大型矿池很自然的会沿着自己的分叉继续挖,因为它的算力多所以它挖出下一个区块的概率要比普通矿工大,形成最长合法链的概率也会更大。而对于刚刚出块的普通矿工而言,它自己的算力是很渺小的,只能寄希望于其他算力能选择它这个区块继续往下挖,但是实际上对其他算力而言也没有理由选择这个普通矿工的区块继续往下挖,风险肯定比选择大型矿池的那个区块要大,从历史经验上肯定是大型矿池的区块更容易形成最长合法链,这就是mining centralization (越是大型矿池得到的收益就越大,超出了算力自身占据的比例),也叫centralization bias(中心化带来的不成比例的优势)。
而且大型矿池在网络中的位置往往是比较好的,很可能是在网络里比较中枢的几个位置附近都有,所以它发布的区块往往可以更快被其他节点接收到。
这也是比特币为什么要调整挖矿难度,这就是为了稳定出块时间,出块时间绝不是越短越好的,不然这个bias影响会更明显。
以太坊中使用的GHOST协议:最初版本
GHOST协议不是以太坊专有的,在以太坊之前就有出现,以太坊是对它做了修改。
GHOST协议的核心思想是,如果挖到了矿,但最后作废了,那么会得到一些安慰奖励。以太坊管那些没有成为最长合法链的分叉block(区块链里是叫orphan block或者stale block)称为uncle block(这是因为在分叉出现时,对最长合法链上的分叉节点的下一个节点而言,那些被放弃的节点是它的叔父节点)。
A->B1,B2,B3
B2->C2
在这种情况下B1和B3就是被放弃的区块,是C2的叔父区块,在C2里还可以把它带上,B1、B3这种叔父区块可以得到78\frac{7}{8}87的出块奖励。以太坊最早的出块奖励是5以太币,现在减少到了3以太币,所以叔父区块的出块奖励就是78⋅3\frac{7}{8} \cdot 387⋅3以太币。
那么当前区块为什么要包含叔父区块?对它自己有什么好处?每包含一个叔父区块,那么自己可以得到132\frac{1}{32}321的额外的出块奖励。每个区块最多可以包含两个叔父区块。
这样高的近乎最长合法链的出块奖励也有利于系统中出现分叉之后及时进行合并。在上面的例子里像当于C2这个区块把B1和B3招安过来了。
因为挖矿的过程是progress free的,所以只要没挖出来,改改header重新再挖是不吃亏的。所以即使C2刚开始只发现B1这一个叔父区块,挖了一会才发现B3,那么再改一下header把B3也加进来也完全不亏。
以太坊中使用的GHOST协议:改进版本
最初版本的GHOST协议有这样几个问题:
- 只能包含两个叔父区块,如果有三个以上就招安不了那么分叉了
- 如果发布区块C2之后才发现B1和B3这样的叔父区块,那么就来不及包含它们了,它们就什么好处都得不到了
- 从商业竞争的角度,C2可以故意选择不包含叔父区块,比如矿池之间的商业竞争,C2矿池只损失132\frac{1}{32}321出块奖励,而B1会损失78\frac{7}{8}87出块奖励
能不能完全没有限制叔父区块的数量?如果完全不限制叔父区块数量,那么大家都靠叔父区块赚78\frac{7}{8}87的出块奖励,以太币就太不值钱了。
以太坊的解决办法是即使C2故意不包含B1,那么沿着C2的后面的节点也被允许包含B1(即便论资排辈不是父亲辈份的区块了),所以总有人愿意包含它。而且即使有很多的uncle block在一个地方分叉,那么也不会受到"只能包含两个叔父区块"的限制,因为后面总有别的区块可以包含它们,而且也可以是B1自己这个矿工或者矿池在最长合法链上挖出新区块的时候把B1包含进来。
这样还会有新的问题:矿工可以在很久之前挖矿难度还比较低的位置上不停产生叔父区块,然后等着被后面的区块包含,这样就能赚到不少的以太币?
所以以太坊要求叔父区块的定义,必须和当前区块在七代以内有共同的祖先 (也即合法的叔父只有六个备份),并且会随着叔父的代际递减给叔父区块被包含的出块奖励,最多78\frac{7}{8}87,最少28\frac{2}{8}82:

如果不限制叔父的备份,那么对全节点来说要包含的状态就太多了。你发布的区块包含叔父,其他节点也需要进行验证。这种uncle reward随着代际递减的机制,也鼓励了出现分叉尽早合并。
而当前节点那包含叔父区块额外得到的132\frac{1}{32}321的出块奖励是不随着叔父的代际变化的。
总结
GHOST协议解决的是state fork的问题,即对当前的区块链的状态出现的临时性的意见分歧,推动尽早地达成最长合法链的一致,也是保障区块链防篡改机制的重要组成。
而那些对于区块链运行的协议出现意见导致的分叉,GHOST协议是无能为力的。
比如之前学习比特币里有一个CHECKMULTISIG的动作,检查多重签名。这个操作有一个bug就是会多弹出一个元素,所以每次要先压入一个元素。为什么不把这个bug改掉?因为一旦改掉就不一致了,就会出现硬分叉。这和中心化的系统不一样,不是直接release版本然后中心化服务器强制要求版本升级就可以实现的。
叔父区块没有动态奖励(gas fee)
对比特币来说,出块的静态奖励就是block reward,动态奖励是tx fee。
对以太坊来说,出块的静态奖励就是block reward(3个以太币),动态奖励是gas fee(执行智能合约要交汽油费)。
但是对uncle block来说,只能得到静态奖励(即2..78\frac{2..7}{8}82..7乘以一般的block reward),没有gas fee这样的动态奖励。不过gas fee实际占据的奖励比例是很小的,所以可以忽略不计。
这一点和比特币是类似的,比特币里tx fee也就占block reward的1%差不多。但是考虑到比特币里每隔一段时间出块奖励就会减半,那么到最后肯定是tx fee占据大头,以太坊会不会也有这样的问题,导致GHOST协议在未来失效?
实际上不会,因为比特币里的block reward减半是为了维护比特币总量,人为制造稀缺性。以太坊里没有这样的规定。而以太坊将5个以太币的出块奖励下调到3个是临时性事件,因为发生了区块回调,所以为了维护挖矿难度才这么做的。
包含叔父区块时,是否应该执行叔父区块里的交易?
不应该执行,因为叔父区块里的交易可能和主链上的交易冲突。比如叔父区块里的交易本来是合法的,但是执行完主链父区块的交易再去执行叔父区块的交易,它说不定就非法了。
以太坊里,不仅不执行,而且根本不检查叔父区块里交易的合法性。只要它是符合挖矿难度要求的(只查header就行),那么就认为它是一个合法的叔父区块。
只有分叉后的第一个区块能作为叔父区块得到uncle reward
如果不这样做,如果是分叉后的整个链都可以被招安得到奖励,那么forking attack的风险就变低了很多。因为分叉攻击是说比如A->B转账,然后过了好几个区块B认为转账已经确认了,可以把货交给A了。这时候A发动forking attack,在A->B转账的那个区块的上一个区块开始分叉,搞一个A->A'的转账,然后不停地往下挖。
如果后面的区块不能作为uncle block,那么分叉攻击的成本很高,一旦不是最长合法链,所有后面挖出来的区块都作废了。但如果能作为uncle block得到uncle reward,那么完全可以尝试攻击一下,攻击成功就把A->B回滚了,如果不成功那么也能得到uncle reward。
所以这种机制也是鼓励出现分叉之后及时进行合并,再往后挖的区块是没有奖励的。
实际例子
BlockHeight就是区块的序号,而UncleNumber就是和叔父区块对应的最长合法链上对应区块的那个序号。所以UncleNumber减去BlockHeight就是两者相差的代际,当代叔父是#2的例子(距离为1),再上一代就是#1的例子(距离为2)。

下图是实际挖出的两个block,左边的block包含一个距离为2的叔父,所以那个叔父区块的block reward是2.25(6/8 * 3)。倒数第三行的block reward从左到右三个部分就是固定出块奖励、gas fee和引入叔父区块带来的奖励。
右边的block引入了一个距离为2的区块和一个距离为1的区块,所以那两个区块的uncle reward加起来是2.25+2.625,而该区块自己引入叔父区块带来的奖励也是翻倍的。
