1、BTC-密码学原理

比特币被称为加密货币(crypto-currency),但其实加密货币是不加密的,区块链上所有交易内容(包括账户地址、转账金额等)都是公开的。比特币中主要用到了密码学中的两个功能:哈希和签名

1)、hash(哈希)

在密码学中用的哈希函数被称为cryptographic hash function,其两个重要性质分别为collision resistance(抗碰撞性)和hiding(隐藏性)

哈希碰撞:

有两个输入x和y,且x!=y,给定一个哈希函数H(),算出来H(x)=H(y),则称为哈希碰撞,即两个不同的输入算出来的哈希值是相等的。一般来说,哈希碰撞是不可避免的,因为输入空间是远远大于输出空间的

collision resistance:

collision resistance保证,如果有H(x)!=H(y),必然可以得到x!=y。当然这是理想状态,在实际应用中哈希碰撞是客观存在的。给定一个x,很难找到一个y,能够在x!=y的前提下,使得H(x)=H(y)就认为其是collision resistance的(目前并不存在一个哈希函数可以从数学上证明具有collision resistance的性质)

collision resistance的用处:

比如我们有一条信息m,m的哈希值H(m),H(m)可以作为message digest(信息摘要),用来检测对这个信息的篡改。如果有人修改信息的内容,哈希值就会发生变化,collision resistance的性质保证,找不到另外一个m’,使得H(m)=H(m’),所以没有办法篡改内容而又不被检测出来

hiding:

哈希函数的计算过程是单向的、不可逆的。给定一个输入x和哈希函数H(),可以算出x的哈希值H(x),但没有办法在已知H(x)和H()的情况下,反推出x的值。换句话说哈希值没有泄露有关输入的任何信息

hiding性质成立的前提是输入空间要足够大,使得蛮力求解的方式是不可行的,而且输出结果分布要比较均匀,各种取值的可能是差不多的

collision resistance和hiding结合实现digital commitment(数据保证):

比如说某个人说他可以预测股市,可以预测第二天哪些股票会涨停。怎么证明这个人预测的是不是准确的?最简单的是提前公布,等待实际结果出现后验证。但实际中,当提前发布预测后,可能会由于预测者本身对股市实际结果造成影响。所以,应该将提前将预测结果写于纸上并密封,交给第三方机构保管,等到实际结果出现后开启密封与实际对比,这就是digital commitment。而第三方机构需要能够使人信服,在实际生活中,很多场景并不存在一个这样的第三方机构,而区块链技术正为此提供了一个很好的解决方法

把预测结果看作x,提前公布H(x),hiding的性质保证此时不知道预测结果是什么。第二天开盘之后,公布预测结果x,collision resistance的性质保证预测结果不会被篡改。然后根据股市实际情况判断预测是否准确

实际使用中,为了保证输入足够随机,会在x后拼接一个nonce(随机数),对其整体取哈希

puzzle friendly(谜题友好):

比特币中还需要第三个性质puzzle friendly。该性质要求哈希值计算事先不可预测,仅仅根据输入很难预测出输出。比如需要得到一个哈希值,存在于某一个范围内,只能通过不停运算查找出来

比特币是区块链,区块链是一个个区块组成的链表,每个区块有一个块头(block header),block header有很多域,其中有一个域是可以设置的随机数nonce,挖矿的过程就是不停地试各种随机数,使得整个block header的哈希值小于等于某个目标阈值,即H(block header)<=target

puzzle friendly性质保证挖矿的过程没有捷径,只能不停地去试大量的nonce才能找到符合要求的解,所以这个过程才可以用来作为工作量证明(proof of work,简称POW)。虽然挖矿的过程需要很多工作量才能找到符合要求的nonce,但是一旦有人找到了这个nonce发布出去后,其他人验证nonce是不是符合要求只需要算一次哈希值就可以了。所以说,挖矿很难,验证很容易(difficult to solve,but easy to verify)

比特币中采用SHA-256哈希函数,满足collision resistance、hiding、puzzle friendly三个性质

2)、签名

在第三方中心化系统中,账户开通依赖于第三方。但去中心化的比特币系统中,不能进行申请账户。比特币中,申请账户是用户自己来处理的,即自己创建一个公钥-私钥对(public key,private key)。公私钥对的概念是来源于非对称加密(asymmetric encryption algorithm)

比特币中,公钥相当于银行账号,别人给你转账只要知道你的公钥就可以了,私钥相当于账户密码,知道了私钥就可以把账户上的钱转走。比特币中,公私钥用于验证签名。比如A要转10个比特币给B,A把交易发布到区块链里。别人怎么知道这个交易确实是A发起的?需要A在发布交易的时候用自己的私钥对交易进行签名,其他人收到交易后,再用A的公钥验证签名的正确性

比特币中一般是先对一个message取哈希,然后再对这个哈希值签名

2、BTC-数据结构

1)、Hash pointer(哈希指针)

指针存储的是某个结构体在内存中的地址,如上图P是指向结构体的指针

哈希指针除了要存储结构体的内存地址之外,还要保存结构体的哈希值,如上图H()是指向结构体的哈希指针。哈希指针不光是可以找到结构体的位置,同时还能够通过结构体的哈希值检测出结构体的内容有没有被篡改

比特币中一个最基本的数据结构就是区块链,区块链是一个个区块组成的链表

区块链和普通链表的区别:

一个区别就是用哈希指针代替普通指针(Block chain is a linked list using hash pointer)

如上图是一个小型的区块链,最前面的区块是系统中产生的第一个区块,叫genesis block(创世纪区块),最后一个区块是最近产生的区块most recent block。每个区块(除创世纪区块)都包含指向前一个区块的哈希指针

通过这样的数据结构可以实现tamper-evident log(防篡改日志)。篡改一个区块的内容的,后面一个哈希指针就对不上,后面一个哈希指针改了,再后面的也要改。所以只要保存最后一个哈希值,就能检测出对区块链中任何部分的修改

普通链表可以改变其中一个元素而不会对其他元素产生影响。区块链是牵一发动全身,改变前面任何一个区块后面所有的区块都要跟着改,所以只要保存最后一个哈希值,就能检测出对区块链中任何部分的修改

有了这个性质,比特币中有些节点只需要保存最近的几千个区块数据就行了,不需要保存整条区块,用到的时候问别人要就行了。有些节点可能是恶意的,所以要验证,算一下别人给我们的区块的哈希值跟我们保存的哈希值是否相等就可以了

2)、Merkle Tree(默克尔树)

如上图是一棵Merkle Tree,最下面的一层是数据块(data blocks),上面的内部节点都是哈希指针。两两结合取哈希,一层层推上去,最后根节点也取哈希叫做根哈希值(root hash)

该数据结构的好处在于:只需要记住根哈希值,就可以检测出对树中任何部位的修改

如上中,Merkle Tree中最后一层第二个节点的内容发生了改变,则对应的第三层第一个节点中第二个哈希值也会发生改变,以及第二层第一个节点中第一个哈希值也会发生改变,进而根节点中第一个哈希值也会发生改变,从而导致根哈希值也发生了改变

在比特币中,不同区块通过哈希值指针连接,在同一个区块中的多个交易(数据块),则通过Merkle Tree的形式组织在一起。区块本身分为两部分:block header(块头)和block body(块身),在block header中保存着根哈希值(没有交易的具体信息),block body中保存着交易列表

Merkle Tree的实际用途:

Merkle Tree可以用于提供Merkle Proof。比特币中节点分为轻节点全节点。全节点保存整个区块的所有内容,而轻节点仅仅保存区块的block header

当需要向轻节点证明某条交易是否被写入区块链,就需要用到Merkle proof。我们将交易到根节点这一条路径称为Merkle proof,全节点将整个Merkle proof发送给轻节点,轻节点即可根据其算出根哈希值,和自己保存的对比,从而验证该交易是否被写入区块链。只要沿着该路径,所有哈希值都正确,说明内容没有被修改过

如上图,轻节点想知道标为黄色的交易是否被包含在这棵Merkle Tree里面。轻节点没有保存交易列表,没有这棵Merkle Tree的具体内容,只有一个根哈希值,因为轻节点只保存区块头信息

轻节点向某个全节点发出请求,请求一个能够证明这个交易被包含在Merkle Tree里面的Merkle proof。全节点收到请求后,需要把标为红色的哈希值发给轻节点。有了这些哈希值之后,轻节点可以在本地计算出标为绿色的哈希值

轻节点首先算出标为黄色的交易的哈希值,然后和全节点提供的右边标为红色的哈希值拼接起来,可以算出上层节点里标为绿色的哈希值

然后再和左边红色的哈希值拼接起来,可以算出上层节点里标为绿色的哈希值

然后再和右边红色的哈希值拼接起来,就可以算出整个Merkle Tree的根哈希值。轻节点把这个根哈希值和block header中的根哈希值比较一下就能知道标为黄色的交易是否被包含在这棵Merkle Tree里面

3、BTC-协议

1)、数字货币中经常出现的问题

比如央行发行的数字货币都有央行的私钥签名,公钥是公开的,用了密码学的非对称加密体系。买东西的时候把数字货币给别人,别人用央行的公钥验证一下,证明这确实是央行发行的。那么面临什么问题呢?

double spending attack(双花攻击):

数字货币本身是带有签名的数据文件,可以进行复制。即对用户来说,可以将同一货币花两次

现在改进一下,还是央行发行数字货币,每个数字货币上给个编号,央行来维护数据库,就是一个大的表,上面记录下每个编号的数字货币在谁手里。每次交易都必须向央行报备。这个方案是中心化的方案,每一次交易必须要央行确认才能证明其合法性

2)、去中心化货币要解决的问题

1)数字货币的发行由谁执行?发行多少?什么时候发行?

在传统中心化货币体系中,这些问题我们可以交给第三方机构(如:央行)。当引入去中心化思想后,系统中节点平等,交易不通过第三方,那么货币发行权的分配必然是一个需要解决的问题

在比特币中由挖矿来决定货币发行权和发行量

2)如何验证交易是否有效?如何防止双花攻击?

在传统中心化体系中,该问题的解决由第三方机构来完成。而剔除这一机构后,交易双方如何能够验证交易的有效性?如何防止系统中恶意用户作恶获取收益?这也是去中心化交易系统需要解决的问题

该问题的解决依赖于系统中维护的一个数据结构,记录货币的使用情况(是否被花过?被谁花过?)。该数据结构由系统中全体用户共同维护,保证了交易的有效性。该数据结构就是区块链

3)、比特币中如何验证交易是否有效?如何防止双花攻击?

如上图,假定A获得发行货币的权利,称为铸币权,发行了10个比特币(该交易称为铸币交易)。A将10个比特币转给了B(5个)和C(5个),A对该交易进行签名,同时该交易需要说明所花掉10个比特币来源(来自铸币交易)

比特币中每个交易都包含了输入和输出两部分,输入部分要说明币的来源,输出部分要给出收款人的公钥的哈希。比如A要转给B钱就要说明B的公钥的哈希是什么

之后,B将自己的5个比特币转给C(2个)和D(3个),该交易需要B的签名,该交易需要说明所花掉的5个比特币来自于第二个交易中

然后,C将自己所拥有的全部7个比特币都转给E,并对该交易签名,可以发现该交易中C的比特币来源于两个交易中

如何防止双花攻击?

需要注意的是,这里面有两种哈希指针。第一种为指向前面的区块,使得各个区块形成链,第二种则是为了说明比特币的来源。说明比特币的来源并非凭空捏造,可以防止双花攻击

比如,B已经把全部的比特币转给C和D,B又要转给F 5个比特币,说明的币的来源来自于第二个交易中。别的节点收到这个交易后,从这个新的区块往币的来源回溯一下,到第三个交易时发现这5个比特币已经在这个交易(B将自己的5个比特币转给C 2个和D 3个)中被花出去了,就说明新的这个交易是不合法的,不会把它接受到区块链里

如何验证交易是否有效?

在进行交易时,需要付款人的签名和收款人的地址。在比特币中,该收款的地址即为收款人的公钥的哈希,可以将其视为银行账户,根据此进行转账交易(虽然公钥可以公开,但实际中更多公开的是公钥的哈希)

在A给B转账的交易中,收款方B需要知道付款方A的公钥,从而验证A的签名是否有效,即A需要提供自己的公钥。区块链上每个节点是独立验证的,其他节点都需要知道付款方A公钥,A通过自己的私钥进行签名,其他节点通过A的公钥来验签,从而验证交易的合法性

比特币交易中输入部分除了要说明币的来源,还要说明付款人的公钥,而且付款人的公钥需要和币的来源里的公钥的哈希对得上。A给B转账的时候提供的公钥需要和铸币交易中公钥的哈希对的上,这样就防止了恶意节点伪造A的公钥来偷走A的比特币

在比特币中,通过执行脚本实现上述验证过程。将当前交易输入脚本与前一个交易输出脚本(说明币的来源的交易)拼接执行,如果可以正确执行,说明交易合法

上图中一个区块仅含有一个交易,实际中一个区块中包含多个交易,交易通过Merkle Tree组织起来,在区块中存储

4)、比特币区块信息

每个区块包含两部分:block header(块头)和block body(块身)

block header里包含区块宏观信息

block header
version(使用比特币哪个版本的协议)
hash of previous block header(指向前一个区块指针)
merkle root hash(Merkle Tree的根哈希值)
target(挖矿难度目标阈值)
nonce(随机数)
  1. 挖矿求解问题:H(block header)<=target
  2. Hash of previous block header只计算区块块头部分的哈希(Merkle root hash保证了block body内容不被篡改,所以只需要计算block header即可保证整个区块内容不会被篡改)
  3. 区块链系统中,轻节点(只存储区块block header信息)只利用区块链,但并不参与区块链系统维护和构造
5)、分布式共识

可否各个节点独立完成区块链构建?

很明显不行,各个节点独立打包交易,形成区块链,必然无法避免区块链内容不一致。从分布式系统角度来说,账本内容需要取得分布式共识(distribute consensus),从而保证区块链内容在不同节点上的一致性

根据FLP不可能结论,在一个异步系统中,网络时延无上限,即使只有一个成员是有问题的,也不可能达成共识

根据CAP定理(Consistency一致性、Availability可靠性、Partition tolerance分区容错性),任何一个分布式系统中,最多只能满足其中两个性质

6)、比特币中的共识协议

比特币中共识协议要解决的问题是有些节点可能是有恶意的。假设系统中大多数节点是好的,有恶意的占小部分,在这种情况下如何设计共识协议?

想法一:直接投票

某个节点打包交易到区块,将其发给其他节点,其他节点检查该候选区块,检查若正确投赞成票,若票数过半数,加入区块链

存在的问题:

  1. 恶意节点不断打包不合法区块,导致一直无法达成共识,时间全花在投票上
  2. 无强迫投票手段,某些节点不投票(行政不作为)
  3. 网络延迟事先未知,投票需要等多久?效率上会产生问题
  4. 更大的一个问题:membership。如果这个区块链对加入成员有要求(比如联盟链),可以基于投票。但比特币系统,任何人都可以加入,且创建账户及其简单,只需要本地产生公私钥对即可。只有转账(交易)的时候,比特币系统才能知道该账户的存在。这样,黑客可以使用计算机专门生成大量公私钥对,当其产生大量公私钥对超过系统中一半数目时,就可以获得支配地位(女巫攻击)。所以,这种简单的投票方案也是不可行的。

比特币中的共识协议:

比特币系统中采用了很巧妙的方案解决这个问题。虽然仍然是投票,但并非简单的根据账户数目,而是依据计算力进行投票

比特币中,每个节点都可以自行组装一个候选区块,而后尝试各种nonce值,这就是挖矿

当某个节点找到了符合要求的nonce(H(block header)<=target),就获得了记账权,从而可以将区块发布到系统中。记账权就是往比特币这个去中心化的账本里写入下一个区块的权力,只有掌握的记账权的节点才能写入下一个区块

其他节点收到区块后,验证区块合法性,比如:

  1. 验证H(block header)<=target
  2. 验证block body中每个交易都是合法的,要有合法的签名并以前没有被花过

如果系统中绝大多数节点验证通过,则接收该区块为最新的区块并加入到区块链中

最长合法链(longest valid chain):

区块链正常运行场景下可能会产生分叉。当两个节点同时获得记账权时,会有两个等长的合法链。在缺省情况下,节点接收最先收到的区块,该节点会沿着该区块继续延续。但随着时间延续,必然有一个链胜出,由此保证了区块链的一致性(被扔掉的区块称为孤儿区块orphan block)

如上图,发生分叉的情况下暂时保存分叉情况,但区块链只承认最长合法链,随着时间推移,必然存在某一条链变成最长合法链

分叉攻击(forking attack,通过在中间插入区块来回滚已发生的区块):

如上图,A用户对上面的A转账给B的记录回滚,从而非法获取利益。在两条链上发现交易都合法。这是一个典型的双花攻击。A给B转账后,用分叉攻击将钱又转回来,覆盖掉原来的记录

在比特币中,这种情况实际上很难发生。因为大多数矿工认可的是最长的合法链,会沿着上面的链继续挖下去。而A这个攻击者要想回退记录,就必须使得下面的链变得比上面的链还长。理论上来说,攻击者需要达到整个系统中51%的计算力,才能使得这种攻击成功

7)、比特币激励机制

为什么系统中节点要竞争记账权?需要提供算力和电力成本,节点为什么要去做?

比特币系统设计之初就考虑到了这个问题,于是引入了激励机制。比特币通过设置出块奖励(block reward)来解决该问题,一个获得合法区块的节点可以在区块中加入一个特殊交易:铸币交易(coinbase transaction)。事实上,这种方式也是唯一一个产生新比特币的途径

比特币系统设计规定,起初每个区块可以获得50个比特币,但之后每隔21万个区块奖励减半

区块中保存交易记录,那么,会不会存在节点只想发布区块而不想打包交易?中本聪在设计系统时引入了交易费。在一个区块中,其输入大于输出,差值就是给区块所属节点的手续费

4、BTC-实现

1)、UTXO

区块链是一个去中心化的账本,比特币采用了基于交易的账本模式(transaction-based ledger)。每个区块里记录的是交易信息,有转账交易,有铸币交易,但是系统中没有显示记录每个账户包含多少个比特币,实际上其需要通过交易记录进行推算。在比特币中,全节点需要维护一个名为UTXO(Unspent Transaction Output,尚未被花掉的交易输出)的数据结构

如上图,A转给B 5个比特币,转给C 3个比特币,B将5个比特币花掉,则该交易记录不保存在UTXO中,C没有花掉,则该交易记录保存在UTXO中

UTXO集合中每个元素要给出产生这个输出的交易的哈希值,以及其在交易中是第几个输出。通过这两个信息就可以定位到UTXO中的输出

为什么要维护这样一个数据结构?

为了防范double spending(双花攻击),判断一个交易是否合法,要查一下想要花掉的比特币是否在该集合中,只有在集合中才是合法的。如果想要花掉的比特币不在UTXO中,那么说明这个比特币要么根本不存在,要么已经被花过。所以,全节点需要在内存中维护一个UTXO,以便快速检测double spending

每个交易会消耗输出,但也会产生新的输出

如上图,A转给B 5个比特币,之后B将其转给D,则UTXO中会删掉A->B这一交易记录,同时会添加B->D这一交易记录

加入有人收到比特币转账,但一直不花,那么这个信息会一直保存在UTXO中。这种情况可能是该用户不想花这些比特币(比如中本聪),也有可能是忘记了私钥导致无法花掉。所以,UTXO是逐渐增大的,但该数据目前来说,一个普通的服务器内存中是可以完全保存这些数据的

2)、Transaction fee(交易费)

每个交易可以有多个输入,也可以有多个输出,但输入之和要等于输出之和(total inputs = total outputs)。存在一些交易的total inputs略大于total outputs,这部分差额作为交易费(Transaction fee)给获得记账权的节点

区块中保存交易记录,那么,会不会存在节点只想发布区块而不想打包交易呢?因此比特币设计了交易费,对于获得记账权节点来说,除了出块奖励之外,还可以得到打包交易的交易费。但目前来说,交易费远远小于出块奖励。等到未来出块奖励变少,可能区块链的维护便主要依赖于交易费了

比特币中,每隔21万个区块出块奖励减半。比特币中平均出块时间是10分钟,基本上出块奖励每4年( 21 万 × 10 分钟 60 分钟 × 24 小时 × 365 天 ≈ 4 年 \frac{21万 \times 10分钟}{60分钟 \times 24小时 \times 365天} ≈ 4年 60分钟×24小时×36521×10分钟4)减半

比特币是基于交易的模式,与之对应还有一种基于账户的模式(比如以太坊)。基于账户的模式要求系统中显示记录账户余额。也就是说,可以直接查询当前账户余额。比特币这种基于交易的模式隐私性较好,但也要付出一定代价,在进行交易时,因为没有账户这一概念,无法知道账户剩余多少比特币,所以就必须说明币的来源。而基于账户的模式则避免了这种缺陷,转账交易就是对一个或多个账户余额的数字减和另一个或多个账户余额的数字加

3)、比特币中具体的区块信息

在这里插入图片描述

如上图是一个区块的信息,字段具体含义如下:

Summary含义
Number of Transactions总交易数
Output Total总输出
Transaction Fees总交易费(所有交易的交易费之和)
Height区块的序号
Timestamp区块的时间戳
Difficulty挖矿难度,每隔2016个区块要调整挖矿难度保持出块时间在10分钟左右
Nonce挖矿时尝试的符合要求的随机数
Block Reward出块奖励
Hashes含义
Hash区块块头的哈希值
Previous Block前一个区块块头的哈希值
Merkle RootMerkle Tree根哈希值

如上图,可以看到,区块块头的哈希值与前一个区块块头的哈希值都是以一长串0开头的,挖矿本身就是尝试各种nonce,使得产生的区块块头的哈希值小于等于目标阈值。该目标阈值表示成16进制就是前面含有一长串的0,所以凡是符合难度要求的区块的块头的哈希值算出来前面都含有一长串的0

在这里插入图片描述

上图是block header的代码中实现的数据结构,可以看到nonce是一个32位的无符号整型数据,在挖矿的时候要不断调整nonce,但是nonce的取值最多有 2 32 2^{32} 232种。由于近年来,挖矿人员越来越多,挖矿难度已经调整的比较大了,而 2 32 2^{32} 232这一搜索空间太小,所以仅调整nonce很大可能找不到正确的结果

还有哪些域可以调整呢?

在这里插入图片描述

仅仅调整nonce是不够的,所以这里可以通过修改Merkle Tree的根哈希值来进行调整。这个怎么能修改呢?

铸币交易(coinbase transaction):

每个发布的区块里有一个特殊的铸币交易,这也是BTC系统中产生新比特币的唯一方式

在这里插入图片描述

铸币交易中有一个CoinBase域,可以写入任何内容。只要改变了CoinBase域写入内容,就可以改变Merkle Tree的根哈希值

如上图,左下角交易为coinbase transaction,该交易发生改变会逐级向上传递,最终导致Merkle Tree的根哈希值发生改变

在实际的挖矿中包含两层循环。外层循环调整coinbase域(可以规定只将其中前x个字节作为另一个nonce),算出block header中根哈希值后,内层循环再调整header里的nonce

普通转账交易:

在这里插入图片描述

如果将输入脚本和输出脚本拼接起来可以顺利执行不出现错误,则说明交易合法

4)、挖矿过程的概率分析

挖矿本质上是不断地尝试各种nonce,来求解这样一个puzzle。每次尝试nonce可以看做是一次Bernoulli trial(伯努利试验:a random experiment with binary outcome)。最典型的伯努利试验就是投掷硬币,正面和反面朝上概率为p和1-p。在挖矿过程中,一次伯努利试验成功的概率极小,失败的概率极大。挖矿就是多次进行伯努利试验,且每次随机。这些伯努利试验便构成了a sequence of independent Bernoulli trials(一系列独立的伯努利试验)。根据概率论相关知识,伯努利试验本身具有无记忆性。也就是说,无论之前做多少大量试验,对后续继续试验没有任何影响

对于挖矿来说,就是多次伯努利试验尝试nonce,最终找到一个符合要求的nonce。在这种情况下,可以采用Poisson process(泊松分布)进行近似,由此通过概率论可以推断出,系统出块时间服从指数分布

通过定期调整挖矿难度使得币平均出块时间维持在10分钟左右

指数分布本身也具有无记忆性。也就是说,对整个系统而言,已经过去10分钟,仍然没有人挖到区块,那么平均仍然还需要等10分钟。也就是说,将来要挖多久和已经挖多久无关

虽然过去的工作可能都会白做,但实际上这才是挖矿公平性的保障。对算力有优势的矿工来说,其之前所做大量工作仍有可能会白费

5)、比特币总量

出块奖励是系统中产生新比特币的唯一途径,每隔21万个区块(4年)出块奖励减半
21 万​ × ​ 50 + 21 万​ × ​ 25 + 21 万​ × ​ 12.5 + … … = 21 万​ × ​ 50 ​ × ​ ( 1 + 1 2 + 1 4 + … … ) = 21 万​ × ​ 50 ​ × ​ 2 = 2100 万 21万 ​\times​ 50 + 21万 ​\times​ 25 + 21万 ​\times​ 12.5 + ……\\ =21万 ​\times​ 50 ​\times​ ( 1 + \frac{1}{2} + \frac{1}{4} + ……)\\ =21万 ​\times​ 50 ​\times​ 2\\ =2100万 21×​50+21×​25+21×​12.5+……=21×​50​×(1+21+41+……)=21×​50​×​2=2100
系统中已经挖出和未挖出的比特币总量是2100万个

挖矿操作并不是在解决数学难题,而是单纯的算力比拼。也就是说,挖矿操作并没有实际意义,但挖矿的过程对于维护比特币系统的安全性是至关重要的(BitCoin is secured by mining)

比特币越来越难被挖到,且出块奖励越来越少,是否说明其未来挖矿的动力将越来越低呢?

实际上恰恰相反。在早期比特币很容易挖到的时候,比特币并不被人们所看好,后来比特币估值上涨,吸引其他人参与挖矿,又进一步促进了比特币价值上涨,进而又吸引更多人参与进来

当出块奖励趋于0时,则整个系统将依赖于交易费运行,这时交易费将成为维护比特币系统运行的重要保障

6)、比特币安全性分析

假设大多数算力掌握在诚实的矿工手中,能否保证写入区块链的交易都是合法的?

需要注意的是,算力低的矿工并非完全不能获得记账权,仅仅是概率上较低的问题。但实际上,即使拥有少量算力的恶意节点,也有一定概率获得某个区块的记账权

1)能否偷币(恶意节点能不能将其他账户上比特币转给自己)?

不能。因为转账交易需要签名,恶意节点无法伪造他人签名

如果恶意节点获得记账权并硬往区块中写入该交易,大多数用户会认为其是一个非法区块,大多数算力将不认可该区块,从而沿着其他路径挖矿,随着时间推移,拥有大多数算力的诚实的节点将会仍然沿着原来区块挖矿,从而形成一条最长合法链,该区块变成孤儿区块

对于攻击者来说,不仅不能偷到其他人的比特币,而且得不到出块奖励,还浪费了挖矿花费的电费等成本

2)能否将已经花过的币再花一遍(double spending)?

M发布一个转账交易给A,已经写到区块链里了。M获得了记账权,把钱再转回给自己,如上图的方式,很明显为一个非法区块,不会被其他节点承认

所以,M只能通过上图的方式将M转账给B的记录回滚掉(分叉攻击)。如上图,此时就有了两条等长合法链,取决于其他节点沿着哪条链往下扩展,最后有一个会胜出,另一个就作废

需要注意的是,区块插入到哪一个区块之后是在刚开始挖矿的时候就要决定的(而不是在获得记账权之后),因为区块头里要填上前一个区块块头的哈希值

如果M转给A的钱产生了某种不可逆的外部效果,下面再把交易回滚,那么M就可以从中不当获益。比如说网上购物,这个网站接受比特币支付,M购买一些商品,M发起交易把账转给这个网站。这个网站监听到交易写入到区块链里了,以为支付成功了,就把商品给了M。M拿到商品后,又发起一个交易,把钱转给自己,把下面这条链扩展成最长合法链,上面的区块就作废了。M这样攻击既得到了商品又把花出去的钱收回来了

如何防范这种攻击?

如果M转账给A的交易不是在最后一个区块,而是后面又跟了几个区块,那么这种攻击的难度就大大增加。M要想回滚交易,要想办法让下面这条链成为最长合法链。诚实的节点不会沿着下面的区块往下扩展,相当于是恶意节点挖下面的链,其他节点挖上面的链的算力比拼。如果大部分算力是掌握在诚实的节点,则最终上面链会胜出,而恶意节点的链会不被认可,从而导致投入成本白费

所以,一种简单防范就是多等几个确认区块(confirmation)。比特币协议中,缺省需要等6个确认区块,此时才认为该记录是不可篡改的。平均出块时间10分钟,6个确认区块就需要1小时,等待时间还是相对较长的

zero confirmation是指交易刚发布出去,还没有写入区块链中的时候,就认为交易已经不可篡改了

zero confirmation实际使用的比较广泛,有两个原因:

  • 两个交易有冲突,节点接收最先听到的交易。上面分叉攻击的例子中M->A后的M->M’大多诚实节点会将其拒绝
  • 购物网站委托全节点监听区块链,从支付成功到发货其实还有比较长的处理时间,如果发现这个交易最后没有写到最长合法链,购物网站可以选择取消发货

3)能否故意不包含某些合法交易?

这个是没关系的,因为比特币协议也没有规定获得记账权的节点必须发布哪些交易,而且没有写进这个区块也可以写进下一个区块,总有诚实的节点愿意发布这些交易。而且比特币系统在正常工作时候也会出现某些交易被滞后发布的情况,可能就是一段时间内的交易太多了,毕竟一个区块不能超过1M

4)selfish mining

正常情况下节点挖到一个区块就立即发布,这是为了得到出块奖励和收取交易费。selfish mining就是挖到的区块都留着,这样的动机是,比如在前面的分叉攻击中,一直等到6个confirmation过了,再一口气把算好的很长的分叉发布出去,替换掉最长合法链

实际上这样做的难度还是很大,因为这个恶意节点的算力要超过那些诚实的算力才可能在一定时间后比它长。另外就是大多诚实的节点已经扩展那个M->A的交易所在的区块了,这个恶意节点的同伙节点也要很多才行

即便不是为了做什么攻击,就是为了赚取出块奖励和收取交易费,selfish mining也有好处:能够减少自己的竞争对手

如上图,大家都在从A挖下一个区块,然后某个节点挖出了B先藏着,这时候别人还在从A挖下一个区块,然后这个节点紧接着挖出了C,将B和C一起发布出去,这样就少了一个节点C的竞争

或者是继续往下挖,当听到有人发布D时,将B和C一起发布出去,这样最长合法链是沿着ABC的,别人挖出的D就作废了

但这样会带来不小的风险,假设在挖出C之前就有人挖出D并且发布了,这时候就只能赶紧把B发布出去,很可能连这个记账权都竞争不到了

在这个动机下,selfish mining的回报并非很高,只是让别人做了无用功,自己少了些竞争,但风险却是挺大的

对应课程

北京大学肖臻老师《区块链技术与应用》公开课

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐