比特币源码分析之五:区块
区块数据结构
区块的数据结构代码在block.cpp中
区块由区块头和交易集合组成,如下图
区块头由以下字段组成
int32_t nVersion; 表示版本号
uint256 hashPrevBlock; 表示前一个区块的hash值
uint256 hashMerkleRoot; 表示交易集合算出来的merkle 树的树根hash
uint32_t nTime; 表示区块的生成时间
uint32_t nBits; 表示当前区块需要的挖矿难度,可以简单理解为定义了挖矿需要hash的前几位是0
uint32_t nNonce; 一个用于挖矿的随机数
Txs是交易的集合
交易的主要结构已经在前面的文章中有介绍这里不再赘述,但是区块中有一个特殊的交易需要单独拿出来介绍一下,这个交易是每一个区块的第一个交易,被叫做coinbasetx
对比之前介绍的标准交易coinbasetx有几个不同
1、coinbasetx必须是在每一个区块的第一个交易
2、coinbasetx没有输入,只有输出,而这个输出一般是矿工的地址,而coinbasetx的输出其实就是给矿工的奖励。比特币体系中所有的比特币都是从这个途径出来了,然后流通在各个账户中。
3、coinbasetx的输入因为没有实际的交易意义,所以被用来存放别的信息,比如区块的witness merkle树的根hash,又比如区块的高度等,其中有一个字段和挖矿有关系,单独列出来,就是输入的脚本中会放一个nExtraNonce字段,这也是一个挖矿时候可以修改的随机数和blockheader中的nNonce字段结合可以达到调整block hash的目的
区块hash的计算方式
区块头中有一个字段hashPrevBlock这个字段表示的就是前一个区块的hash值,那么区块的hash值是怎么计算出来的呢?
其实很简单,区块的hash值就是区块头所有字段的hash256。那么细心的读者一定会注意到,如果区块的hash值只计算了区块头,那么交易集合怎么能保证不被恶意的第三方串改?
这个问题是区块头中有一个hashMerkleRoot,这个字段就是通过交易集合算出来的一个hash值,从网上盗图一张,放在这里供读者理解。
其中L1 L2 L3 L4就是一个一个的交易。
区块生成方式-挖矿
挖矿的主要代码在mining.cpp和miner.cpp中
先贴出来主逻辑
UniValue generateBlocks(…) 函数名称
{
While(…)主循环
{
nHeight = chainActive.Height(); 获取当前链的高度
生成一个区块数据结构,填上了当前的交易以及coinbasetx
pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript));
CBlock *pblock = &pblocktemplate->block;
调整区块coinbasetx中的输入脚本中的nExtraNonce
IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce);
不停的调整区块头中的nNonce来碰撞正确的hash
while(pblock->nNonce<nInnerLoopCount&&
!CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus()))
{
pblock->nNonce;
}
如果区块头中的nNonce的值用完了(加到最大值了),重启大循环,调整nExtraNonce
if (pblock->nNonce == nInnerLoopCount) {
continue;
}
尝试把挖矿出来的区块放入到候选链->主链->广播出去
ProcessNewBlock(Params(), shared_pblock, true, nullptr)
}
}
这段代码主要是完成以下步骤
1、生成一个区块的模版,填写一些基本的字段
2、调整coinbasetx的nExtraNonce
3、从0- nInnerLoopCount 累加区块头中的nNonce字段,如果累加到了nInnerLoopCount,转入第2步
4、计算区块头hash是否达标(也就是前n位是否为0),达标就进入第5步,否则重复3步
5、将生成好的区块经过验证等流程进入到候选链,进而进入主链,进而进入广播流程。
其中解释几个关键点
1、模版中的前n位为0,n是怎么确认的?
有一个公式,笔者也没有详细研究,原理就是比特币设想每10分钟生成一个区块,而每2016个区块会检查一下,这2016个区块生成的平均时间是否超过或者小于10分钟,如果超过10分钟就会把n调低,也就是降低挖矿难度,反之则把n调高。
另外一个疑问,如果某些恶意矿工,人为的把n设置低,来达到挖矿的目的会怎么样?岂不是它挖矿更容易?
这个问题也引出了比特币中伟大的发明,你把n降低了,再公网中广播的时候,区块的工作量就比别的矿主的少,也就导致了你的区块可能被别的矿工挖出来的区块pk掉的概率高,这就保证了矿工不敢随意降低n。(这么解释可能会比较难理解,后续文章会找机会进一步解释)
2、矿工的挖矿费是怎么确定的?
矿工的挖矿费是有两部分构成
1)、交易费,也就是所有交易的输出减去所有交易的输入的差价就是交易费Fee
2)、奖励,这个奖励是每4年减半,比如当前的奖励是25个比特币,再过一个四年周期会变成12.5个,依次类推。4年这个时间刻度是靠区块链的高度结合每一个区块10分钟的设想生成时间确定的。