说说比特币的 “全网难度”
最近被好多朋友问到,怎么理解比特币的“全网难度”,大家都在纠结这个东西是什么、有什么意义,以及它是如何调整的。这篇文章会试着把这几个问题讲明白。
0. 预备知识
请先对区块链、比特币,以及比特币挖矿有一些了解。
1. 区块头结构和挖矿
比特币区块头一共80
字节
struct header_structure { // BYTES NAME
uint32_t nVersion; // 4 version
uint8_t hashPrevBlock[32]; // 32 previous block header hash
uint8_t hashMerkleRoot[32]; // 32 merkle root hash
uint32_t nTime; // 4 time
uint32_t nBits; // 4 target
uint32_t nNonce; // 4 nonce
};
简单来说
- 挖矿是矿工互相竞争比特币交易的记账权,谁能最快打出下一个有效区块,谁就能获得新块的coinbase奖励以及区块中所有交易包含的手续费
- “有效区块”的意思是,区块头的SHA256结果要小于等于一个目标值(target)
- 矿工从内存池中按优先级取出一定量的待确认交易,构建Merkle树,树根的Hash值,填充到区块头的
Merkle Root
字段 - 矿工不断调整区块头字段的值,目的是构造出一个有效的新区块
- 对区块头做
SHA256
运算,如果结果小于等于一个目标值(target),则新块构建成功,广播到全网,随后开始下一次算力竞赛 - 否则,调整区块头部分字段的值(修改
Nonce
或者通过调整交易来修改Merkle Root
),重新做计算
2. 目标值(target)
区块头中的Bits
字段,标识了当前区块头Hash之后要小于等于的目标值(target)。注意,区块头SHA256
的结果,有256位,而Bits
,只有32位(4字节)。Bits
通过如下运算,得到target
。以区块277316为例
Bits
值为0x1903a30c
(十进制为419668748
),这个值被存为系数/指数格式,前2
位十六进制数字为幂,接下来得6
位为系数。在这个区块里,0x19
为幂(exponent ),而0x03a30c
为系数(coefficient),计算公式为
target = coefficient * 256^(exponent – 3)
所以这个区块的target
值为
target = 0x03a30c * 256^(0x19 - 3)
= 238,348 * 256^22
= 22,829,202,948,393,929,850,749,706,076,701,368,331,072,452,018,388,575,715,328
十六进制为0x0000000000000003a30c00000000000000000000000000000000000000000000
也就是说高度为277316的有效区块的头信息哈希值,要小于等于这个目标值。高度277136区块的Hash值实际为0x0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4
3. 难度(difficulty)
从中本聪的创世区块中可以看到,Bits
为0x1d00ffff
(十进制为486604799
)时,难度(difficulty)为1
(注意,区块头中并没有存储difficulty
的字段)
根据Bits
,算出target
为0x00ffff * 256^26
,即0x00000000ffff0000000000000000000000000000000000000000000000000000
。也就是说,为了构建下一个合法区块,需要不断对块头做SHA256运算,直到找到一个区块头的Hash结果,前32位的值均为0(小于target)
SHA256运算的结果被认为是一致的随机序列,可以说,SHA256的结果中的某一位的值,为0或为1的概率相同。所以做一次计算,满足上述条件(前32位的值均为0)的概率为1 / (2^32)
,也就是平均要做2^32
次运算,才能找到这个值
为了方便理解,我们把为了使区块头的SHA256结果小于某个目标值(target),平均要尝试的计算次数,定义为难度(difficulty),1
difficulty ≈ 2^32
次 = 4294967296
次 ≈ 4.2 * 10^9
次 ≈ 4G
次运算
以前几天的区块501509为例,Bits
值为0x18009645
(十进制为402691653
),target
为0x009645 * 256 ^ 21
。创世区块的target
为0x00ffff * 256 ^ 26
,难度为1
,所以得到区块501509的难度为
(0x00ffff * 256 ^ 26)/ (0x009645 * 256 ^ 21)
= 65535/38469 * (256^5)
= 1.703579505575918 * 2^40
= 1873105475221.611
≈ 1.87 * 10^12
也就是1.87T(1T = 10^12
)
综上,可以得出公式
difficulty_当前 = target_创世区块 / target_当前
其中,target_创世区块
为定值0x00000000ffff0000000000000000000000000000000000000000000000000000
,target_当前
根据当前区块头中的Bits
字段算出
同时,还能得出另一个结论
出块时间(单位:秒) ≈ difficulty_当前 * 2^32 / 全网算力
当前比特币全网难度 1.93 T,全网算力 15.32 EH/s(1秒计算15.32E 次Hash),算出来的出块时间约为
1.93T * 2^32 / 15.32EH/s
= 1.93 * 10^12 * 4.295 * 10^9 / 15.32 * 10^18
= 8289.35 * 10^18 / 15.32 * 10^18
≈ 541 秒
总结一下
- 难度(difficulty)反映了矿工找到下一个有效区块的难易程度,难度随 区块头目标Hash值(target)的变动而变动,target值越小,难度越大
- 在难度(difficulty)不变的情况下,全网算力越大,出块时间理论上越快
4. 难度(difficulty)的调整
如前所述,区块头目标Hash值(target)决定了难度(difficulty),进而影响比特币的出块时间。根据设计,比特币要保证平均每10分钟的区块生成速度,这是比特币新币发行和交易完成的基础,需要在长期内保持相对稳定。
挖矿设备不断更新、淘汰,矿工不断加入、流失,导致全网算力保持实时变化,但整体来看在不断提高。为了保证比特币的平均出块时间稳定在十分钟,需要定期调整难度。
难度调整逻辑被写在代码中,在每个全节点中独立自动发生。每产生2016个区块,网络中的所有全节点都会调整难度。难度的调整公式是由产生最新2016个区块的花费时长与20160分钟(两周,即这些区块以10分钟一个的速率产生所期望花费的时长)比较得出的。难度是根据实际时长与期望时长的比值进行相应调整的(或变难或变易),简单来说,如果网络发现区块产生速率比10分钟要快时会增加难度。如果发现比10分钟慢时则降低难度。
这个逻辑可以简单表示为
Difficulty_新 = Difficulty_原 * ( 20160分钟 / 产生2016个区块的实际花费时长 )
从代码能看到,为了防止难度变的太快,每个周期在调整的时候,如果需要调整的幅度超过4
倍,也只会按4
倍(难度调成原来的4倍或1/4)调整
unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nFirstBlockTime, const Consensus::Params& params)
{
if (params.fPowNoRetargeting)
return pindexLast->nBits;
// Limit adjustment step
int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
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;
bnNew.SetCompact(pindexLast->nBits);
bnNew *= nActualTimespan;
bnNew /= params.nPowTargetTimespan;
if (bnNew > bnPowLimit)
bnNew = bnPowLimit;
return bnNew.GetCompact();
}
请再次注意,难度的调整,是通过调整区块头中的Bits
字段来实现的,区块头中并没有直接存储全网难度(difficulty)的字段
5. 总结
到这里,相信你已经知道文章开头问题的答案。简单的说,比特币的全网难度(difficulty)
- 反映了矿工找到下一个有效区块的难易程度,难度随 区块头目标Hash值(target)的变化而变化,target值越小,难度越大
- 区块头目标Hash值(target)由
Bits
字段的值计算得出 - 定义
Bits
值为0x1d00ffff
时的难度(difficulty)为1
- 从创世区块起,每产生2016个区块,网络中的所有全节点都会自动调整难度,以保证比特币约十分钟的出块速度
6. 参考