下面来看一下比特币中的共识协议。比特币中共识要解决的一个问题是,有些节点可能是有恶意的。我们假设系统中大多数节点是好的,有恶意的是小部分,那么这种情况下,怎么去设计一个共识协议呢。一种想法是,既然系统中大多数节点是好的,那么直接投票行不行?比如,某一个节点,它提出一个候选区块,它根据收到的交易信息,选一下哪些交易是合法的,然后把这些交易按照某个顺序打包到一个区块里,然后它把这个候选区块发布给所有的节点。其它节点收到这个区块之后,检查一下这里面的交易是否都是合法的,如果都是合法的,就投赞成票,如果有一个交易是非法的,就投反对票,最后算一下得票,如果超过半数是赞成的,那这个区块就被正式接受,写到区块链里,这是一个投票的方案。可不可行呢?如果不可行的话,就是说大家都要参与这个计算,那如果恶意节点一直持续的攻击,持续的发出错误的,结果呢,大家就不停的投票,让时间都浪费在投票上,区块链就没法往下发展了。还有一个效率上的问题,因为要投票,但网络延迟的情况,事先也不能确定,每轮投票等多久,会有一个效率问题。
这些其实属于细节一些的问题,有一个更大的问题是,基于投票的方案,首先要确定谁有投票权,要有个membership的问题。如果这个区块链的membership是有严格定义的,比如,某个区块链不是谁都可以加入的,只有某些符合条件的才能加入,这种情况下基于投票的方案是可行的。但比特币系统不是这样的,比特币系统中创建一个账户是很容易的,在本地产生一个公私钥对就是一个账户,不需要任何人批准,其实别人都不知道你产生了一个账户。你本地产生一个公私钥对之后,别人怎么知道呢?等你转账的时候别人才会知道。假如你只是产生一个公私钥对,你什么事都不干,别人是不知道的,只有你跟外部发生交易的时候,别人才知道有你这个账户的存在。
有恶意的节点可以通过一台超级计算机产生大量的账户,产生的账户超过总数的一半,就可以获得控制权从而操纵投票结果,这种叫作女巫攻击。
投票不行,那怎么办呢?比特币系统当中,用了一个很巧妙的机制来解决这个问题。也是投票,但不是按照账户的数目投票,而是用计算力来投票。每个节点都可以在本地组装出一个候选区块,把他认为合法的交易放到这个区块里,然后尝试各种nonce值。
BlockHeader里面有一个域是个随机数nonce,组装好区块之后就开始尝试各种各样的随机数,这个随机数是4个bytes的,看哪个随机数能够满足H(block header) <= target这个不等式的要求,求得的哈希落在指定范围之内。
如果某个节点找到了符合要求的nonce,我们就认可他获得了记账权,所谓的记账权就是往比特币这个去中心化的账本里写入下一个区块的权利。只有找到这个nonce获得记账权的节点,才有权利发布下一个区块。那么其他节点收到这个区块之后,要验证一下这个区块的合法性。先验证一下这个block header的内容填得对不对,像block header里有一个域——nBits域,它实际上是target这个目标域值的一个编码,检查一下这个nBits域设置得是不是符合比特币协议中规定的难度要求。然后查一下这个nonce,选出了nonce是不是整个block header的哈希小于等于这个目标域值。换句话说,你想要发布一个区块,你是不是真的有权利发布这个区块,你是不是真的获得了记账权?就把block header中的那几项都检查一遍,如果都符合要求,然后看一下block body里面的交易列表,验证一下每个交易是否都是合法的。第一要有合法的签名,第二以前没有被花过。如果有任何一个不符合要求,那么这个区块是不能够被接收的,要被放弃掉。
假设有一个区块,经过检查都是符合要求的,那是不是就可以接受它?
有没有可能一个区块已经检查过了,block header符合要求,所有的交易列表也符合要求,但是我们可能仍然不愿意去接受的有几种可能。有什么可能?区块是插在区块链中间的某一个位置,它不是在最后面,而是插在中间。收到一个区块之后,你是怎么知道它插在哪里?是通过hash of previous block header,就是根据前一个区块的指针就知道它是插在哪个位置。
插在中间的位置会有什么问题? 验证是不是double spending的时候,是从当前区块到币的来源之间,中间这些区块有没有把这个币已经花过。如果中间的区块没有花过,那么交易就是合法的。而验证某个分叉上的交易是不是合法,不会去查另外分叉上的交易。这就会面临一种情况,即使某个区块中的交易内容都是合法的,但这个区块它不在最长合法链上(longest valid chain)。
这个例子,说明比特币协议中规定接受的区块应该是在扩展最长合法链。一个区块应该接在最长合法链的后面,才能是合法的区块。上面讲的例子其实是一个分叉攻击的例子,叫做forking attack。通过往区块链中间位置插入一个区块来回滚某个已经发生了的交易。
区块链在正常情况下也可能出现分叉。如果有两个节点同时获得记账权,它们怎么获得记账权的呢?每个节点在本地自己组装一个它认为合适的区块,然后去试各种各样的nonce。如果两个节点在差不多同一个时间找到了符合要求的nonce,那么它们都可以把区块发布出去。这会出现两个等长的分叉,如果按照最长合法链原则,这两个都是最长合法的,都是合法的。那该接受哪一个呢?比特币协议当中,缺省情况下,每个节点是接受它最早收到的。所以,不同节点根据在网络中的位置不同,有些节点可能先听到这个区块会接受它,也有些节点可能先听到另一个区块,会接受另一个区块。
比特币协议中就是如果沿着你这个区块往下继续扩展,就算是认可你发布的这个区块。比如,他接收了你的区块之后往下又扩展了一个新的区块,就表明他是认可了你的这个区块,如果他不扩展你这个区块,就是没有认可你这个区块。所以,如果比特币系统中出现两个节点差不多同时发布区块的情况,那么这种等长的临时性的分叉会维持一段时间,直到其中某一个分叉最后胜出。所以出现临时分叉的时候,其实是一个算力的竞争,当然还有一些运气的竞争,看到底哪一个分叉的区块增长得比较快,就有一个会成为最长合法链,变成大家都接受的。
为什么大家要竞争这个记账权呢?首先,获得记账权的节点本身有一定的权利,它可以决定哪些交易被写到下一个区块里。但是设计协议的时候,不应该让这个成为争夺记账权的主要动力。如果这个成为主要动力就有问题了,因为我们是希望凡是合法的交易都应该能够被写入到区块链里。那怎么办呢?比特币中设计了一个很巧妙的机制来解决这个问题。这个叫做出块奖励——block reward,就是比特币协议中规定,获得记账权的那个节点在发布的区块里可以有一个特殊的交易,就是前面讲过的铸币交易。在这个交易里,可以发布一定数量的比特币。
前面讲过,一个去中心化的数字货币要解决两个问题,第一个问题是谁有权发行货币,第二个问题是怎么验证交易的合法性?这节的内容到现在为止都是在讲第二个问题,怎么验证交易的合法性?现在我们回过头讲第一个问题,谁来决定发行货币。
这个coinbase transaction——是比特币系统中发行新的比特币的唯一方法,其它所有的交易都只不过是把已有的比特币从一个账户转移到另外一个账户,包括我们有的时候用法币去购买比特币,这个铸币——coinbase transaction是唯一的产生新的比特币的途径。
这个铸币交易不用指明币的来源,因为这个币是凭空造出来的。那能造多少币呢?一开始的时候,比特币刚上线的时候,每一个发布的区块可以产生50个比特币。这个BTC就是比特币的符号,不同的加密货币都有自己的符号,BTC就是比特币的符号。一开始比特币的出块奖励是50个比特币,但是协议中规定,21万个区块以后这个出块奖励减半,就变成了25个。比特币一开始的21万个区块,每个区块发布的时候里面可以发行50个比特币,之后每个区块里就只能发行25个比特币。再过21万个区块,又要减半,就变成了12.5个的比特币。现在每个区块里只能发布只能产生多少个比特币?看上去减少得很快,而且现在的竞争比以前激烈得多。
如果出现了分叉,其中一个分叉胜出了,剩下那个分叉的区块里的coinbase transaction所得到的出块奖励是不是就没有用了?基本是的。就是如果有一个很长的区块变成了最长合法链,分叉里记得有多少比特币,但是验证交易的时候是验证某个区块所在那个分叉是否正确,比特币协议中要求是接受最长合法链,所以如果交易是接在一个很短的分叉上面,在这个分叉上得到比特币是没有作用的,因为大多数诚实的节点是不接受的。
比特币这个共识机制要取得的共识具体是什么?前面的例子,就是讲的分布式哈希表,有一个系统有很多台服务器,共同维护一个分布式哈希表。要取得的共识是什么?是这个哈希表中的内容,包含哪些key – value paire,那个是要取得的共识。
比特币系统中要取得的共识,是这个去中心化账本里的内容,这是大家要取得的共识。那么谁能决定这个账本里的内容呢?只有获得记账权的节点才能够往里面写东西。那怎么获得记账权呢?就是解H(block header) <= target这个puzzle。为什么说比特币的共识机制是靠算力来投票?是因为上一节讲的puzzle friendly这个性质。这个性质保证了求解puzzle的过程没有捷径,只能一个一个nonce去试,所以如果某一个节点它的算力是另一个节点的十倍,那么它获得记账权的概率也是那个节点的十倍。靠算力来投票,这就是比特币中投票的特殊性,它不是一人一票,也不是一台计算机一票,而是看你每秒钟能够试多少个nonce的数目。这个有时候叫作hash rate,这个决定了投票的权重。某个节点的hash rate越高,那么获得记账权得到出块奖励的概率也就越大。
这个方法怎么能够防范前面讲的女巫攻击的问题呢?
因为投票是靠算力投的,创建多少个账户其实是没有影响的,可以在你的服务器上面创建1个账户、100个账户,或者创建1万个账户,但并不会使你的hash rate增加,并不会使你每秒钟能够尝试的nonce数目增加,所以投票的权重并没有改变。