难度值(difficulty)是矿工们在挖矿时候的重要参考指标,它决定了矿工大约需要经过多少次哈希运算才能产生一个合法的区块。比特币的区块大约每10分钟生成一个,如果要在不同的全网算力条件下,新区块的产生保持都基本这个速率,难度值必须根据全网算力的变化进行调整。简单地说,难度值被设定在无论挖矿能力如何,新区块产生速率都保持在10分钟一个。
难度的调整是在每个完整节点中独立自动发生的。每2016个区块,所有节点都会按统一的公式自动调整难度,这个公式是由最新2016个区块的花费时长与期望时长(期望时长为20160分钟即两周,是按每10分钟一个区块的产生速率计算出的总时长)比较得出的,根据实际时长与期望时长的比值,进行相应调整(或变难或变易)。也就是说,如果区块产生的速率比10分钟快则增加难度,比10分钟慢则降低难度。
1、先上代码
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast)
{
const unsigned int nTargetTimespan = 14 * 24 * 60 * 60; // 两周的秒数
const unsigned int nTargetSpacing = 10 * 60; //10分钟
const unsigned int nInterval = nTargetTimespan / nTargetSpacing;
// Genesis block
if (pindexLast == NULL) //如果为创世区块,则高度为0,上一区为空,返回最小难度即1
return bnProofOfWorkLimit.GetCompact();
// Only change once per interval
if ((pindexLast->nHeight+1) % nInterval != 0) //判断是否到达改变难度的间隔
return pindexLast->nBits;
// Go back by what we want to be 14 days worth of blocks
const CBlockIndex* pindexFirst = pindexLast;
for (int i = 0; pindexFirst && i < nInterval-1; i++)
pindexFirst = pindexFirst->pprev;
assert(pindexFirst);
// Limit adjustment step
unsigned int nActualTimespan = pindexLast->nTime - pindexFirst->nTime; //求得上一次生成2016个区块的时间
printf(" nActualTimespan = %d before bounds\n", nActualTimespan);
if (nActualTimespan < nTargetTimespan/4) 设置实际花费时间的最大最小值
nActualTimespan = nTargetTimespan/4;
if (nActualTimespan > nTargetTimespan*4)
nActualTimespan = nTargetTimespan*4;
// Retarget
CBigNum bnNew;
bnNew.SetCompact(pindexLast->nBits); //取旧难度值
bnNew *= nActualTimespan; //bnNew=bnNew*(nActualTimespan(实际花费时间) /nTargetTimespan(计划花费时间--两周))
bnNew /= nTargetTimespan;
if (bnNew > bnProofOfWorkLimit) //如果新值大于最小工作证明限制值(实际上即难度更小)
bnNew = bnProofOfWorkLimit; //则取最小工作量证明限制值
///综上,即根据上一次生成2016个区块所花时间与标准两周时间进行比较,如果小于,则证明难度需加大,bnNew是一个与难度成反比的值,至于如何利用bnNew来求得实际难度设置,则需继续研读代码。
/// debug print
printf("\n\n\nGetNextWorkRequired RETARGET *****\n");
printf("nTargetTimespan = %d nActualTimespan = %d\n", nTargetTimespan, nActualTimespan);
printf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str());
printf("After: %08x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str());
return bnNew.GetCompact();
}
2、个人在看相关书籍时,总发现有:
新难度值 = 旧难度值 * ( 过去2016个区块花费时长 / 20160 分钟 )
个人认为,这种描述不准确,根据代码所述,bnNew是一个与难度调整值成反比的数,并非是难度越大,该值越大,因此应当称为“易度值”似乎更合适。