深化分析完成比特币折半的十五行代码

云栖号资讯:【点击查看更多职业资讯】
在这儿您能够找到不同职业的第一手的上云资讯,还在等什么,快来!

北京时刻 5 月 12 日 3 时 23 分左右,比特币在区块高度 630000 处完结诞生以来第三次折半,比特币区块奖赏由 12.5 枚 BTC 减至 6.25 枚 BTC,剩下待挖掘比特币数量仅剩约 262 万。
那么,什么是比特币折半,该工作背面的代码作业原理是什么呢?
让咱们一同深化了解其间风趣的细节吧。

区块补助和奖赏折半

先来简略回忆一下一个根底知识点,所谓“矿工”,是指此刻此刻散布于世界各地,正在工作硬件和软件核算下一个比特币区块哈希值的人。
假如矿工们及时处理了比特币区块链网络中的数学难题,他们就能够取得区块奖赏。
以上描绘里呈现了不少时尚的流行语,是不是?这就来让咱们对它们进行逐一解说。

什么是哈希值?

比特币整个采矿的概念规划得非常奇妙。实践上,采矿并不是一个冥思苦索解题的进程,它更多的是一种测验猜出一个奇特数字的蛮力测验。
比特币网络几乎在一切当地都采用了 SHA256 哈希算法。世界各地的矿工都在测验工作的采矿功用,能够用下面这个简化版别的函数来表明:

SHA256(
$previousBlockHash,
$newTransactionsToBeIncluded,
$magicNumber
);

其间 $magicNumber 也被称为随机数(nonce),在密码学中是指一个只被运用一次的数字。经过在新区块的哈希核算中包括从前区块的哈希值,实践上就形成了一个链式结构,将每个从前的区块逐一链接,一向链接到新的区块停止。因而,区块链本质上便是个高端版别的链表。
矿工们所做的便是一向不断地猜想 magicNumber 的值。他们一遍又一遍地工作相同的核算,用递加(或随机)的穷举法来试 magicNumber 的有效值。经过这样的核算方法,矿工每次都会改动上述函数的哈希值成果。
那他们什么时候算“赢”呢?
一旦他们找到一个开始 0 的数量足够多的哈希值。
便是这样:开始有足够多的 0。
这个答案便是如此简略而粗犷,看起来并不巨大上。而采矿核算的难度就取决于哈希值的开始需求多少个 0。当第一个区块被挖掘出来时,它的哈希值开始只要 8 个 0:

00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048

而在编写本文时,该区块链核算出来的第 629828 个区块的哈希值,它开始有 19 个 0 :

0000000000000000000133e7bffe43530e508183ec48a89bad23a370692b16e8

而开始需求的 0 数量越多,就越难猜出这个随机数。
这儿多说一句,比特币这个算法的精巧之处在于,它每隔 2016 个区块会主动从头核算其难度方针(即开始需求多少个 0),但本文就不再为此赘述了。
采矿奖赏
假如你猜对了这个随机数,你就会得到奖赏。这种奖赏以新铸造出来的比特币的方法发放。
本质上,这些比特币是随便发作的。只不过它既不廉价也不是信手拈来。比特币网络中采矿是要花费很多核算才能和电量的。付出这些辛勤劳动后,你得到了比特币。为了保护稳固这个比特币网络,你作为一名矿工会得到理所应当的奖赏。
这些新铸造的比特币是在所谓的生成买卖(Coinbase Transaction)创立的。这是一种共同的买卖,它包括在每个区块之中,其作用是付出必定数量的比特币奖赏给猜中了正确哈希值的矿工。
是的,抢手的“Coinbase 加密钱银交换所”的姓名就来源于这个词汇,它原本是指每个区块中对矿工供给奖赏的特别买卖(生成买卖)的引证。
每一个被正确核算出来的区块,会主动核算出矿工奖赏,并跟着比特币网络的开展而对奖赏金额进行主动调整。它的作业原理也规划得非常奇妙,请让我来带你了解一下背面的代码。
GetBlockSubsidy() 函数
矿工奖赏折半的魔法发作在函数 GetBlockSubsidy() 中,该函数包括在源代码的 src/validt.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)
plain
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;
}

那么,这个函数包括什么功用?让咱们来分析一下代码。尽管我现已有一段时刻没碰 C 言语了,但走运的是,这段代码适当简单读懂。
现已发作过多少次比特币折半了?

让咱们从顶部开端看:

int halvings = nHeight / consensusParams.nSubsidyHalvingInterval;

上面最终一个一致参数 consensusParams.nSubsidyHalvingInterval,在 src/chainparms.cpp 文件中被界说为一致规矩的一部分。这些是比特币网络上每个人都必须恪守的一套公共规矩。
这儿需求阐明的是,对这些一致规矩的任何更改都将创立一个“硬分叉(Hard fork)”,即一组不再遵从原始区块链规矩的新规矩。

consensus.nSubsidyHalvingInterval = 210000;

这个常量表明,每发作 210,000 个区块,就会呈现一次比特币折半。其间变量 nHeight 指的是当时的区块高度(Height)。或许换句话说,现已挖掘出来的区块的数量。
而 5 月 12 日比特币挖掘数量就到达 630,000 个区块了。所以此刻,这个等式就变为:

int halvings = 630000 / 210000;

这将使折半次数值变为 3。这也是比特币网络第三次将其区块奖赏折半。
可是假如成果是一个浮点值,明显这个成果并不符合该变量所声明的 int 类型,那会发作什么工作呢?比方:

int halvings = 620000 / 210000;

这将得到 2.952380952 的核算成果。可是,由于变量被界说为整数类型,因而会经过直接抹去小数点后边的值,而取整为该成果范围内最小的整数值。所以,这时的折半次数是 2。
区块奖赏完毕
让咱们来看看这个代码片段:

// Force block reward to zero when right shift is undefined.
if (halvings >= 64)
return 0;

由于 nSubsidy 是个 64 位的有符号整数,在履行右移操作时或许呈现未界说行为,这意味着 x >> 65 操作会变成 x >> 1,就会导致数值环回,所以这儿需求保存对折半次数大于等于 64 的查看。这关于上面所贴的后续代码很重要。
开始的比特币中心代码并没有包括这个 bug 的修正,直到 2014 年才兼并了这个 pull request 。
这部分经常被误解为“将会有 64 次比特币折半”。这并不正确。其实只会有 33 次比特币折半,后边咱们会看到关于这一点的解说。

你能得到多少比特币作为奖赏?

“折半”一词其实指的是对矿工能够取得的比特币数量进行约束。每采出 210,000 个区块,这个奖赏金额就会减为一半。

CAmount nSubsidy = 50 * COIN;
nSubsidy >>= halvings;
return nSubsidy;

一开端,在 10 多年前,在网络上每挖掘出一个区块,就会奖赏矿工 50 个比特币。
然后,在 210,000 个区块被挖掘之后,这个奖赏金额被折半为 25 个比特币。又采出 210,000 个区块之后,变成了 12.5 个比特币。这便是截止到这次折半之前的状况。
而这次折半之后,比特币采矿奖赏又会被减为一半,也便是说矿工只能得到 6.25 个比特币的奖赏。之后再采出 210,000 个区块,奖赏就会变成 3.125。以此类推。
这段代码中有一个奇妙的位运算操作,我想在这儿着重一下:

nSubsidy >>= halvings;

我打赌你并不是每天都能在自己的代码中看到这样的 >>= 运算符。
让咱们为每个变量填上实践的值,重写代码如下:

CAmount nSubsidy = 50 * 100000000;
nSubsidy >>= 3;
return nSubsidy;

区块奖赏有一个固定的初始值 50。而依照 src/amount.h 文件中的界说,每枚比特币又可分为 1 亿个更小的基本单位(Satoshi,即“聪”)。假如想要说得更精确的话,矿工不会得到 1 整个“比特币”作为奖赏,而是将 1 个比特币均分为 1 亿份,然后得到“1 亿”份这样的基本单位作为奖赏。
而这样 1 亿份基本单位加在一同等于 1 个比特币。
这给出了 nSubsidy 的初始值为 50 亿基本单位。假如用二进制来表明,能够得到如下数字:

100101010000001011111001000000000

表明将位右移 3 位。这就得到了:

100101010000001011111001000000

当你将该二进制值转换为其十进制表明方法时,会得到 625,000,000。换句话说,这便是 625,000,000 个基本单位或 6.25 个比特币。
鄙人一次折半发作时,会跟着右移一位让上面的二进制数值结尾的 0 又削减一个,然后有效地再次将奖赏金额折半。
由于初始值 50 亿一共只要 33 比特位,咱们将在第 33 比特折半后让区块奖赏变为 0,这大约会发作在 2140 年。
且这个假定的条件是,到那个时候,买卖所付出的采矿奖赏应该还足以补偿矿工的电力耗费和硬件本钱。

定论

比特币的钱银供应量经过硬编码进行了约束,每个人都能够看到和检查这段代码。
矿工数量巨大,即便不是百万数量级,至少也是数以千计,能让这么多矿工能以调和的方法一同作业的主意真的令人感到难以想象。假如你从前测验设置过恣意一个包括 5 个节点的集群,你就会理解要在多个节点间达到一致或大都裁定是多么困难的一件工作,由于你需求像比特币网络相同考虑到:

  • 怎么让一切节点达到一致?
  • 怎么避免成果发作误差?
  • 怎么避免网络连接中止形成的影响?
  • 怎么避免数据丢失形成的影响?
  • 假如一个节点在履行写操作的进程中溃散了怎么办?

而在比特币网络上这一切都正常工作,并且是在硬件、网速、节点版别和软件均为不知道的条件下工作的——这都是由于规划时所包括的数学原理和社会一致,让每个人在该网络上都遵从相同的规矩。
这真是令人入神的技能啊。

作者介绍:
Mattias Geniar,独立开发人员,Linux 系统管理员和通用问题处理者。

【云栖号在线讲堂】每天都有产品技能专家共享!
课程地址:https://yqh.aliyun.com/zhibo

当即参加社群,与专家面对面,及时了解课程最新动态!
【云栖号在线讲堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时刻:2020-05-19
本文作者:Mattias Geniar
本文来自:“InfoQ 微信大众号”,了解相关信息能够重视“InfoQ”