前言
文章抛开技术的实现细节,着重讲解比特币如何解决分布式存储带来的一致性问题,如果读者已经对区块链的分布式存储和同步的原理和解决方案非常清楚,为了不浪费读者的时间,可以忽略。
为了避免读者缺少对比特币、区块链、挖矿知识点的了解而产生过多的疑惑,文章前小部分专门为这部分读者准备,通过简单了解比特币是什么?区块链是什么?挖矿的原理,方便部分读者消化接下来的知识点,希望可以给读者一些收获,如果发现本人有理解不对的地方,或者有需要补充的地方,欢迎评论交流。
比特币是什么
2008 年中本聪发表了一篇《比特币:一种点对点式的电子现金系统》(Bitcoin: A Peer-to-Peer Electronic Cash System)论文,它在论文中构思出比特币的雏形(建模),描述了一种去中心化、对等式、基于数学和密码学构建的加密货币——比特币。正由于这个革命性的发明,2015 年,加州大学洛杉矶分校金融学教授 Bhagwan Chowdhry 曾提名中本聪为 2016 年诺贝尔奖经济学奖的候选人。
2009 年中本聪发布了首个比特币软件,并正式启动了比特币金融系统,截止至今日(2018-02-16)1 BTC ≈ ¥6.4万,最高时 1 BTC ≈ ¥12万,最低 1 BTC ≈ ¥0.4。
如果你问目前挖矿还来得及吗?那我们先来看一组数据。
该系统截止至 2018-02-15 08:00:00,已经有 16,867,188 个比特币被挖出,占总发行量 2100 万的 80.3%,只剩下不到 20% 的比特币未被开采,所以现在才想去出挖矿有点为时已晚。
区块链是什么
比特币去中心化底层采用的技术就是区块链,其本质是一个分布式数据库,用于存储每一笔比特币交易记录。比特币网络仅仅认可和维护比特币网络上每一个节点存储着的那条完全相同且长度最长的区块链,所有经过检验并符合要求的交易记录都会被“矿工”打包成进区块,然后发送给比特币网络上所有的客户端节点,客户端节点检查区块没有问题之后,将区块连接起来串成一条链,这条链很形象的就被称为区块链。
挖矿的原理
区块是由比特币网络上被称为“矿工”的节点所生成,他们负责接收到网络上的所有的比特币交易记录,逐个检查这些交易记录是否符合要求,比如检查每条记录是否有正确的数字签名,交易是否重复使用等等;然后将符合要求的交易记录添加到自己增加制作的新区块中。
中本聪为了让添加区块的变得困难,当“矿工”成功制作好这个新区块时,还需要完成两个额外的工作。
- 创建一个字符串,
字符串 = 前一个区块的SHA256函数值 + 新区块的基本信息 + 新区块的所有交易记录
。 - 寻找一个随机数,使得
SHA256(字符串 + 随机数)
满足某个 256 位的二进制 Hash 值(比如满足 Hash 值的前 n 位为 0,当 n = 50,计算出这个随机数的概率就是 1 / (2 的 50 次方))。
通过设置 n
的大小即可改变随机数被计算出来的概率,导致第二点的难度非常高,高到比特币网络平均每 10 分钟才会有一个矿工产生一个新区块;随着现在挖矿设备不断的升级,计算 hash 值的速度也越来越快,目前该难度值控制在每产生 2016 个区块(两个星期)就会动态改变一次,使得整个比特币网络平均每隔 10 分钟才会计算出一个符合要求的随机数。
当矿工计算出这个随机数之后,马上把这个随机数添加到新区块中,马上把这个新区块发送给比特币网络上的各个客户端节点,各个节点检查没问题之后就会把这个区块添加到自己的区块链的尾部,这样子矿工才有可能得到比特币的奖励。
一致性问题的产生
对于比特币来说,每一笔转账记录都代表着“钱”,它是系统运行的基础,如果把交易记录保存在一台电脑上,当电脑发生故障时,那么整个交易系统将瘫痪无法正常运行,所以这种做法不具备高可用性;交给专业的值得信赖的公司管理和维护?比如银行,那么谁能保证公司或个人在巨大的金钱诱惑下或在他人的威胁下,不会篡改交易记录呢?中本聪发明比特币的其中一个目的,就是消除对银行等经融机构的依赖。所以比特币采用的方案是,将每一条比特币转账信息都发送到网络上,让所有运行比特币客户端的计算机都存储所有的比特币交易信息,这样,每一条记录都会被很多计算机存储,不用担心记录缺失,而这样会带来三个一致性方面的问题:
- 交易记录如何同步?
- 如何防止纪录被篡改?
- 如何防止同一笔比特币交易被重复使用?
三个一致性问题各自的痛点
- 交易记录如何同步
如果一部分客户端没联网、没有登录比特币客户端或者电脑关机,导致没有接收到交易记录,那交易记录肯定是没有被这部分计算机存储的,这样不同计算机上面的比特币交易记录就会不一致,那到底以谁为准?如何让他们互相同步从而储存相同的交易记录呢?
- 如何防止交易记录被篡改
如果黑客篡改了网络上某个节点的一条或多个交易记录,导致比特币网络上的多个节点的交易记录不一致,甚至出现交易信息前后矛盾的问题,从而导致比特币网络无法运行,那么如何保证交易记录不被篡改呢?
- 如何防止同一笔比特币交易记录被重复使用
假设只有 S 转账 10BTC 给 A,并且交易记录已经得到验证且有效,此时 A 账号中只有 S 转账给他的 10 BTC,而 A 几乎同时向 B 和 C 账号转入 10 BTC,如下表格:
序号 | 交易记录 | 数字签名 |
记录1 | “S账号”支付10BTC给“A账号” | S用自己的私钥加密【SHA-256(“S账号”支付10BTC给“A账号”)】 |
记录2 | “S账号”支付10BTC给“A账号” -> “A账号”支付10BTC给“B账号” | A用自己的私钥加密【SHA-256(“S账号”支付10BTC给“A账号” -> “A账号”支付10BTC给“B账号”)】 |
记录3 | “S账号”支付10BTC给“A账号” -> “A账号”支付10BTC给“C账号” | A用自己的私钥加密【SHA-256(“S账号”支付10BTC给“A账号” -> “A账号”支付10BTC给“C账号”)】 |
由于地域问题和网络等问题,不同矿工节点先接收到的交易记录可能会发生这样的情况
不同的客户端先接收的比特币交易记录不一致,而客户端只认可最先接收到的交易记录是有效的,比如记录 2 先被接收,则记录 3 作废,同理,记录 3 先被接收,则记录 2 作废,不同计算机对同一交易记录的有效性产生分歧,导致不同节点的交易记录不一致要如何解决呢?
如何解决一致性问题
比特币底层使用区块链技术解决一致性问题,那它是如何解决的呢?下面开始讲解区块链如何解决上面提到的三个一致性问题。
- 交易记录如何同步
区块链会被存储在网络上每一个节点中(客户端),如果你有一个比特币交易的客户端,一段时间没有联网之后再次连接到网络,客户端会自动向网络中的其它节点发起同步自己没有的区块的操作,检查无误之后才逐一把区块添加到自己的区块链上。
这样,每一个启动并联网的客户端都会同步所有的比特币交易记录了。而检查的过程是十分严格的,包括了每一区块中每一笔的交易记录是否符合要求,区块间的交易记录是否符合要求,下个区块是否包含上一个区块的 SHA-256(上个区块的所有记录) 值,检查区块是否符合要求等。
- 如何防止记录被篡改?
在讲解区块链如何防止交易记录被篡改之前,我们有必要了解一下区块链中关于区块的基础知识。
在区块链中,每个区块可以看成由消息头和消息体组成。
- 消息头:包含区块的创建时间、区块的 hash 值,上个区块所有交易数据的 hash 值等。
- 消息体:区块的交易记录。
区块链中的每一个区块都会包含上一个区块的 Hash 值,构成如下图:
这里的 Hash 值其实就是一种散列函数,比特币中使用的 Hash 函数是 SHA256。只要给定一个输入值 x, 就可以得到一个固定长度的输出值 H(x) ,例如字符串 123
输入到 SHA256 hash 函数的值为:
SHA256("123") = a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3
将返回值转换成 256 位的二进制数:
1010011001100101101001000101100100100000010000100011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
该算法目前还没有存在有效的破解手段,所以是安全的。
到这里可以开始讲解防篡改问题了,当交易记录被存放到区块中,并且这个区块被加入到区块链上,那么这个区块中的所有信息都是不可修改,一旦修改,就会出现前后 hash 不一致,导致区块链断裂。
除非恶意的节点总数的 CPU 算力比诚实节点总数的 CPU 算力还要强,那才有可能被“坏人”得逞,因为他可以依靠自己的算力,将节点之后的所有区块重新打包并发布到比特币网络上所有的节点,最终成为那条最长的区块链从而被比特币网络所认可。
然而这种情况几乎是不可能的,第一,因为比特币每次打包区块,都需要完成一个难度非常高的工作,这个工作就是猜随机数,专业术语叫做工作量证明(Proof-of-Work,PoW),难度随着矿工算力的总和动态变化的,比特币网络通过使用这个策略,让全网平均每 10 分钟只会产生一个新区块,限制了恶意节点批量修改区块的可能性;第二,比特币经过了 8 年时间的发展,随着比特币价格的攀升,越来越多的矿工加入到“挖矿”当中,这使得比特币网络算力不断增强,想要超过全网的算力总和的可能性微乎其微。
为了解决防止篡改问题,比特币也付出了非常大的代价,它让全网几乎所有的算力都花在了计算错误的 hash 上,导致了非常多的资源浪费。
- 如何防止同一笔交易被重复使用?
中本聪在他的论文中提到:
We propose a solution to the double-spending problem using a peer-to-peer network. The network timestamps transactions by hashing them into an ongoing chain of hash-based proof-of-work, forming a record that cannot be changed without redoing the proof-of-work.
大致意思:中本聪提出通过对等式网络(peer-to-peer network)来解决双重支付(double-spending) 问题。网络上的交易记录按照时间的先后顺序(这个时间是由矿工记账时决定的)被散列成一个持续的基于Hash的工作证明,形成一种不重新验证(hash),就无法更改记录的工作证明链。
严格来讲,对等式网络 + 时间戳 + Hash 才是解决双重支付的主要手段,而区块链只是它们的存储形式而已。
知道双重支付的解决方案,在讲解解决双重支付原理之前,还需要知道一个知识点,比特币软件是如何计算用户的余额(剩下多少可用的比特币)?
在比特币中,计算某个钱包余额的过程,是通过计算这个钱包地址的所有相关的转账记录,来得出这个钱包地址对应还剩多少比特币未被使用。
举例说明如何计算 A 钱包的余额:
序号 | 交易记录 | 数字签名 |
记录1 | “S账号”支付10BTC给“A账号” | S用自己的私钥加密【SHA-256(“S账号”支付10BTC给“A账号”)】 |
记录2 | “S账号”支付10BTC给“A账号” -> “A账号”支付10BTC给“B账号” | A用自己的私钥加密【SHA-256(“S账号”支付10BTC给“A账号” -> “A账号”支付10BTC给“B账号”)】 |
记录3 | “S账号”支付10BTC给“A账号” -> “A账号”支付10BTC给“C账号” | A用自己的私钥加密【SHA-256(“S账号”支付10BTC给“A账号” -> “A账号”支付10BTC给“C账号”)】 |
假设有上面的比特币转账记录,并且交易记录时有效的,比特币客户端通过计算 A 钱包相关的每一笔交易记录的输入(接收 BTC 转账记录)和输出(消费 BTC 转账记录)值来得出 A 钱包所剩可用的比特币数量,如下表格:
钱包 | 转账 | 可用比特币 |
A | +10 BTC | 10 BTC |
A | -10 BTC | 0 BTC |
A | -10 BTC | -10 BTC |
上面的 -10 BTC 只是为了演示计算比特币的过程,真正的比特币中不可能会出现这种情况。
了解余额计算原理之后再回头来看看,区块链如何解决双重支付问题。
这个过程中可能会出现三种情况:
- 两条记录被同一个成功制作新区块的矿工节点接收:由于平均每隔 10 分钟全网才能有一位“矿工”生成一个新区块,这个矿工在接收到两条交易记录的时候,假设记录 2 先接收,记录 3 慢接收,“矿工”根据接收的前后顺序检查这两条交易记录是否有效,用户余额是否足够支持本次的转账交易,很明显,记录 3 作废。
- 两条交易记录被先后两个不同的区块的矿工节点接收:此时,先接收的交易记录会被添加到新区块中,假设比特币客户端节点验证没问题,然后把区块添加到区块链尾部;慢接收的交易记录矿工在检查该钱包地址在转账余额时(检查余额并不是只在局限在当前的区块的交易记录,而是会检查主链上的其它区块中的记录),发现该钱包地址在上一笔交易之后,余额已经不满足下一笔转账交易,所以慢接收的交易记录作废。
- 两条交易记录被不同的矿工同时打包进新区块并发送给所有客户端节点:这个情况是当矿工 A 先接收到记录 2,然后才接收到记录 3;矿工 B 先接收到记录 3,然后才接收到记录 2,并且两个矿工都同时计算出随机数,同时将新区块发送给比特币网络上的所有客户端节点,这个时候区块链可能会出现分叉,比特币协议规定,分叉之后最先达到 6 个区块的那个分支,被认定为主链,此时短叉链作废,包括里面的交易记录。
综上所述,比特币网络总是有办法知道你把一分钱花两次,所以双重支付是不可能的。