最近一直在刷LeetCode题目,为了换换脑子,劳逸结合一下,也来聊聊近年来流行的比特币。最为一名程序猿,还是挺好奇这种新兴加密货币技术的。之前在网上也看过不少关于介绍比特币或是区块链文章,大多数的讲解很难让我们能有一个直观并清晰的认识。正好今天有时间,我也来试着解释一下这个神秘的比特币到底是何方神圣,他的工作原理又是怎样的?
我是从17年底开始接触比特币的,一个朋友拉着我去投资挖矿,当时我一脸懵逼,每天搬砖已经很辛苦了,还要再让我去挖矿?!后来才知道,挖矿的含义原来是获得比特币的一种方式。好吧,我也是从那时候开始,逐渐的了解到了一些比特币的相关知识。
一,什么是比特币?
先看看网上给出的定义:
比特币(BitCoin)是一种P2P形式的虚拟货币。点对点的传输意味着一个去中心化的支付系统。比特币不依靠特定货币机构发行,它通过特定算法的大量计算产生,比特币经济使用整个P2P网络中众多节点构成的分布式数据库来确认并记录所有的交易行为。P2P的去中心化特性与算法本身可以确保无法通过大量制造比特币来人为操控币值。基于密码学的设计可以使比特币只能被真实的拥有者转移或支付。这同样确保了货币所有权与流通交易的匿名性。
摘自维基百科
这段定义写的一气呵成,严谨且科学,感觉貌似是看明白了,但是细琢磨起来还是一头雾水,有些不明觉厉。我们还是用白话来逐一理解下。
二,去中心化的记账系统
首先,比特币他是一种数字加密货币,而不是一种法币(国家政府发行的货币,比如人民币或者美元都称为法币)。那么,它与法币的区别在哪里呢?我们先从法币说起,平时我们在网上买衣服交话费或是充值游戏点卡,最终都是要绑定我们银行卡的,如果我们银行卡中余额不足的话,那么上述操作就无法完成。这些账单都是银行帮我们记录的。当然,银行的账单并不会细致到例如:张三花了1块钱买了一斤白菜;李四收到了王五给他的新年红包20元。然而,账单其实很简单,他的主要目的是要明确账户的余额,所以银行只关心账户里钱的变化情况,而不是每笔花销的用途。所以,一个银行的账单记录应该是这样的:
用户A 进账1000元 用户B 出账500元 用户C 转账100元给用户D(这实际上应该是2条记录,用户C出账100元和用户D进账100元) ... ...
每个用户只能看到和自己相关的账目记录,别人的记录我们无权过问,银行作为一个信誉机构,他就处于了一个中心的地位,所有的账目都由他来管理,并且所有人也都无条件信任银行的账目是正确无误的。
然而虚拟货币是如何记账的呢?比特币系统中并没有银行这个角色,所有比特币用户都有一个账本,并且这些账本之间的内容需要保持一致。比如说小明支付了10个比特币给小红,这时小明要将这条记录记到自己的账本上,同时,他还必须要将这条消息通知到包括小红在内的所有人。这样大家都会将这条消息记录到各自的账本上。
再举一个完整一点的例子,比如,比特币网络上一共有A,B,C,D四个人。(懒得起名字,就用字母代替吧)
1. A 支付10BTC 给B, 同时A需要将这条信息告诉B,C,D 2. B 支付20BTC 给C, 同时B需要将这条信息告诉A,C,D 3. D 支付50BTC 给A, 同时D需要将这条信息告诉A,B,C
这时,网络中一共发生了3笔比特币交易,这些记录应该保存在了A,B,C,D四个人各自的账本中,每个账本中应该包含有3条相同的信息(实际中根据网络延时等原因,记录的顺序可能会不同),分别是:
A -> B 10BTC B -> C 20BTC D -> A 50BTC
我们知道,一个账本的容量是有限制的,不可能无限记录账目,比特币设计的一个账本大概可以记录4000条左右的数据。这一个账本我们就称他为一个区块。当一个账本记满之后,这个账本会被某一个人打包连接到之前账本的尾部,我们将这种连接的账本成为区块链。
说道这里,大家可能会产生一些疑问,为何每个人都要记账?最终使用谁记录的账本连接到区块链上?接下来,我们来讨论这两个问题。
三,记账奖励
其实,记账是有奖励的,比特币之父中本聪当年的设计便是,每记录一个区块的账单,记账者可以获得50个BTC的奖励,不过,奖励是每四年一个周期进行减半的。比特币在2008年诞生,从2008年开始之后的四年内,记账者每记录一个区块,就能获得50BTC的奖励,但是2012年到2016年这个区间内,奖励变为了25BTC,再之后的四年,剩下12.5BTC,以此类推。这种记账奖励,是比特币网络中产生比特币的唯一方式。换句话说,比特币是发行总量一定,且发行量越来越少的一种数字货币。那么比特币一共会发行多少枚呢?我们知道,每个区块被记录出来需要大约10分钟,也就是一小时能出约6个区块,另外,最初的奖励是50BTC,那么从2008年起最开始的4年能产生多少比特币呢?
// 每个区块的奖励50 * 每小时产生的区块数6 * 一天24小时 * 一年365天 * 4年 50 * 6 * 24 * 365 * 4 = 10252800;
上面算出的只是前四年的数字,考虑到每四年奖励会减半,那么总量应该如何计算呢?
50 * 6 * 24 * 365 * 4 * (1 + 1/2 + 1/4 + 1/8 .....) = 2100万
从上面的计算结果可知,比特币的总量是不会超过2100万枚,所以它不像法币那样可以无限增发,这也是比特币的另一个特性,称为稀缺性。
除了记账奖励之外,记账者还可以从账单中的每一笔交易中获得一定的手续费,这个手续费要远远低于银行法币转账的费用。另外,每笔手续费是交易发起者支付的,比如A支付给B 10个比特币,其实A是要花费大约10.0001个BTC,这个手续费的具体金额是不固定的,详细算法稍微有些复杂,在这里先不做讨论。
我们看到,记账可以获得丰厚的奖励,所以,这也是大家相互争抢记账的原因,那么,接下来我们就要讨论另一个问题,既然,网络中的每个人都有一本账,只有将账本打包提交到区块链上的那个人才能获得该奖励,那么谁的账单会被采用呢?
四,挖矿机制
我们先总结一下之前说过的内容,首先,比特比网络中会产生很多笔交易,每笔交易都会被网络中的每个人记录下来,当大家的当前账本记录满之后,需要选出一个人的账本,将其打包后连接到之前的账本的后面。
这时,谁会成为那个幸运儿,取决于谁能先计算出一道数学题目,优先算出题目的人便有权利打包当前账本并获得比特币奖励。那么,要计算什么题目呢?这里我们要简单的介绍一下SHA256算法。这个算法是美国国家安全局设计的一种秘法散列函数。如果你完全不了解这个东西,你可以简单的理解为,SHA256是一种正向计算很容易,而逆向运算复杂到逆天的一种计算。举个例子,有这么一个公式:
x^3 + sin(x) + log(x) = y;
如果已知x来求y的话很简单,反之,已知y来求x的话就稍微复杂一些了。然而SHA256是一种逆向运算更加复杂的操作,以当前计算机的运算能力来说几乎无法反向推出结果,简单来看,SHA256的计算是将任何一个字符串变为一个2进制数的过程,这个2进制数一共有256位,所以称为SHA256。比如:
SHA256("iPhone") = 01010001110110001..... (共256位)
每一个单词,不论长短,都会生成一个唯一与之对应的256位2进制数字,哪怕你SHA256一部苍老师的电影,同样会生成一个256位数字。他的正向计算速度非常快,几乎可以瞬间完成,但是想通过一串0101的数字反向推出开始的那个单词(比如例子中的iPhone)就会非常困难。
简单的了解了SHA256之后,我们再回到比特币的问题,如果想获得记账权利,你必须要快速的解决一道数学难题,而这道数学题就是反推SHA256的原始字符串。
我们已知这个原始字符串是由下列内容组成:(实际内容要多于下面的列表,由于无关紧要,所以可以忽略掉不去考虑。)
- 前区块的头部信息
- 账单内容
- 打包时间
- 一个随机数
系统将上面的一坨信息组合成一个字符串s,并将s进行2次SHA256运算,得到结果result。这时,系统会让大家根据result求出s是什么?
SHA256(SHA256(s)) = result;
简单的看下题目,我们可以知道,result是已知信息,前区块链中的头部信息,账单内容和打包时间都可以查询到,只有那个随机数是未知的,只要能求出那个随机数,也就可以推算出整个s是什么了。
之前我们说过,以当前计算机的性能来说,通过SHA256的结果反向逆推原始字符串几乎是不可能的,更何况系统做了2次SHA256运算!所以绝大部分的计算机是通过正向计算碰运气的方式来解题的,举个简单的例子来说明一下,
比如已知 2 + x = 5,求 x 等于几?
如果通过逆向运算,我们可知 5 – 2 = 3,所以x就是3。但是如果像SHA256那样无法逆向运算呢?那么我们只能通过大量的正向运算来碰运气。计算过程大概如下:
2 + x = 5; 第一步,让x = 0,那么 2 + 0 = 2; 2 != 5,继续下一步 第二步,让x = 1,那么 2 + 1 = 3; 3 != 5,继续下一步 第三步,让x = 2,那么 2 + 2 = 4; 4 != 5,继续下一步 第四步,让x = 3,那么 2 + 3 = 5; 5 == 5,得出x为3
比特币网络计算SHA256的难题同样是这个思路,计算机需要不停的改变随机数的值,再做2次SHA256运算,看看结果是否等于系统给出的那组2进制数即可。网络中优先算出这个结果的人就拥有了记账的权利,他会将当前的账本打包添加至原先的区块链尾部。而这整个计算数学题的过程我们称之为挖矿,参与计算的所有计算机称为矿工。
(题外话:Leetcode中也出现过类似挖矿的题目,有兴趣可以参照 LEETCODE 1237. Find Positive Integer Solution for a Given Equation解题思路分析)
其实,解题时,系统的要求没有那么高,并非一定要算出一个完全和result相同的256位数字,比特币网络会根据当前全网总算力,定期调整计算难度。当整体算力升高时,难度会相应上升,算力降低时,难度也会相应下调。控制难度的方式就是调整result的位数。举个例子,把难度调到最低,也就是说能计算出与result的第一位相同的数即可,因为是2进制,第一位非0即1,所以概率有百分之五十之高。如果难度调到最高,那么必须要计算出与result所有的256位都相同才可以,那么概率就变为2的256次方分之一。
到此为止,比特币网络的基本原理差不多描述清楚了,但是作为一名合格的码农,天生就有着寻找程序漏洞的强迫症,那么比特币网络是否有缺陷呢?我们假设以下场景:
A想转账10比特币给B,但A的账户中只有1个比特币,其他人如何验证这笔交易是否可行?
再比如,A的账户中有10个比特币,A同时发出两笔转账,A转账给B 10个比特币,A转账给C 10给比特币。这个问题也叫做双重支付,那么比特币系统是否会发现这种非法交易呢?这些问题,我们要从比特币的安全性说起。
五,身份认证
比特币网络中关键的一点就是安全性问题,出于对安全性的考虑,首先便是确定用户的身份。确定用户身份的目的之一是要知道对方钱包的余额,比如A要转账一笔比特币给B,那么我们首先要清楚A的钱包中是否有足够的余额来保证这笔交易。前文我们说过了,所有的账单在网络中人手一份,通过账单很容易计算出其钱包的余额,换句话说,所有人的钱包信息都是公开透明的,这种通过之前的交易记录确定余额的方法被称为追溯法。那么当一笔转账将要发生时,其他人如何验证这笔交易是否正确呢?这样从钱包的产生说起。
在比特币网络中,建立一个钱包很容易,简单来理解,新建一个钱包需要产生一个钱包所对应的id,这个id当然在网络中是不能够重复的。通过id系统会计算出一个私钥,这个私钥是需要绝对保密,只能钱包拥有者才能知道,这相当于银行卡的密码,一旦这个密码被其他人知道,那么你钱包中的比特币也会被他人盗走。另外,通过私钥还可以生成一个公钥,这个公钥是可以在网络中公开的要素,最后,通过公钥,可以产生一个钱包地址,任何的转账都会通过对方的钱包地址进行。好了,一下子听到了这么多概念,是不是有些混乱?我们先逐个分析一下。
id -> 私钥 -> 公钥 -> 钱包地址
- id。这个id是为了区别于其他钱包的唯一标识,用于排重。
- 私钥相当于密码,是绝对保密的信息。私钥用于对数据进行加密操作。
- 公钥区别于私钥,是可以公开于全网络的,他用来解密私钥加密过的内容。
- 钱包地址很好理解,转账就是通过钱包地址互相进行交易。例如,A转账给B,其实就是通过A的钱包地址转账到了B的钱包地址。
那么,一笔转账是如何完成的呢?结合前文讲过的知识再加上这节我们正在讨论的内容,可以更加细致的表述出整个转账流程
举个例子:用户A的密钥是1234(只有A自己知道),公钥是5678,这时A要给B转账10个比特币
- A想要转账10BTC 到B的钱包地址。
- 为了证明自己这笔转账的可行性和真实性,A需要先做以下工作,将【我是A,我要转账给B 10个比特币】这个信息制作成摘要,摘要实际上也是一个字符串,是通过Hash函数简单计算出来的,例如摘要可能是:ABCD1234 (这个字符串只是随便写的一个例子,实际中的摘要要比这个长很多)。然后再将这段摘要利用自己的私钥进行加密,加密运算是一个稍微复杂的数学运算过程,因为这个操作很像实际中在文件上签名的动作,所以这个过程叫做数字签名,将摘要加密后,摘要就变为了XYZ567(这也仅仅是一个随便写的例子)
- A将这条消息广播给网络中的其他所有用户。广播时,A需要广播出以下全部信息
a. 【我是A,我要转账给B 10个比特币】这个原始信息
b. A的公钥【5678】
c. 用A的私钥加密过的摘要【XYZ567】 - 这时,网络中收到这条消息的人就要对其进行验证了。 首先要确认A的账户中是否存在至少10个BTC,这是能够转账的前提。(当然,要考虑到手续费的话,实际验证时要看A的账户中是否存在至少10.0001个左右的比特币) 接下来要判断这条信息的真伪性,也就是说,我们怎么知道这条信息是否是A自己发出来的呢?这就需要用到A提出的那些证明材料了。首先利用A提出的原始信息对其进行hash运算得到一个摘要,应该是:ABCD1234 。另外,A还提供了他的公钥(5678)以及用A的私钥加密后的摘要文件(XYZ567),我们上文说了,公钥是解密用的,所以,我们可以用A提供的公钥解密那个加密后的摘要文件(XYZ567),解密后得到未加密的摘要文件也应该是:ABCD1234,因为A的密钥只有其自己知道,这说明这个加密文件是A自己加密得到的,这个验证过程称为签名验证。并且这也从另一方面证明了私钥的重要性,如果私钥被别人得知,那么这个人就可以利用这个私钥进行签名盗取账号中的比特币了。
我们用一张图来总结上面的内容:
通过这一节我们了解了利用比特币支付时,是如何进行验证的,同时我们得知,比特币网络使用的是私钥加密,公钥解密方式,加密与解密的钥匙并不是同一把,这在密码学上称为非对称性加密。
接下来我们再看看比特币网络是如何防止二重支付的?
六,双重支付问题
首先我们要明白什么是双重支付。举个例子,我们要卖房子,标价100万人民币。我们同时跟A和B两个人签订了买卖房协议,那么我们就分别从A和B两个人手里分别获得了100万,总共200万的收入,这在法律上肯定是不被允许的行为。其实,比特币网络同样可能存在这样的违规行为,那么如何避免这一种行为呢?我们还是通过举例子来说明。
比如用户A有10个比特币,他在同一时间发出了2个广播,分别是
- A转账10BTC给B
- A转账10BTC给C
显然这两条信息只有一条可以被验证,那么网络是如何验证的呢?因为网络广播或者说是网络信息传输本身根据网络情况是有时间延时的,网络中的有的人可能会先收到第一条通知,再接收到第二条,这样,当第一条被验证成功之后,由于A的账户余额已不足,因此第二条信息将被丢弃。同样,网络中其他人有可能是先收到的第二条通知,这时同理,之后收到的第一条通知将被丢弃。那么问题来了,网络中各个账本间的信息就无法保证一致性。所以A的比特币是转给B了,还是给C了?
我们看上图,矿工1和2的记账信息是A给B 10个比特币,而矿工3,4,5则记录了A给C 10个比特币,这时,根据上文挖矿原理中讲到的,他们在比赛计算数学题,所以,谁先算出来解,谁就有权利将自己的账本打包,并连接到区块链的尾部,同时,其他所有人都会放弃自己的账本,认同该矿工的账本为正确信息。比如矿工1首先破解了当前难题,那么区块链就变成了下图的样子,也就是说所有人都认为A将10个比特币转给了B。
因此,在比特币网络中,是不会出现双重支付问题的。但是,关于安全性的讨论还没有结束,我们仍要考虑到另一种情况,矿工是否可以篡改数据呢?
七,篡改数据
我们上文说到过,打包一个新的区块并将其链接到当前区块链尾部,需要根据目前最后一个区块的头信息经过复杂的数学计算来得出系统给定的那个随机数,最先算出结果者为胜,这样他将得到挖矿奖励,并且他的账本会获得大家的承认。不过这只是通常情况,比特币网络中会有例外发生,比如,一个矿工不承认当前区块链中的某个区块N,那么根据规则,他是可以重新根据区块N-1的头信息计算结果,计算完成后将自己的账本打包并列链接到区块N-1的尾部,这就发生了分叉行为。如下图:
另外还有一种情况,比如两名矿工同时算出解,他们可能会同时将自己的账本打包并上传至区块链尾部,这时区块链也发生了与上图同样的分叉行为。那么哪条链才是主链呢?其实比特币网络中拥有一个最长链原则,也就是链长度最长的那条将被网络承认,短的那条将被舍弃。
再回到上图的例子,当前,区块3和区块3’处在同一区块高度(链长度相同),各有一部分矿工分别承认这两个区块,所以接下来哪个链先出下一个区块就变得很关键,谁先出块,谁的链就会变长,同时短链将被舍弃。短链的所有矿工也就会开始追寻长链(按照长链的最后一个区块头部继续挖矿)。当然,如果这两个链再次同时出块,那么分出胜负还要再等到下一个回合。所以,遇到大额比特币转账时,一般要等到6个块确认之后才算交易成功。
说了这么多,关键点来了,根据以上的规则,攻击者是可以篡改区块链数据的!比如还是这张图,区块3’是攻击者在区块2进行了分叉然后提交的自己账本,当然这个账本中肯定包含了许多见不得人的秘密。这时,如果攻击者能够先于所有追随区块3的矿工,算出区块3’的难题的话,那么他将成功将自己的账本变为大家公认的信息!
这种骚操作显然是可行的,但是这种攻击在比特币网络中却从来没有出现过,为什么呢?这还要回到挖矿难度上来分析,我们上文反复在讲,难题的计算是及其复杂的,攻击者孤身一人要抵抗整个网络的算力几乎是不可能的。除非,攻击者能够掌握了全网百分之50以上的算力,这样他才具备攻击的可能性,这种攻击被称为51%攻击。不过攻击的代价和成本也是相当之高,所以这几乎是无法完成的任务。不过,数字货币除了比特币之外还有很多,并不是每种货币的网络都拥有足够多的算力支撑,因此对于一些很小的货币网络,实行51%攻击还是很简单的事情,这也是为什么我们经常被建议尽量投资主流货币的原因。
好了,关于比特币今天就说这么多,其实不论是数字货币还是区块链技术,他们都是刚起步阶段,同样都存在很多尚未解决的问题。比如比特币的挖矿机制被认为非常消耗资源。另外比特币区块的大小限制了交易的速度。再者区块链技术还没有真正的运用到实际生活中等等。关于这些话题,网上的讨论也有很多,这都是我们今后需要努力的方向。
本网站文章均为原创内容,并可随意转载,但请标明本文链接如有任何疑问可在文章底部留言。为了防止恶意评论,本博客现已开启留言审核功能。但是博主会在后台第一时间看到您的留言,并会在第一时间对您的留言进行回复!欢迎交流!
本文链接: https://leetcode.jp/程序猿白话分析比特币原理/
Pingback引用通告: LEETCODE 1237. Find Positive Integer Solution for a Given Equation解题思路分析 - LEETCODE从零刷LEETCODE从零刷