深入理解比特币系列(25): 难度系数及调整周期

比特币的区块平均10分钟生成一个,这就是比特币的心跳,是货币发行速率和交易达成速度的基础。不仅是在短期内,而且在未来几十年内它都需要保持恒定的生成速度。在此期间,计算机性能将飞速提升。此外,参与挖矿的人和计算机也会不断变化。为了能让新区块的产生保持在平均10分钟左右的速度,挖矿的难度必须根据这些变化进行调整。那么该如何调整,并由谁来调整呢?

事实上,难度是一个动态的参数,会定期调整以达到每10分钟一个新区块的目标。简单地说,难度被设定在,无论挖矿能力如何,新区块产生速率都保持在平均10分钟一个。

在一个完全去中心化的网络中,这样的调整是在每个完整节点中独立自动发生的。每2,016个区块中的所有节点都会调整难度。难度的调整公式是由最新2,016个区块的花费时长与20,160分钟(两周,即这些区块以10分钟一个速率所期望花费的时长)比较得出的。难度是根据实际时长与期望时长的比值进行相应调整的(或变难或变易)。简单来说,如果网络发现区块产生速率比10分钟要快时会增加难度。如果发现比10分钟慢时则降低难度。

这个公式可以总结为如下形式:

New Target = Old Target * (Actual Time of Last 2016 Blocks / 20160 minutes)

在比特币核心客户端中的难度调整代码如下:

// Limit adjustment step
    int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
    LogPrintf("  nActualTimespan = %d  before bounds\n", nActualTimespan);
    if (nActualTimespan < params.nPowTargetTimespan/4)
        nActualTimespan = params.nPowTargetTimespan/4;
    if (nActualTimespan > params.nPowTargetTimespan*4)
        nActualTimespan = params.nPowTargetTimespan*4;

    // Retarget
    const arith_uint256 bnPowLimit = UintToArith256(params.powLimit);
    arith_uint256 bnNew;
    arith_uint256 bnOld;
    bnNew.SetCompact(pindexLast->nBits);
    bnOld = bnNew;
    bnNew *= nActualTimespan;
    bnNew /= params.nPowTargetTimespan;

    if (bnNew > bnPowLimit)
        bnNew = bnPowLimit;

参数Interval(2,016区块)和TargetTimespan(1,209,600秒即两周) 的定义在文件chainparams.cpp中。

为了防止难度的变化过快,每个周期的调整幅度必须小于一个因子(值为4)。如果要调整的幅度大于4倍,则按4倍调整。由于在下一个2,016区块的周期不平衡的情况会继续存在,所以进一步的难度调整会在下一周期进行。因此平衡哈希计算能力和难度的巨大差异有可能需要花费几个2,016区块周期才会完成。

需要注意的是目标难度与交易的数量和金额无关。