“AI 技术+人才”如何成为企业增长新引擎?戳此了解>>> 了解详情
写点什么

深入剖析实现比特币减半的十五行代码

  • 2020-05-15
  • 本文字数:3799 字

    阅读完需:约 12 分钟

深入剖析实现比特币减半的十五行代码

北京时间 5 月 12 日 3 时 23 分左右,比特币在区块高度 630000 处完成诞生以来第三次减半,比特币区块奖励由 12.5 枚 BTC 减至 6.25 枚 BTC,剩余待开采比特币数量仅剩约 262 万。


那么,什么是比特币减半,该事件背后的代码工作原理是什么呢?


让我们一起深入了解其中有趣的细节吧。

区块补贴和奖励减半

先来简单回顾一下一个基础知识点,所谓*“矿工”,是指此时此刻分布于世界各地,正在运行硬件和软件计算下一个比特币区块哈希值*的人。


如果矿工们及时解决了比特币区块链网络中的数学难题,他们就可以获得区块奖励


以上描述里出现了不少时髦的流行语,是不是?这就来让我们对它们进行逐个解释。

什么是哈希值?

比特币整个采矿的概念设计得十分巧妙。实际上,采矿并不是一个冥思苦想解题的过程,它更多的是一种尝试猜出一个神奇数字的蛮力尝试。


比特币网络几乎在所有地方都采用了 SHA256 哈希算法。世界各地的矿工都在尝试运行的采矿功能,可以用下面这个简化版本的函数来表示:


SHA256(    $previousBlockHash,    $newTransactionsToBeIncluded,    $magicNumber);
复制代码


其中 $magicNumber 也被称为随机数(nonce),在密码学中是指一个只被使用一次的数字。通过在新区块的哈希计算中包含以前区块的哈希值,实际上就形成了一个链式结构,将每个以前的区块逐个链接,一直链接到新的区块为止。因此,区块链本质上就是个高端版本的链表


矿工们所做的就是一直不断地猜测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
复制代码


nSubsidy >>= 3 表示将位右移 3 位。这就得到了:


100101010000001011111001000000
复制代码


当你将该二进制值转换为其十进制表示形式时,会得到 625,000,000。换句话说,这就是 625,000,000 个基本单位或 6.25 个比特币。


在下一次减半发生时,会随着右移一位让上面的二进制数值末尾的 0 又减少一个,从而有效地再次将奖励金额减半。


因为初始值 50 亿总共只有 33 比特位,我们将在第 33 比特减半后让区块奖励变为 0,这大约会发生在 2140 年。


且这个假设的前提是,到那个时候,交易所支付的采矿奖励应该还足以补偿矿工的电力消耗和硬件成本。

结论

比特币的货币供应量通过硬编码进行了限制,每个人都可以看到和审查这段代码。


矿工数量庞大,即使不是百万数量级,至少也是数以千计,能让这么多矿工能以和谐的方式一起工作的想法真的令人感到不可思议。如果你曾经尝试设置过任意一个包含 5 个节点的集群,你就会明白要在多个节点间达成共识或多数仲裁是多么困难的一件事情,因为你需要像比特币网络一样考虑到:


  • 如何让所有节点达成共识?

  • 如何防止结果发生偏差?

  • 如何防止网络连接中断造成的影响?

  • 如何防止数据损失造成的影响?

  • 如果一个节点在执行写操作的过程中崩溃了怎么办?


而在比特币网络上这一切都正常运转,而且是在硬件、网速、节点版本和软件均为未知的条件下运行的——这都是因为设计时所包含的数学原理和社会共识,让每个人在该网络上都遵循相同的规则。


这真是令人着迷的技术啊。


作者介绍:


Mattias Geniar,独立开发人员,Linux 系统管理员和通用问题解决者。


英文原文:


Dissecting the code responsible for the Bitcoin halving


2020-05-15 17:5713573

评论

发布
暂无评论
发现更多内容

TCP协议认知篇

邱学喆

TCP协议 拥塞避免算法 慢启动算法 坚持定时器 TCP状图切换

计算机网络常用知识总结

Java 架构 后端 网络 计算机

吐血整理!金九银十必问的1000道Java面试题及答案

Java 编程 程序员 架构 面试

技术分析| 实时音视频通讯中的流媒体是怎样传输的

anyRTC开发者

音视频 WebRTC 流媒体 流媒体传输

cocoapods 的主模块如何判断子模块有没有被加载?

fuyoufang

ios swift 8月日更

阿里资深架构师终于把微服务架构与实践第2版PDF分享出来了

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

ipfs挖矿合法吗?ipfs挖矿靠谱吗?

区块链 IPFS ipfs挖矿 ipfs矿机 filecoin挖矿

打开vscode好像打开了原神?vscode原神背景推荐,比博燃

CodeNongXiaoW

vscode vscode背景 原神

Spring 配置加载

樊江。

Spring Framework

阿里巴巴首发:Java核心框架指导手册1小时点击量破千万!

Java 编程 面试 程序人生 Alibaba

如何从内部保障企业数据安全?用IT运维审计系统可以吗?

行云管家

网络安全 数据安全 堡垒机 IT运维 运维审计

关于飞书的告警通知,这里有个更好的办法

睿象云

运维 告警 运维平台 智能告警

kubelet 1.14 升级 kubelet 1.20 容器重启问题

Geek_f24c45

Docker Kubernetes kubelet

👊 【Spring技术原理】异步编程机制以及功能分析讲解

洛神灬殇

spring springboot 异步编程 8月日更

CERT和CWE之间有什么联系?

鉴释

安全编码规范 cwe cert

云原生多云容器编排平台karmada上手指南

谐云

云原生 开源技术

图数据库在百度汉语中的应用

百度Geek说

数据库 后端

模块一作业

陈家豪

架构实战营

Goroutine & Channel

Vibyird

并发编程 channel CSP Go 语言 goroutine

WebRTC中的RefCountedObject解析

她的男人是程序员

华为18级工程师三年心血终成趣谈网络协议文档(附大牛讲解)

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

高可用 | Xenon 实现 MySQL 高可用架构 部署篇

RadonDB

MySQL 数据库 Xenon RadonDB

最全互联网后端免费技术分享视频资源学习社区

hanaper

来!看排名一年上升16位的ClickHouse,如何在京东落地实践

京东科技开发者

数据库 Clickhouse

量化交易炒币机器人系统搭建

量化系统19942438797

机器人 量化交易

区块链产业大爆发!未来究竟是谁的机会?

CECBC

微信业务架构 | 架构实战营

樊江。

架构实战营

如何基于分布式KV研发一款消息中间件

Java 编程 面试 后端 中间件

WorkPlus高端制造业移动数字化平台解决方案—华晨宝马

WorkPlus

即时通讯 移动办公平台 移动数字化底座 移动数字化基座 企业即时通讯平台

如何对接口参数的描述进行集中管理

CodeNongXiaoW

大前端 测试 后端 接口工具

交易所智能炒币机器人开发||量化交易炒币机器人系统搭建

Geek_23f0c3

量化交易机器人系统开发 炒币机器人

深入剖析实现比特币减半的十五行代码_语言 & 开发_Mattias Geniar_InfoQ精选文章