术语表 /挖矿与共识
nBits
又称 Compact target、难度位。
- nBits 是什么?
- nBits 是每个区块头中长度为四字节的字段,它以一个指数和一个三字节系数组成的紧凑、类浮点编码方式,对挖矿目标值进行打包。
比特币需要在区块头中使用一个256位的目标值,而区块头无法为此腾出32字节,因此它用四个字节存储该值的一个取整版本。第一个字节是指数,其余三个字节是系数,目标值等于系数乘以256的(指数减三)次方。每个节点都会重新计算自己预期的值,因此声称目标值比规则允许的更容易的区块头会被直接拒绝。
运作方式
nBits 被解读为一个指数字节加上一个三字节系数,二者合并成一个完整的256位数字。
以创世区块携带的值0x1d00ffff 为例。指数是0x1d,即29,系数是0x00ffff,即65,535。因此目标值等于65,535乘以2的208次方,写成十六进制是八个零,随后是 ffff,再用零补足到64位。这是主网允许的最容易的目标值,也是难度1的定义。
有两个特殊之处容易让人困惑。第一是精度。仅有24位的系数无法表示任意的256位数字,因此编码后的目标值始终是取整后的值,从目标值转换为 nBits 再转换回来,并不总能得到最初的值。第二是符号位。如果系数的最高位被置位,该值会被视为负数,因此编码器会将系数下移一个字节,并相应增大指数。这就是创世区块的值写作0x1d00ffff,而不是看上去更直观的0x1cffff00 的原因,也是系数内部出现前导零字节属于正常现象而非错误的原因。
在序列化的区块头中,该字段位于字节偏移量72处,并以小端序写入,因此这四个字节在磁盘和网络上显示为 ff ff 00 1d。
在哪里会遇到它
nBits 以多个不同的名称出现,围绕它的大多数困惑由此而来。
区块浏览器将其标为"bits",并在更易读的难度数字旁显示该十六进制值。Bitcoin Core 的 RPC 命令 getblock 将其作为"bits"返回,getblocktemplate 则将其作为候选区块头必须满足的要求交给挖矿软件。在源代码中,它是 CBlockHeader 的 nBits 成员,由 arith_uint256::SetCompact 和 GetCompact 进行转换。矿池几乎不会将其展示出来:Stratum 在任务通知中把这四个字节发送给矿机,固件无需理会其含义。
该字段的变化也是按周期进行,而非连续变化。它在整个2,016个区块的窗口内保持不变,只在难度调整时才取新值,因此相隔一小时挖出的两个区块,其 nBits 字段相同,除非两者之间发生了难度调整。
nBits vs 目标阈值
nBits 是编码,目标阈值是它所编码的数字。节点从不直接将区块头哈希与 nBits 比较,而是先把这四个字节展开成完整的256位目标值,再检查哈希是否小于或等于该值。在交谈中把 nBits 当作目标值本身无妨,但在代码中是错误的,因为相差一个单位的两个 nBits 值,解码后得到的目标值在绝对数值上相差极大。