这一节讲比特币系统的具体实现。
区块链是一个去中性化的账本,比特币采用的是基于交易的去中心化账本模式。每个区块里记录的是交易信息,有转账交易,有铸币交易。但是比特币系统中,并没有哪个地方显式地记录每个账户上有多少钱。比如,你想知道a这个账户上有多少钱,就需要通过交易记录来计算区块链中共有多少往a这个地址上转账的交易,转入了多少个币,这些币当中呢有多少已经被花掉了,还有多少没有花掉,这样就可以算出a这个账户上当前的余额是多少。
比特币系统的全节点要维护一个叫做UTXO ( Unspent Transaction Output ) 的数据结构——还没有被花出去的交易的输出。就是区块链上有很多交易,有些交易的输出可能已经被花掉了,有些还没有被花掉。所有那些还没有被花掉的交易的输出组成的集合就是这个UTXO。
注意,一个交易可能有多个输出。比如说a的一个转账交易,可能是给b转了5个比特币,给c转了3个比特币。b拿到这5个比特币之后把它花掉了,所以这个输出就不在UTXO里面了。c收到这3个比特币还没花出去,所以这个输出是在UTXO里的。就是说同一个交易可能有的输出在UTXO里面,有的输出不在这个UTXO里面。
UTXO集合中的每个元素要给出产生这个输出的交易的哈希值,以及它在这个交易里是第几个输出。这两个信息就可以定位到这个UTXO中的输出。那么要这个UTXO集合干嘛用呢,为什么要维护这样一个数据结构?是为了检测double spending。就是新发布的一个交易是不是合法,我们要查一下这个UTXO,你想花掉的币只有在UTXO这个集合里,才是合法的。如果不在这个集合里,说明想花掉的这个币,要么是不存在的,要么是以前已经被花出去过了,所以全节点要在内存中维护UTXO这样一个数据结构,以便快速检测double spending。
每个交易会消耗掉一些未花输出,同时也会产生新的未花输出。上面的例子当中,a把5个比特币转给b之后,b把它花出去了,b把这5个比特币给d了。这个时候,b的这个未花输出就不在UTXO里了,但是又产生一个新的未花输出。这个新的未花输出又保存在UTXO里。就是说随着交易的发布,每个交易要消耗一些UTXO这种未花输出,但是又会产生一些新的未花输出。如果某个人收到比特币的转账交易之后,这个钱始终都没花,那么这个信息就一直保存在UTXO里面。比如传说中的比特币的发明人中本聪,他就是不花的。还有一些人可能是想花,但是没办法花,因为他把密钥给丢了,这些东西就属于要永久的保存在UTXO里。区块链上的统计数据显示,这个UTXO的集合是在逐渐增大,但到目前为止,装在一个普通的服务器的内存里,还是完全没有问题的。每个交易可以有多个输入,也可以有多个输出,所有输入的金额加起来要等于所有输出的金额,这个叫做total inputs = total outputs。上面的例子当中是给出了两个输出的例子。输入也是可以有多个的,而且这多个输入不一定是来自于同一个地址。这也是为什么一个交易可能需要有多个签名,每个输入地址都要提供对应的签名。
有些交易total inputs略微大于 total outputs,比如total inputs是1个比特币,total outputs可能是0.99个比特币,这里面的差额0.01个比特币就作为交易费给获得记账权发布区块的那个节点。前面的内容里讲过,节点要消耗计算资源来竞争这个记账权,获得出块奖励。发布一个区块可以有一个特殊的coinbase transaction获得一定数量的比特币作为报酬,所谓的block reward。但是只有这个出块奖励可能是不够的,发布区块的那个节点凭什么要把你的交易打包到区块里,这样做对他有什么好处?
假如某个比较自私的节点,他发布区块的时候,可能想只包含他自己的那些交易,别的交易都不管。因为把别的交易打包进去对他来说没什么好处,而且还有一定的代价,因为你要验证这个交易的合法性,而且区块里如果装进的交易多了的话,占用的带宽也比较多,在网络上传播的速度也会慢,所以如果只是有出块奖励这个机制的话,就可能会出现有些情况就是这个节点如果比较自私的话,它就不管别人的交易,它只打包自己的交易。所以比特币系统设计了第二个激励机制,就是这个交易费,叫作transaction fee。
这个可以理解成是一种小费。你把我的交易打包进这个区块里,我给你一点小费。目前比特币系统当中交易费的金额都很小,也有一些简单的交易是没有交易费的。目前来说,矿工挖矿去争夺这个记账权,主要的目的还是为了得到出块奖励。因为那个有12.5个比特币,但是前面也讲过,出块奖励是要逐渐减小的,每隔21万个区块要减半。21万个区块大概是多长时间呢?比特币系统设计的平均出块时间是十分钟,就是说整个比特币系统,平均每隔十分钟会产生一个新的区块,计算一下的话,大约是4年的时间。产生21万个区块,平均大概需要4年的时间。换句话说,每隔4年,这个出块奖励会减半。那么很多年以后,这个出块奖励可能变得就很小了,到那个时候,可能交易费就变成主要的了。
除了比特币这种基于交易的模式(transaction – based ledger)之外,与之对应的还有另一种模式,是基于账户的模式(account – based ledger)。像后面要讲的以太坊,用的就是这种基于账户的模式,在这种模式当中,系统是要显式的记录每个账户上有多少个币。这跟我们平时的日常体验就比较接近了,就像你要知道你银行账户的余额,你去登录银行账户就可以查得到。比特币这种基于交易的模式,它的隐私保护性比较好一点,但是也有一些代价。比如前面的内容里多次讲到的,比特币当中的转账交易要说明币的来源。
下面我们看一些区块链上的具体例子,看一下比特币系统中具体的区块信息。
一个区块的例子。这个是从blockchain.info这个网站上截的图。
图中显示这个区块里包含了686个交易;总的输出多少比特币;总的交易费,这是把区块里686个交易的交易费加在一起一共是那么多;最下面一行是出块奖励。可以比较一下两者差了大约100倍,出块奖励还是占大部分,与交易费相比,出块奖励是交易费的大约100倍,这是矿工挖矿的主要动力,是为了得到这个block reward。里面的Height就是区块的序号;Timestamp是这个区块的时间戳,是这个区块的产生时间;Difficulty是挖矿的难度,每隔2016个区块要调整这个难度,以保持出块时间在十分钟左右;Nonce就是挖矿时尝试的随机数,这里的nonce值是最后找到的符合难度要求的。
右边显示的是三个哈希值,第一个是这个区块的块头的哈希值,第二个是前一个区块的块头的哈希值,注意,计算哈希值的时候,都是只算block header,不包含block body中具体的交易列表。
这两个哈希指有什么共同的特点?前面都有一长串的零。一个区块的哈希值和上一个区块的哈希值开头都是有一长串的零。这个不是偶然的,前面讲过,所谓的挖矿,就是不断调整随机数nonce,使得整个block header的哈希值小于等于给定的目标阈值。这个目标阈值表示成16进制就是前面有一长串的零,所以凡是符合难度要求的区块,它的块头的哈希值算出来都是要有一长串的零。
第三个是Merkle Root,就是这个区块中包含的那些交易构成的merkle tree的根哈希值。