区块链分布式共识协议
Posted 宣之于口
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了区块链分布式共识协议相关的知识,希望对你有一定的参考价值。
分布式共识协议
一、概述
总结:
- 私有链:封闭生态的存储系统,采用PAXOS、RAFT最佳
- 联盟链:半公开半开放特性,采用拜占庭容错的PBFT算法比较合适
- 公有链:POW、POS、DPOS是比较适合的高安全性的协议
RAFT与PBFT共识协议对比
协议 | 特点 | 容错节点类型 | 容错节点 | 算法复杂度 |
---|---|---|---|---|
PBFT | 半开放式 | 故障节点、拜占庭节点 | 3f+1 | O( n 2 n^2 n2) |
RAFT | 封闭式 | 故障节点 | 2f+1 | O(n) |
容错节点类型
首先介绍一下节点类型:
- 故障节点:节点因为系统繁忙、宕机或者网络问题等其它异常情况导致的无响应
- 拜占庭节点:可以故意对集群的其它节点的请求无响应,还可以故意发送错误的数据,或者给不同的其它节点发送不同的数据,使整个集群的节点最终无法达成共识
RAFT
只支持容错故障节点,假设集群总节点数为n,故障节点为 f ,根据小数服从多数的原则,集群里正常节点只需要比 f 个节点再多一个节点,即 f+1 个节点,正确节点的数量就会比故障节点数量多,那么集群就能达成共识
PBFT
系统中存在拜占庭节点,意味着该节点发布给其他不同节点的信息可能会是不一致的
假设系统节点数量为2f+1时,当前系统各个节点状态一致,下一个状态有两种情况,A情况和B情况。若仅仅收到f+1节点就认为是正确的,那么f个拜占庭节点在知晓其他诚实节点投票结果的前提下,显然,剩下的节点各有不同的投票结果。那么他们只需要给那些投票A情况的节点,发出信息说自己投的是A情况,给投票B情况的节点说自己投的是B情况。由于本身就有f个节点,那么诚实的节点必定会收到欺骗,那么系统就会存在A情况B情况两种,出现不一致。
算法复杂度
RAFT
核心共识过程是日志复制这个过程,这个过程分两个阶段,日志记录,提交数据。两个过程都只需要领导者发送消息给跟随者节点,跟随者节点返回消息给领导者节点即可完成,跟随者节点之间是无需沟通的。所以如果集群总节点数为 n,对于日志记录阶段,通信次数为n-1,对于提交数据阶段,通信次数也为n-1,总通信次数为2n-2,因此raft算法复杂度为O(n)
PBFT
核心过程有三个阶段,分别是pre-prepare阶段,prepare阶段和commit阶段。对于pre-prepare阶段,主节点广播pre-prepare消息给其它节点即可,因此通信次数为n-1;对于prepare阶段,每个节点如果同意请求后,都需要向其它节点再广播parepare消息,所以总的通信次数为n*(n-1),即n2-n;对于commit阶段,每个节点如果达到prepared状态后,都需要向其它节点广播commit消息,所以总的通信次数也为n*(n-1),即n2-n。所以总通信次数为(n-1)+(n2-n)+(n2-n),即2n2-n-1,因此pbft算法复杂度为O(n2)
二、开放式
POW
工作量证明,通过竞争记账的方式解决去中心化的记账系统的一致性问题, 即以每个节点的计算能力即“算力”来竞争记账权的机制。
1. 区块头
如下图所示,区块头由上一区块哈希,时间戳,难度值,随机调整数(nonce),Merkle根.
2. 相关术语
难度值
难度值是比特币系统中的节点在生成区块时的重要参考指标,它决定了节点大约需要经过多少次哈希运算才能产生一个合法的区块
难度的调整 : 在每个完整节点中独立自动发生的。每2016个区块,所有节点都会按统一的公式自动调整难度.(例如比特币为10分钟)
新
难
度
值
=
旧
难
度
值
∗
过
去
2016
个
区
块
花
费
时
长
20160
分
钟
新难度值 = 旧难度值 * \\frac过去2016个区块花费时长20160分钟
新难度值=旧难度值∗20160分钟过去2016个区块花费时长
目标值
目标值的大小与难度值成反比。比特币工作量证明的达成就是矿工计算出来的区块哈希值
其中最大目标值为一个恒定值:0x00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
目
标
值
=
最
大
目
标
值
难
度
值
目标值 = \\frac最大目标值难度值
目标值=难度值最大目标值
3. 基本流程
每一个比特币节点都会收集所有尚未确认的交易,并且会将归集到一个数据块中,这个数据块将和前面一个数据块集成在一起。矿工节点会附加一个随机调整数,并计算前一个区块的SHA-256 Hash运算值。矿工节点不断进行重复尝试,直到它找到的随机调整数使得产生的Hash值低于某个特定的目标为止。
第一步:确定输入
将Merkle根及其他参数组装成区块头,作为工作量证明输入
第二步:调整难度值,执行计算
将Nonce会从0到N递增,并对每次变更后的的区块头做双重SHA256运算即SHA256(SHA256(Block_Header))
,将结果值与当前网络的目标值做对比,如果小于目标值,则解题成功,工作量证明完成。
第三步 :广播
当矿工节点成功计算出target,就将结果进行广播。此时可能会出现分叉的情况,即可能存在多个矿工同时挖到一个包含有效随机数nonce的区块。这个时候,这些矿工将自己挖到的区块广播到网络中。在比特币的协议中,节点收到一个有效区块后,将会忽略后到的区块。
问题如左图所示,AB两个矿工节点同时得到正确解进行广播,此时网络中出现两条不同且长度一样的区块链。
解决办法如右图所示:矿工将会在各自的分叉链上挖矿,直到其中一条链的长度超过另外一条链的长度。此时,网络中所有的矿工都将选择在最长链上继续挖矿。
4. 特点
缺点
- 能源浪费:消耗了大量的资源来计算这个意义不大的nouce值
- 共识达成的周期较长:每次达成共识需要全网共同参与运算,使得共识达成的周期长,效率低下,不合适商业应用,可监督性弱
- 容易产生分叉:需要等待多个确认,区块确认时间难以缩短
- 算力集中:矿池出现导致算力集中,这是中心化的一种表现
优点
- 完全去中心化:任何一台有算力的计算机都可以加入到交易验证当中来
- 安全性:51% ATTACK,增大了篡改者的篡改成本,这使得篡改者想要作恶交易需要全网51%算力
POS
权益证明,是一种由系统权益代替算力决定区块记账权的共识机制,拥有的权益越大则成为下一个区块生产者的概率也越大。
1. 基本流程
币龄
币龄 = 持有的币数 * 持有币的天数
目标值 : 由公式可见,两个区块目标间隔时间即为10分钟
当
前
区
块
目
标
值
=
前
一
个
区
块
目
标
值
∗
(
n
I
n
t
e
r
v
a
l
−
1
)
∗
10
∗
60
+
2
∗
前
两
个
区
块
时
间
间
隔
(
n
I
n
t
e
r
v
a
l
+
1
)
∗
10
∗
60
当前区块目标值 = 前一个区块目标值 * \\frac(nInterval - 1)*10*60 + 2*前两个区块时间间隔(nInterval + 1) * 10 * 60
当前区块目标值=前一个区块目标值∗(nInterval+1)∗10∗60(nInterval−1)∗10∗60+2∗前两个区块时间间隔
其中:
nInterval = 1008 // 即区块间隔为10分钟时,1周产生1008个区块
PoS证明计算公式: 由公式可见,币龄越大,挖到区块的机会越大
p
r
o
o
f
h
a
s
h
<
币
龄
∗
目
标
值
proofhash < 币龄 * 目标值
proofhash<币龄∗目标值
2. 特点
缺点
- 马太效应:因为持币越多,持有的越久,币龄就越高,越容易挖到区块并得到激励,持币少的人基本上没有机会,这样整个系统的安全性实际上会被持币数量较大的一部分人掌握,造成富者愈富,贫者愈贫的局面。
- 无利害关系攻击:当出现分叉时,PoS可以在两条链上同时挖矿并获得收益。发起攻击的分叉链是极有可能成功的,因为所有人也 都在这个分叉链上达成了共识;而且甚至不用持有 51%的权益,就可以成功发 起分叉攻击
优点
- 共识时间短:缩短了共识达成的时间
- 能源减少:不需要做大量消耗能源挖矿
三、半开放式
PBFT
PBFT是一种状态机副本复制算法,即服务作为状态机进行建模,状态机在分布式系统的不同节点进行副本复制。每个状态机的副本都保存了服务的状态,同时也实现了服务的操作。
1. 主节点
主节点负责对客户端发来的交易进行排序打包
生成
通过轮换或随机算法选出某个节点为主节点,此后只要主节点不切换,则成为一个视图(view)
ViewChange
当主节点挂了(超时无响应)或者从节点集体认为主节点是问题节点时,就会触发ViewChange事件,ViewChange完成后,视图编号将会加1。
2. 相关术语
可容忍的拜占庭节点数f
f
=
[
N
−
1
3
]
f=[\\fracN-13]
f=[3N−1]
3. 基本流程
如下图所示,PBFT共识的核心三个阶段分别是pre-prepare阶段(预准备阶段),prepare阶段(准备阶段),commit阶段(提交阶段)。C表示客户端,N表示4个节点,其中0为主节点,3为故障节点。
request阶段
客户端将请求发送到区块链中的主节点
消息: <Request,operation,timestamp,client>
pre-prepare阶段
主节点收集请求,将交易按照接收的时间顺序打包成块,进行验证,计算执行结果,并分配提案编号n(可以理解为区块高度)。最后将定序好的请求写入PrePrepare消息中广播给所有共识节点
消息:<<PRE-PREPARE,view,n,digest(m)>,message>
其中message为新生成的区块,digest为主节点基于交易结果计算新区块的哈希摘要
prepare阶段
从节点在收到主节点的PrePrepare消息后,首先进行消息合法性检查,检查当前的视图与区块号等信息,然后计算执行结果,检查通过后向共识节点广播Prepare消息。同时接受其他节点的perpare消息,节点在收到2f个Prepare消息以及相应的PrePrepare消息后验证通过,则该节点拥有了一个叫prepared certificate的证书,请求在这个节点上达到了prepared状态。
消息: <PREPARE,view,n,digest(m),id_node>
其中digest为从节点基于交易结果计算新区块的哈希摘要
commit阶段
commit阶段用来确保每个节点都结束了prepare,已经达到commit状态,从而保证request在不同的view中也能顺利执行。
原因:在新的view里,commit阶段的请求将保留,pre-prepare和prepare阶段的请求将被遗弃。
从节点进入prepared状态后,将广播消息,自己已经拥有一个prepared认证证书。
消息: <COMMIT,view,n,digest(m),id_node>
reply阶段
如果一个节点收到2f+1条commit消息,则该节点拥有了一个叫committed certificate的证书,请求在这个节点上达到了committed状态。即可提交新区块及其交易到本地的区块链和状态数据库。当以上阶段处理完毕后,节点会返回消息给客户端
消息: <REPLY,view,timestamp,id_node,result>
四、封闭式
RAFT
强烈推荐英文动画演示RAFT
1. 相关术语
角色
在RAFT中,拥有三种角色:
- Leader: 领导者,读写请求都只能向该节点发送,以此保证一致性,一般一次只有一个Leader
- Follower: 追随者,leader节点向follower节点同步每条写请求
- Candidate: 候选者,当集群内没有leader节点时,候选者可以被推选成为领导者
复制状态机
在一个分布式系统数据库中,如果每个节点都执行相同的命令序列,那么最终他们会得到一个一致的状态。
也就是说,为了保证整个分布式系统的一致性,我们需要保证每个节点执行相同的命令序列,也就是说每个节点的日志要保持一样。
如上图所示,简单来说总共分为以下几个流程:
第一步:一致性模块(分布式共识算法)接收到了来自客户端的命令
第二步:一致性模块把接收到的命令写入到日志中,该节点和其他节点通过一致性模块进行通信确保每个日志最终包含相同的命令序列
第三步:每个节点的机都会按照相同的序列去执行他们,从而最终得到一致的状态
第四步:将达成共识的结果返回给客户端
任期
每次选举都是一个任期,每个任期可以是任意时长,任期用连续的整数进行标号。
每个任期首先进行Leader选举,选举时,多个Candidate竞争成为Leader,一旦某个节点成为Leader,其他节点则变回Follower,成为Leader的节点将在该任期内一致担任Leader,如果该Leader节点发生故障,其他节点会在新的任期内进行选举。任何一个任期内都不会有多个Leader。
Raft系统中,任期是一个及其重要的概念,每个节点都维护着当前任期的值,每次节点间的通信都包含任期信息,每个节点在检测到自己的任期值低于其他节点,都会更新自己的任期值,设置为检测到的较高的值。当Leader和Candidate发现自己的任期低于别的节点,则立即把自己转换为Follower。
超时机制
选举定时器(eletion timeout):即Follower等待成为Candidate状态的等待时间,这个时间被随机设定为150ms~300ms之间,即使所有节点同时启动,由于随机超时时间的设置,各个节点一般不会同时转为Candidate,先转为Candidate的节点会先发起投票,从而获得多数票。
RPC
Raft节点间的通信采用RPC调用, Raft算法核心部分只需要用到两个RPC: Follower不会发起任何RPC
-
RequestVote:由Candidate发起用于Leader选举
-
AppendEntries:由Leader发起,用于心跳和日志复制
2. 基本流程
领导人选举
Raft采用心跳机制触发Leader选举
第一步:当系统启动时,所有节点初始化为Follower状态,设置任期为0,并启动计时器,计时器超时后,Follower节点转化为Candidate节点
第二步:Candidate节点并行的向集群中的其他服务器节点发送RequestVote RPC请求
此时存在三种情况
- 如果在计时器超时前接收到多数节点的同意投票,则转换为Leader
- 接受到其他节点的AppendEntries,说明其他节点已经被选为Leader, 则转换为Follower
- 计时器超时时还没有接受到以上两种信息中的任何一种,则重新开始选举
第三步:假设该节点成为Leader节点,则会向所有节点发送AppendEntries。所有Candidate收到后,转换为Follower,选举结束
日志复制
Raft是强Leader机制,日志只能从Leader复制到其他节点
下图是正常情况下的日志复制:
第一步:客户端发送请求给Leader
第二步:Leader接收客户端请求, 先将数据写在本地日志,这时候数据是 Uncommitted
第三步:Leader发送 Append Entries RPC给Follower节点,数据在 Follower 上没有冲突,则将数据暂时写在本地日志,Follower 的数据也还是 Uncommitted
第四步:Follower 将数据写到本地后,返回 OK
第五步:Leader 收到后成功返回,只要收到的成功的返回数量超过半数 (包含Leader),Leader 将数据的状态改成Committed
第六步:Leader 再次给 Follower 发送 AppendEntries 请求,收到请求后,Follower 将本地日志里 Uncommitted 数据改成 Committed
第七步:Leader节点将结果返回给客户端(与第六步同时进行)
下面讲一下Network Partition情况下的日志复制
假设在Network Partition情况下,将节点分成两边,一边两个节点,一边三个节点:这里不重复将同步过程,从上图可以看到共识失败。因为只有两个节点,少于等于(n-1)/2个节点,数据状态仍是 Uncommitted。所以在这里,服务器会返回错误给客户端
我们看一下另一边的状态:首先会选出新的leader,需要注意的是这个leader的term更大,通过相似的过程,数据同步到另外两个 Follower。因为这里有三个节点,大于(n-1)/2个节点,所以数据committed成功。
那么当网络恢复之后,5个节点再次处于同一个网络状态下,数据发生冲突。两个节点 Partition 的 Leader 自动降级为 Follower,同时这里的数据没有committed成功,所以进行删除。然后同步leader数据,通过这么一个过程,就完成了在 Network Partition 情况下的复制日志,保证了数据的一致性。
总结一下当网络失败之后的同步:一切以最新的leader为准,将follower冲突日志全部删除,然后同步为leader的日志。
Leader通过Append Entries RPC请求失败发现Follower节点与自己日志不一致,领导人针对每一个跟随者维护了一个 nextIndex,这表示下一个需要发送给跟随者的日志条目的索引地址。当一个领导人刚获得权力的时候,他初始化所有的 nextIndex 值为自己的最后一条日志的index加1。当Append Entries RPC失败时,领导人会不断减小 nextIndex 值并进行重试。最终 nextIndex 会在某个位置使得领导人和跟随者的日志达成一致。这时就会把跟随者冲突的日志条目全部删除并且加上领导人的日志。
五、参考文章
8.以太坊的POS共识机制(二)理解 Serenity :Casper
以上是关于区块链分布式共识协议的主要内容,如果未能解决你的问题,请参考以下文章