随着比特币接近主流采用和认可,其以挖矿为特征的基本安全模型每天都受到越来越多的关注和审查。人们越来越关注和关注比特币挖矿对环境的影响、底层模型的安全性和去中心化程度,甚至量子计算突破对比特币和其他加密货币未来的潜在影响。
很多时候,工作量证明被描述为“密码学难题”,但真正的难题是什么?为了真正理解这些问题(以及任何可能的答案),您需要对比特币挖矿本身及其演变有一个基本的了解。本文将探讨工作量证明的所有技术组件和移动部分,以及它们如何相互无缝同步以使比特币成为今天的去中心化平台。
为什么挖矿有效:加密单向哈希
比特币区块链通常被描述为加密安全的数据库,因此是不可变的。支持这种不变性和安全性的底层技术是加密哈希。密码散列函数是一种数学函数,简单地说,它接受任何输入并将其映射到固定大小的字符串。
然而,这些函数有四个特殊属性,使它们对比特币网络非常宝贵。他们是:
- 确定性——对于加密散列函数的任何输入,结果输出将始终相同。
- 快速——在给定任何输入的情况下,计算散列函数的输出是一个相对较快的过程(不需要大量计算)
- 唯一——函数的每个输入都应该产生一个完全随机且唯一的输出(换句话说,没有两个输入会产生相同的输出)
- 不可逆——给定哈希函数的输出,无法获得原始输入
这些规则为比特币挖矿保护网络提供了基础。
特别是比特币协议的创建者中本聪选择使用SHA-256 哈希函数作为比特币挖矿的基础。这是一个特定的密码散列函数,已在数学上证明具有上述属性。它总是输出一个256 位的数字(最基本的计算单位),通常以 64 个字符的十六进制数字系统表示,以方便人类阅读。
SHA-256 函数的输出通常称为其输入的哈希值。
哈希函数的输入导致完全唯一的输出
这是一个 SHA-256 函数输入和输出的示例(您可以在这里自己尝试一下):
Input to SHA-256:
<Bitcoin Transaction>
Output to SHA-256:
77077b1f4c3ad44c83dc0bdb8d937e9b71c0ef07a35c2664bb7da85be738eacf
有趣的是,在比特币协议中使用散列的大多数地方,都使用了双重散列。这意味着原始 SHA-256 函数的输出随后会直接放回 SHA-256 函数以获得另一个输出。这是该过程的样子:
Input to SHA-256(first round):
<Bitcoin Transaction>
Output (first round):
77077b1f4c3ad44c83dc0bdb8d937e9b71c0ef07a35c2664bb7da85be738eacf
Input to SHA-256 (second round):
77077b1f4c3ad44c83dc0bdb8d937e9b71c0ef07a35c2664bb7da85be738eacf
Output (second round and final result):
3c6c55b0e4b607b672b50f04e028a6951aed6dc97b91e103fb0f348c3f1dfa00
双重哈希用于防止生日攻击。生日攻击是一种攻击者能够通过使用完全不同的输入(称为冲突)产生与另一个输入相同的哈希的场景。这打破了唯一性的第三个属性。没有它,两个完全不同的比特币区块可能会由完全相同的哈希表示,从而允许攻击者潜在地切换区块。
使用 SHA-256 函数,这种攻击发生的概率无限小。如果不是几乎不可能,SHA-256 将被视为已损坏。
然而,其他散列函数在过去已经被“破坏”了。为了防止 SHA-256 将来发生这种情况(并有效地破坏比特币的安全模型),最好对hash 进行哈希处理。这将发生冲突的可能性减半,使协议更加安全。
在非常高的水平上,比特币挖矿是一个系统,其中所有比特币交易都发送给比特币矿工。矿工选择价值 1 兆字节的交易,将它们捆绑为 SHA-256 函数的输入,并尝试找到网络接受的特定输出。第一个找到此输出并将区块发布到网络的矿工会以交易费用和新比特币的创建形式获得奖励。让我们更进一步,深入了解比特币区块链本身,看看矿工究竟做了什么来确保网络安全。
比特币挖矿:技术介绍
引入挖矿是为了解决双花问题。如果我有 1 个比特币并将其发送给 Bob,然后尝试将相同的比特币发送给 Alice,网络会确保只接受一笔交易。它通过称为采矿的众所周知的过程来做到这一点。
在深入研究技术细节之前,重要的是要了解为什么需要挖矿来保护网络。由于现在存在法定货币,我们持有的货币是由美联储创建和验证的。由于比特币在去中心化和共识的严格假设下运作,因此不存在任何中央机构来验证该货币的发行并为其加盖时间戳以及对该货币发生的任何交易的验证。
Satoshi Nakamoto 提出了当时唯一已知的解决方案,以在面向共识的系统中解决此验证问题。这个方案在比特币白皮书中被称为工作量证明,它优雅地证明了交易是由那些愿意花费足够的物理计算能量和时间来进行验证的人验证的,同时引入了诱导市场竞争的激励措施。这种竞争使去中心化的特性能够在生态系统中有机地出现和繁荣。
块内的外观
一个比特币区块主要由两个部分组成:
1.交易,以默克尔树的形式
采矿计算机收集足够的交易来填充一个块并将它们捆绑到一个默克尔树中。
merkle 树是一个相对简单的概念:交易作为叶子位于树的底部,并使用 SHA-256 函数进行哈希处理。使用 SHA-256 函数再次对两个叶子事务的组合进行哈希处理,以形成叶子的父节点。这个父节点与散列交易的其他父节点一起不断向上哈希,直到创建一个根。这个根的哈希实际上是它下面的交易的唯一表示。
默克尔树的根是树中每个交易的哈希值的组合。回想一下,对于散列函数的任何输入,输出都是完全唯一的。因此,一旦网络上的大多数节点接收到一个挖掘的块,默克尔树哈希的根就充当该给定块中所有交易的不可更改的摘要。
如果恶意行为者尝试更改块中交易的内容,则其哈希值将被更改。哈希的这种变化将向上传播到交易的默克尔树,直到根的哈希改变。然后,任何节点都可以通过将更改块的默克尔树的根与有效块的默克尔树的根进行比较来快速捕捉到这种恶意行为。
2.区块头
区块头是区块本身内容的摘要。它包含以下六个组件:
- 比特币客户端运行的软件版本
- 区块的时间戳
- 其包含交易的默克尔树的根
- 它之前的块的哈希
- 一个随机数
- 目标_
请记住,交易默克尔树的根充当区块中每笔交易的有效摘要,而无需查看每笔交易。前一个块的散列允许网络按时间顺序正确放置块。这就是术语区块链的来源——每个区块都链接到前一个区块。随机数和目标是挖矿的关键。它们是解决矿工需要解决的 SHA-256 难题的基础。
请注意,块头中的所有这些数据都使用一种称为little-endian的符号压缩成 80 个字节,这使得节点之间的块头传输变得非常高效。出于解释的目的,我们将忽略这种压缩并假设数据是其原始形式。
解释采矿问题
存储在块头中的目标只是一个存储在位中的数值。在传统的以 10 为底的表示法中,这个目标的范围在 0 到 2²²⁴(一个67 位以上 的数字)范围内的某个地方,具体取决于有多少矿工同时竞争解决这个问题。
回想一下,SHA-256 的输出只是一个数字。矿工的目标是获取当前区块的头部,向其添加一个称为nonce的随机数,并计算其哈希值。哈希的这个数值必须小于目标值。这里的所有都是它的。但说起来容易做起来难。
回想一下 SHA-256 的第一个属性:哈希函数的输入总是会产生相同的输出。因此,如果矿工拿到块头,对其进行哈希处理,并意识到哈希值不小于目标值,他们将不得不以某种方式更改输入,以便尝试找到低于目标值的哈希值。
这就是nonce的用武之地。
矿工在块头中添加一个数字(从 0 开始),称为nonce,并对该值进行哈希处理。如果哈希值不小于目标值,矿工将随机数加 1,再次将其添加到块头,并对更改后的值进行哈希处理。这个过程不断重复,直到找到小于目标值的哈希值。
采矿示例
这是组成第一个块头的粗略近似值:
- 创世区块中交易的默克尔根:
Merkle Root:
4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
- 第一个已知的比特币版本:
0.1.0
- 区块的时间戳:
2009–01–03 18:15:05
- 目标(这也是最高的目标):
Target:
0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
- 没有前一个区块哈希——这是第一个区块,所以这是一个独特的案例
将其组件添加在一起后的最终块头:
让我们使用这个大标题并计算双哈希:
SHA-256 of header:
7d80bd12dfdccbdde2c41c9f406edfc05afb3320f5affc4f510b05a3394e1c91
SHA-256 of the previous result (final result):
c5aa3150f61b752c8fb39525f911981e2f9982c8b9bc907c73914585ad2ef12b
当转换为以 10 为底时,目标和输出哈希都是非常大的数字(请记住,长度超过 67 位)。下面的 Python 函数并没有尝试在此处演示两者的比较,而是处理比较:
def isBlockHashLessThanTarget(blockHash, target):
return int(blockHash, 16) < int(target, 16)
如果哈希值小于目标,则返回 True,否则返回 false。
这是我们的目标和块哈希的结果:
现在我们将原始块的十六进制值加 1。这是以下结果:
注意由于添加了随机数,最后一个数字现在是 1
然后,我们运行相同的散列算法并对这些更改的数据进行比较。如果它不低于目标,请继续重复。一旦找到成功的哈希,用于查找此解决方案的最新随机数将保存在块中。创世区块上列出的随机数是 2,083,236,893。
这意味着中本聪在找到一个可以接受的哈希之前迭代了这个过程超过 20 亿次。我已经为这个创世纪区块挖掘过程编写了一个小的 Python 实现,可以在我的GitHub 上找到。
subhan-nadeem/bitcoin-mining-python
bitcoin-mining-python - 比特币挖掘算法的 Python 实现
github.com
看看你需要多长时间才能成功挖掘创世区块!
警告:extraNonce
块头中的 nonce 值存储为 32 位数字。这意味着任何人能够达到的最高随机数是2³²(大约 40 亿)。经过 40 亿次迭代,nonce 耗尽,如果没有找到解决方案,矿工再次陷入困境。
对此的解决方案是在coinbase(区块的交易内容,存储为 merkle 树)中添加一个称为extraNonce 的字段。这个 extraNonce 的大小仅受区块本身大小的限制,因此只要区块大小在协议限制范围内,它就可以像矿工希望的那样大。
如果随机数的所有 40 亿个可能值都用完,则将额外的随机数添加并递增到coinbase。计算新的默克尔根和随后的新块头,并再次迭代随机数。重复此过程,直到找到足够的散列。
最好在nonce用完之前避免添加extraNonce,因为对 extraNonce 的任何更改都会更改 merkle 树。这需要额外的计算才能向上传播更改,直到计算出 merkle 树的新根。
矿工奖励
以最快的速度成功发布区块的矿工将获得凭空创造的全新比特币奖励。该奖励目前为 12.5 BTC。这些比特币是如何产生的?
每个矿工只需在他们的区块中添加一个新的输出交易,在开始开采该区块之前将 12.5 个比特币归属于自己。网络协议将在接收到新验证的块时接受此特殊交易为有效。这种特殊事务称为生成事务。矿工有责任在挖掘之前将此交易添加到区块中。至少有一个案例是矿工在挖一个区块之前忘记将奖励添加到交易中,有效地销毁了 12.5 BTC!
验证工作量证明
假设我们的矿工发现了一个小于目标的哈希值。该矿工所要做的就是将具有原始六个组件的挖掘块发布到任何连接的节点。接收区块的这个节点将首先验证交易集,确保所有交易都是有效的(例如,所有交易都经过适当的签名,并且硬币不会被重复使用和/或无中生有)。
然后,它将简单地对块头进行双重哈希 ,并确保该值低于块包含的目标值。一旦该块被认为有效,新节点将继续在网络上传播该块,直到每个节点都有一个最新的分类帐。
如您所见,任何给定节点都可以轻松验证新发布的块。然而,向网络发布一个有效的区块需要大量的计算能力(因此,电力和时间)。这种不对称性使网络得到保护,同时允许希望在网络上进行经济活动的个人以相对无缝的方式进行。
出块时间和调整目标
当第一批矿工开始挖矿时,他们每个人都监控出块时间。每个比特币区块都有 10 分钟的固定区块时间。这意味着,鉴于网络上当前的计算能力(网络哈希率)水平,节点总是希望平均每 10 分钟产生一次新验证的块。我们可以合理地期望在 10 分钟内生成块,因为在给定网络哈希率的情况下,找到块的概率是已知的。
例如,让我们以比特币中存在的最简单的目标:创世块。任何单个散列小于最简单目标的概率是 2³² 中的 1。这是超过四十亿分之一。因此,我们可以合理地期望有人运行 2³² 次挖掘问题迭代以找到合适的哈希值。网络上的节点预计每 10 分钟将在网络上的所有矿工中运行 40 亿次这样的迭代。
如果在大样本量的块中,块开始出现的速度超过 10 分钟,这非常清楚地表明网络上的节点正在以比 10 分钟快得多的速度迭代 40 亿个哈希。这种情况促使每个节点根据网络功率的增加(或减少)按比例调整目标,以确保每 10 分钟继续出块。
实际上,网络上的节点监控2016个区块的区块时间,精确到两周。每两周,将总出块时间与预期出块时间(即 20160 分钟)进行比较。
要获得新的目标,只需将现有目标乘以过去两周的总实际出块时间的比率即可得到预期出块时间。这将根据网络上进入或退出计算能力的数量按比例调整目标。
计算新目标的公式,每个比特币节点每 20160 分钟(两周)运行一次
出块时间和轻松计算找到有效块的概率的能力使节点可以轻松监控和确定网络上的总算力并调整网络。无论向网络添加多少计算能力或增加速度有多快,平均出块时间将始终保持在 10 分钟。目前网络上的总哈希率为每秒 28.27 exahash。即每秒在网络上的所有计算机上运行28.27 x 10¹⁸哈希。