主页 > imtoken钱包最新版本 > 比特币挖矿难度调整源码分析

比特币挖矿难度调整源码分析

imtoken钱包最新版本 2023-04-11 07:54:05

参见“掌握比特币”8.7.第 3 节

用于比特币难度调整部分。

首先介绍CBigNum。

CBigNum

CBigNum 是在 openssl 库中定义的 BIGNUM 的包装类。公钥加密需要能够处理非常大的整数。标准数据类型无法满足要求。 BIGNUM 可以存储任意长度的整数。

CBigNum 类的结构并不复杂。它由一堆不同类型的 BIGNUM 构造函数组成,包括 char、short、int、long、int64、int256 及其无符号版本和向量等。它还重构了加法、减法、乘法、除法、位操作等运算符,等等。所有实际的工作代理都给了 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 区块周期不平衡会持续,所以在下一周期会进行进一步的难度调整。所以可能需要几个 2016 区块周期才能平衡哈希算力和难度的巨大差异。

这部分将上一个难度循环所花费的时间与预期时间进行比较,并根据结果进行调整。如果调整范围大于 4 倍,则调整 4 倍。

if (nActualTimespan < nTargetTimespan/4)
        nActualTimespan = nTargetTimespan/4;
if (nActualTimespan > nTargetTimespan*4)
        nActualTimespan = nTargetTimespan*4;

小于4倍,按比例调整。

bnNew *= nActualTimespan;
bnNew /= nTargetTimespan;

至此,难度调整周期完成。