《精通比特币》解读 第十章 - 挖矿和共识

10.1 简介

“挖矿”这个词有点误导。 类似贵金属的提取,人们的注意力集中在挖矿的报酬,每个区块中创造的新的比特币。虽然获得报酬是挖矿的一种激励,但挖矿的主要目的不是获得报酬或者产生新币。如果您只是把挖矿视为创建新币的过程,则误解了挖矿这个过程的目标的意义。挖矿最重要的作用是去中心化清算机制的基础,通过这种机制,交易得到验证和清算。 挖矿是使得比特币与众不同的一种发明,它实现去中心化的安全机制,是P2P数字货币的基础。

​挖矿确保了比特币系统安全,并且在没有中央权力机构的情况下实现了全网络范围的共识。新币和交易费的奖励是矿工维持网络安全的一种激励方案,同时实现了货币供应。

提示
挖矿的目的不是创造新的比特币。 这是激励机制。 挖矿是一种机制,这种机制实现了去中心化的安全。

​矿工们验证每笔新的交易并把它们记录在总帐簿上。平均每10分钟就会有一个新的区块被“挖掘”出来,每个区块里包含着从 上一个区块产生到目前这段时间内发生的所有交易,从而将这些交易添加到区块链中。我们把包含在区块内且被添加到区块链上的交易称为“确认”(confirmed)交易,交易经过“确认”之后,新的拥有者才能够花费他在这些交易中得到的比特币。

作为维护网络安全的回报,矿工们在挖矿过程中会得到两种类型的奖励:创建新区块产生的新币奖励,以及区块中所含交易的交易费。为了得到这些奖励,矿工们争相完成一种基于加密哈希算法的数学难题,这些难题的解决,称为“工作量证明”,包含在新区块中,作为矿工消耗重要的算力的证明。解决工作量证明算法难题从而获得相应报酬以及在区块链上记录交易的权力的这种竞争机制,是比特币安全的基石。

该过程被称为挖矿是因为它的奖励(新比特币的生成)被设计为速度递减模式,类似于贵重金属的挖矿过程。比特币的货币是通过挖矿发行的,类似于中央银行通过印刷银行纸币来发行新货币。矿工通过创造一个新区块得到的比特币数量大约每四年(或准确说是每210,000个块)减少一半。开始时为2009年1月每个区块奖励50个比特币,然后到2012年11月减半为每个区块奖励25个比特币。之后在2016年7月再次减半为每个新区块奖励12.5个比特币。基于这个准则,比特币挖矿奖励以指数方式递减,直到大约在2140年,届时所有的比特币(20,999,999,980)全部发行完毕。在2140年之后,不会再有新的比特币产生。

比特币矿工们同时也会获取交易费。每笔交易都可能包含一笔交易费,交易费是每笔交易的输入和输出的差额。成功“挖出”新区块的矿工可以得到该区块中包含的所有交易“小费”。目前,这笔费用占矿工收入的0.5%或更少,大部分收益仍来自挖矿所得的比特币奖励。然而随着挖矿奖励的递减,以及每个区块中包含的交易数量增加,交易费在矿工收益中所占的比重将会逐渐增加。渐渐地,矿工的报酬将主要是交易费,这将构成矿工的主要激励。在2140年之后,新区块产生的新比特币数量下跌到0,矿工的收益都将只由交易费构成。

在本章中,我们先来分析(examine)挖矿作为比特币的货币发行机制(monetary supply mechanism),然后再来了解挖矿的最重要的功能:支撑比特币安全的去中心化共识机制

​为了了解挖矿和共识,我们将跟随Alice的交易,看看Jing的挖矿设备如何收到交易并将交易加入一个区块中。 然后,我们将继续跟踪区块从被挖出,加入到区块链,并通过共识过程被比特币网络接受。

10.1.1 比特币经济学和货币创造
​通过创造出新区块,比特币以一个确定的并且不断减慢的速率被新铸造出来。大约每十分钟产生一个新区块,每一个新区块 都伴随着一定数量从无到有的全新比特币。每开采210,000个块,大约耗时4年,货币发行速率降低50%。在比特币运行的第一个四年中,每个区块创造出50个新比特币。

2012年11月,比特币的新发行速度降低到每区块25个比特币。2016年7月,降低到12.5比特币/区块。2020年的某个时候,也就是在区块630,000,它将再次下降至6.25比特币。新币的发行速度会类似这样以指数级降低,继续进行32次“减半”后直到第6,720,000块(大约在2137年开采出),达到比特币的最小货币单位1 satoshi。最终,在经过693万个区块之后,大约在2140年,所有的差不多共 2,099,999,997,690,000聪比特币,或者说差不多2,100万比特币,将全部发行完毕。在那之后,新的区块将不再包含新比特币,矿工的收益将只来自交易费。图10-1展示了在货币发行速度不断降低的情况下,比特币总流通量与时间的关系。
这里写图片描述
Figure 10-1. Supply of bitcoin currency over time based on a geometrically decreasing issuance rate

注意
比特币挖矿发行的最大数量也就成为挖矿可能获得的奖励的上限。 在实践中,矿工可能故意挖出一个区块,却只拿走比全额奖励少的奖励。这样的区块已经被开采了,未来可能会有更多被开采,这样导致一个稍低的货币发行总量。

​ 在例10-1的示例代码中,我们计算了比特币的发行总量
Example 10-1. A script for calculating how much total bitcoin will be issued

# Original block reward for miners was 50 BTC
start_block_reward = 50# 210000 is around every 4 years with a 10 minute block interval
reward_interval = 210000
def max_money():
# 50 BTC = 50 0000 0000 Satoshis
current_reward = 50 * 10**8
total = 0
while current_reward > 0:
total += reward_interval * current_reward
current_reward /= 2
return total
print "Total BTC to ever be created:", max_money(), "Satoshis"

例10-2展示了运行脚本的输出结果
Example 10-2. Running the max_money.py script

$ python max_money.py
Total BTC to ever be created: 2099999997690000 Satoshis

总量有限并且发行速度递减创造了一种抵抗通货膨胀的固定货币供应量供应模式。不像法币(fiat currency)可被中央银行无限制地印刷出来,比特币永远不会因超额印发而出现通胀。

通货紧缩货币
总量固定并且发行速率递减的货币发行模式的最重要并且最有争议的一个推论就是这种货币往往天生是通货紧缩的(deflationary)。通缩是一种由于货币的供应和需求不匹配导致的货币增值的现象,它使货币升值并且汇率( exchange rate)上升。它与通胀相反,价格通缩意味着随着时间推移货币有越来越强的购买力。

许多经济学家认为通缩经济是一种灾难,不惜任何代价都要避免。因为在快速通缩时期,人们往往会囤积(hoard)货币,而不是花费它,人们预期着商品价格会下跌。这种现象充斥了日本经济“失去的十年”,就是因为在需求坍塌之后导致货币进入通货紧缩的漩涡。

比特币专家们认为通缩本身(per se)并不坏。更确切地说,我们将通缩与需求坍塌联系在一起是因为通货紧缩只有这个例子供我们学习。法币是有可能被无限制印刷出来的,因此经济很难进入通货膨胀的漩涡,除非遇到需求完全崩塌并且毫无意愿发行货币的情形。而比特币的通缩并不是需求坍塌(collapse in demand)引起的,而是由于事先可预见的有节制的货币供应。

​通货紧缩的积极方面当然是与通货膨胀相反。 通货膨胀会导致缓慢但又不可避免的货币贬值(debasement of currency),这是一种隐性税收的形式,惩罚在银行存钱的人从而实现解救债务人(包括政府这个最大的债务人)。政府控制下的货币容易遭受轻易发行债务(easy debt issuance )的道德风险(moral hazard),之后可能会以牺牲储蓄者为代价,通过贬值来抹去债务。

比特币这种不是因经济快速衰退(rapid economic retraction)而引起的通缩特性是否是一个问题仍有待观察,或者有可能是它的一个优势,因为通货膨胀和贬值的保护远远大于(far outweighs)通货紧缩的风险。

10.2 去中心化共识
​在上一章中我们了解了区块链,可以将区块链看作一本记录所有交易的公开总帐簿(列表),比特币网络中的每个参与者都把能接受区块链,把它看作是证明所有权的权威记录。

​但在不考虑相信任何人的情况下,比特币网络中的所有参与者如何达成对任意一个所有权的共识呢?所有的传统支付系统都依赖于一个信任模型,即有一个中央权威机构提供清算(clearinghouse)服务,主要是验证并清算所有的交易。比特币没有中央机构,所有的完整节点都有一份公共总帐的备份,这份总帐可以被视为权威记录。区块链并不是由一个中央机构创造的,它是由比特币网络中的所有节点各自独立竞争组装完成的。比特币网络中的所有节点,依靠着节点间的不稳定的网络连接所传输的信息,最终得出同样的结论并组装产生了每一个节点都有的同样的公共总帐的一个备份。这一章将分析比特币网络不依靠中央机构而达成共识的过程

​中本聪的主要发明就是这种去中心化的自然共识(emergent consensus)机制。自然发生的(emergent),是指共识不是明确完成的——因为共识达成时,没有明确的选举或者说在某一固定时刻。共识是数以千计的独立节点遵守了简单的规则,通过异步交互自然而然的产物。所有的比特币属性,包括货币、交易、支付以及不依靠中央机构和信任的安全模型都源自这项发明。

比特币的去中心化共识由所有网络节点的4个独立过程相互作用而产生(emerge):

  • 每个全节点依据综合标准(comprehensive list of criteria)对每个交易进行独立验证
  • 挖矿节点将交易独立打包(aggregation)进新区块,进行工作量证明算法的验算(demonstrated computation)
  • 每个节点独立的对新区块进行校验并组装进区块链
  • 每个节点对区块链进行独立选择,在工作量证明机制下选择累计工作量最大的区块链。

在接下来的几节中,我们将分析这些过程,了解它们之间如何相互作用并达成全网的自然共识,从而使任意节点组合出它自己的权威、可信、公开的总帐本的副本。

10.3 交易的独立校验(Independent Verification of Transactions)
在第6章中,我们知道了钱包软件通过收集UTXO、提供正确的解锁脚本、然后构造一个新的输出给接收者这一系列步骤来创建交易。产生的交易随后将被发送到比特币网络中相邻的节点,从而使得该交易能够在整个比特币网络中传播。

​然而,在交易传递到相邻的节点前,每一个收到交易的比特币节点第一步就是验证该交易,这将确保只有有效的交易才会在网络中传播,而无效的交易将会在第一个收到该交易的节点处被丢弃。

每一个节点在校验每一笔交易时,都需要对照一个长长的标准检查表:

  • 交易的语法和数据结构必须正确。
  • 输入与输出列表都不能为空。
  • 交易的字节大小是小于 MAX_BLOCK_SIZE 的。
  • 每一个输出值,以及总量,必须在值的规定范围内 (小于2,100万个币,大于阈值dust)。
  • 没有hash=0, N=–1的输入(coinbase交易不应当被传递)。
  • nLockTime等于INT_MAX ,或者nLocktime 和 nSequence的值满足MedianTimePast
  • 交易的字节大小是大于或等于100的。
  • 交易中的签名操作数量(SIGOPS)应小于签名操作数量上限。
  • 解锁脚本( scriptSig )只能够将数字压入栈中,并且锁定脚本(scriptPubkey)必须要符合isStandard的格式
    (该格式将会拒绝非标准交易)。
  • 相匹配的交易必须存在于交易池中或者在主分支的一个区块中。
  • 对于每一个输入,如果引用的输出存在于交易池中任何别的交易中,该交易将被拒绝。
  • 对于每一个输入,在主分支和交易池中寻找引用的输出交易。如果输出交易缺少任何一个输入,该交易将成为一个孤
    立的交易。如果与其匹配的交易还没有出现在池中,那么该交易将被加入到孤立交易池中。
  • 对于每一个输入,如果引用的输出交易是一个coinbase输出,则它必须至少获得 COINBASE_MATURITY(100)个确认。
  • 对于每一个输入,引用的输出是必须存在的,并且没有被花费。
  • 使用引用的输出交易获得输入值,并检查每一个输入值和总值是否在值的规定范围内 (小于2100万个币,大于0)。
  • 如果输入值的总和小于输出值的总和,交易将被拒绝。
  • 如果交易费用太低(minRelayTxFee)以至于无法进入一个空的区块,交易将被拒绝。
  • 每一个输入的解锁脚本必须依据相应输出的锁定脚本进行验证。

​这些条件能够在比特币核心客户端下的 AcceptToMemoryPool 、 CheckTransaction 和 CheckInputs 函数中获得更详细的阐述。 请注意,这些条件会随着时间发生变化,为了处理新型拒绝服务(denial-of-service)攻击,或者有时候也为了包含进更多类型的交易而放宽规则(relax the rules)。

​每一个节点都会在收到交易后并且在广播前对该交易进行独立校验,为有效的(但未确认)交易建立一个池,这个池叫做transaction pool,或者memory pool或者mempool。

10.4 挖矿节点
在比特币网络中,一些节点是专用节点,称为“矿工”。第1章中,我们介绍了Jing,在中国上海的计算机工程专业学生,他 就是一位矿工。Jing通过矿机挖矿获得比特币,矿机是设计用于挖比特币的专用计算机硬件系统。Jing的这台专业挖矿设备连接着一个运行完整比特币节点的服务器。与Jing不同,一些矿工是在没有完整节点的条件下进行挖矿,正如我们在“Mining Pools”一节中所述。与其他任何完整节点一样,Jing的节点在比特币网络中进行接收和传播未确认的交易。然而,Jing的节点还能够把这些交易打包进入一个新区块。

​同其他节点一样,Jing的节点时刻监听着传播到比特币网络中的新区块。然而,新区块的到达对挖矿节点有着特殊的意义,矿工间的竞争以新区块的传播而结束,如同宣布获胜者。对于矿工们来说,收到一个有效的新区块意味着别人已经赢得了比赛,而自己则输了这场竞争。然而,一轮竞争的结束也代表着下一轮竞争的开始。新区块并不仅仅是象征着比赛结束的方格旗,它也是下一个区块竞赛的发令枪。

10.5 打包交易至区块(Aggregating Transactions into Blocks)
验证交易后,比特币节点会将这些交易添加到内存池中,或者说交易池,用来暂存交易,以等待交易被加入(挖矿)到一个区块中。与其他节点一样,Jing的节点会收集、验证并传递新的交易。而与其他节点不同的是,Jing的节点会把这些交易打包到一个候选区块(candidate block)中。

​让我们继续跟进Alice从Bob咖啡店购买一杯咖啡时产生的那个区块。Alice的交易包含在区块 277,316 中。为了演示本章中提到的概念,我们假设这个区块是由Jing的挖矿系统挖出的,并且继续跟进Alice的交易,因为这个交易已经成为了这个新区块的一部分。

Jing的挖矿节点维护了一个区块链的本地副本。当Alice买咖啡的时候,Jing节点的区块链已经组装到了区块277,314。Jing的节点监听着网络上的交易,尝试挖出一个新区块,同时也监听着由其他节点发现的区块。当Jing的节点在挖矿时,它从比特币网络收到了区块277,315,这个区块的到来意味着挖出区块277,315的竞赛结束了,同时也意味着挖出区块277,316的竞赛开始了。

在上一个10分钟期间,当Jing的节点正在寻找区块277,315的解答的同时,它同时也在收集交易,为下一个区块做准备。到目前为止,它在内存池中已经收集到了几百笔交易。当接收到区块277,315并验证它时,Jing的节点同时也会将它与内存池中的全部交易进行比较,并在内存池中移除已经在区块277,315中出现过的交易,确保任何留在内存池中的交易都是未确认的,等待被记录到一个新区块中。

​Jing的节点立刻构建一个新的空区块(empty block),做为区块277,316的候选区块。这个区块被称作候选区块(candidate block)是因为它还不是一个有效的区块,因为它还没有包含一个有效的工作量证明,而只有在矿工成功找到工作量证明算法的一个解之后,这个区块才生效。

​现在,Jing的节点从内存池中打包了所有的交易,新的候选区块包含有418笔交易,总的交易费为0.09094925个比特币。你可以通过比特币核心客户端命令行来查看这个区块,如例10-3所示。

*Example 10-3. Using the command line to retrieve block 277,316*
$ bitcoin-cli getblockhash 277316
0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4
$ bitcoin-cli getblock 0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4
{
	"hash" : "0000000000000001b6b9a13b095e96db41c4a928b97ef2d944a9b31b2cc7bdc4",
	"confirmations" : 35561,
	"size" : 218629,
	"height" : 277316,
	"version" : 2,
	"merkleroot" : "c91c008c26e50763e9f548bb8b2fc323735f73577effbc55502c51eb4cc7cf2e",
	"tx" : [
		"d5ada064c6417ca25c4308bd158c34b77e1c0eca2a73cda16c737e7424afba2f",
		"b268b45c59b39d759614757718b9918caf0ba9d97c56f3b91956ff877c503fbe",
		... 417 more transactions ...
	],
	"time" : 1388185914,
	"nonce" : 924591752,
	"bits" : "1903a30c",
	"difficulty" : 1180923195.25802612,
	"chainwork" : "000000000000000000000000000000000000000000000934695e92aaf53afa1a",
	"previousblockhash" : "0000000000000002a7bbd25a417c0374cc55261021e8a9ca74442b01284f0569"
}

10.5.1 创币交易(The Coinbase Transaction)
任何区块中的第一笔交易都是一笔特殊交易,称为创币交易(coinbase transaction)。这个交易是由Jing的节点构造并包含了挖矿工作的报酬(reward)。

注意
当块277,316开采时,每个块的奖励是25比特币。 此后,已经过了一个“减半”时期。 2016年七月份的奖励变为12.5比特币,到2020年,在210000区块时,将再次减半。

​Jing的节点会创建一个支付到自己钱包的创币交易:“向Jing的地址支付25.09094928个比特币”。Jing挖出一个区块获得的总报酬是coinbase奖励(25个新比特币)和区块中全部交易的交易费的总和,如例10-4所示。

*Example 10-4. Coinbase transaction*
$ bitcoin-cli getrawtransaction d5ada064c6417ca25c4308bd158c34b77e1c0eca2a73cda16c737e7424afba2f 1
{
	"hex" :
	"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0f03443b0403858402062f503253482fffffffff0110c08d9500000000232102aa970c592640d19de03ff6f329d6fd2eecb023263b9ba5d1b81c29b523da8b21ac00000000",
	"txid" : "d5ada064c6417ca25c4308bd158c34b77e1c0eca2a73cda16c737e7424afba2f",
	"version" : 1,
	"locktime" : 0,
	"vin" : [
	    {
		   "coinbase" : "03443b0403858402062f503253482f",
		   "sequence" : 4294967295
	    }
	],
	"vout" : [
		{
		   "value" : 25.09094928,
	       "n" : 0,
	       "scriptPubKey" : {
			    "asm" : "02aa970c592640d19de03ff6f329d6fd2eecb023263b9ba5d1b81c29b523da8b21OP_CHECKSIG",
				"hex" : "2102aa970c592640d19de03ff6f329d6fd2eecb023263b9ba5d1b81c29b523da8b21ac",
				"reqSigs" : 1,
				"type" : "pubkey",
				"addresses" : [
					"1MxTkeEP2PmHSMze5tUZ1hAV3YTKu2Gh1N"
				]
			}
		}
	]
}

与常规交易不同,创币交易没有输入,不消耗UTXO。它只包含一个被称作coinbase的输入,从无到有创建新的比特币。创币交易有一个输出,支付到矿工自己的比特币地址。创币交易的输出将这25.09094928个比特币发送到矿工的比特币地址,在本例中是 1MxTkeEP2PmHSMze5tUZ1hAV3YTKu2Gh1N。

10.5.2 Coinbase奖励与矿工费(Coinbase Reward and Fees)
为了构造创币交易,Jing的节点首先计算总的交易费,通过已添加到区块中的418笔交易的输入和输出分别进行求和,交易费按如下计算:

Total Fees = Sum(Inputs) - Sum(Outputs)

​在区块277,316中,总的交易费是0.09094928个比特币。

紧接着,Jing的节点计算出这个新区块正确的奖励额。奖励额的计算是基于区块高度的,以每个区块50个比特币为开始,每产生210,000个区块减半一次。这个区块高度是277,316,所以正确的奖励额是25个比特币。

计算过程可以参看比特币核心客户端中的GetBlockSubsidy函数,如例10-5所示。
Example 10-5. Calculating the block reward — Function GetBlockSubsidy, Bitcoin Core Client, main.cpp

CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams)
{
	int halvings = nHeight / consensusParams.nSubsidyHalvingInterval;
	// Force block reward to zero when right shift is undefined.
	if (halvings >= 64)
	return 0;
	CAmount nSubsidy = 50 * COIN;
	// Subsidy is cut in half every 210,000 blocks which will occur approximately every 4 years.
	nSubsidy >>= halvings;
	return nSubsidy;
}

初始奖励额(subsidy),以聪为单位进行计算,通过将 50 与 COIN 常量(100,000,000聪)相乘得到结果,将初始奖励额(nSubsidy变量)设置为50亿聪。

紧接着,这个函数用当前区块高度除以减半间隔( SubsidyHalvingInterval变量 )计算得到减半次数(halvings变量)。每 210,000个区块为一个减半间隔,对应本例中的区块277316,所以结果是1。

减半次数的最大值是64,所以如果超出64,代码强行将奖励额置为0(即只获得交易费)。

然后,这个函数会使用二进制右移操作将奖励额(变量 nSubsidy )一分为二,每一轮操作将减半一次。在区块277,316这个例子中,只需要将值为50亿聪的奖励额二进制右移一次(即对分一次),得到25亿聪,也就是25个比特币。之所以采用二进制右移操作,是因为相比于整数或浮点数除法,二进制右移操作的效率更高。

​最后,将coinbase奖励额(nSubsidy)与交易费(nFee)相加,并返回总和。

注意
如果Jing的挖矿节点把coinbase交易写入区块,那么如何防止Jing奖励自己100甚至1000个比特币?
答案是,不正确的奖励将导致区块被其他人视为无效,浪费了Jing用于工作证明的电力。 只有这个区块被大家认可,Jing才能得到报酬。

10.5.3创币交易的结构
经过这些计算,Jing的节点构造了一个创币交易,支付给自己25.09094928枚比特币。

如例10-4所示,创币交易的结构比较特殊,与一般交易输入需要指定一个先前的UTXO不同,它包含一个“coinbase“输 入。我们之前已经分析了交易输入的结构(第六章,Table 6-2)。现在让我们来比较一下常规交易输入与创币交易输入。表10-1展示了常规交易输入的结构,表10-2展示了创币交易输入的结构。

Table 10-1. The structure of a “normal” transaction input
这里写图片描述

Table 10-2. The structure of a coinbase transaction input
这里写图片描述

在Coinbase交易中,前面两个字段设置的值不代表UTXO的引用。第一个字段的32个字节全部被填充0,而不是一个“transaction hash”。“output index”字段的4个字节全部被填充0xFF(十进制的255)。“Unlocking Script”(scriptSig)由coinbase data代替,这个数据域给矿工使用,我们将在下面看到。

10.5.4 Coinbase数据(Coinbase Data)
​创币交易不包含“解锁脚本“(又称作 scriptSig)字段,这个字段被coinbase data替代,长度必须在2字节和100字节之间。除了开始的几个字节外,矿工可以任意使用coinbase data的其他部分,随意填充任何数据。

以创世区块为例,中本聪在coinbase data中填入了这样的数据“The Times 03/Jan/ 2009 Chancellor on brink of second bailout for banks“(2009年1月3日 财政大臣第二次对银行施以援手),用于对日期的证明,同时也传达了一个消息。现在,矿工使用coinbase data包含进一些额外的随机数和字符串用来标识挖出它的矿池。

coinbase前几个字节也曾是可以任意填写的,但现在情况不同了。按照(As per)BIP34 协议,规定了version-2 的区块(版本字段为2的区块),这个区块的高度索引必须跟在脚本操作“push“之后,填充在coinbase字段的起始处。

​我们以例10-4中的区块277,316为例,coinbase就是交易输入的“解锁脚本“(或scriptSig)字段,这个字段包含有十六进制值03443b0403858402062f503253482f。下面让我们来解码这段数据。

​第一个字节是03,指示脚本执行引擎将后面3个字节压入脚本栈(见附录B的表4-1),紧接着的3个字节,0x443b04, 是以小端(little-endian)格式(相反的,最低有效字节在先)编码的区块高度。翻转字节的顺序得到0x043b44,表示为十进制是277,316。

​紧接着的几个十六进制数(03858402062)用于编码extra nonce(参见 10.11.1 “The Extra Nonce Solution ),或者一个随机值, 用于寻找一个合适的工作量证明解决方案。

​coinbase数据结尾部分(2f503253482f)是ASCII编码的字符串 /P2SH/,表示挖出这个区块的挖矿节点支持BIP0016所定义的 P2SH改进方案。在P2SH功能引入到比特币的时候,需要矿工们打信号,用以表明是支持BIP0016还是BIP0017。支持BIP0016的矿工将/P2SH/放入coinbase数据中,支持BIP0017的矿工将 p2sh/CHV 放入他们的coinbase数据中。最后,BIP0016在选举中胜出,很多矿工继续在他们的coinbase中填入/P2SH/以表示支持这个功能。

例10-6使用了libbitcoin库(在之前“其他替代客户端、资料库、工具包”章节中提到)从创世区块中提取coinbase数据,并显示 出中本聪留下的信息。libbitcoin库中自带了一份创世区块的静态拷贝,所以这段示例代码可以直接从库中检索创世区块。

*Example 10-6. Extract the coinbase data from the genesis block*
/*
Display the genesis block message by Satoshi.
*/
#include <iostream>
#include <bitcoin/bitcoin.hpp>
int main()
{
	// Create genesis block.const bc::block_type block = bc::genesis_block();
	// Genesis block contains a single coinbase transaction.
	assert(block.transactions.size() == 1);
	// Get first transaction in block (coinbase).
	const bc::transaction_type& coinbase_tx = block.transactions[0];
	// Coinbase tx has a single input.
	assert(coinbase_tx.inputs.size() == 1);
	const bc::transaction_input_type& coinbase_input = coinbase_tx.inputs[0];
	// Convert the input script to its raw format.
	const bc::data_chunk raw_message = save_script(coinbase_input.script);
	// Convert this to an std::string.
	std::string message;
	message.resize(raw_message.size());
	std::copy(raw_message.begin(), raw_message.end(), message.begin());
	// Display the genesis block message.
	std::cout << message << std::endl;
	return 0;
}

我们使用GNU C++编译器编译源代码并运行生成的可执行文件,​如例10-7中展示。
Example 10-7. Compiling and running the satoshi-words example code

$ # Compile the code
$ g++ -o satoshi-words satoshi-words.cpp $(pkg-config --cflags --libs libbitcoin)
$ # Run the executable
$ ./satoshi-words
^D��<GS>^A^DEThe Times 03/Jan/2009 Chancellor on brink of second bailout for banks

10.6 构造区块头
为了构造区块头,挖矿节点需要填充六个字段,如表10-3中所示。
Table 10-3. The structure of the block header
这里写图片描述

​在区块277,316被挖出的时候,区块结构中用来表示版本号的字段值为2,长度为4字节,以小端格式编码,值为 0x20000000。

​接着,挖矿节点需要填充“Previous Block Hash”(也称作prevhash)。在本例中,这个值就是区块277,315的区块头的哈希值,区块277,315是Jing的节点先前从网络上接收到的, 并且它是Jing的节点认可的并选择出来作为候选区块277316的父区块。区块277,315的区块头哈希值为:

0000000000000002a7bbd25a417c0374cc55261021e8a9ca74442b01284f0569

提示
通过选择特定的父区块,即由候选区块头中的Previous Block Hash字段指定的区块,Jing正在通过提交挖矿算力来扩展以该特定区块结尾的区块链。 从本质上讲,这就是Jing如何用他的挖矿算力为最长难度的有效区块链进行“投票”的。

​下一步就是用merkle树汇总全部的交易,以便将merkle root的哈希值添加到区块头的相应字段中。创币交易被列为区块中的首个交易,然后将另外的 418笔交易添至其后,这样区块中的交易一共有419笔。正如我们在“Merkle Trees”章节中见到的那样,树中必须有偶数个叶子节点,所以需要复制最后一个交易,共创建420个叶子节点,每个叶子节点包含有一笔交易的哈希值。这些交易的哈希值然后成对地组合,创建树的每一层,直到所有交易最终合并成树的根节点。merkle树的根节点将全部交易数据汇总为一个32字节长度的值,你可以在例10-3中的"merkle root"中看到,或者如下所示:

c91c008c26e50763e9f548bb8b2fc323735f73577effbc55502c51eb4cc7cf2e

​Jing的挖矿节点会继续添加一个4字节的时间戳,以Unix纪元时间戳编码,即自1970年1月1日0点到现在总共流逝的秒数。本例中的时间戳1388185914对应的时间是2013年12月27日,星期五,23:11:54 UTC/GMT。

Jing的节点然后需要填充Target字段(难度目标值),这个字段定义了为使该区块有效,所需满足的工作量证明。 target以“尾数-指数”(mantissa-exponent)的编码格式,以"target bits"为度量的(metric)存储在区块中。这种编码的第一个字节表示指数,后面跟着的3字节表示尾数(系数)。以区块277316为例,"target bits"的值为0x1903a30c,第一部分0x19是一个十六进制格式的指数,后面部分0x03a30c是系数(coefficient)。target的概念在"Retargeting to Adjust Difficulty"章节中有解释,“target bits”的相关陈述在“Target Representation”章节中。

​最后一个字段是nonce,初始化为0。

​随着除了Nonce字段的其它全部字段的填充完成,区块头现在就完成了,并且挖矿过程就可以开始进行了。挖矿的目标是找到一个随机值nonce,使得区块头的哈希值小于target。挖矿节点通常需要尝试数十亿甚至数万亿(trillions)个不同的nonce取值,直到找到一个满足条件的nonce值。

10.7 挖矿(Mining the Block)
既然Jing的节点已经构建了一个候选区块,那么Jing的矿机是时候进行“挖掘”了,寻找工作量证明算法的一个解答以使这个区块生效。从本书中我们已经学习了比特币系统中多个不同地方用到的哈希加密函数。比特币挖矿过程使用的是 SHA256 哈希函数。

​用最简单的术语来说,挖矿就是不断重复计算区块头的哈希值,修改一个参数,直到生成的哈希值与特定的target相匹配的一个过程。哈希函数的结果无法提前得知,也没有一个能生成特定哈希值的模式。哈希函数的这个特性意味着生成与特定的target相匹配的哈希值的唯一方法就是不断反复的尝试,每次随机修改输入,直到碰巧出现期望得到的哈希值。

10.7.1 工作量证明算法
哈希函数输入一个任意长度(arbitrary-length)的数据,输出一个长度固定(fixed-length)且确定的结果,可将其视为输入的数字指纹(digital fingerprint)。对于任何特定的输入,哈希的结果每次都一样,并且任何人都可以用相同的哈希算法来计算和验证哈希结果。一个加密哈希算法的关键特性就是两个不同的输入几乎不可能(computationally infeasible)产生相同的数字指纹(也被称为冲突(collision))。因此,用某种方法选择一个输入去生成一个想要的哈希值是几乎不可能的,更别提用随机输入的方式生成想要的哈希值了。

​无论输入的大小是多少,SHA256函数的输出的长度总是256bit。在例10-8中,我们将使用Python解释器来计算语句 “I am Satoshi Nakamoto” 的SHA256哈希值。

Example 10-8. SHA256 example

$ python
Python 2.7.1
>>> import hashlib
>>> print hashlib.sha256("I am Satoshi Nakamoto").hexdigest()
5d7c7ba21cbbcd75d14800b100252d5b428e5b1213d27c385bc141ca6b47989e

例10-8展示了"I am Satoshi Nakamoto" 的哈希计算结果:5d7c7ba21cbbcd75d14800b100252d5b428e5b1213d27c385bc141ca6b47989e 。这个256比特长的数字就是句子的哈希或者说摘要,它依赖于句子的每一个部分,添加一个字母,标点符号,或者任何其他字符都会产生一个不同的哈希值。

如果我们改变原句子,得到的应该是完全不同的哈希值。例如,我们在句子末尾加上一个数字,运行例10-9中的简单Python脚本。

Example 10-9. SHA256 script for generating many hashes by iterating on a nonce

# example of iterating a nonce in a hashing algorithm's input
import hashlib
text = "I am Satoshi Nakamoto"
# iterate nonce from 0 to 19
for nonce in xrange(20):
	# add the nonce to the end of the text
	input = text + str(nonce)
	# calculate the SHA-256 hash of the input (text+nonce)
	hash = hashlib.sha256(input).hexdigest()
	# show the input and hash result
	print input, '=>', hash

执行这个脚本就能生成这些只是末尾数字不同的语句的哈希值。如例10-10所示,通过增加末尾的数字,得到不同的哈希值。

Example 10-10. SHA256 output of a script for generating many hashes by iterating on a nonce

$ python hash_example.py
I am Satoshi Nakamoto0 => a80a81401765c8eddee25df36728d732...
I am Satoshi Nakamoto1 => f7bc9a6304a4647bb41241a677b5345f...
I am Satoshi Nakamoto2 => ea758a8134b115298a1583ffb80ae629...
I am Satoshi Nakamoto3 => bfa9779618ff072c903d773de30c99bd...
I am Satoshi Nakamoto4 => bce8564de9a83c18c31944a66bde992f...
I am Satoshi Nakamoto5 => eb362c3cf3479be0a97a20163589038e...
I am Satoshi Nakamoto6 => 4a2fd48e3be420d0d28e202360cfbaba...
I am Satoshi Nakamoto7 => 790b5a1349a5f2b909bf74d0d166b17a...
I am Satoshi Nakamoto8 => 702c45e5b15aa54b625d68dd947f1597...
I am Satoshi Nakamoto9 => 7007cf7dd40f5e933cd89fff5b791ff0...
I am Satoshi Nakamoto10 => c2f38c81992f4614206a21537bd634a...
I am Satoshi Nakamoto11 => 7045da6ed8a914690f087690e1e8d66...
I am Satoshi Nakamoto12 => 60f01db30c1a0d4cbce2b4b22e88b9b...
I am Satoshi Nakamoto13 => 0ebc56d59a34f5082aaef3d66b37a66...
I am Satoshi Nakamoto14 => 27ead1ca85da66981fd9da01a8c6816...
I am Satoshi Nakamoto15 => 394809fb809c5f83ce97ab554a2812c...
I am Satoshi Nakamoto16 => 8fa4992219df33f50834465d3047429...
I am Satoshi Nakamoto17 => dca9b8b4f8d8e1521fa4eaa46f4f0cd...
I am Satoshi Nakamoto18 => 9989a401b2a3a318b01e9ca9a22b0f3...
I am Satoshi Nakamoto19 => cda56022ecb5b67b2bc93a2d764e75f...

每个语句都生成了一个完全不同的哈希值。它们看起来是完全随机的,但你在任何计算机上用Python执行上面的脚本都能重现(reproduce)这些完全相同的哈希值。

​类似这样的用作变量的数字叫做nonce(随机数)。Nonce是用来改变加密函数输出的,在这个示例中改变了这个语句的 SHA256指纹。

​为了使这个哈希算法变得富有挑战,我们来设定一个目标:找到一个语句,使他的十六进制哈希值是以0开头的。幸运的是,这并不困难!例10-10展示了语句 “I am Satoshi Nakamoto13” 的哈希值是 0ebc56d59a34f5082aaef3d66b37a661696c2b618e62432727216ba9531041a5 ,刚好满足条件。我们尝试了13次找到它。从概率的角度来看,如果哈希函数的输出是均匀分布的(evenly distributed),我们可以期望每16次得到一个以0开头的十六进制哈希值(十六进制数字为0到F)。从数字的角度来看,我们要找的是小于 0x1000000000000000000000000000000000000000000000000000000000000000 的哈希值。我们称这个阈值为target,目标就是找到一个小于这个目标(target)的哈希值。如果我们减小这个目标值,那找到一个小于它的哈希值会越来越难。

简单打个比方,想象一个游戏,其中的玩家不断重复扔一对骰子以得到小于一个特定点数。第一局,目标是12,只要你不扔出两个6, 你就会赢。然后下一局目标为11,玩家只能扔10或更小的点数才能赢,不过也很简单。假如几局之后目标降低为5,现在有一半以上机率扔出来的骰子加起来点数会超过5,因此无效。随着目标越来越小,要想赢的话,扔骰子的次数会 指数级(exponentially)的上升。最终当目标为2时(最小可能值),平均只有扔36次才会有一次,或者说2%的概率,才会产生一个获胜的结果。

从一个知道骰子游戏目标为2的观察者的角度来看,如果有人成功扔胜利了一次,那么就可以推测他平均尝试了36次。换句话说,可以估计出实现目标难度而取得成功所需的工作量。 当算法是基于诸如SHA256的确定性函数时,输入本身就能证明必须要一定的工作量才能产生一个低于目标的结果。 因此,称之为工作量证明(Proof-of-Work)。

​提示
尽管每次尝试产生一个随机的结果,但是任何可能的结果的概率可以预先计算。 因此,特定难度的结果构成了具体工作量的证明。

​在例10-10中,成功的nonce是13,且这个结果能被所有人独立确认。任何人将13加到语句 “I am Satoshi Nakamoto” 后面再计算哈希值都能验证它比目标值要小。这个正确的结果同时也是工作量证明(Proof of Work),因为它证明我们的确花时间找到了这个nonce。验证这个哈希值只需要一次计算,而我们找到nonce值却花了13次。如果目标值更小(难度更大),那我们需要多得多的哈希计算才能找到合适的nonce,但是任何人验证它时只需要一次哈希计算。此外,知道目标值后,任何人都可以用统计学来估算其难度,因此就能知道找到这个nonce需要多少工作。

提示
工作证明必须产生小于目标的哈希值。 更高的目标意味着找到低于目标的哈希是不太困难的。 较低的目标意味着找到低于目标的哈希是比较困难的。目标和难度是成反比(inversely related)。

​比特币的工作量证明(Proof-of-Work)和例10-10中的挑战非常类似。矿工构建了一个候选区块,里面装着一些交易。接下来,这个矿工计算这个区块头的哈希值,看其是否小于当前 target 值。如果这个哈希值不小于目标值,矿工就会修改这个nonce(通常将其加 1)然后再试一次。按当前比特币网络的难度,矿工得试10^15次(10的15次方)才能找到一个合适的nonce使区块头哈希值足够小。

例10-11是用Python实现的一个简化很多的工作量证明算法。
Example 10-11. Simplified Proof-of-Work implementation

#!/usr/bin/env python
# example of proof-of-work algorithm
import hashlib
import time
max_nonce = 2 ** 32 # 4 billion
def proof_of_work(header, difficulty_bits):
	# calculate the difficulty target
	target = 2 ** (256-difficulty_bits)
	for nonce in xrange(max_nonce):
		hash_result = hashlib.sha256(str(header)+str(nonce)).hexdigest()
		# check if this is a valid result, below the target
		if long(hash_result, 16) < target:
			print "Success with nonce %d" % nonce
			print "Hash is %s" % hash_result
			return (hash_result,nonce)
	print "Failed after %d (max_nonce) tries" % nonce
	return nonce
	
if __name__ == '__main__':
	nonce = 0
	hash_result = ''
	# difficulty from 0 to 31 bits
	for difficulty_bits in xrange(32):
		difficulty = 2 ** difficulty_bits
		print "Difficulty: %ld (%d bits)" % (difficulty, difficulty_bits)
		print "Starting search..."
		# checkpoint the current time
		start_time = time.time()
		# make a new block which includes the hash from the previous block
		# we fake a block of transactions - just a string
		new_block = 'test block with transactions' + hash_result
		# find a valid nonce for the new block
		(hash_result, nonce) = proof_of_work(new_block, difficulty_bits)
		# checkpoint how long it took to find a result
		end_time = time.time()
		elapsed_time = end_time - start_time
		print "Elapsed Time: %.4f seconds" % elapsed_time
		if elapsed_time > 0:
			# estimate the hashes per second
			hash_power = float(long(nonce)/elapsed_time)
			print "Hashing Power: %ld hashes per second" % hash_power

你可以任意调整难度值(按二进制bit数来设定,即哈希值开头多少个bit必须是0)。然后执行代码,看看在你的计算机上求解需要花费多长时间。在例10-12中,你可以看到该程序在一个普通笔记本电脑上的执行情况。

Example 10-12. Running the Proof-of-Work example for various difficulties

$ python proof-of-work-example.py*
Difficulty: 1 (0 bits)
[...]
Difficulty: 8 (3 bits)
Starting search...
Success with nonce 9
Hash is 1c1c105e65b47142f028a8f93ddf3dabb9260491bc64474738133ce5256cb3c1
Elapsed Time: 0.0004 seconds
Hashing Power: 25065 hashes per second
Difficulty: 16 (4 bits)
Starting search...
Success with nonce 25
Hash is 0f7becfd3bcd1a82e06663c97176add89e7cae0268de46f94e7e11bc3863e148Elapsed Time: 0.0005 seconds
Hashing Power: 52507 hashes per second
Difficulty: 32 (5 bits)
Starting search...
Success with nonce 36
Hash is 029ae6e5004302a120630adcbb808452346ab1cf0b94c5189ba8bac1d47e7903
Elapsed Time: 0.0006 seconds
Hashing Power: 58164 hashes per second
[...]
Difficulty: 4194304 (22 bits)
Starting search...
Success with nonce 1759164
Hash is 0000008bb8f0e731f0496b8e530da984e85fb3cd2bd81882fe8ba3610b6cefc3
Elapsed Time: 13.3201 seconds
Hashing Power: 132068 hashes per second
Difficulty: 8388608 (23 bits)
Starting search...
Success with nonce 14214729
Hash is 000001408cf12dbd20fcba6372a223e098d58786c6ff93488a9f74f5df4df0a3
Elapsed Time: 110.1507 seconds
Hashing Power: 129048 hashes per second
Difficulty: 16777216 (24 bits)
Starting search...
Success with nonce 24586379
Hash is 0000002c3d6b370fccd699708d1b7cb4a94388595171366b944d68b2acce8b95
Elapsed Time: 195.2991 seconds
Hashing Power: 125890 hashes per second
[...]
Difficulty: 67108864 (26 bits)
Starting search...
Success with nonce 84561291
Hash is 0000001f0ea21e676b6dde5ad429b9d131a9f2b000802ab2f169cbca22b1e21a
Elapsed Time: 665.0949 seconds
Hashing Power: 127141 hashes per second

​你可以看出,随着难度位一比特位一比特位地增加,查找正确结果的时间会加倍地增长。如果你考虑整个256bit数字空间,每次多限制一个比特位要置为0,你就把查找空间缩减了一半。在例10-12中,为寻找一个nonce使得哈希值开头的26位值为0,一共尝试了8千多万次。即使普通笔记本每秒可以达120,000多次哈希计算,这个求解过程依然需要10分钟。

​在写这本书的时候,比特币网络要寻找区块头的哈希值小于:

0000000000000000029AB9000000000000000000000000000000000000000000

​可以看到,这个目标哈希值开头有很多个0,这意味着可接受的哈希值范围大幅缩小,因此找到一个有效的哈希值更加困难。网络生成下一个区块平均需要每秒计算1.8 septa-hashes(thousand billion billion hashes)。这看起来像是不可能的任务,但幸运的是比特币网络已经拥有3 exa-hashes per second(EH/sec)的处理能力,平均每10分钟就可以找到一个新区块。

10.7.2 难度表示

​在例10-3中,我们看到区块中包含有难度目标(target),其被记为"难度位",或简称"bits",在区块277,316中,它的值为 0x1903a30c。 这个记法将工作量证明的target表示为系数/指数(coefficient/exponent)的格式,前两位十六进制数字为指数(exponent),接下来得六位十六进制数字为系数(coefficient)。在这个区块里,0x19为指数,而 0x03a30c为系数。
计算难度目标的公式为:

target = coefficient * 2^(8 * (exponent – 3))

​由此公式,以及难度位的值 0x1903a30c,可得:

target = 0x03a30c * 2^(0x08 * (0x19 - 0x03))^
=> target = 0x03a30c * 2^(0x08 * 0x16)^
=> target = 0x03a30c * 2^0xB0^

​按十进制计算为:

=> target = 238,348 * 2^176^
=> target = 22,829,202,948,393,929,850,749,706,076,701,368,331,072,452,018,388,575,715,328

转化回十六进制后为:

=> target = 0x0000000000000003A30C00000000000000000000000000000000000000000000

​也就是说高度为277,316的有效区块的区块头哈希值是小于这个目标值的。这个数字的二进制表示中必须超过60位的前导位都是0。在这个级别的难度,一个每秒可以处理1万亿次(1 tera-hash per second 或 1 TH/sec)哈希计算的矿工平均每8,496个区块,或者平均每59天,才能找到一个正确结果。

10.7.3 难度目标与难度调整(Retargeting to Adjust Difficulty)
​如前所述,目标决定了难度,进而影响求解工作量证明算法所需要的时间。这导致了一个明显的问题:为什么这个难度值是可调整的?由谁来调整?如何调整?

​比特币的区块平均每10分钟生成一个。这就是比特币的心跳,是货币发行频率和交易结算速度的基础。不仅是在短期内,而是在几十年内它都必须要保持恒定。在此期间,计算机性能将飞速提升。此外,参与挖矿的人和计算机也会不断变化。为了保持区块的生成时间是10分钟,挖矿的难度必须根据这些变化进行调整。事实上,工作量证明的target是一个动态的参数,会定期调整以达到每10分钟一个新区块的目标。简单地说,难度被设定在,无论当前挖矿算力如何,新区块产生速率都保持在10分钟一个。

​那么,在一个完全去中心化的网络中,这样的调整是如何做到的呢?难度的调整是在每个节点中独立自动发生的。 每2,016个区块,所有节点都会调整工作量证明的难度。难度的调整公式是由最新2,016个区块的花费时长与20,160分钟(即这2,016个区块以10分钟一个的速率所期望花费的时长)比较得出的。难度是根据实际时长与期望时长的比值计算得到的,并进行适当的调整(或变难或变易)。简单来说,如果网络发现区块的速率比10分钟要快时会增加难度(target降低)。如果比10分钟慢则降低难度(target增加)。

​这个公式可以总结为:

New Target = Old Target * (Actual Time of Last 2016 Blocks / 20160 minutes)

​ 例10-13展示了比特币核心客户端中使用的难度调整代码:
Example 10-13. Retargeting the Proof-of-Work — CalculateNextWorkRequired() in pow.cpp

// Limit adjustment step
int64_t nActualTimespan = pindexLast->GetBlockTime() - nFirstBlockTime;
LogPrintf(" nActualTimespan = %d before bounds\n", nActualTimespan);
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;
arith_uint256 bnOld;
bnNew.SetCompact(pindexLast->nBits);
bnOld = bnNew;
bnNew *= nActualTimespan;
bnNew /= params.nPowTargetTimespan;
if (bnNew > bnPowLimit)
	bnNew = bnPowLimit;

注意
虽然目标校准(calibration)应该每2,016个块发生,但是由于最初的Bitcoin Core客户端的一个错误(off-by-one error),它是基于之前的2,015个块的总时间(而不是正常应该的2,016个),导致了难度调整有偏差(bias),向更高难度提高了0.05%。

​参数Interval(2,016区块)和TargetTimespan(1,209,600秒即两周) 的定义在文件chainparams.cpp中。

为了防止难度的变化过快,每个周期的调整幅度必须小于一个因子(值为4)。如果要调整的幅度大于4倍,则按4倍调整。进一步的难度调整会在下一个难度调整时期进行,因为不平衡的情况会继续存在于接下去的2,016区块中。因此哈希算力和难度的巨大差异(large discrepancies)可能需要花费几个2,016区块周期才能平衡抵消掉(balance out)。

​提示
挖出一个比特币区块的难度大约需要整个网络花费10分钟来处理,会根据之前的2,016个区块挖矿所需的时间对后续2,016个区块的挖矿难度进行调整。通过降低或者升高难度实现。

值得注意的是目标难度与交易的数量和金额无关。这意味着哈希算力(hashing power)的强弱,即让比特币更安全的电力投入量,也与交易的数量完全无关。换句话说,当比特币的规模变得更大,使用它的人数更多时,即使哈希算力与当前的算力水平相比没有任何提升,比特币的安全性也不会受到影响。哈希算力的增加代表着市场力量(market forces ),即更多的矿工为竞争得到比特币报酬而加入了挖矿队伍。只要追求报酬的诚实矿工群体控制着足够多的哈希算力,那么就足以阻止"接管"攻击(“takeover” attacks),因此,比特币就足够安全。

挖矿难度和电力消耗以及将比特币兑换成用于支付这些电力的现金的比率密切相关。高性能挖矿系统就是要用当前硅芯片以最高效的方式将电力转化为哈希算力。挖矿市场的关键因素就是每度电(one kilowatt-hour of electricity)转换为比特币后的价格,因为这决定着挖矿活动的营利情况,也因此刺激着人们选择进入或退出挖矿市场(mining market)。

10.8 成功挖出区块(Successfully Mining the Block)
前面已经看到,Jing的节点已经创建了一个候选区块,准备拿它来挖矿。Jing有几套配置了ASIC(application-specific integrated circuits,专用集成电路)的挖矿设备,上面有成千上万个集成电路可以超高速地并行运算SHA256算法。许多这种专用机器通过USB或者本地局域网连接到他的挖矿节点上。接下来,运行在Jing的桌面电脑上的挖矿节点将区块头传送给这些挖矿硬件,让它们以每秒亿万次(trillions)的速度进行nonce测试。

​在对区块277,316的挖矿工作开始大概11分钟后,其中一个硬件挖矿机器求得了一个解并将解发回到挖矿节点。当把这个结果放进区块头时,nonce 4,215,469,401 就会产生一个区块哈希值:

0000000000000002a7bbd25a417c0374cc55261021e8a9ca74442b01284f0569

该值小于难度目标值:

0000000000000003A30C00000000000000000000000000000000000000000000

​Jing的挖矿节点立刻将这个区块发给它的所有相邻节点。这些节点在接收并验证这个新区块后,继续传播此区块。当这个新区块在网络中扩散(ripples out)时,每个节点都会将它作为区块277,316加到自身节点的区块链副本中。当挖矿节点接收到并验证了这个新区块后,它们会放弃构建相同高度的区块的计算工作,并立即开始计算区块链中的下一个区块,并使用​Jing的这个区块作为父区块。通过在Jing的这个新发现的区块上继续扩建,其他的矿工本质上就是将他们的挖矿算力投票给Jing的这个区块,并扩展了区块链。

​下节将介绍每个节点进行区块验证、选择最长链、达成共识,并以此形成一个去中心化区块链的过程。

10.9 校验新区块(Validating a New Block)
比特币共识机制的第三步是通过网络中的每个节点独立校验每个新区块。当已完成解答的新区块在网络中传播时,每一个节点在将它转发到对等节点之前,会进行一系列的测试去验证它。这确保了只有有效的区块才会在网络中传播。独立校验还确保了诚实的矿工生成的区块可以被纳入(incorporated)到区块链中,从而获得奖励。行为不诚实的矿工所产生的区块将被拒绝,这不但使他们失去了奖励,而且也浪费了寻找工作量证明的解所花费的努力,因此遭受(incurring )电力成本的损失,也不会获得赔偿(compensation)。

​当一个节点接收到一个新的区块,它将对照一个长长的标准清单对该区块进行验证,若没有通过验证,这个区块将被拒 绝。这些标准(criteria)可以在比特币核心客户端的CheckBlock函数和CheckBlockHead函数中看到,其中包括:

  • 区块的数据结构语法上有效
  • 区块头的哈希值小于目标难度(确认工作量证明)
  • 区块时间戳早于未来两个小时(允许时间错误)
  • 区块大小在可接受的限制之内
  • 第一个交易(且只有第一个)是coinbase交易
  • 使用之前章节“Independent Verification of Transactions”已经讨论过的交易检查清单检查区块中的所有交易并确保它们都是有效的

网络中的每一个节点对每一个新区块的独立校验确保了矿工无法欺骗。在前面几节中,我们看到了矿工们如何去记录一笔交 易,以获得在此区块中创造的新比特币和交易费。为什么矿工不为他们自己记录一笔一千个比特币奖励,而是正确奖励的交易呢?这是因为每一个节点根据相同的规则对区块进行校验。一个无效的coinbase交易将使整个区块无效,这将导致该区块被拒绝,因此,该交易就不会成为总账的一部分。矿工们必须构建一个正确的区块,基于所有节点都遵守的共享的规则,并且根据工作量证明的正确解答进行挖矿。他们要花费(expend)大量的电力挖矿才能做到这一点,并且如果他们作弊,所有的电力和努力都会白费。这就是为什么独立校验是去中心化共识的关键组成部分。

10.10 区块链的组装与选择(Assembling and Selecting Chains of Blocks)

​比特币去中心化的共识机制的最后一步是将区块集合至有最大工作量证明的链中。一旦一个节点验证了一个新的区块, 它将尝试将新的区块连接到到现存的区块链,将它们组装起来。

节点维护三种区块集合:第一种是连接到主链上的,第二种是从主链上产生分支的(secondary chains,二级链),最后一种是在已知链中没有找到已知父区块的区块(孤儿区块)。在验证过程中,一旦有任一验证标准验证失败的,区块就会被节点拒绝,所以也不会加入到任何一条链中。

​任何时候,主链都是累计了最多工作量证明的区块链。在大多数情况下,主链也是包含最多区块的那个链,除非有两个等长的链,并且其中一个有更多的工作量证明。主链也会有一些分支,这些分支中的区块与主链上的区块互为“兄弟”区块。这些区块是有效的,但不是主链的一部分。 保留这些分支的目的是如果在未来的某个时刻其中一个分支延长了并超过了主链,那么后续的区块就会引用它们。在下一节“Blockchain Forks”,我们将会看到在同样的区块高度,几乎同时(simultaneous)挖出区块时,是如何产生二级链(secondary chains)的。

​当节点接收到新区块时,它会尝试将这个区块插入到现有区块链中。节点会看一下这个区块的“previous block hash”字段,这个字段是该区块对其父区块的引用。同时,新的节点将尝试在已存在的区块链中找出这个父区块。大多数情况下,父区块是主块链的“末梢”,这就意味着这个新的区块延长了主链。举个例子,一个新区块277,316引用 了它的父区块277,315。收到277316区块的大部分节点都已经将277315最为主链的末梢,因此,连接这个新区块并延长区块链。

​有时候,新区块所延长的并不是主链,这一点我们将在“10.10.1 Blockchain Forks”中看到。在这种情况下,节点将新的区块添加到备用链,同时比较备用链与主链的工作量。如果备用链比主链积累了更多的工作量,节点将聚集于备用链,意味着节点将选择备用链作为其新的主链,而之前的主链则成为了备用链。如果节点是一个矿工,它将开始构造新的区块,来延长这个更新更长的区块链。

​如果节点收到了一个有效的区块,而在现有的区块链中却未找到它的父区块,那么这个区块被认为是“孤块”(orphan)。孤块会被保存在孤块池中,直到它们的父区块被节点接收到。一旦收到了父区块并且将其连接到现有的区块链上,节点就会将孤块从孤块池中取出,并且连接到它的父区块,让它作为区块链的一部分。孤块现象通常发生在这种情况,即当两个区块在很短的时间间隔内被挖出来,并且是以相反的顺序被接收到(即子区块比父区块先接收到)。

通过选择最大累积工作量(greatest-cumulative-work)的区块链,所有的节点最终在全网范围内达成共识。随着更多的工作量证明被添加到链中,链之间的暂时差异最终会得到解决。挖矿节点通过选择将新挖出的区块扩展到哪条链就代表着用他们的挖矿算力进行“投票”给哪条链。

下一节我们将分析​相互竞争的链(forks)之间的差异是如何通过独立选择最大累积工作量的区块链来解决的。

10.10.1 区块链分叉(Blockchain Forks)
由于区块链是去中心化的数据结构,所以不同副本之间不是总保持一致的。区块有可能到达不同节点的时间是不同的,导致节点有不同的区块链全貌。解决的办法是,每一个节点总是选择并尝试延长代表着最大工作量证明的区块链,也就是最长的链或者说最大累积工作量的链(greatest cumulative work chain)。节点通过累加链上的每个区块的工作量,可以得到建立这个链所要付出的工作量的总量。只要所有的节点选择最长累计工作量的区块链,整个比特币网络最终会趋于(converges)一个一致的状态。不同区块链之间发生的临时差异导致的分叉(forks),随着更多的区块添加到其中的分叉中而最终趋于一致(eventual reconvergence ),这个问题得到解决。

​提示
本节中描述的区块链分叉是由于整个比特币网络中的传输延迟而自然发生的。 我们也将在本章稍后部分分析故意引起的分叉(deliberately induced forks)。

在接下来的几个图表中,我们将通过网络跟踪“fork”事件的过程。 该图是比特币网络的简化表示。 为了便于描述,不同的区块被显示为不同的形状(星形,三角形,倒置三角形,菱形),遍布网络。 网络中的每个圆表示一个节点。

每个节点都有自己的全局区块链视图。 当每个节点从其邻居接收到区块时,它会更新其自己的区块链副本,选择最大累积工作量的链。 为便于描述,每个节点包含一个代表着区块的图形,表示它相信该区块是主链的末端区块。 因此,如果你在节点里面看到一个星形,那就意味着该节点认为星形代表的区块处于主链的末端。

在第一张图(图10-2)中,网络有一个统一的区块链视角,星形区块作为主链的末端区块。
这里写图片描述
Figure 10-2. Before the fork — all nodes have the same perspective

​当同时有两个候选区块竞争想要延长最长区块链时,分叉事件就会发生。一般情况下,分叉发生在两名矿工在较短的时间 间隔内,各自都算得了工作量证明算法解的时候。两个矿工在各自的候选区块都发现了一个解,便立即传播自己的“获胜”区块给最接近的相邻节点,该相邻节点会将区块传播到整个网络。每个节点收到有效区块后都会将其并入区块链,将区块链延长了一个区块。如果该节点在随后又收到了另一个候选区块,而这个候选区块又拥有同样父区块,那么节点会将这个候选区块连接到备用链(secondary chain)上。其结果是,一些节点先收到了一个候选区块,而另一些节点先收到了另一个候选区块,这时两个竞争版本的区块链就出现(emerge)了。
在图10-3中,我们看到两个矿工(NodeX和NodeY)几乎同时(simultaneously)挖到了两个不同的区块。这两个区块都是星形区块的子区块,可以构建在星形区块上从而延长这个区块链。为了方便追踪,我们把节点X产生的区块标记为三角形,把节点Y生产的区块标记为倒三角形。
这里写图片描述
Figure 10-3. Visualization of a blockchain fork event: two blocks found simultaneously

​例如,我们假设矿工节点X为三角形区块找到了一个工作量证明的解,构建在星形父区块的顶端,扩展区块链。与此同时,节点Y也为倒三角形区块(他的候选区块)找到了一个工作量证明的解,扩展星形区块所在区块链。现在有两个可能的区块,节点X产生的三角形区块和节点Y产生的倒三角形区块,这两个区块都是有效的,均包含一个工作量证明的有效解,并且都延长同一个父区块(星形区块)。这两个区块可能包含了大多数相同的交易,只是在交易的排序上可能有些许不同。

​当两个区块开始在网络传播时,一些节点首先接收到三角形区块,另外一些节点首先接收倒三角形区块。如下图10-4所示,比特币网络上的节点对于区块链的视图分成了两种,一派以三角形区块为顶点,而另一派以倒三角形区块为顶点。
这里写图片描述
Figure 10-4. Visualization of a blockchain fork event: two blocks propagate, splitting the network

在图中,随机选择的节点X首先接收到三角形块,并用它扩展星形区块链所在的链。节点X选择了三角形区块所在的链作为主链。之后,节点X也收到了倒三角区块。由于是第二收到的,因此这个倒三角形区块被判定为已经竞争失败了。然而,倒三角形区块不会被丢弃,它被链接到星形父区块,并形成了一个备用链。虽然节点X认为自己已经正确选择了获胜的链,但是它还是会保存“失败”的链,使得它保存着继续汇集所需的信息,因为“失败”的链最终是有可能“获胜”的。

​在网络的另一端,节点Y根据自己的事件序列的视角构建一个区块链。它首先获得倒三角形区块,并选择那条链作为“赢家”。当它稍后收到三角形区块时,它也将三角形区块连接到星形父区块并形成了一个备用链。

​双方都是“正确的”或“不正确的”。两者都是自己关于区块链的有效视图。只有之后才会知道其中到底哪一个获胜(prevail),由这两条竞争链是如何被其他工作量延伸所决定。

区块链视图与节点X相类似的挖矿节点将立即开始挖掘候选区块,延长到以“三角形”区块作为末端区块的链上。通过将三角形作为候选区块的父区块,意味着它们用自己的哈希算力进行投票。它们的投票表明支持已经选择作为主链的那条链。

​同样,区块链视图与节点Y相类似的挖矿节点将开始构建一个以倒三角形区块作为其父区块的候选区块,扩展它们认为是主链的那条链。现在,比赛再次开始。

分叉问题几乎总是在一个区块内就被解决了。网络中的一部分算力专注于“三角形”区块为父区块,在其之上建立新的区块;另一部分算力则专注在“倒三角形”区块上。即便算力在这两个阵营中平均分配,也总有一个阵营抢在另一个阵营前找到工作量证明的解并将其传播出去。我们可以举个例子,假如基于“三角形”区块工作的矿工找到了一个“菱形”区块,延长了区块链(星形-三角形-菱形),他们会立刻传播这个新区块,整个网络都将视其为一个有效解,如下图10-5所示。

这里写图片描述

Figure 10-5. Visualization of a blockchain fork event: a new block extends one fork, reconverging the network

​选择“三角形”作为上一轮比赛的胜出者的所有节点将简单地将区块链扩展一个区块,然而,选择“倒三角”作为获胜者的节点现在将看到两个链:星形-三角形-菱形和星型-到三角形。星形-三角形-菱形这条链现在比其他链条更长(更多累积的工作量)。因此,这些节点将星形-三角形-菱形设置为主链,并将星型-倒三角形链变为备用链,如图10-6所示。这是一个链的重新共识,因为这些节点不得不(are forced to)修改他们对区块链的视图,把新的证据纳入更长的链。任何从事延伸星形-倒三角形 这条链的矿工现在都将停止这项工作,因为他们的候选区块是“孤儿”,因为他们的“倒三角形”父区块不再处于最长的链中。 “倒三角形”区块内的交易重新插入到内存池中用来包含在下一个区块中,因为它们所在的区块不再位于主链中。整个网络重新回到单一区块链状态,星形-三角形-菱形,“菱形”成为链中的最后一个区块。所有矿工立即开始在以“菱形”区块为父区块的候选区块上工作,以扩展这条星形-三角形-菱形链。
这里写图片描述
Figure 10-6. Visualization of a blockchain fork event: the network reconverges on a new longest chain

从理论上来说,发生分叉并在两个区块上各自延伸是有可能的,这种情况发生在因先前分叉而相互对立起来的矿工,又几乎同时发现了两个不同的区块。然而,这种情况发生的几率是很低的。单区块分叉(one-block fork)每天都会发生,而双块分叉(two-block fork)则最多是每几个星期发生一次。

比特币将区块间隔设计为10分钟,是在更快速的交易确认(settlement of transactions,交易结算)和分叉的概率间作出的折中方案。更短的区块产生间隔会让交易清算更快地完成,也会导致更加频繁地区块链分叉,与之相对地,更长的间隔会减少分叉次数,却会导致更长的清算(settlement)时间。

10.11 挖矿和算力竞赛(Mining and the Hashing Race)

比特币挖矿是一个极富竞争性的行业。自从比特币存在开始,每年比特币算力都成指数增长。一些年份的增长还体现出技术的变革,比如在2010年和2011年,很多矿工开始从使用CPU升级到使用GPU,进而使用FGPA(field programmable gate array,现场可编程门阵列)挖矿。在2013年,ASIC挖矿的引入,引起了挖矿算力的另一次巨大飞跃,它把SHA256算法直接固化在挖矿专用的硅芯片上。一台采用这种芯片的矿机可以提供的算力,比2010年的比特币全网算力还要大。

下面的列表显示了比特币网络最初五年的总算力:
2009
0.5 MH/sec–8 MH/sec (16× growth)
2010
8 MH/sec–116 GH/sec (14,500× growth)
2011
16 GH/sec–9 TH/sec (562× growth)
2012
9 TH/sec–23 TH/sec (2.5× growth)
2013
23 TH/sec–10 PH/sec (450× growth)
2014
10 PH/sec–300 PH/sec (3000× growth)
2015
300 PH/sec-800 PH/sec (266× growth)
2016
800 PH/sec-2.5 EH/sec (312× growth))

在图10-7的图表中,我们可以看到近两年里,矿业和比特币的成长引起了比特币网络算力的指数增长(每秒网络总算 力)。
这里写图片描述
Figure 10-7. Total hashing power, terahashes per second (TH/sec)

随着比特币挖矿算力的爆炸性增长,难度也相应增长从而与之匹配。图10-8中的相对难度值显示了当前难度与最小难度 (第一个块的难度)的比例。
这里写图片描述
Figure 10-8. Bitcoin’s mining difficulty metric

近两年,ASIC芯片变得更加密集(denser),特征尺寸接近硅芯片制造业(silicon fabrication )前沿的(cutting edge)16纳米。目前,ASIC制造商的目标是超越(overtake)通用CPU芯片制造商,设计特征尺寸为14纳米的芯片,因为挖矿的利润率驱动这个行业以比通用计算(general computing)更快的速度发展。对比特币挖矿而言,已经没有更多巨大飞跃的空间,因为这个行业已经触及了摩尔定律的最前沿,摩尔定律指出计算密度大约每18个月增加一倍。尽管如此,随着更高密度芯片的竞赛和对应的可部署几千个这种芯片的高密度数据中心的竞赛,网络算力继续保持指数增长。现在的竞争已经不再是比较单一芯片能挖多少矿,而是一个矿场能塞进(be squeezed into)多少芯片,并处理好散热(dissipating the heat)和提供足够的(adequate)电力问题。

10.11.1 随机值升位方案 (The Extra Nonce Solution)

自2012年以来,比特币挖矿发展出一个解决区块头结构的根本性限制的方案。在比特币的早期,矿工可以通过遍历随机数 (Nonce)直到生成的哈希值小于target从而挖出一个区块。随着难度的增加,矿工经常在尝试了40亿个nonce值后仍然没有出块。然而,这很容易通过更新区块的时间戳并计算经过的时间来解决。因为时间戳是区块头的一部分,它的变化可以让矿工再次遍历nonce的值。然而,当挖矿硬件的速度超过了4GH/秒,这种方法变得越来越困难,因为随机数的取值在一秒内就被用尽了。随着ASIC矿机的出现并且超过了TH/秒的hash速率后,挖矿软件为了找到有效的块,需要更多的空间来储存nonce值。时间戳可以延伸一点,但如果把它移动得太过遥远(too far into the future),会导致区块变为无效。区块头里面需要一个新的“改变”的源头。解决方案是使用coinbase交易作为额外随机值的来源,因为coinbase脚本可以储存2-100字节的数据,矿工们开始使用这个空间作为额外随机值的空间,允许他们去探索一个大得多的区块头值的范围来找到有效的区块。这个coinbase交易包含在merkle树中,这意味着任何coinbase脚本的变化将导致Merkle根的变化。8个字节的额外随机数,加上4个字节的“标准”随机数,允许矿工每秒尝试2^96(8后面跟28个零)种可能性而无需修改时间戳。如果未来矿工尝试完了以上所有的可能性,他们还可以通过修改时间戳来解决。同样,coinbase脚本中也有更多的空间可以为将来随机数的额外空间扩展做准备。

10.11.2 矿池
在这个激烈竞争的环境中,个体矿工独立工作(也称为solo矿工)没有一点机会(don’t stand a chance)。他们找到一个区块以抵消电力和硬件成本的可能性非常小,以至于可以称得上是赌博,就像是买彩票。就算是最快的消费型ASIC挖矿系统也不能和那些在巨大机房里拥有数万芯片并靠近水电站的商业矿场竞争。现在矿工们合作组成矿池,汇集数以千计的参与者的算力并分享奖励。通过参加矿池,矿工们得到整体回报的一小部分,但通常每天都能得到,因而减少了不确定性。

让我们来看一个具体的例子。假设一名矿工已经购买了算力共计14,000GH/S,或14TH/S的设备,在2017年,该设备大约需要花费2500美元。该设备运行时功率为1.3千瓦(KW),每日耗电32千瓦时,以很低的电费率(electricity rates)计每日平均成本1或2美元。以目前的比特币难度,该矿工solo方式挖出一个区块大约需要4年。如果这个矿工确实在这个时限内挖出一个区块,奖励12.5个比特币,每个比特币价格大约为1000美元,可以得到12,500美元的收入。这甚至不能覆盖该期间内的硬件和电力消耗的全部成本,净亏损约为1,000美元。而且,在4年的时间周期内挖出一个区块的机会依赖于矿工的运气。他有可能在4年中挖出两个区块从而赚到非常大的利润。或者,他有可能5年都挖不到一个块,从而遭受巨大的经济损失。更糟的是,比特币的工作量证明 (POW)算法的难度可能在这段时间内显著上升(go up significantly),按照目前哈希算力的增长速度,这意味着矿工在设备被下一代更有效率的矿机取代之前,最多1年的时间要实现收支平衡(break even)。如果这个矿工加入矿池,而不是等待4年内可能出现一次的$12,500的意外收获(windfall),他每周能赚取大约50-60美元。矿池的定期支付能帮他随时间分期偿还(amortize)硬件和电力的成本,并且不用承担巨大的风险。在1-2年后,硬件仍然会过时,风险仍然很高,但是在此期间的收入(revenue)至少是不变的和可靠的。从经济上分析,这只有在非常低的电力成本(低于1美分每千瓦),非常大的规模时才有意义。

矿池通过专用挖矿协议(specialized pool-mining protocols)协调(coordinate)成百上千的矿工。个人矿工在创建矿池账号后,配置他们的矿机以连接到矿池服务器。他们的挖矿设备在挖矿时保持和矿池服务器的连接,和其他矿工同步各自的工作。这样,矿池中的矿工分担挖矿任务,之后分享奖励。

成功出块的奖励支付到矿池的比特币地址,而不是单个矿工的。一旦奖励达到一个特定的阈值,矿池服务器便会定期支付奖励到矿工的比特币地址。通常情况下,矿池服务器会为提供矿池服务收取一个百分比的费用。

参加矿池的矿工把候选区块的搜寻解的工作量分割,并根据他们挖矿的贡献赚取“份额”。矿池为赚取“份额”设置了一个比较高的target(低难度),通常比比特币网络难度低1000倍以上。当矿池中有人成功挖出一块,矿池获得奖励,并和所有矿工按照他们做出贡献的“份额”数的比例进行分配。

矿池对任何矿工开放,无论大小、专业或业余(armateur)。一个矿池的参与者中,有人只有一台小矿机,而有些人有一车库高端(high-end)挖矿硬件。有人只用几十度电挖矿,也有运行一个数据中心的人会消耗兆瓦级的电量。矿池如何衡量每个人的贡献,从而公平分配奖励,避免作弊的可能呢?答案是使用比特币的工作量证明算法来衡量每个矿工的贡献,但是设置一个较低的难度,因此,即使是池中最小的矿工也能经常分得奖励,这足以激励他们为矿池做出贡献。通过设置一个较低的赚取份额的难度,矿池可以计量出每个矿工完成的工作量。每当矿工发现一个小于矿池难度(pool target)的区块头哈希,就证明了它已经完成了寻找结果所需的哈希计算工作。更重要的是,这些工作,能以一个统计学上可衡量的方法,代表为寻找比比特币网络的目标难度值更低的哈希所做的全部工作所做的贡献。成千上万的矿工尝试寻找低的哈希值,最终可以找到符合比特币网络target要求的足够低的哈希值。

让我们回到骰子游戏的类比。如果骰子玩家的目标是扔骰子结果都小于4(整体网络难度),一个矿池可以设置一个更容易的目标,统计有多少次池中的玩家扔出的结果小于8。当池中的玩家扔出的结果小于8(矿池份额目标),他们赚取到份额,但他们没有赢得游戏,因为没有完成游戏目标(小于4)。但池中的玩家会更经常的达到较容易的矿池难度目标,非常有规律地赚取他们的份额,尽管他们没有完成更难的赢得比赛的目标。有时(Every now and then),池中的一个成员有可能会扔出一个小于4的结果,矿池获胜。然后,收益可以根据在池中玩家所赚取的份额进行分配。尽管<=8的目标并没有赢得游戏,但是这是一个衡量玩家们骰子扔出次数的公平方法,同时它偶然会产生一个小于4的结果。

同样的,矿池会设置一个矿池(更高,更容易的)的target,以使得矿工能经常找到小于矿池的target的区块头哈希, 从而赢取份额。有时(every now and then,),其中一次尝试会产生一个小于比特币网络target的区块头哈希,从而使之成为一个有效块,然后整个矿池获胜。

10.11.2.1 托管矿池(Managed pools)

大部分矿池是“托管的”,意思是有一个公司或者个人经营着一个矿池服务器。矿池服务器的所有者叫矿池管理员(pool operator),同时他从矿工的收入中收取一定百分比的费用。矿池服务器运行专业软件以及协调池中矿工们活动的矿池采矿协议。矿池服务器同时也连接到一个或多个比特币全节点并且可以直接访问一个区块链数据库的完整副本。这使得矿池服务器可以代替(on behalf of)矿池中的矿工验证区块和交易,缓解(relieve)他们运行一个完整节点的负担。对于矿池中的矿工,这是一个重要的考量(consideration),因为一个完整节点要求一个拥有最少100-150GB的永久储存空间(磁盘)和最少2GB到4GB内存(RAM)的专用计算机(dedicated computer)。

此外,运行在一个全节点上的比特币软件需要监控、维护和频繁升级。由于缺乏维护或资源导致的任何宕机时间(downtime)都会伤害到矿工的利润。对于很多矿工来说,不需要跑一个完整节点就能采矿,也是加入托管矿池的另一个大好处。

矿工连接到矿池服务器采用的是一个挖矿协议,比如Stratum (STM)或者 GetBlockTemplate (GBT)。一个旧标准GetWork (GWK) 自从2012年底已经基本上废弃(obsolete)了,因为它不支持在哈希速度超过4GH/S时采矿。STM和GBT协议都创建包含候选区块头模板的区块模板(block templates)。矿池服务器通过打包交易,添加coinbase交易(和额外的随机值空间),计算merkle root, 并连接到上一个区块哈希来建立一个候选区块。这个候选区块的头部作为模板发送给每个矿工。然后,矿工用这个区块模板在比比特币网络target更高的target(即更容易)下采矿,并发送成功的结果返回给矿池服务器以赚取份额。

10.11.2.2 P2P矿池(Peer-to-peer mining pool (P2Pool))

托管矿池存在矿池管理人作弊的可能,管理人可以利用矿池进行双重支付或使区块无效(参见“10.12 共识攻击”)。 此外,中心化的矿池服务器代表着单点故障(single-point-of-failure)。如果由于拒绝服务攻击(denial-of-service attack),导致服务器挂了或者被减慢,池中的矿工就不能采矿。在2011年, 为了解决这些中心化问题,提出和实施了一个新的矿池挖矿方法:P2Pool,P2Pool是一个点对点的矿池,没有中央管理人。

P2Pool通过将矿池服务器的功能去中心化,实现一个并行的类似区块链的系统,名叫份额链(share chain)。一个份额链是一个难度低于比特币区块链的区块链系统。份额链允许池中矿工在一个去中心化的矿池中合作,以每30秒一个份额区块的速度在份额链上采矿,并获得份额。份额链上的区块记录了贡献工作的矿工相应的(proportionate)份额,并且继承了前一个份额区块上的份额记录。当其中一个份额区块实现了比特币网络的难度目标时,它将被广播并包含到比特币的区块链上,并奖励所有为获胜的份额区块贡献份额的矿工。本质上说,份额链允许所有矿工通过类似比特币区块链系统的去中心化共识机制跟踪记录所有份额, 而不是由矿池服务器跟踪记录矿工的份额和奖励。

P2Pool挖矿比在矿池中挖矿要复杂的多,因为它要求矿工运行硬盘空间、内存、网络带宽充足的专用计算机来支持一个比特 币全节点和P2Pool节点软件。P2Pool矿工连接他们的采矿硬件到本地的P2Pool节点,它通过发送区块模板到矿机来模拟一个矿池服务器的功能。在P2Pool中,个体矿工创建自己的候选区块,打包交易,非常类似于solo矿工,但是他们之后是在份额链上合作挖矿。P2Pool是一种比单独挖矿有更细粒度收入优势的混合方法。但是不需要像托管矿池那样给管理人太多控制权。

即使P2Pool减少了矿池运营商的权力集中度,但也可能容易受到份额链本身的51%攻击。 P2Pool的广泛采用(much broader adoption)并不能解决比特币本身的51%攻击问题。 相反,作为多样化采矿生态系统(diversified mining ecosystem)的一部分,P2Pool整体来说使得比特币有更好的鲁棒性(more robust)。

10.12 共识攻击(Consensus Attacks)

比特币的共识机制指的是,被矿工(或矿池)试图使用他们自己的哈希算力实行欺骗或破坏的难度很大,至少理论上是这样。就像我们前面讲的,比特币的共识机制依赖于这样一个前提,那就是绝大多数的矿工,出于自己利益(out of self-interest)最大化的考虑,都会通过诚实地挖矿来维持整个比特币系统。然而,当一个或者一群矿工拥有了整个系统中的大量算力,他们就可以通过攻击比特币的共识机制来达到破坏比特币网络的安全性和可用性的目的。

值得注意的是,共识攻击只能影响未来的共识,或者说,最近的过去(最多影响过去10个块)。而且随着时间的推移,比特币账本变的越来越不容易更改。理论上,能够实现任意深度的分叉,但实际上,要想实现一个非常深的分叉需要的算力是巨大的,这使得旧区块几乎是不可篡改的(practically immutable)。同时,共识攻击也不会影响用户的私钥以及加密算法(ECDSA)的安全。共识攻击也不能偷窃比特币、不使用签名地花费比特币、更改比特币地址、改变过去的交易或者所有权纪录。共识攻击能够造成的唯一影响是影响最近的区块(最多10个)并且导致拒绝服务,中断(disruptions on)未来区块的生成。

共识攻击的一个典型场景就是“51%攻击”。在这个场景中,一群矿工控制了大部分(51%)的比特币全网算力,他们联合起来打算攻击比特币系统。由于这群矿工可以生成绝大多数的区块,他们就可以通过故意制造分叉来实现“双重花费”或者通过拒绝服务的方式来攻击特定的交易或者特定的地址。区块链分叉/双重支付攻击指的是攻击者通过在之前已经确认的区块下面分叉从而使得这些已经确认的区块无效,然后在另一条替代链上重新汇聚这些区块。有了充足算力的保证,一个攻击者可以接连(in a row)使最近的6个或者更多的区块无效,从而使得这些区块中的被认为是无法更改(6个确认)的交易变的无效。值得注意的是,双重支付只能在攻击者自己的交易上实现,因为只有攻击者自己才能生成一个合法的签名,用于双重支付交易。如果攻击者可以通过使交易无效而实现对于不可逆转的交易支出或者产品不予付款, 那么这种双重支付自己的交易是有利可图的。

让我们分析一个“51%攻击”的实际案例吧。在第1章我们讲到,Alice 和 Bob 之间使用比特币完成了一杯咖啡的交易。咖啡 店老板 Bob 愿意在 Alice 给自己的交易支付还未确认(被挖进一个区块中)的时候就向其提供咖啡,这是因为这种一杯咖啡的小额交易遭遇双花攻击的风险和快速顾客服务的方便性相比,显得微不足道。这就和大部分的咖啡店对低于25美元的信用卡消费不会向顾客索要签名是一样的,因为和顾客有可能拒付(chargeback)的风险比起来,向用户索要信用卡签名从而延迟交易造成的成本更高。相反,使用比特币支付的大额交易被双重支付的风险就高得多了,因为买家(攻击者)可以在全网广播一个花费相同输入(UTXO)的竞争交易(competing transaction),以达到取消向商家支付的目的。双重支付攻击可以有两种方式发生:要么是在交易被确认之前,要么攻击者利用区块链分叉撤销几个区块。51%攻击允许攻击者在新分叉的链上双重花费自己的交易(新生成一个同样金额的交易),因此,取消在旧链上对应的交易。

再举个例子:不怀好意的攻击者Mallory前往Carol的画廊买了一副描绘伟大的中本聪的三联组画,Carol将画以$250000的价格卖给Mallory,用比特币支付。并没有等待六个或者更多个交易确认之后,而仅仅只等待了一个确认之后,Carol就将这幅画包装好,并交给了Mallory。这时,Mallory的一个同伙Paul,他经营着一个巨大的矿池,当Mallory的这笔交易被写进一个区块时,同伙Paul就启动了一个51%攻击。首先,Paul利用自己矿池的算力重新挖出一个与包含Mallory的那笔交易的区块相同高度的一个新区块,并且在新区块里将原来的Mallory支付给Carol的交易替换成了另外一笔交易,双重花费同一笔输入。这笔双重支付交易使用了跟原有交易相同的UTXO,但付款到了Mallory的钱包地址,而不是付给Carol,本质上就是允许Mallory持有这笔比特币。然后,Paul利用矿池再挖出一个新的区块,这样,包含这笔“双重支付”交易的区块链比原来的区块链长(即在包含Mallory的交易的那个区块下产生了一个分叉)。当区块链分叉支持了新的链(更长的),双重支付交易就取代了原来支付给Carol的交易。现在,Carol失去了三幅昂贵的画,同时也没有收到价值25万美金的比特币付款。在整个过程中,Paul矿池里的矿工可能自始至终都没有觉察到这笔双重支付交易,因为他们都是使用自动化的挖矿机进行挖矿的,并且不能监控每一笔交易或者每一个区块。

为了避免这类攻击,售卖高价值商品的商家在交付商品给买家之前必须等待交易至少有6个确认。或者,商家应该使用第三方的多重签名账户(escrow multisignature account)进行交易,并且使用第三方账户后也需要等待交易获得数个确认。确认数越多,越难通过51%攻击使交易无效。对于高价值的商品,即使买家在付款之后需要等待24小时之后卖家才能发货(delivery), 使用比特币支付也是方便和高效的,而这24小时,大约可以达到144个全网确认数。

共识攻击中除了“双重支付”攻击,还有一种攻击场景就是拒绝对某个特定的比特币参与者(特定的比特币地址)提供服务。一个拥有了系统中绝大多数挖矿算力的攻击者,可以轻易地忽略某一笔特定的交易。如果这笔交易存在于由另一个矿工所挖的区块中,该攻击者可以故意分叉,然后重新挖出相同高度的区块,仍然可以移除特定的交易。只要攻击者控制了绝大多数算力,这种类型的攻击就会造成持续的对某一个或某一批特定地址拒绝服务。

并不像名字那样,51%攻击场景并不真正一定要51%的算力,实际上,即使使用较小百分比的哈希算力,依然可以尝试发起这种攻击。51%阈值是指在这个水平的算力其发起的攻击尝试几乎保证是成功的。本质上来看,共识攻击就是下一个区块的拔河比赛(tug-of-war) ,"强大"的那组更有可能获胜。拥有较少的哈希算力,那么获胜的概率就会减少,因为其他矿工用他们的"诚实"挖矿算力控制着区块的生成。从另一个角度看,一个攻击者拥有的算力越多,他刻意创造的分叉就可能越长,他能使之无效的最近过去的区块就会越多,他能控制的未来的区块就越多。一些安全研究组织利用统计模型声称(claim that),算力达到全网的30%就有可能发起共识攻击了。

全网哈希算力的急剧增长已经使得比特币系统不再可能被单个矿工攻击,因为solo矿工已经不可能占据全网很小比例的挖矿算力。但是,中心化控制的矿池则引起了矿池操作者出于利益而施行攻击的风险。托管矿池的矿池操作者控制着候选区块的生成,同时也控制着哪些交易会被包含进新生成的区块中。这给了矿池操作者剔除特定交易或者引入双重支付交易的权力。如果这种权利的滥用被矿池操作者在一定范围内不明显的使用的话 ,那么矿池操作者就可能在不为人知的情况下发动共识攻击并获益。

然而,并不是所有的攻击者都是为了利益。一个可能的攻击场景就是,攻击者仅仅是为了破坏(disrupt)整个比特币网络,而不是为了利益。这种恶意的(malicious)旨在破坏比特币的攻击需要巨大的投入和精心的计划,但是,是有可能被资金充足的、政府支持的(state-sponsored)攻击者发起的。或者,这类资金充足的攻击者也会积聚挖矿硬件,说服矿池运营者,通过拒绝服务攻击其他矿池等来攻击比特币共识机制。所有这些场景理论上都是可行的,但是,随着比特币全网哈希算力持续呈指数级增长,这些攻击场景变的越来越不切实际(increasingly impractical)。

毫无疑问,一次严重的共识攻击事件短期内势必会削弱(erode)人们对比特币系统的信心,有可能导致比特币价格的显著下降(causing a significant price decline)。然而,比特币系统和相关软件也一直在持续改进( constantly evolving),所以比特币社区会对共识攻击快速做出对策,以使整个比特币系统比以往更加稳健和可靠。

10.13改变共识规则(Changing the Consensus Rules)

共识规则决定着交易和区块的有效性(validity)。 这些规则是所有比特币节点之间协作的基础,并且负责将所有本地视角汇聚到全网络一致的单一区块链中。

虽然共识规则在短期内是不变的(invariable),并且在所有节点之间必须一致(consistent),但长期来看它们并不总是不变的。 为了演进和开发比特币系统,共识规则必须随时(from time to time)改变以适应新功能,改进,或修复bug。然而,与传统软件开发不同,升级到共识系统要困难得多,需要所有参与者之间的协调(coordination)。

10.13.1硬分叉(Hard Forks)

在前面章节比特币分叉(“1010.1 Blockchain Forks”)中,我们研究了比特币网络如何短暂地分叉(briefly diverge),网络中的两个部分在短时间内处于区块链的两个不同分支。我们看到这个过程作为网络的正常运行的一部分,是如何自然发生的,以及网络如何在一个或多个区块被挖掘之后,重新汇集到一个共同的区块链。

另一种场景,网络也可能会分叉到两条链:这是由于共识规则发生了改变。这种分叉称为硬分叉(hard fork),因为这种分叉后,网络不会重新收敛到单条链上。而是,这两条链各自独立发展(evolve independently)。当比特币网络的一部分节点按照与网络中的其他节点不同的共识规则运行时,硬分叉就会发生。 这可能是由于bug或者由于权衡之后改变了共识规则的实现而发生的。

硬分叉可被用于改变共识规则,但需要系统中的所有参与者的协调配合。没有升级到新共识规则的任何节点都不能参与共识机制,并且在硬分叉时刻就会被强制到单独的链上(are forced onto a separate chain at the moment of the hard fork)。因此,硬分叉引起的改变可以被认为是不“向前兼容”,因为未升级的系统不能再处理新的共识规则。

让我们用具体的例子来分析一下硬分叉的技术。
下图10-9显示区块链出现两个分叉。在块高度4处,发生单一个区块分叉(one-block fork)。这是我们在前面章节“Blockchain Forks”中看到的自发分叉(spontaneous fork)的类型。随着区块5的挖掘,网络重新收敛到一条链上,分叉被解决了。

这里写图片描述
Figure 10-9. A blockchain with forks

然而,之后在区块高度6处发生了硬分叉。我们假设原因是由于共识规则的变化发布了新实现的客户端版本。从块高度7开始,运行新版本的矿工,将接受新类型的数字签名(digital signature),我们称之为“Smores”签名,它不是基于ECDSA的签名。紧接着,运行新版本的节点创建了一笔包含Smores签名的交易,然后一个更新了软件的矿工挖矿出了包含此交易的区块7b。

任何尚未升级软件的节点或者矿工,在验证Smores签名时都无法处理区块7b。从他们的角度来看,包含Smores签名的交易和包含该交易的区块7b都是无效的,因为他们是根据旧的共识规则进行评估的。这些节点将拒绝该笔交易和区块,并且不会传播它们。正在使用旧规则的任何矿工都不接受区块7b,并且将继续挖掘其父区块是区块6的候选区块。事实上,如果使用旧规则的矿工连接的所有节点也都是遵守旧规则的,那么他们就不会传播这个区块,因此该使用旧规则的矿工甚至可能接收不到区块7b。最终,他们将开采区块7a,该区块在旧的规则下是有效的,并且他不包含任何有Smores签名的交易。

这两条链从此处继续分叉。 “b”链上的矿工将继续接受并开采含有Smores签名的交易,而“a”链上的矿工将继续忽视这些交易。即使区块8b不包含任何Smores签名的交易,“a”链上的矿工也无法处理。对他们来说,它似乎是一个孤儿区块(orphan block),因为它的父区块“7b”不被识别为一个有效的区块。

10.13.2硬分叉:软件,网络,采矿和链(Hard Forks: Software, Network, Mining, and Chain)

对于软件开发人员来说,术语“分叉”具有另一个含义,对“硬分叉”一词增加了困惑。在开源软件中,当一组开发人员选择遵循不同的软件路线图并启动开源项目的竞争实现时,会发生分叉。我们已经讨论了两种会导致硬分叉的情况:共识规则中的bug,以及对共识规则的有意的修改。在有意改变共识规则的这种情况下,软件分叉发生在硬分叉之前。但是,对于这种类型的硬分叉,实现了新共识规则的新版本软件必须被开发,采用和启动后,才会发生。

试图改变共识规则的软件分叉的例子包括Bitcoin XT,Bitcoin Classic和最近的Bitcoin Unlimited。但是,这些软件分叉都没有产生硬分叉。虽然软件分叉是一个必要的前提条件,但它本身不足以发生硬分叉。硬分叉要发生,相互竞争的实现方案(competing implementation)必须被采用,并且新的共识规则需要被矿工,钱包和中间节点(intermediary nodes)激活。相反,有许多比特币核心客户端的替代实现方案(alternative implementations),甚至还有未改变共识规则和消除bug的软件分叉,能够在网络上共存(coexist)并相互操作(interoperate),最终并未导致硬分叉。

不同的共识规则在交易或区块的验证中会有明确的和清晰的不同的方式。共识规则也可能是有轻微的差别,比如用于比特币脚本或加密原语(如数字签名)的共识规则的实现。最后,共识规则还可能由于不曾预料的(unanticipated)的方式从而表现的不同,比如由于系统限制或实现细节所产生的隐含共识约束。一个后者的例子(即不同的实现细节)就是在将Bitcoin Core 0.7升级到0.8期间发生的意料之外的硬分叉,这是由于用于存储区块的Berkley DB的实现的一些限制引起的。

从概念上讲,我们可以将硬分叉看成四个发展阶段:软件分叉( a software fork),网络分叉(a network fork),挖矿分叉(a mining fork)和区块链分叉(a chain fork)。

该过程始于开发者创建的替代实现方案的客户端,这个客户端对共识规则进行了修改。

当这种新版本的客户端部署在网络中时,一定百分比的矿工,钱包用户和中间节点可能采用并运行该版本客户端。产生的分叉将取决于新的共识规则是否用于区块,交易或系统的其他方面。如果新的共识规则与交易有关(pertain to transactions),那么使用新规则的钱包创建一笔交易时就产生了一个网络分叉,紧接着,当该交易被挖进一个区块时就产生了一个硬分叉。如果新共识规则与区块有关,那么硬分叉过程始于当一个区块是根据新共识规则挖出来时。

首先,是网络分叉。基于旧的共识规则实现的节点将拒绝任何根据新规则创建的交易和区块。此外,遵循旧的共识规则的节点将暂时禁止和断开与发送这些无效交易和区块的节点的连接。因此,网络将分为两部分:旧节点将只连接到旧节点,新节点将只连接到新节点。基于新规则的单个交易或区块将通过网络传播,导致出现两个网络。

一旦使用新规则的矿工开采出了一个区块,挖矿算力和区块链也将分叉。新的矿工将在新区块上挖掘,而老矿工将根据旧的规则挖掘一个单独的链。分割的网络将使得矿工按照各自的共识规则工作着,将不会接收到彼此的区块,因为它们连接到的是两个分割的网络。

10.13.3分离矿工和难度(Diverging Miners and Difficulty)

随着矿工们被分裂成开采两条不同的链,链之间的哈希算力也被分开。两条链之间的挖矿算力可以按照任意比例分开。新的规则可能只有少数算力跟随,或者也可能是绝大多数算力。

我们假设,例如,80%-20%的分割,大多数挖矿能力使用新的共识规则。我们还假设分叉在难度调整周期(retargeting period)之后立即出现。

这两条链将各自从难度调整周期中继承难度。新的共识规则拥有80%的之前的可用的挖矿算力。从这条链的角度来看,与上一周期相比(vis-a-vis the previous period),挖矿算力突然下降了20%。区块将会平均每12分钟挖出一次,这意味着可以扩大这条链的挖矿算力下降了20%。这个区块发行速度将持续下去(除非哈希算力发生了改变),直到2016个区块被开采出,这将大约需要24,192分钟(每个区块需要12分钟),或者16.8天。 16.8天后,难度调整将发生,难度(difficulty)将降低20%,使得重新每10分钟产生一个区块,这个难度调整是基于此链中哈希算力的减少量。

少数算力的那条链,根据旧规则继续挖矿,现在只有20%的哈希算力,将面临更加艰巨的任务。在这条链上,平均每50分钟挖出一个区块。这个难度将不会在2016个区块挖出之前进行调整,这将需要100,800分钟,或者大约10周的时间。假设每个区块具有固定容量,这也将导致交易量减少5倍,因为每小时可用于记录交易的区块太少了。

10.13.4有争议的硬分叉(Contentious Hard Forks)
这是共识软件开发的黎明。正如开源开发改变了软件的方法和产品,创造了新的方法论,新工具和新社区,共识软件开发也代表了计算机科学的前沿领域(a new frontier)。在比特币发展路线图的辩论(debates),实验(experiments)和艰难(tribulations)之中,我们将看到新的开发工具,实践,方法论和社区的出现。

硬分叉被视为有风险,因为它们迫使少数人被迫选择升级或是留在少数派链条上。将整个系统分为两个竞争系统的风险被许多人认为是不可接受的风险。结果,许多开发人员不愿(reluctant)使用硬分叉机制来实现对共识规则的升级,除非整个网络都能几乎达成一致(near-unanimous)。任何没有被几乎所有人支持的硬分叉建议被认为是“有争议”(contentious)的,不愿冒险分裂系统。

硬分叉的问题在比特币开发社区也是非常有争议的(controversial),尤其是与控制最大区块大小限制的共识规则的任何相关提议改变时。一些开发商反对任何形式的硬分叉,认为它太冒险了。另一些人认为硬分叉机制是升级共识规则的重要工具,避免了“技术债务”,并与过去提供了一个干净的了断(a clean break)。最后,有些开发商认为硬分叉机制应该很少使用,应该经过认真的计划,并且只有在几乎达成一致的共识下才建议使用。

我们已经看到出现了新的方法来解决(address)硬分叉的风险。在下一节中,我们将看一下软分叉,以及BIP-34和BIP-9信号和激活共识修改的方法。

10.13.5软分叉(Soft Forks)

并非所有共识规则的改变都会导致硬分叉。只有向前不兼容的(forward-incompatible)共识规则变化才会导致分叉。如果共识规则的改变也能够让未修改的客户端按照之前的规则仍然认为交易或者区块是有效的,那么就可以在不进行分叉的情况下实现共识修改。

软分叉(soft fork)就被引入用来与之前的硬分叉区分不同的升级方法。实际上,软分叉根本不是分叉。软分叉是共识规则的向前兼容(forward-compatible)的改变,它允许未升级的客户端程序能够按照新共识规则继续工作。

软分叉的一个不是很明显的方面就是,软分叉升级只能用于限制共识规则,而不是扩展共识规则。为了能够向前兼容,根据新规则创建的交易和区块在旧规则下也必须有效,但是反之不一定这样(not vice versa)。新共识规则只能限制什么是有效的,否则,当根据旧规则创建的交易和区块被拒绝时,还是会将触发硬分叉。

软分叉可以通过多种方式实现——该术语不是定义一个单一方法,而是一组方法,这组方法都有一个共同点:它们不要求所有节点都升级或强制未升级节点必须脱离共识(out of consensus)。

10.13.5.1软分叉重新定义NOP操作码(Soft forks redefining NOP opcodes)
基于对NOP操作码的重新解释(re-interpretation),在比特币中实现了许多软分叉。 Bitcoin脚本有10个操作码保留着供将来使用,NOP1到NOP10。 根据共识规则,这些操作码出现在脚本中时被解释为无效的(null-potent)运算符,这意味着它们没有任何效果。 在NOP操作码之后继续执行,就好像它不存在一样。

因此,软分叉可以修改NOP操作码的语义(semantics)从而给它新的含义。例如,BIP-65(CHECKLOCKTIMEVERIFY)重新解释了NOP2操作码。 实现了BIP-65的客户将NOP2解释为OP_CHECKLOCKTIMEVERIFY,并在其锁定脚本中包含该操作码的UTXO上,施加了绝对锁定时间(absolute locktime)的共识规则。 这种变化就是一个软分叉,因为在BIP-65下有效的交易在任何没有实现BIP-65的客户端上也是有效的。 对于旧的客户端,该脚本包含的NOP代码,将被忽略。

10.13.5.2其他软分叉升级的方式(Other ways to soft fork upgrade)

NOP操作码的重新解释既是计划的,也是很明显的一种共识升级的机制。然而,最近,引入了另一种软分叉机制,其不依赖于NOP操作码用于非常特定类型的共识变化。这在隔离见证(附录D Segregated Witness)中有更详细的检查。 Segwit是一个交易结构的体系结构变化(architectural change),它将解锁脚本(witness, 见证)从交易内部移动到一个外部数据结构(segregating it,将其隔离)。Segwit最初被设想为硬分叉升级,因为它修改了一个基本的结构(交易)。在2015年11月,一位从事Bitcoin Core工作的开发人员提出了一种机制,通过这种机制,可以将segwit作为软分叉引入。该机制是指修改在segwit规则下创建的UTXO的锁定脚本,使得未修改的客户端将任何锁定脚本视为可被任何解锁脚本赎回。因此,segwit可以被引入,而不需要每个节点都升级或产生区块链分叉,即软分叉。

很有可能还有其他向前兼容的软分叉升级机制有待发现。

10.13.6对软分叉的批评(Criticisms of Soft Forks)

基于NOP操作码的软分叉是相对无争议的(uncontroversial)。 NOP操作码被放置在比特币脚本中,明确的目标就是允许无干扰的(non-disruptive)升级。

然而,许多开发人员担心其他软分叉升级方式会产生不可接受的折衷(tradeoffs)。对软分叉更改的常见批评包括:

技术性债务(Technical debt)
由于软分叉在技术上比硬分叉升级更复杂,所以会引入技术性债务(technical debt),这术语是指由于过去的设计的权衡取舍而导致未来增加代码维护的成本。代码复杂性又增加了bug和安全漏洞的可能性。

验证放松(Validation relaxation)
未经修改的客户端将交易视为有效,而不评估修改了的共识规则。实际上(In effect),未经修改的客户端不会使用全面的共识规则进行验证,因为它们无视新规则。这适用于基于NOP的升级,以及其他软分叉升级。

不可逆转升级(Irreversible upgrades)
因为软分叉产生的交易带有额外的共识约束,所以它们在实践中成为不可逆转的升级。如果软分叉升级在被激活后想倒退回去,那么任何根据新规则创建的交易都可能导致资金流失。例如,如果根据旧规则对CLTV交易进行评估,则不存在任何时间锁定的约束,因此可以在任何时间花费。因此,评论家认为(contend),由于bug而不得不被回退的失败的软分叉将几乎肯定会导致资金的流失。

10.14使用区块版本发出软分叉信号(Soft Fork Signaling with Block Version)

由于软分叉允许未经修改的客户在共识规则下继续运作,“激活”软分叉的机制是通过矿工的准备就绪的信号实现的:大多数矿工必须同意他们准备并愿意执行新的共识规则。 为了协调他们的行动,有一个信号机制,使他们能够表达他们对共识规则改变的支持。 该机制是在2013年3月随着BIP-34的激活被引入的,并在2016年7月随着BIP-9的激活而被取代。

10.14.1 BIP-34信号和激活

在BIP-34中的第一个实现中使用区块版本字段来允许矿工表示对特定的共识规则更改已经准备就绪。在BIP-34之前,将区块版本设定为“1”,是按照惯例(convention),而不是根据共识规则。

BIP-34定义了一个共识规则变更,要求将Coinbase交易的coinbade字段(输入)包含区块高度。在BIP-34之前,coinbase字段可以让矿工选择包含进任意数据。在BIP-34激活之后,有效区块必须在coinbase的起始处包含特定的区块高度,并且使用大于或等于“2”的版本号进行标识。

为了表示BIP-34已激活并已做了相应更改,矿工们将区块版本号设置为“2”而不是“1”。这不会立即使版本号是“1”的区块无效。一旦激活,版本号是“1”的区块将变得无效,并且所有版本号是“2”的区块在coinbase中需要包含区块高度,才能有效。

BIP-34基于1000个区块的滚动窗口定义了两步激活机制。矿工将通过构建版本号是“2”的区块表示他已对BIP-34已经准备就绪。严格来说,由于共识规则目前尚未被激活,所以这些区块尚不需要遵守(comply)在coinbase交易中包含区块高度的新共识规则。共识规则分为两个步骤激活:

  • 如果75%(最近的1000个区块有750个)标有版本“2”,则版本号是“2”的区块的coinbase交易中必须包含区块高度,否则会被视为无效并被拒绝。版本号是“1”的区块仍然被网络接受,不需要包含区块高度。 在此期间,新旧共识规则共存(coexist)。

  • 当95%(最近1000个区块有950个)是版本“2”时,版本号是“1”的区块将不再被视为有效。版本号是“2”的区块只有当它们的coinbase交易中包含有区块高度(根据先前阈值)时才有效。此后(Thereafter),所有区块必须遵从(comply)新的共识规则,所有有效区块的coinbase交易中必须包含区块高度。

根据BIP-34规则成功发信号和激活后,该机制再次使用两次以激活软分叉:

  • BIP-66 用区块版本号"3"表示签名的严格DER编码(Strict DER Encoding of Signatures)已经按照BIP-34样式激活,并且使版本号是“2”的区块无效。

  • BIP-65 用区块版本号"4"表示CHECKLOCKTIMEVERIFY已经按照BIP-34样式激活,并且使版本号是“3”的区块无效。

BIP-65激活之后,BIP-34的信号和激活机制将退休,并被接下去描述的BIP-9信号机制所代替。

这个标准在BIP-34(Block v2, Height in Coinbase)中定义。

10.14.2 BIP-9信号和激活(BIP-9 Signaling and Activation)

BIP-34,BIP-66和BIP-65使用的机制成功地激活了三个软分叉。 然而,它又被取代了,因为它有几个限制:

  • 通过使用区块版本号的整数值,一次只能激活一个软分叉,因此需要协调软分叉的提议和同意的优先次序。
  • 此外,由于区块版本是递增的,所以机制并没有提供一种直接的方式来拒绝变更并提出一个不同的方法。如果旧客户端仍然在运行,他们可能会错误地将信号认为是新的更改,而其实是以前拒绝更改的信号。
  • 每个新的更改都不可逆转地(irrevocably)减少了可用于将来更改的区块版本号。

BIP-9的提出就是用来克服这些挑战,提高实现未来变化的速度和便利性。

BIP-9将区块版本号解释为一个比特字段(a bit field)而不是一个整数。由于区块版本号最初用作整数,从版本1到4,因此只有剩余的29位可用作位字段。这剩余的29位,可以独立同时的用在29个不同的提案上表示准备就绪的信号。

BIP-9还设置了发信号和激活的最大时间。这样,矿工们就不需要一直发出信号。如果提案在TIMEOUT期间(在提案中定义)未被激活,则该提案就被视为被拒绝的。该提议可以使用不同位的信号重新提交,更新激活周期。

此外,在TIMEOUT过去后,该特征被激活或者被拒绝,信号位可以被再次用于另一个特征而不会混淆。因此,可以有29个更改可以并行同时的发出信号,TIMEOUT期过后可将这些位“再循环”用于提议新的更改。

注意
虽然信号位可以重复使用或回收利用,只要投票周期不重叠(overlap),但BIP-9的作者建议仅在有必要时才重复使用信号位,主要是由于旧软件中可能存在bug,可能会导致发生意外的行为。 总之,我们不应该期望在所有29位都被使用一次之前重用信号位。

建议的更改由包含以下字段的数据结构标识:

名称(name)
用于区分提案的简短描述。 通常,BIP将提案描述为“bipN”的形式,其中N是BIP的编号。

位(bit)
用区块版本号字段相应的位表示,从0到28,矿工使用它表示批准此提案。

开始时间(starttime)
该时间(based on Median Time Past, or MTP, 基于中位时间过去),是指相应位的值被译作提议的信号准备就绪的开始时间。

结束时间(endtime)
该时间(基于MTP)是指,如果尚未达到激活阈值,提议的更改被认为拒绝的时间。

与BIP-34不同,BIP-9基于2016个区块的难度调整周期,在整个间隔中都计数激活信号。 在每个难度调整期间,如果一个提案的批准的区块数超过95%(1916 of 2016),则该提案将在下一个难度调整周期就被激活。

BIP-9提供了一个提案的状态图,以说明(illustrate)一个提案的各个阶段和转换,如图10-10所示。

当提案的参数在比特币软件中被知道(定义),该提案就开始处于DEFINED状态。 若区块的MTP大于开始时间,则提议的状态转换到STARTED状态。 如果在难度调整期间已经超过了投票阈值,但是并未超过超时时间,则提案状态转换为LOCKED_IN。 一个难度调整周期过后,该提案变为ACTIVE状态。一旦达到ACTIVE状态,提案将永远(perpetually)处于这个状态。 如果在达到投票阈值之前已经超时了,则提案状态将变为“FAILED”,表示已拒绝的提案。 已拒绝的提案将永远处于那个状态。

这里写图片描述
Figure 10-10. BIP-9 state transition diagram

BIP-9首次实现用于激活CHECKSEQUENCEVERIFY和相关BIP(68,112,113)。 名为“csv”的提案在2016年7月激活。

标准定义在BIP-9 (Version bits with timeout and delay).

10.15共识软件开发(Consensus Software Development)

共识软件开发持续不断发展,对于改变共识规则的各种机制有很​​多讨论。就其本质而言,比特币在协调和使变化达成共识方面树立了非常高的标杆(sets a very high bar)。作为一个去中心化的系统,不存在将意愿强加于网络中参与者的“权威”。权力分散(diffused)在多个支持者(constituency)之间,如矿工,核心开发者,钱包开发商,交易所,商家和终端用户之间。这些支持者不能单方面(unilaterally)做出决定。例如,矿工在理论上可以通过简单多数(51%)来改变规则,但受到其他支持者的同意的限制。如果他们单方面行事,其他参与者可能会拒绝遵守,将经济活动保持在少数链上。没有经济活动(交易,商人,钱包,交易所),矿工们将开采空白区块,无价值的币。这种权力的扩散(diffusion)意味着所有参与者必须协调,否则不会有任何改变。这个系统的现状(status quo)就是处于稳定状态,只有在绝大多数(very large majority)达成一致的情况下,才能发生一些很少的改变。软分叉的95%门槛就反映了这一现实。

重要的是要认识到,共识软件开发并没有完美的解决办法。硬分叉和软分叉都涉及权衡取舍(tradeoffs)。对于某些类型的更改,软分叉可能是一个更好的选择;对其他更改来说,硬分叉可能是一个更好的选择。没有完美的选择,都带有风险。共识软件开发的一个不变的特征是,改变是困难的,共识强制妥协(change is difficult and consensus forces compromise)。

有些人认为这是共识软件系统的弱点。总有一天,你会像我一样意识到,这其实是系统的最强大之处(greatest strength)。

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页