主页 > 放imtoken钱包安全吗 > 比特币挖矿难度调整源码分析
比特币挖矿难度调整源码分析
比特币难度调整见《精通比特币》8.7.3节
先介绍一下CBigNum。
CBigNum
CBigNum 是 openssl 库中定义的 BIGNUM 的包装类。 公钥加密需要能够处理非常大的整数。 标准数据类型是不够的。 BIGNUM 可以存储任意长度的整数。
CBigNum 类的结构并不复杂。 它由一堆用于构造不同类型BIGNUM的构造函数组成比特币挖矿难度查询,包括char、short、int、long、int64、int256及其无符号版本和vector等。它还重构了加、减、乘、除等运算符,按位运算等。所有实际工作都委托给 BIGNUM 类行。 CBigNum 的大部分代码只是为 BIGNUM 的函数准备输入数据。
难度调整
这部分源码比较简单,如下:
unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast)
{
const unsigned int nTargetTimespan = 14 * 24 * 60 * 60; // two weeks
const unsigned int nTargetSpacing = 10 * 60;
const unsigned int nInterval = nTargetTimespan / nTargetSpacing;
// Genesis block
if (pindexLast == NULL)
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;
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 /= nTargetTimespan;
if (bnNew > bnProofOfWorkLimit)
bnNew = bnProofOfWorkLimit;
/// debug print
printf("\n\n\nGetNextWorkRequired RETARGET *****\n");
printf("nTargetTimespan = %d nActualTimespan = %d\n", nTargetTimespan, nActualTimespan);
printf("Before: x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str());
printf("After: x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str());
return bnNew.GetCompact();
}
nTargetTimespan 是两周的秒数,nTargetSpacing 是 10 分钟的秒数。 nInterval = nTargetTimespan / nTargetSpacing; 那是 2016 块。
难度调整在每个完整节点内独立且自动发生。 所有节点每 2,016 个区块调整一次难度。 难度调整公式是通过比较最近的 2,016 个区块花费的时间与 20,160 分钟(两周,或者以 10 分钟的速度计算这些区块预计花费的时间)来计算的。 根据实际时间与预期时间的比率调整难度(更难或更容易)。 简单来说,如果网络发现出块速度快于 10 分钟,就会增加难度。 如果发现慢于10分钟,则降低难度。
首先,函数会计算当前区块是否已经到达下一个难度周期。 如果不是,难度位将是前一个块的难度位。 返回 pindexLast->nBits;
如果达到了新的难度循环,则循环:
for (int i = 0; pindexFirst && i < nInterval-1; i++)
pindexFirst = pindexFirst->pprev;
展望2016个区块,此时pindexFirst指向上一个难度周期的第一个区块。
nActualTimespan = pindexLast->nTime - pindexFirst->nTime
即上一个难度周期最后一个块的时间戳减去第一个块的时间戳比特币挖矿难度查询,结果就是上一个难度周期花费的总时间nActualTimespan。
为防止难度变化过快,每个周期的调整必须小于一个因子(值 4)。 如果要调整的范围大于4倍,则调整4倍。 由于在下一个2016个区块的周期会继续不平衡,所以下一个周期会做进一步的难度调整。 因此,平衡散列能力和难度的巨大差异可能需要几个 2,016 个块周期才能完成。
这部分是将上一个难度周期所经过的时间与预期时间进行比较,并根据结果进行调整。 如果调整范围大于4倍,则调整4倍。
if (nActualTimespan < nTargetTimespan/4)
nActualTimespan = nTargetTimespan/4;
if (nActualTimespan > nTargetTimespan*4)
nActualTimespan = nTargetTimespan*4;
如果小于4倍,按比例调整。
bnNew *= nActualTimespan;
bnNew /= nTargetTimespan;
至此,完成一个难度调整周期。