区块链技术分享之 ~ 解读比特币挖矿难度
比特币的挖矿难度,需要从了解比特币挖矿原理开始。
比特币挖矿原理
比特币挖矿原理,即通过不断变换区块头中随机数字段(即nNonce),对区块头做双重SHA256哈希计算,以使得哈希计算结果小于区块头中给定的挖矿目标值(即nBits)的过程。
附区块头代码如下:
class CBlockHeader
{
public:
int32_t nVersion; // 区块版本号
uint256 hashPrevBlock; // 前一个区块哈希
uint256 hashMerkleRoot; // 交易列表的默克尔树根值
uint32_t nTime; // 区块时间戳
uint32_t nBits; // 挖矿目标值
uint32_t nNonce; // 随机数字段
//其它代码略
};
补充:随机数字段为32位整数,容量有限,可变换空间仅为2<sup>32</sup>,即4G左右,ASIC矿机在毫秒级即可搜索完毕。
为解决区块头搜索空间不足的问题,ASIC矿机出现后,即在Coinbase交易中开辟了一块更大空间的ExNonce字段(扩展随机数)用于ASIC矿机搜索,变换ExNonce值,即可变换Coinbase交易,由此达到变换hashMerkleRoot(交易列表的默克尔树根值),并最终变换区块头的目的。
注:Coinbase交易,为区块交易列表中的第一笔交易,即构建挖矿工挖矿收益的那笔特殊交易。
区块头哈希与挖矿目标值的比较
从挖矿原理知道,挖矿即为比较区块头哈希和挖矿目标值的过程。但区块头哈希为64位十六进制串(即256位整数),而挖矿目标值为32位整数,两者如何比较?
以高度为548,215的区块为例,块哈希为:00000000000000000008fb7ae5932c92687d82e56d1fa0c23c73535e035c77c7,挖矿目标值为:0x17272fbd。数据取自:https://btc.com/block/548215。
其实,在区块头中存储的nBits,并非真正的挖矿目标值,而是挖矿目标值的压缩形式。
真正的挖矿目标值bnTarget,为256位整数(或64位十六进制串),与其压缩形式nBits,可使用如下公式换算:
bnTarget = -1<sup>s</sup> m 256<sup>e-3</sup><BR />
注:nBits为32位整数,其中s为其中第9位,代表正负;m为其中后23位;e为其中前8位;共计32位。
在实际中,32位的整数nBits,需要先转换为256位整数bnTarget,实际验证时,是使用区块头哈希与bnTarget做比较,验证区块是否有效。
挖矿目标值nBits与bnTarget转换代码如下:
// 本代码仅供参考,有兴趣者可自行解读
arith_uint256& arith_uint256::SetCompact(uint32_t nCompact, bool* pfNegative, bool* pfOverflow)
{
int nSize = nCompact >> 24;
uint32_t nWord = nCompact & 0x007fffff;
if (nSize <= 3) {
nWord >>= 8 * (3 - nSize);
*this = nWord;
} else {
*this = nWord;
*this <<= 8 * (nSize - 3);
}
if (pfNegative)
*pfNegative = nWord != 0 && (nCompact & 0x00800000) != 0;
if (pfOverflow)
*pfOverflow = nWord != 0 && ((nSize > 34) ||
(nWord > 0xff && nSize > 33) ||
(nWord > 0xffff && nSize > 32));
return *this;
}
挖矿目标值与挖矿难度
由于比特币挖矿,是对区块头做哈希计算,使得计算结果小于挖矿目标值的过程。因此挖矿目标值越小,挖矿难度越大;挖矿目标值越大,挖矿难度越小。
在比特币中,挖矿目标值和挖矿难度,可以互相换算。换算方法为:
挖矿难度 = 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF / 挖矿目标值
注:其中0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF为常量,即比特币网络中定义的最大挖矿目标值(此时挖矿难度最小)。
还以高度为548,215的区块为例,挖矿目标值压缩形式nBits为:0x17272fbd,真正的挖矿目标值为:000000000000000000272fbd0000000000000000000000000000000000000000。
挖矿难度 = 0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF / 000000000000000000272fbd0000000000000000000000000000000000000000 = 7.18 T
补充:也可以这样通俗理解,挖矿目标值的前导0个数越多,挖矿难度越大;挖矿目标值的前导0个数越少,挖矿难度越小。
挖矿难度与全网算力
一般常识,全网算力增加,会导致挖矿难度变大;全网算力减少,会导致挖矿难度变小。但这个规律的背后理论支撑是什么呢?
挖矿难度与全网算力之间,可以通过如下公式来换算:
全网算力(P) = 全网难度 * 2<sup>32</sup> / 600 / 10<sup>15</sup>
注:其中600为600秒,即10分钟,即比特币的理论爆块周期;10<sup>15</sup>为:1P = 10<sup>15</sup>
如当前全网难度为7.18 T,此时全网算力约为 7.1810<sup>12</sup> 2<sup>32</sup> / 600 / 10<sup>15</sup> = 51396 PH/S
注:计算全网算力,需使用全网难度计算,此处为了区别其他算力形式。如计算矿机算力,需使用矿池下发给矿机的难度。
全网难度的调整逻辑
众所周知,比特币每隔两周调整一次全网挖矿难度。其调整逻辑为如下所述:
事实上,比特币并非严格每两周调整一次难度,而是每2016个区块调整一次难度,两周为2016个区块的理论用时。
1、计算过去2016个区块的总用时;
2、校验过去2016个区块的总用时,是否超过上下限,即是否超过理论用时的4倍(即56天),或低于理论用时的1/4(即3.5天);
如果超过上限,按上限处理;如果低于下限,按下限处理;
3、调整后难度目标值 = 当前难度目标值 * 过去2016个区块总用时 / 2016个区块的理论用时;
4、调整后难度目标值,不能低于最小难度时的挖矿目标值。
附比特币难度目标值调整代码如下:
unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)
{
if (params.fPowNoRetargeting) // 每2016个区块内,不调整难度目标值
return pindexLast->nBits; // 返回当前难度目标值
// Limit adjustment step
int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime; // 计算过去2016个区块的总用时
if (nActualTimespan < params.nPowTargetTimespan/4) // 是否超过理论用时的4倍(即56天)
nActualTimespan = params.nPowTargetTimespan/4; // 按上限处理
if (nActualTimespan > params.nPowTargetTimespan*4) // 是否低于理论用时的1/4(即3.5天)
nActualTimespan = params.nPowTargetTimespan*4; // 按下限处理
// Retarget
const arith_uint256 bnPowLimit = UintToArith256(params.powLimit); // 比特币网络最大难度目标值(此时难度最小)
arith_uint256 bnNew;
bnNew.SetCompact(pindexLast->nBits); // 当前难度目标值
bnNew *= nActualTimespan;
bnNew /= params.nPowTargetTimespan; // 调整后难度目标值 = 当前难度目标值 * 过去2016个区块总用时 / 2016个区块的理论用时
if (bnNew > bnPowLimit) // 挖矿难度不能小于区块链网络指定的最小难度
bnNew = bnPowLimit;
return bnNew.GetCompact();
}
全网难度周期与挖矿收益变化
按上所述,全网算力的变化与全网难度的变化有联动关系。
但全网算力增加,并不会立即导致全网难度的调整,而是在每2016个区块后,全网难度才会上升。每2016个区块,称为一个难度周期。
进入新的难度周期,也会导致挖矿收益的变化。
如果全网难度增加,意味着同等算力的矿机,在新的难度周期内爆块概率降低,进而挖矿收益降低。
同理如果全网难度降低,意味着同等算力的矿机,在新的难度周期内爆块概率增加,进而挖矿收益增加。
附相关工具链接