state fork

区块链由一条链变为两条链就叫分叉。分叉可能是多种原因造成的,比如挖矿的时候,两个节点差不多同一个时候挖到了矿,就会出现一个临时性的分叉,我们把这个分叉叫作state fork,是由于对比特币区块链当前的状态有意见分歧而导致的分叉。

前面还讲过分叉攻击(forking attack),它也属于state fork,也是属于对比特币这个区块链当前的状态产生的意见分歧,只不过这个意见分歧是故意造成的,人为造成的,所以我们又叫它deliberate fork。

protocol fork

还有一种产生分叉的情况是,比特币的协议发生了改变,比特币系统需要软件升级。

但在一个去中心化的系统里,升级软件的时候没有办法保证所有的节点同时都升级软件。 假设大部分节点升级了软件,少数节点因为种种原因可能没有升级,有可能是还没来得及升级,也可能是不同意对这个协议的修改。这个时候也会出现分叉,这种分叉叫protocol fork(协议分叉)。

根据对协议修改的内容不同,我们又可以进一步分成硬分叉和软分叉。

硬分叉

出现硬分叉的情况:如果对比特币协议增加一些新的特性,扩展一些新的功能,这些时候那些没有升级软件的这些旧的节点,它是不认可这些新特性的,认为这些特性是非法的。

硬分叉的一个例子就是比特币中的区块大小限制(block size limit)。比特币系统规定每个区块最多是1M字节,有些人认为1M的限制太小了,也增加了交易的延迟。可以计算一下:1M=1百万字节;一个交易大概认为是250个字节;1百万/250=4000。 所以一个区块大概是4000个交易。平均10分钟出现一个区块:4000/(60×10)=7。大概每秒钟产生7笔交易,这个处理交易的速度是非常低的。

如果有人发布一个软件更新,把block size limit从1M增加到4M。假设大多数节点更新这个软件,把block size limit更新到4M,少数节点没有更新。这里的大多数节点和少数节点不是按照账户数目来算的,而是按照算力。此时新节点认为区块大小限制是4M,旧节点认为是1M。

这时系统运行会发生什么?假如一个新节点挖出一个区块,这个区块比较大,但旧节点不认可,它忽略大区块的存在会继续沿着它的前一个小区块接着挖。而旧节点如果挖出了区块,新节点是认可的,因为4M的限制指不能超过4M,比4M小是可以的。

那为什么会产生分叉呢?大区块挖出之后,因为大多数区块是更新了的,是认可新的大区块的,所以会沿着它继续挖。只有少数旧节点会沿着只有小区块的链往下挖。这时新节点认为上下两条链都是合法的,但上面那条是最长合法链,所以会沿着上面一条挖。而且算力足够大会使上面那条链越来越长。而旧节点认为上面的链无论多长都是非法的,它们只会沿着下面的链挖。当然上面的链也可能出现小区块,因为新节点也可能挖出大小不到1M的区块,虽然这种是新旧节点都认可的,但这是没有用的,因为这条链上它们认为有非法的区块。所以这种分叉是永久性的,只要旧节点不更新软件,分叉就不会消失,所以才叫它硬分叉。

那么旧节点挖出的小的区块还有没有出块奖励呢?出现hard fork后出现了两条平行运行的链,平行运行链彼此之间有各自的加密货币。下面链的出块奖励在下面链里是认的。而分叉之前的币按道理应该是上下两条链都认可,所以会拆成两部分。

软分叉

软分叉出现的情况是什么?如果对比特币协议加一些限制,加入限制之后原来合法的交易或区块在新的协议中有可能变的不是合法了;但新的区块在原来的协议中还是合法的。这就引起软分叉。

一个去中心化的系统,改变一个参数,就可能导致分叉,而且取决于这个参数是怎么改的。有可能是硬分叉,有可能是软分叉。假设系统中大部分节点接受了更新,如果旧节点认为新区块是合法的,会形成软分叉;如果旧节点认为新节点是非法的,会形成硬分叉。

假设大部分节点把区块大小由1M改为0.5M。旧节点依然以1M为准,这时候会出现什么情况?假如一个区块链开始分叉,新节点挖出小区块,这种区块旧节点也是认的。而旧节点挖出的大区块新节点是不认的,旧节点们挖的区块可能就白挖了。这样下去,旧节点看到上面链更长,而且是合法的之后,就会转去挖上面链。旧节点转向上面链挖的话,问题可能又会出现:它们可能又挖出了大区块。而新节点不认这个,新节点会继续沿着大区块前面一个小区块挖,如下图所示。

实际中可能出现软分叉的情况:给某些目前协议中没有规定的域增加一些新的含义,赋予它们一些新的规则,典型的例子就是coinbase域。前面讲过每一个发布的区块里可以有一个铸币交易(coinbase transaction),coinbase transaction里有一个域叫coinbase域,这个域的用途是没有规定的。

前面讲过coinbase域的一个用途:把coinbase前八个字节用来做extra nonce。但coinbase域不止是八个字节,后面还有很多,剩下的字节有人就提议做UTXO集合的根哈希值。

目前UTXO集合只是每个全节点自己在内存中维护的,主要是为了快速查找、判断某个交易是不是属于double spending,但这个集合的内容并没有写到区块链里。如果想要知道某个账户上有多少钱,这个信息并不在比特币系统中。如果是全节点,可以根据UTXO集合算一下。但如果是轻节点,它只能询问全节点。全节点返回一个结果,怎么知道这个结果是否属实呢?答案是无法验证。

在这个背景下,有人提议把UTXO集合当中的内容组织成一棵Merkle tree,将这个Merkle tree的根哈希值写在coinbase域里面。coinbase域当中的内容最终往上传递的时候会传递到block header里的根哈希值里。这样,轻节点可以像Merkle proof那样验证全节点发送的账户余额。

假设有人发布一个软件更新,规定coinbase域要按照以上的要求来填写,大多数节点都升级了软件,少数节点没有更新,这就属于软分叉。因为对于新节点发布的区块,旧节点认为是合法的;但旧节点发布的区块新节点是不认的。

总结:soft fork是什么?只要系统中拥有半数以上算力的节点更新了软件,那么系统就不会出现永久性的分叉,只可能有一些临时性的分叉。hard fork特点是什么?必须是所有的节点都要更新软件,系统才不会出现永久性的分叉,如果有小部分节点不愿意更新,那么系统就会分成两条链。