前言
初看这个文章标题,大家可能会有两个疑问:
第一,什么是去中心化网络环境?简单来说,“去中心化”指的是在一个网络环境中,所有的节点地位都是相对平等的,没有严格意义上的主从之分。我们以数据读取写入为例,一个很典型的主从网络环境就是我们目前使用非常广泛的MySQL主从库架构,主库负责写,从库负责读,从库从主库同步数据,从库天然的相信主库的数据。所以主从库节点之间的地位是不平等的,主从架构的名字也是将这一点体现的淋漓尽致。
另一个问题应该是,什么是达成共识?基于第一个问题的场景,如果节点之间是相对平等的,那么在需要对某一条数据进行落库保存的时候,怎么样去保存数据,是由其中一个节点先去保存然后在同步给其他节点,还是并行保存?节点间的数据会出现不一致吗,出现不一致以后该怎么办?如果有恶意最坏的节点估计捣乱整个过程,那网络是不是会崩溃?等等等等,这些都是达成共识需要考虑到的问题。所以达成共识可以理解为:保证整个网络数据的一致性。
看到这里,大家是不是对这个话题有了一个比较直观的了解和感受了,相信大家应该也已经迫不及待的想要一探究竟了吧。那就让我们继续往下,来揭开这项技术神秘的面纱吧
一个典型的应用场景
“比特币”这个词,相信大家都有所耳闻。它的数据信息存储在一个叫做“区块链”的数据库当中,而“区块链”本身就是一个“去中心化的分布式存储架构”。区块链是一种以块+链结构实现的数据存储,利用节点之间的共识算法保存数据,利用密码学的方式保证了数据传输和访问的安全,利用自动化脚本实现的智能合约来操作数据的去中心化分布式存储架构。
区块链的组成
去中心化的分布式存储架构。描述了一种数据的存储结构(区块)和存储形式(链)
-
去中心化:没有中心节点,直接通过点对点进行数据传输
-
分布式:区块存储于各个节点
-
区块:区块头和区块体
-
链:按照时间顺序依次相连前后两个区块
针对前言中提到了问题,为了方便大家更好的理解,我们举两个日常生活中常见的例子来看:
- 转账场景
- 提案场景
在实际生活中,提议可能来自于团队内任何一位同学,每个同学会有自己的提议,到底该听谁的?
如果有一套大家都能够认可的规则,来验证提议是否合理。如果合理就同意,反之拒绝。这样就可以在没有中心节点(老大)的情况下,决策出团队都认可的一个提议。这整个达成一致的过程就是“共识”,验证提议是否合理的规则就是“共识算法”。
共识的目的是保证团队内,所有成员都认可这件事情,即外部人员在询问团队内任何一个同学的时候,大家对这件事给出来的答案都是一致的。(即:全网数据的一致)
共识算法
PoW
工作量证明(Proof of work),在比特币应用中俗称“挖矿”
比特币是区块链技术的一种应用,使用的就是PoW共识算法。在一次交易发生时,首先会对这笔交易进行合法性校验,这里主要会依赖于密码学的一些原理和机制。当交易被验证是合理时,就需要对将这笔交易记录下来,即“记账”操作。这个时候网络内所有的节点都会进行一个谜题的计算,这个谜题是SHA256算法函数,上面提到的Target和Nounce都会影响到这个函数的计算难度。第一个解出答案的节点拥有记账的权利,它会创建新的区块将交易记录写入,同时将解出的答案广播给其他节点进行验证。如果验证通过,那么其他节点也会创建新的区块并写入交易记录。
作为报酬,第一个解出答案的节点会拥有一定量的比特币。
整体交易共识流程
-
客户端A在节点1上产生新的交易1
-
节点1向全网进行广播,要求对交易1进行记账。每个记账节点接收到这个请求后,将收到的交易信息放入一个区块中
-
每个节点通过PoW算法,计算本节点的区块的哈希值,尝试找到一个具有足够工作量难度的工作量证明(解答谜题)
-
节点3找到了一个工作量证明并向全网广播。当且仅当包含在该区块中的交易都是有效且之前未存在过的,其它节点才会认同该区块的有效性
-
其它节点接收到广播信息后,若该区块有效,接受该区块,并跟随在该区块的末尾,制造新区块延长该链条,将被接受的区块的随机哈希值视为新区块的随机哈希
工作量证明流程
思考
为什么计算难度这么大
比特币的创建者认为,10分钟产生一个区块是一个比较合理的情况。在全网算力不断提升的情况下,只能够增加计算的难度,来保证10分钟左右稳定产生一个区块。而这个限制目前已经成为了比特币网络的一个瓶颈,按照10分钟一个区块来算,一个区块的大小是1M,最多只能包含2000笔左右的交易,也就是3~4笔/秒的交易。
数据不一致怎么办
由于网络通信的关系,每一个节点收到的交易记录顺序并不是完全一样的。比如同一时间,发生了2笔交易记录Tx1和Tx2。A节点收到了Tx1,然后开始计算;B节点收到了Tx2,然后开始计算。这两笔交易都是合法的,所以A和B都会将记录写入到各自本地的区块链中,同时发出广播消息给其他节点。这个时候,整个网络就会同时存在一条分叉链。如下图:
比特币协议规定,分叉点开始,后续哪个分支最先到达6个区块,则被认定为正式的链,其他分支都会被丢弃。算力决定了区块的生成,如果大部分算力集中在一个分支上,那么这条分支大概率是被大家可信的。
PBFT
实用拜占庭容错算法(Practical Byzantine Fault Tolerance)
故事背景
拜占庭帝国想要进攻一个强大的敌人,为此派出了10支军队去包围这个敌人。这个敌人虽不比拜占庭帝国,但也足以抵御5支常规拜占庭军队的同时袭击。这10支军队在分开的包围状态下同时攻击。他们任一支军队单独进攻都毫无胜算,除非有至少6支军队(一半以上)同时袭击才能攻下敌国。他们分散在敌国的四周,依靠通信兵骑马相互通信来协商进攻意向及进攻时间。困扰这些将军的问题是,他们不确定他们中是否有叛徒,叛徒可能擅自变更进攻意向或者进攻时间。在这种状态下,拜占庭将军们才能保证有多于6支军队在同一时间一起发起进攻,从而赢取战斗?
情况分析
-
先看在没有叛徒情况下,假如一个将军A提一个进攻提议(如:明日下午1点进攻,你愿意加入吗?)由通信兵通信分别告诉其他的将军,如果幸运的话,他收到了其他6位将军以上的同意,发起进攻。如果不幸,其他的将军也在此时发出不同的进攻提议(如:明日下午2点、3点进攻,你愿意加入吗?),由于时间上的差异,不同的将军收到(并认可)的进攻提议可能是不一样的,这是可能出现A提议有3个支持者,B提议有4个支持者,C提议有2个支持者等等。
-
再加一点复杂性,在有叛徒情况下,一个叛徒会向不同的将军发出不同的进攻提议(通知A明日下午1点进攻, 通知B明日下午2点进攻等等),一个叛徒也会可能同意多个进攻提议(即同意下午1点进攻又同意下午2点进攻)。
前提条件
-
系统中“非拜占庭(正常)”节点的状态是一致的,会执行正确的验证逻辑和提议回复,同样的输入会给相同的结果
-
系统中“拜占庭(恶意)”节点可能给出多种提议回复,也可能不给出回复
-
系统中存在一个“主节点”,主节点是通过view-change选举出来的,主节点的作用是用来对客户端的请求进行排序和分发
详细解决思路
整个共识过程包含以下五个阶段:
REQUEST请求阶段
C向主节点0发送请求,请求包含<REQUEST:具体提案详情,提案摘要,客户端标识,客户端签名>等信息
PRE-PREPARE预准备阶段
主节点0验证收到的客户端请求。如果通过,分配当前视图v下的一个唯一编号n,然后广播<PRE-PREPARE:具体提案信息,提案摘要,v,n,主节点签名>信息
v:view,一个主节点会对应一个view,主节点发送的提案如果超过一定时间没有被reply,那么就会触发view-change,变更主节点
n:number,具体某一个view下的提案编号
PREPARE准备阶段
副节点(1,2,3)在收到主节点的消息后,会依次进行如下校验步骤:
-
验证主节点签名是否正确(正确:通过)
-
当前副本节点是否已经收到了同一视图v下的n(否:通过)
-
验证消息和提案摘要
如果验证通过,那么当前节点会向所有其他节点广播一条<PREPARE:提案摘要,v,n,i,i节点签名>消息
i:当前节点编号(1,2,3)
COMMIT阶段
所有节点收到PREPARE消息,会进行以下校验过程:
-
验证副节点签名是否正确(正确:通过)
-
当前副本节点是否已经收到了同一视图v下的n(否:通过)
-
验证消息和提案摘要
如果验证通过,并且当前节点收到超过2/3同一个<v,n>的PREPARE消息,那么当前节点会向所有其他节点广播一条<COMMIT:提案摘要,v,n,i,i节点签名>消息
Reply阶段
所有节点,若收到commit消息,依靠签名检查消息是否正确,若同一个<v,n>的commit的消息数量超过所有节点的1/3,则认为可以完成request要求的业务。构建reply消息,直接回复给client。Client根据是否收到超过1/3个节点的正确回复,判断系统是否完成了请求request。
思考
什么是view-change
分布式一致性算法都需要提供一定的一致性和可用性。如果缺少了可用性,那么这个一致性算法没有办法长期提供一致性服务。如果主节点是恶意节点,一直不给副本节点发出消息,那么客户端的请求永远没有办法达成达成一致。通过View Changes,可以选举出新的、让请求在有限时间内达成一致的主节点,向客户端响应,从而满足可用性的要求。
PBFT有一个全局的视图编号view number: v,主节点是根据 v mod n =i 得到节点i为主节点。视图转换,就是v递增,主节点也相应转移到另一个节点
-
当client发送请求给主节点后,一定时间没收到回复,则会发送请求给其他backup节点, backup节点收到request后,起一个timer,如果timer过期,还没执行这个request(commit还没达一致),则backup节点发起view-change
-
backup节点会广播一个view-change消息,包含原来视图编号v和下个视图编号v+1。如果节点收到的view-change消息多于2/3,则说明view-change达成一致
-
当 v+1 mod n = j , 新的主节点是j,收到足够的view-change消息后, 就广播new-view消息,告诉其他节点使用新视图。
-
其他节点收到new-view后,确认消息签名正确,进入新视图
有了视图转换,如果主节点down了,就会触发视图转换更换另一个主节点,如果下一个主节点也是down的,则继续切换,直到找到可用的主节点。
PREPARE-COMMIT为什么是2/3
假设节点总数是n,其中作恶节点有f,那么剩下的正确节点为n - f,意味着只要收到n - f个消息就必须做出决定(因为f个恶意节点可能不会发送消息),但是这n - f个消息有可能有f个是由作恶节点(作恶节点也可以什么都不干)冒充的,那么正确的消息就是n - f - f(最恶劣的情况下)个,为了多数一致,正确消息必须占多数,也就是n - f - f > f,即n>3f。所以容错概率就是1/3,节点收到收到2/3的消息就能保证多数一致了。
COMMIT-REPLY为什么是1/3
如果客户端收到了f+1条的消息,那么就一定能够保证有1条消息是来自于非恶意节点所发送的COMMIT消息。非恶意节点在发出COMMIT消息的时候,已经能够保证全网大多数节点同意了这次提案。
对比
| 属性/算法 | Pow | PBFT |
|---|---|---|
| 容错 | (n-1)/2<50%,n为节点总数 | (n-1)/3<1/3,n为节点总数 |
| 优点 | 1)节点自由进出,容易实现2)恶意攻击者实现成本高 | 1)使用了加密技术来防止欺骗攻击和重播攻击,以及检测被破坏的消息。消息包含了公钥签名、消息验证编码(MAC)和无碰撞哈希函数生成的消息摘要(message digest)2)可以容忍恶意节点 |
| 缺点 | 1)资源浪费,所有的节点都需要参与共识计算2)出现算力集中(矿场),违背了去中心化的原则3)链分叉问题 | 1)通信复杂度On2,节点数量超过100个的时候,性能出现明显下降2)受网络影响,延迟较大 |
| 应用场景 | 公有链:比特币,以太坊 | 私有链/联盟链:Hyperledge |
参考文档
区块链共识机制技术一--POW(工作量证明)共识机制_a soldiers的博客-CSDN博客_共识机制
分布式共识算法(拜占庭容错算法)的系列整理一:PBFT、PoW、PoS、DPos_每天净瞎搞的博客-CSDN博客_pow和pbft