我们在上一篇博客中设计了几个最简单的区块链,并且很容易添加更长的区块链来覆盖掉最初的合法交易,为了维持整个系统的健康运行,我们需要设计一定的激励机制。我们都知道,有种东西叫做“挖矿”,在比特币系统中平均每10分钟,比特币系统会奖励一定量的比特币(最初是50个,每四年减少一半)。但是在网络中很多个网络节点一起“挖矿”,我们该怎么分配这个奖励呢?这就是工作量证明(Proof-of-Work)算法产生的原因。
一、Proof-of-Work
一个正常的区块链系统随时都会产生交易,这时候就会有很多服务器将一个时间段(BTC为10分钟,Asch为10秒)的交易打包到一个区块,并添加到现有的区块链中,但是最终的这个时间段该选择哪个服务器打包的区块为准,这是个问题。在BTC中采用了一种叫做工作量证明的算法来决定采用哪一台服务器打包的区块并给与奖励。
工作量算法大致为:在一个时间段内多台服务器同时对交易进行打包后,连带区块的Hsh信息一起经过SHA256算法进行运算,在区块头和奖励交易的coinBase里,各有一个变量nonce,如果运算结果不符合难度要求,那么就调整nonce的值继续运算。如果某台服务器率先计算出符合难度的区块,他就会广播这个区块,其他服务器验证没问题后就可以添加到现有的区块链上,然后所有服务器又继续竞争下一个区块,这个过程就叫做挖矿。
在工作量证明算法中采用了SHA256,他的特点是难以运算但是容易验证,所以找到一个符合难度要求的区块需要耗时10分钟,但是验证它是否有效只是瞬间的事
举一个简单的例子: 假设有一群人玩一个扔硬币游戏,每个人有十枚硬币,依次扔完十枚硬币,最后看十枚中的正面和反面的排序结果。由于是有顺序的,这里的结果有2^10种可能。现在有个规定,在一轮游戏中,谁先扔出了前4枚硬币都是正面的结果,谁就可以得到奖励。于是大家都开始扔十枚硬币并统计结果。前五枚都是正面的可能性有2^6种,因此一个人能获取该结果的期望尝试次数为2^4。 如果规定正面的个数越多,那么每个人的尝试次数就会越多,而这里的个数就是难度。如果玩游戏的人逐渐增多,那我们就可以要求前6个、前8个是正面,这样每轮游戏的时间依然差不多。这也是为什么比特币的算力大增,而依然能保持平均每10分钟产生一个区块的原因。
二、实现方式
任何一个数据经过SHA256运算后都会得到256bit的二进制数,可以通过调整最开始的部分连续0的个数作为难度,若我们要求第一个bit为0,那么平均没两次运算就可以得到一个结果,但是若我们要求连续10个bit都为0,这时候就需要平均2^10次才能得到一次需要的结果了,我们就可以通过调整连续0的个数来达成调整难度的目标。
我们在区块的头信息中添加一个变量nonce,就可以通过不断调节nonce的值来重新计算整个区块的Hash值,直到满足难度要求。
三、代码实现
我们先改造一下前面的代码,先在Block类中添加一个nonce变量
然后在Block类中添加一个mineBlock方法
1 | mineBlock(difficult){ |
这里的difficult指的是从这里以开始连续为0的个数,如果不符合要求,哪个nonce加1,然后重新计算区块的Hash值。
将calculateHash方法进行修改加上nonce
1 | calculateHash() { |
我们接着继续在Blockchain类里定义一个难度
再把挖矿的过程应用到添加区块到区块链的过程中
添加一个区块
运行后可以得到一个结果
1 | Mining block 1 |
这里的nonce表明了计算的次数,Hash是最后得到的结果,我们设置的难度为2.期望计算的次数是2^8次(Hash中一个字符占4个bit),我们再将难度改为4,得到
1 | Mining block 1 |
这里的计算次数就比困难度为2增加了很多,消耗的时间也更多了。