【BTC】挖矿难度调整
目录
一、挖矿难度的概念
二、调整挖矿难度的原因
三、比特币挖矿难度的调整方法
四、比特币系统实际情况
五、课后练习时的思考
本章节主要讲解比特币挖矿难度的调整。
一、挖矿难度的概念
挖矿是不断尝试 block header 里的随机值,使整个 block head 的哈希值小于等于给定目标阈值。目标阈值(target)越小,挖矿难度越大。比特币使用 SHA256 哈希算法,产生 256 位哈希值,整个输出空间有二的 256 次方个可能取值。调整挖矿难度即调整目标空间在输出空间的占比,通俗说类似哈希值前面需有一定数量的零,但实际目标阈值并非简单前面为零。挖矿难度与目标阈值成反比,难度为 1 时对应目标阈值很大,此时挖矿最容易。
- 挖矿的基本操作:老师介绍到挖矿的核心是不断尝试 block header(区块头)里的随机值,通过这种尝试使得整个 block header(区块头)的哈希值小于等于给定的目标阈值。一旦一个矿工最先尝试到了一个随机值,正好使得区块头的哈希值小于给定的target,则该矿工就获得了该区块的记账权,它就可以来打包该区块进行发布了,并获得出块奖励。其他没挖到矿的节点就会同步被挖到矿的人发布的区块,执行区块的交易脚本,更新本地维护的数据,保证所有区块链网络中各节点的一致性,进而达成共识。。这是挖矿的关键操作,也是理解挖矿难度的基础。
- 哈希算法与输出空间:比特币使用的哈希算法是 SHA256,该算法产生的哈希值是 256 位。基于此,整个输出空间存在2的256次方个可能的取值。老师指出调整挖矿难度实际上就是调整目标空间在整个输出空间中所占的比例。为了便于学生理解,老师还提到一种通俗的说法,即哈希值前面要有多少个零,例如 256 位的哈希值,若合法区块要求算出来的哈希值前面至少有 70 个零,但老师也强调这只是一种通俗表述,并非十分准确,因为目标阈值并非简单的前面都是零,从某一位置开始后面就都变成一的情况。
- 挖矿难度与目标阈值的关系:老师明确指出挖矿难度与目标阈值是成反比的关系。即目标阈值(target)越小,意味着目标空间在输出空间中所占的比例越小,那么挖矿难度就越大;反之,目标阈值越大,挖矿难度就越小。
- 挖矿难度为 1 的特殊情况:老师特别提到当挖矿难度(difficulty)等于 1 的时候,这是挖矿难度最小的情况。此时所对应的目标阈值是一个非常大的数,因为在哈希值与目标阈值的比较中,目标阈值越大,目标空间占比越大,挖矿就越容易。并且挖矿难度与目标阈值的这种反比关系还体现在计算当前挖矿难度时,是用难度为 1 时对应的目标阈值除以当前的目标阈值,得到的结果就是当前的挖矿难度。
二、调整挖矿难度的原因
若不调整挖矿难度,随着系统总算力增强,出块时间会越来越短。出块时间过短虽能加快交易写入区块链,但会引发问题。比如区块在网络传播需几十秒,若两个节点差不多同时发布区块,易出现分叉;出块时间越短,分叉越频繁,不仅有二分叉,还可能出现多分叉,这不利于系统达成共识,危害系统安全,因为分叉会分散总算力,降低发动 51% 攻击所需的算力。出块时间并非越短越好,也不是比特币协议设计的十分钟就最优,只要在一定常数波动范围内保持稳定即可,像以太坊出块时间为 15 秒,也需调整挖矿难度来保持稳定。
这部分内容老师从正反两个方面深入探讨了调整挖矿难度的原因,不仅分析了不调整挖矿难度会带来的问题,还通过对比不同出块时间所产生的影响,阐述了出块时间稳定的重要性,具体如下:
- 不调整挖矿难度的后果:老师指出,如果系统里的总算力不断增强,而挖矿难度却保持不变,那么出块时间就会越来越短。在比特币系统发展初期,参与的人较少,按照设计可能是十分钟出一个区块。但随着参与人数增多,挖矿的人越来越多,使用的设备也越来越先进,出块时间可能会逐渐缩短,从最初的十分钟变成一分钟,甚至到后来可能十秒钟就出一个区块,极端情况下不到一秒就能出一个区块。
- 出块时间过短的弊端:
- 分叉问题:虽然出块时间短看似能加快交易写入区块链,提高系统响应时间,但实际上存在严重问题。因为区块在网络上传播需要几十秒的时间,在其他节点还未收到某个区块之前,如果有两个节点差不多同时挖到矿并且发布了区块,就会出现分叉情况。当出块时间越来越短,这种分叉现象会成为常态,而且不仅仅是二分叉,可能会出现很多个分叉,比如十个区块差不多同时被挖出来,就会出现十分叉。
- 对系统共识和安全的危害:分叉过多对系统达成共识是不利的,并且会危害到系统的安全。比特币协议假设大部分算力掌握在诚实的矿工手里,系统的总算力越强,安全性就越好,因为发动 51% 攻击(即恶意节点掌握系统 51% 以上算力来干坏事,如回滚交易等)所需要的算力就越大。但如果出现很多分叉,系统的总算力就会被分散。例如在出现十分叉的情况下,节点会根据在网络中的位置不同选择沿着其中某个分叉继续往下扩展,而恶意节点可以集中算力扩展自己的分叉,这样很快就能使自己的分叉变成最长合法链。由于好人的算力被分散,此时发动攻击可能不需要 51% 的算力,可能 10% 几的算力就足够了。所以,出块时间不是越短越好。
以上就是A给B转完钱之后,恶意节点进行分叉攻击,又加了一个分叉把钱再转会给A,进行交易回滚,如果分叉太多,就会分散诚实节点的算例,那么恶意节点集中力量沿着恶意分叉的分支进行出块,就很有可能实现最长有效链,这样的话它的分叉攻击就成功了。
- 出块时间的合理范围:老师表明比特币协议中设计的十分钟出块间隔不一定是最优的,将其改成八分钟或五分钟理论上也可行,但出块时间要有一个常数的波动范围,不能无限地减少下去。有些人认为比特币十分钟的出块间隔对于支付系统来说时间过长,做一个支付需要等待很久才能得到确认。而以太坊的出块时间降低到了 15 秒,其出块速度是比特币的 40 倍。当出块时间大幅度下降后,以太坊就需要设计新的共识协议(如 GHOST 协议)来应对,在该协议中,对于分叉产生的 orphan block(孤块)不能简单丢弃,而是要给予一定奖励(uncle reward)(以太坊对于分叉的无效区块,是不会给予奖励的,这也就造成了恶意节点分叉攻击成本会加大)。但即便如此,以太坊同样需要调整挖矿难度,使得出块时间保持稳定,这进一步说明了出块时间稳定的重要性,无论设置成多长时间,都不能无限减少。
三、比特币挖矿难度的调整方法
比特币协议规定每 2016 个区块重新调整目标阈值,大约每两个星期调整一次。调整公式为:新目标阈值 = 旧目标阈值 × (实际产生 2016 个区块花费时间 / 预期产生 2016 个区块花费时间)。若实际时间超过两个星期,说明出块间隔超十分钟,应降低挖矿难度,目标阈值会变大;若实际时间小于两个星期,说明出块速度过快,应提高挖矿难度,目标阈值会变小。实际代码中,上调和下调都有四倍的限制,以避免系统出现意外情况导致目标阈值大幅波动。调整是写在比特币系统代码里的,每挖到 2016 个区块自动进行,恶意节点若不调整,其发布的区块将不被其他矿工认可,因为检查区块合法性时无法通过。
这部分内容老师详细讲解了比特币挖矿难度的调整方法,包括调整的周期、依据的公式、公式中各参数的含义、调整的上下限限制,以及如何确保所有矿工同步调整等方面,具体如下:
- 调整周期:比特币协议中规定每 2016 个区块要重新调整一下目标阈值,因为每个区块的出块时间设计为十分钟,经过计算(2016 个区块 ×10 分钟 / 区块 ÷60 分钟 / 小时 ÷24 小时 / 天),大概是每两个星期调整一次。
- 调整公式及参数含义:老师给出了目标阈值迭代更新的方法,公式为:新目标阈值 = 旧目标阈值 ×(实际产生 2016 个区块花费时间 / 预期产生 2016 个区块花费时间)。其中,预期时间(expect time)是在理想状况下,按照每十分钟产生一个区块,产生 2016 个区块所需要的时间,即 2016×10 分钟(两个星期);实际时间(actual time)是系统中产生最近的 2016 个区块实际花费的时间。如果实际时间超过了两个星期,说明平均下来出块的间隔超过了十分钟,此时挖矿难度应该调低,让出块更容易。从公式看,因为实际时间比两个星期大,所以(实际产生 2016 个区块花费时间 / 预期产生 2016 个区块花费时间)这部分大于一,乘完之后新的目标阈值(target)会变大,由于挖矿难度与目标阈值成反比,目标阈值变大则挖矿难度降下来。反之,如果实际时间小于两个星期,说明出块速度有点太快了,这时应该提高挖矿的难度。因为实际时间比两个星期小,(实际产生 2016 个区块花费时间 / 预期产生 2016 个区块花费时间)是个小于一的数,所以算出来的新目标阈值会变小,相当于挖矿难度提高了。
- 调整的上下限限制:在实际代码当中,上调和下调都是有四倍的限制。即如果实际时间非常长,超过了八个星期(正常应该是两个星期),那么在计算时也只按照八个星期来算,这意味着目标阈值增大最多也是增大四倍,不会超过这个限度。这主要是为了避免系统中出现意外情况,导致目标阈值有特别大的波动。相反,如果实际时间非常短,不到半个星期就把 2016 个区块都挖出来了,这个时候同样按照半个星期来算,目标阈值最小减小的时候最多是减小四倍,也不会一次性减小得特别多。
- 确保所有矿工同步调整的机制:在去中心化的系统中,要让所有矿工同时调整目标阈值,是通过将调整方法写在比特币系统的代码里实现的。每挖到 2016 个区块,系统会自动进行调整,因为代码中就是这样设定的,且代码是开源的。对于有恶意的节点,如果到该调整的时候故意不调,其挖矿难度可能比别人小,出块速度会比正常的快一点。但如果其设置的目标阈值不符合新的难度要求,其发布的区块在检查合法性时就通不过。因为在 block header(区块头)里没有直接存储目标阈值(target),而是存储了一个 nBits(在 block header里只有四个字节,可以认为是目标阈值的一个压缩编码)。如果恶意矿工该调的时候不调,那么检查其区块合法性时就会发现问题,这个区块是不会被其他诚实矿工接受的。
比特币源码中是用nBits来记录target的值的,可以理解为nBits为target的压缩编码,因为target这个数是256位的,因为采用哈希算法就是SHA-256,而我们用4字节的nBits就能表示出target。
四、比特币系统实际情况
比特币系统中总算力在早期增长不明显,后来呈现指数级增长,且有波动。挖矿难度变化与算力增长基本同步,反映出参与挖矿的人增多和设备更先进。比特币出块时间从 2010 年到现在总体稳定在十分钟上下,说明难度调整达到预期目的。同时还提到不同公式计算挖矿难度和目标阈值时的区别,实际比特币代码里用的是目标阈值。
这部分内容老师展示了比特币系统中总算力、挖矿难度以及出块时间的实际变化情况,通过图表和数据直观呈现了系统的运行状态,具体如下:
- 总算力的变化情况:老师展示了一张图,该图显示在比特币没有流行起来之前,有很长一段时间算力没有太明显的增长。在前面的这些年,hash rate(哈希率,代表总算力)看上去几乎是零贴着 x 轴,但实际上这些年算力也是在增长的,只是后面这几年算力增长得太快,所以前面这部分看上去像是一条直线。而去年是加密货币涨得非常猛的一年,这也体现在了 hash rate 的增长上。从去年年初到现在的这部分曲线显示,算力呈现出指数级的增长。即便在这段黄金时期,算力也不是单调递增的,中间存在很多波动,但总的来说增长得非常快。
算力增长图
- 挖矿难度的变化情况:挖矿难度的变化情况与算力的增长基本上是同步的。这符合难度调整的设计目标,即通过调整挖矿难度,使得出块时间保持稳定。老师特别强调所展示的图显示的是挖矿难度,而非目标阈值。从最近半年的难度调整曲线可以明显看出,难度是一段一段的,每隔两个星期难度上一个台阶。这种现象说明挖矿的人越来越多,使用的设备越来越先进,反映出大家对比特币的热情越来越高。反之,如果某个加密货币的挖矿难度越调越小,说明挖矿变得越来越容易,这不是好事,意味着大家对这个加密货币的热情在逐渐减少,如果持续出现这种情况,一般来说这个加密货币就会慢慢走向衰落。
近些年的挖矿难度增长图
近半年的挖矿难度增长图
- 出块时间的变化情况:从 2010 年到现在每天的出块时间图来看,总的来说出块时间稳定在十分钟上下波动。这表明比特币系统的难度调整达到了预期目的,通过调整挖矿难度有效地控制了出块时间,使其保持在相对稳定的水平。再看最近半年的出块时间,按天做平均后,同样也是维持在十分钟左右,进一步验证了难度调整的有效性和出块时间的稳定性。
出块时间图,基本维持在10分钟上下
近半年出块时间图
五、课后练习时的思考
程序中nonce采用从0自增的方式是不是最优?现实挖矿过程种也是从0开始自增来尝试的吗?这样的尝试nonce的方式会不会比较低效呢,有没有更好的方案呢?
简单地从0自增,感觉上确实“很笨”,不像是一个“最优”的策略。这恰恰触及了工作量证明(POW)设计的精髓。
我们来深入探讨一下这个问题。
1. 从0自增是“最优”策略吗?
对于单个独立的计算单元来说,是的,这几乎就是最优策略。
原因在于 SHA-256 这类加密哈希函数的特性:
- 雪崩效应 (Avalanche Effect): 输入的微小变化(比如 nonce 从 1 变成 2),会导致输出的哈希值发生巨大且完全不可预测的变化。hash("MyNickname1") 和 hash("MyNickname2") 的结果之间没有任何关联性。
- 单向性 (Pre-image Resistance): 你无法从一个期望的哈希值(比如 00000abc...)反向推算出原始输入是什么。
正是因为这两点,寻找合格 nonce 的过程不存在任何捷径。你无法根据上一次失败的哈希结果,来“智能”地预测下一个 nonce 应该是什么。每一次尝试都是一次独立的、概率均等的“抽奖”。
在这种情况下,“最优”的搜索策略就是不重复地、系统性地遍历所有可能性。从0开始简单自增,完美地做到了这一点:
- 实现简单: 编程逻辑非常清晰。
- 无重复: 保证了你不会浪费算力去尝试一个已经试过的 nonce。
- 无遗漏: 保证了你会覆盖整个搜索空间。
所以,对于一个独立的“矿工”而言,无论是从0顺序递增、随机不重复地选择 nonce,还是从一个巨大的随机数开始递增,找到答案的期望时间是完全一样的。顺序递增只是最简单、最不容易出错的实现方式。
2. 真实世界的挖矿也是这样吗?
基本原理是,但实际操作要复杂得多,主要是为了解决规模化和协作的问题。
真实世界的挖矿(以比特币为例)面对的是全球数百万台专业矿机(ASIC)的竞争,每秒进行的哈希计算次数是天文数字(EH/s 级别,即 1018 次/秒)。
在这种规模下,简单的从0自增会遇到两个核心问题:
- Nonce 空间耗尽: 比特币区块头中留给 nonce 的空间只有32位,意味着 nonce 只能有 232(约42.9亿)个取值。以现在的算力,这个空间在不到一秒内就会被遍历完。
- 协作冲突: 如果全球所有矿工都从0开始自增,那他们绝大部分时间都在做着完全重复的无用功。
为了解决这些问题,真实世界的挖矿引入了更复杂的机制:
方案一:分布式协作(矿池)
绝大多数矿工会加入一个“矿池”(Mining Pool)。矿池服务器会给每个加入的矿工分配不同的计算任务。比如:
- 矿池对矿工A说:“你负责计算 nonce 从 0 到 1,000,000 的范围。”
- 对矿工B说:“你负责计算 nonce 从 1,000,001 到 2,000,000 的范围。”
- ...依此类推。
这样,大家分工合作,就不会做重复的工作,极大地提高了整个矿池找到正确哈希值的效率。
方案二:扩展搜索空间(ExtraNonce)
当32位的 Nonce 空间被快速用尽后怎么办?矿工们会巧妙地调整另一个可以变化的数据。
在比特币中,要哈希的不仅仅是“昵称+Nonce”。实际上被哈希的是区块头(Block Header),它包含了多个字段,其中一个是“默克尔树根(Merkle Root)”。这个值是区块中所有交易数据的哈希摘要。
矿工可以在自己打包的区块里,修改一笔特殊的“铸币交易”(Coinbase Transaction),在这笔交易中有一个可以自由修改的区域,被称为 ExtraNonce。
挖矿的完整流程是这样的:
- 矿工从网络上获取一批交易,打包成一个候选区块,并计算出对应的默克尔树根。
- 开始遍历区块头中的 Nonce(从0到232−1)。
- 如果在遍历完所有 Nonce 后还没找到合格的哈希值,矿工就会改变 ExtraNonce。
- 改变 ExtraNonce 会导致“铸币交易”变化,从而导致整个区块的默克尔树根也发生变化。
- 这样矿工就得到了一个全新的区块头可以去哈希。然后,他可以再次从0开始遍历那42.9亿个 Nonce。
这个过程可以无限重复。每一次修改 ExtraNonce,都相当于开启了一个全新的、42.9亿次的“抽奖”机会。
总结:有没有更好的方案?
对于“如何猜测Nonce”这个问题,没有比暴力尝试更好的方案,这是由哈希算法的安全性决定的。
但对于“如何组织暴力尝试”这个问题,有很多更好的方案,核心思想就是并行化和分工:
方面 (Aspect) | 我们的练习程序 | 真实世界的挖矿 (以比特币为例) |
搜索者 | 单个程序 | 全球成千上万的专业矿机(ASIC) |
搜索方式 | 单线程、顺序自增 | 大规模并行:一台ASIC矿机内有成百上千个计算核心同时运算。 |
Nonce范围 | 从0开始,无限增大 | 分工协作:矿池分配不同的Nonce范围或不同的任务。 |
搜索空间 | 固定 昵称,只改变 nonce | 空间扩展:当32位 Nonce 用尽,通过修改 ExtraNonce 来改变 Merkle Root,获得全新的哈希问题,再从0开始尝试 Nonce。 |
效率核心 | 抓住核心逻辑 | 通过硬件(ASIC)、分工(矿池)和扩展搜索空间(ExtraNonce)来最大化每秒的尝试次数。 |
总而言之,从0自增“低效”的感觉是对的,但“低效”不在于策略本身,而在于单枪匹马地执行这个策略。真实世界通过极致的工程化、并行化和协作,将这个“笨办法”的效率推向了物理极限。
相关文章:【BTC】比特币网络-CSDN博客
【BTC】比特币系统的具体实现_btc操作系统-CSDN博客
【BTC】协议(共识机制)_btc协议-CSDN博客
【BTC】数据结构_比特币区块头数据结构-CSDN博客
【BTC】密码学原理-CSDN博客