BTC-协议
约 6457 字大约 22 分钟
2021-09-01
相关术语
double spending attack 双花攻击
Bitcoin minting 铸币权
Bitcoin mining 挖矿
BitCoin Script 比特币执行脚本
coinbase tx(transaction) 铸币交易
distribated consensas 分布式共识
distribated hash table 分布式哈希表
impossibility result 不可能结论
asynchronous 异步的
CAP Theorem CAP理论
Consistency 一致性
Availability 可用性
Partition tolerance 分区容错性
Byzantine Generals Problem 拜占庭将军问题
Byzantine Fault Tolerance,BFT 拜占庭容错算法
Consensus in BitCoin 比特币共识机制
hyperledger fabric 联盟链
membership 会员
sybil attack 女巫攻击
longest valid chain 最长合法链
block reward 区块奖励
forking attack 分叉攻击
orphan block 孤块
puzzle friendly 拼图友好
mining 挖矿
miner 矿工
digtal gold 数字黄金
hash rate 哈希率
coinbase transaction Coinbase交易
加密货币方案
思考
首先不考虑去中心化,设想一下例如何设计出一个加密货币?
方案一
假设一个机构是大家都信任的,比如说央行。人民币是怎么发行的,印钞厂印刷纸币,上面有各种防伪标记,要买东西,就把人民币给卖家。
如果央行发行数字货币,发行的数字货币都有央行的私钥签名。央行的公钥可以是大家都知道的。所以我收到一个数字货币,可以验证是不是真的央行发行的数字货币。如果拿着这个数字货币,去买东西,卖家验证一下是否是央行发行的,如果是真的,就收下。完成了交易。
这个过程里面是没有区块链的。 这里面用到的是,密码学中的公私钥体系,非对称加密算法,但是没有用到区块链,这样一个方案有问题么?
还是存在很多问题的:
- 私钥容易泄露,央行的私钥泄露的话会有很大问题! 这个问题倒是不大,一般不会泄露,如果央行的私钥泄露,那我们普通的人私钥更容易泄露。
- 还有就是,货币的发行量未知,容易导致通货膨胀。如果像美国一样,量化宽松,发行好多,会影响市场!
- 等等...
但是其实有个更大的问题,这种方法根本就不行!!!
比如买东西,收到数字货币之后,因为数字货币本身就是文件,这个文件有央行的签名,这个内容是不可伪造的,本身是100块钱的数字货币,没法改成200块钱。但是它是可以复制的,无限复制。买A的东西花一次,买B得东西花一次。这个跟纸质的人民币是不一样的,我把一张一百元给了卖家,我就没有了,没法花两次,上面各种防伪,很难复制。这是最致命的问题:双花攻击。双花支付。double spending attack
。
数字货币面临的主要挑战就是怎么防范double spending attack
双花攻击。
方案二
刚才的方案是不行的。接下来改进一下,现在给发行的数字货币添加上编号,央行会维护一个数据表,维护编号,记录该数字货币是谁的。现在买东西,不光是要验证数字货币是不是央行的,现在还要跟央行验证这个数字货币以前我有没有花出去过。确认会不会以前给过别人,现在又想给你了。如果确认后,没问题,那么央行还要会把这个数字货币的编号更新,更新成属于卖家了,不再属于前者。 但是这个方案太麻烦了,正确性没问题,但这还是中心化的方式。如果每次交易都通过央行验证就太麻烦了。
方案三
有没有一个方案,把需要央行验证的职能,转移到广大的用户层面,这也是比特币系统,即数字货币要解决的另一个问题。
去中心化数字货币俩个重点要解决的问题
一个去中心化的数字货币需要解决两个问题:
一个是数字货币的发行,谁来发行,发行多少,什么时候发行。
怎么验证交易的有效性,怎么防止
double spending attack
。
第一个问题;在比特币中是由挖矿决定,之后探究;第二个问题:怎么解决double spending attack
。
其实这个解决方案和方案二类似,只不过不是用央行决定,而是由所有用户共同维护的。这个数据结构就是区块链。
比特币交易解析
比特币中一个交易,都包含了输入和输出俩个部分。输入部分要说明币的来源,输出部分要给出收款人的公钥hash。这个过程其实有俩种指针,一种是之前介绍的指向前一个区块的哈希指针,链接各个区块之间的把他们串起来构成一个链表。还有一种是指向前面某个交易的指针,是为了说明币得来源是从哪来的。
比如说用户A,他获得了发行货币的权利,叫Bitcoin minting
铸币权。他发行10个比特币。把这个操作写到文件里面。然后他给B和C交易了,每人5个。接着,B又转给C 2个和D 3个。C又转给E 1个。C的情况比较特殊,C的来源有俩个,如图所示。这就构成了一个区块链。
为什么要说明币得来源?要证明这个钱不是凭空捏造的,是有记录的,同时也为了防范double spending attack
。
比如下面这种情况,B已经把钱转给C和D了。他又想转给F 5个币。在右侧,B发起交易,给F转5个币,单纯的验证签名好像没问题。但是B的币是从哪来的呢?这个B的来源在第二块,所以验证从右侧到B的来源回溯一下,发现B其实已经花掉了他的5个币,所以这是不合法的。就不会把他接受到区块链里。这就是检测double spending
。
再仔细看下,例A把钱转给B这个交易。这个交易需要什么信息? 需要有A的签名,同时还需要有B的地址。在比特币系统中收款地址是通过公钥推算出来的。这个地址就相当于银行帐号。那么A是如何知道B的地址的?B的公钥是公开的,但是他也不会告诉所有人的,没这个必要。
日常生活中,比如A需要给B转钱,A需要知道B的银行帐号。那怎么知道B的银行帐号?B要告诉A才行。为什么要给B转钱,比如A购买B的产品,B接受的支付方式是银行转账。那A就得问B的银行帐号是多少,B就告诉A。但是,银行本身是不提供这种查询功能的。指定某个人,查询某个人的银行帐号,不是这样的。比特币系统是类似的, A要给B转账,比特币内部也不提供这种查询功能。
那么B需要知道A的哪些信息么?好像不需要,反正是A给B转账,收钱就完事了。其实需要知道一些信息:重要B要知道A的公钥。虽然说B要知道A的公钥就知道钱是哪来的,但这不是最重要的。最重要的是不仅B要知道A的公钥,重要所有节点都要知道A的公钥!用来验证签名。(签名是什么:私钥签名,公钥验证。)所以每个区块链都要验证是否合法,即便不是转给其他人的,其他人也是要验证的。那新的问题,怎么才能知道A的公钥?是交易中A自己说的。这个交易的输入部分,除了说明币的来源,还要说明A的公钥是什么。
但是这会不会有安全漏洞?怎么能让A自己说呢?如果有人冒名顶替,比如B'。例B' 伪造一个A到B的转账交易,(注意,伪造的是交易!)它用它自己的公钥在输入中说是A的公钥,用它自己的私钥签名。那别的节点收到这个交易,用B' 的公钥去验证B' 的私钥,那肯定是合法的呀。以为这个交易是合法的。这不就等于B' 把A账户的钱偷走了么?
这个问题是存在的! 怎么去避免这种问题呢?重要每个交易分为输入和输出俩部分。输入部分要说明币的来源,和发起人的公钥。输出部分要给出收款人的公钥。看一下A的币是哪来的。是铸币交易来的,叫做coinbase tx
,它的输出里面有A的公钥,所以A到B的转账交易里说明的A的公钥要和币的来源的里面说明的A的公钥要对的上才行。如果对不上,说明是不对的,这个币当时不是给你的。所以B' 去伪造的时候,检测是无法通过的。即输入部分发起人的公钥和前一个块输出部分的收款人的公钥对不上。所以这些指向币的来源的哈希指针是很有必要的!!!
非对称加密
这里再加深一下非对称加密的概念。A给B发送信息,是用B的公钥加密,B收到之后,用自己的私钥解密。B需要给A回复的时候,用的是A的公钥加密,A收到之后用A自己的私钥解密。
在比特币中,这些验证过程,是通过执行脚本(BitCoin Script
)来实现的。每个交易的输入是一段脚本,包括给出公钥,这个公钥也是在这个输入的脚本里包含的。每个交易的输出也是一段脚本。重要那么验证交易是不是合法的,就是把当前交易的输入脚本和前面那个交易,就是提供币的来源的交易的输出脚本拼在一起。如果能够顺利执行,才说明是合法的。
以上图示,其实是简化的。实际区块中,可能包含很多的交易。这些交易就组织成Merkle Tree
。每个区块分成块头和块身俩个部分。
Block header
又包含:
version
hash of previous block header
merkle root hash
target
(目标域,挖矿相关)nonce
(随机数,挖矿相关)
Block body
包含:
tansaction list
(交易列表)
节点又分为全节点full node
和轻节点light node
。全节点是保存所有的信息的,区块链的所有信息,然后验证每一个交易。所以全节点也叫做fully validating node
。轻节点没有存历史交易信息,没有办法独立验证合法性。轻节点是没有参与区块链的构造和维护,只是利用了区块链做一些查询等简单的操作。
每个帐号都可以发布交易。那么这些交易是怎么写进区块链中的呢?这些交易是广播给所有节点的,有些交易是合法的,有些交易是非法的。那么谁来决定,哪些交易写到下一个区块中呢?首先,每个节点独立决定,肯定是不行的,一致性得不到保证。用分布式的术语来说:账本内容要取得分布式的共识(distribated consensas
)。
共识机制
分布式共识
分布式共识个简单例子就是分布式哈希表(distribated hash table
)。比如系统里有很多台机器,共同维护一个全局的哈希表。这里面需要取得共识的是什么,是哈希表中包含了哪些key-value pair
。A插入一个pair,B也要能读到。分布式领域是有很多"不可能结论",impossibility result
。其中最著名的就是 FLP impossibility result
。FLP是三个作者姓的字母。他们的结论是:在一个异步asynchronous
的系统里即使只有一个成员(faulty
)是有问题的,也不可能取得共识。
还有一个比较著名的,叫做CAP Theorem
。CAP是指分布式系统的三个性质。Consistency
:一致性,Availability
:可用性,Partition tolerance
:分区容错性。任何分布式系统中,这三个性质最多满足两个,不可能这三个性质都满足。Javaer泪洒当场,有谁记得被八股文支配的恐惧。
分布式共识的一个著名协议,Paxos
。这个协议能够保证一致性,但是可能存在的是它可能一致无法达成共识。
扩展
拜占庭将军问题(Byzantine Generals Problem
)是一个关于分布式系统一致性的著名问题,由莱斯利·兰伯特(Leslie Lamport)等人在1982年提出。这个问题简洁地介绍如下:
背景:假设拜占庭帝国的将军们必须通过信使相互通信来协调作战计划,但由于叛徒的存在,部分将军可能会收到错误的信息或者故意发送错误的信息。
问题核心:即使在部分成员(将军)可能不按预期行事(比如叛变、发送错误信息等)的情况下,如何保证整个系统(剩余忠诚的将军)能够达成一致的决策。
分布式系统中的应用:在分布式计算领域,这个问题被用来描述在网络中多个节点必须达成一致决定时,如何防止恶意节点发送错误信息或不发送信息,从而影响整个系统的一致性和可靠性。
解决方案:拜占庭将军问题的解决方案通常涉及到设计算法,以确保即使在存在拜占庭节点(即不可靠的节点)的情况下,系统仍然能够正常运作并达成一致。
区块链技术:拜占庭容错(
Byzantine Fault Tolerance
, BFT)算法是解决拜占庭将军问题的一种方法,它在区块链技术中尤为重要,因为区块链需要在去中心化的网络中确保所有节点对交易的一致性。
拜占庭将军问题是理解和设计分布式系统一致性协议的一个基础性问题,对于确保分布式系统的安全性和可靠性至关重要。
思考
比特币中的共识协议,Consensus in BitCoin
。有些节点可能是恶意的。怎么去设计共识协议呢?一种想法是,既然大多数是好的,直接投票行不行?
比特币共识机制
比如说某一个节点,它提出一个候选区块。根据收到的交易信息,选一下哪些是合法的。然后把这些交易按照某种顺序,打包到一个区块里。然后把候选区块发布给所有节点。然后每个几点收到这个区块后,检查一下这里面的交易是不是都是合法的,如果都是合法的,它就投赞成票。如果有一个交易是非法的就投反对票。如果得票超过半数,这个区块就正式接受,接到区块链里。
这个方案可能存在的问题:
- 如果一个恶意区块,不断提出有问题的区块,让所有节点投票,这时间都浪费在投票上了,区块链就没法往后发展了。
- 没有办法保证每个节点都投票,有些节点就犯懒,就不投,不作为。也没撤。
- 还有效率的问题,网络延迟不稳定,每轮投票等多久不好界定。
以上都是实际存在且更细节的问题,但其实还有一个更大的问题!
任何基于投票的方案,首先要确认谁有投票权。要有一个membership
的问题。不是谁都可以加入的。比如联盟链(hyperledger fabric
),只有某些符合条件的大公司才能加入,这种情况下投票是可以的。但是比特币不是这样的,比特币系统中创建一个账户是很容易的。就在本地产生一个公钥私钥对就是一个账户,不需要任何人批准,其实别人都不知道产生了一个账户。只有跟外部产生交易的时候别人才会知道这个账户。如果有恶意的组织,搞一台超级计算机,不停的产生账户。产生的账户数超过一半它就有控制权了。它就可以操纵投票结果。这种情况叫做女巫攻击(sybil attack
)。
比特币中用了一个很巧妙的机制去解决这个问题。也是投票,但不是按照帐号的数目投票。而是用计算力投票,每个节点都可以在本地组装一个候选区块,把它认为合法的交易放到这个区块里。然后开始尝试各种nonce
值来满足H(block header) <= target
。block header
有一个域,是个随机数nonce
。组装好区块后,就开始试各种各样的随机数,nonce
是4 bytes
的。看哪个值是满足求出来的hash是落在target
之内的。如果某个节点找到了符合要求的nonce
。我们就说,它获得了记账权。
block header
内容对不对。比如block header
中一个nBits
域,它实际是target
目标域值的一个编码。检查一下nBits
域设置的是不是比特币中规定的难度要求。再查看nonce
,是不是整个H(block header)
是不是在target
内。假设投符合要求,然后看一下block body
里面的交易列表,验证一下每个交易都是合法的。第一,要有合法的签名;第二,以前没有被花过。如果有任何异常,那么这个区块不能被接受的,会被放弃掉。假设一个区块,符合各项检查验证,是不是可以接受它呢?有没有一种可能,一个区块满足各项验证,但是仍然不愿意接受它?假设有个区块链,上面是A转给B,下面是A又转给自己。但是这俩个各自都是合法的。这不是double spending
么?这就要重申一下什么是double spending
:是判断这个区块所在的分支上有没有被花过俩次,是从当前区块到币的来源之间,有没有花过。所以,显然,当前图示不是double spending
。这个图示操作,相当于把交易回滚了。虽然这个区块的交易是合法的,但是它不在最长合法链上(longest valid chain
)!
这个例子,说明比特币协议中规定,接受的区块应该是扩展最长合法链。这个例子,其实是分叉攻击的例子,又叫做forking attack
。通过区块链中间插入一个区块,来回滚某个已经发生的交易。其实区块链在正常情况下,也可能产生分叉。比如某个时间,俩个区块都找到了符合的nonce
并且同时发不了俩个合法区块,这就产生了分叉。
如果按照最长合法链原则,该接受哪个呢?比特币缺省情况下,是接受它最早接收到的那个。所以不同节点在网络中位置不同,有些节点可能先接收到上面的,有些可能先接收到下面的。那么什么叫接收一个区块呢? 比特币系统如果它沿着你这个区块往下继续扩展,就算是认可并接收了你这个区块。所以,如果俩个矿工,俩个节点,差不多同时发布区块的情况,这种临时性的分叉会维持一定时间。直到某一个分支最后胜出。上面的分支先找到了下一个区块,就算胜出了。下面的叫做orphan block
。就被丢弃了。
在发散一下。出现分叉时候,有些节点沿着上面的扩展,有些沿着下面的扩展。每个分支上都有算力来竞争。看谁的算力强,同时也看谁的运气好,谁先找到下一个。然后有一个分支就会成为最长合法链,变成大家都接受的。所以,为什么大家要竞争这个记账权?消耗很多计算资源,去争夺。还挺费电。有什么好处呢?首先获得记账权的节点,本身有一定权利。它可以决定哪些交易被写到下一个区块里。但是设计协议的时候,不应该让这一点成为争夺记账权的主要动力。因为是希望,凡事合法的交易,都应该被写入区块链里。比特币中,设计了很巧妙的机制来解决这个问题。
重要这个叫做出块奖励(block reward
)。获得记账权的节点,在发布的区块里,可以有一个特殊的交易,就是前面说过的铸币交易(coinbase tx
)!在这个交易里,可以发布一定数量的比特币。注意!!
在开始的时候说过,有个去中心化的数字货币,要解决两个问题:
- 谁来发行货币;
- 怎么验证交易的合法性;
到此处为止,都是在讲第二个问题,怎么验证交易的合法性。 现在开始,探讨第一个问题,谁来决定发行货币!!!
coinbase transaction
是比特币系统中发行新的比特币唯一方法!!!其他所有的交易,都只是把已有的比特币转移到另外的账户。包括在国外,比如用法币去购买比特币、国外有的交易所,可以开一个账户。然后用美元购买比特币。都是,都没有产生新的比特币。而coinbase transaction
是唯一产生新币的方法,而且不用指明币的来源!因为是凭空造出来的。
那么能造多少币呢?一开始,每一个发布的区块,可以产生50个BTC
。这就是出块奖励。但是协议规定,21w个区块以后,这个奖励就会减半,就会变成25个BTC
。现在已经变成12.5个BTC
。而且现在算力竞争比以前大的多了。
那么回到刚才的问题。出现发叉,并且胜出一个最长链后。那个orphan block
得到的奖励是不是没有用了?对的,就没用了!
比特币的共识机制
所以,比特币的共识机制,要取得的共识到底是什么?前面讲的例子,分布式系统中,有很多台服务器,共同维护一个哈希表。那个例子中,要取得的共识是什么?是哈希表中的内容,其中包含哪些key-value pair
。那比特币中要取得的共识是什么?就是这个去中心化账本里面的内容!那么谁能决定内容呢,只有获得记账权的节点才能往里面写入东西!那怎么获得记账权呢,就是解决H(block header) <= target
这个puzzle
。
为什么说比特币的共识机制是靠算力去投票,因为比特币的puzzle friendly
这个性质。这个性质保证了求解H(block header) <= target
这个puzzle
的过程没有捷径!!! 只能一个一个nonce
去尝试!!!所以一个节点是另一个几点算力的10倍。那么它获得记账权的概率肯定比另一个高的多。这就是比特币中投票的特殊行,它不是一人一票,也不是一台计算机一票,而是看每秒可以试多少个nonce
的数目,叫做hash rate
。一个节点的hash rate
越高,那么获得记账权,得到出块奖励的概率是越大的。
那么这个共识机制,是怎么防范女巫攻击(sybil attack)的问题?因为投票是靠算力投的,其实创建多少个账户是没有影响的。
一般把比特币争夺记账权的过程叫做挖矿,mining
。这是一个比较形象的说法。因为比特币也被认为是digtal gold
。所以这个求解过程,就类似淘金。所以争夺记账权的节点,也被称为矿工,miner
。