主页 > imtoken钱包苹果版用不了 > 比特币挖矿难度调整源码分析

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

imtoken钱包苹果版用不了 2023-01-17 10:23:53

比特币难度调整详情请参考《掌握比特币》第三节8.7.

我们先介绍一下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;

至此,一轮难度调整完成。