PBFT共识及视图切换

27 篇文章 4 订阅
2 篇文章 0 订阅

区块链的一致性目标:
一致性:如果一个诚实节点认为在区块链的某个高度h的区块是B,那么不会有任何其他诚实节点认为高度h的区块是 B’,B’不等于B
活性:系统可以持续对新区块达成共识,诚实节点可以确认新的区块

首先为了实现活性,需要这个网络中的节点发布新区块,于是,简单地规定诚实节点当收到新区块时就确认
分叉情况3
假设一个诚实节点a在高度h广播了区块B,然后另一个诚实节点b收到了这个区块。
这个时候,如果他接收并确认这个区块,那么就可能会出现情况1——也许另一个诚实节点c没有收到B,于是在高度h发了B’,而诚实节点d接受并确认了这个B’,于是,b和d就产生了不一致。

1,为什么会出现两个诚实节点a和c都发了新区块的情况呢?为什么不能设置一个时刻表不让诚实节点同时出块,而是让它们轮流出块呢?

一种最简单的方法,是设一个时刻表,规定今天下午1点10分是a的出块时间,1点20分是b的出块时间……这种方法虽然简单粗暴,但是在一个同步系统,或者说,在消息延时是有上界的系统中,是可行的。然而,如果换了一个异步系统,我们的确可以采用这种方法,但是由于消息的延时没有上界,所以无论这个时刻表两个节点出块间隔多长,我们都无法保证不会出现分叉情况。

另一种最简单的思路是说,那么,排好一个顺序,比如把所有节点编好号,然后轮流出块。然后后一个节点等前一个节点出完块之后再出。

实际上,很多传统的BFT算法,例如PBFT,都是这么做的。然而,这并不能保证情况3不会出现。因为这里存在一个问题:后一个节点如何知道前一个节点没有出块呢?如果所有的节点都是诚实的,那这种方法没问题。但只要有哪怕一个节点可能故障(甚至不需要有恶意),那么这种方法就无法保证活性,因为后一个节点也许永远等不到前一个节点出块。
于是规定一个时限——在这个时限达到之后,如果诚实节点c判断上一个节点没有出块,那么他就应该出块了。
但是,实际上由于异步系统的原因,c的判断未必是对的,也许a发了区块只是c还没有收到。于是,这种方法仍旧无法避免两个诚实节点都出了块的情况。

比特币引入了一种新思路,即通过POW降低两个节点同时出块的几率,但实际上这种方法仍旧不能保证不会出现情况3,在异步网络中更是如此。
因此,实际上,并无法在异步网络中保证一个诚实节点不会同时受到两个诚实节点发出的区块。更深层次的原因其实仍旧是一致性(没有两个冲突的区块)和活性(确认新区块)的矛盾:如果我们想要保证一致,那么就无法保证活性,反之亦然。

是不是能够换一种确认规则,来避免在有冲突区块的时候确认区块?
当网络中存在两个冲突的区块的时候,如何确认才能保证一致性呢?

需要每个节点都广播自己收到的区块,然后,当有了2f+1(f是恶意节点数量)[至少f+1个诚实节点]个一致的答案的时候再确认,例如,如果自己收到的区块是B,然后,刨掉自己也发出去的消息,只需要再收到2f个节点发来的“我收到的是B”的消息,就可以确定——不会有另一个诚实节点对区块B’进行了一致确认。

然而,仅仅保证了一致,还并没有保证活性,所以,还需要如下步骤来保证活性,即,最终所有诚实节点都能够确认B——所有确认了B的节点再广播自己对于B的确认,所有收到f+1条“关于B的确认”的诚实节点也同样可以对B进行活性确认。

上述解决方案会带来以下分叉问题

情况4:
诚实节点a收到2f+1条“收到B”的消息,这个时候认为不会有另一个诚实节点确认不同的区块B’,于是,对B进行了确认,并发送了“确认B”。然而,在这个时候出现了大规模的网络异常,导致没有其他诚实节点成功收到2f+1个“收到B”的消息,同时也没有人收到f+1个B的“确认B”。然后,轮换中下一个出块节点b在长时间没有收到2f+1条区块B的消息之后,发了区块B’。这个时候,网络中的诚实节点c收到了B’,广播自己收到了B’的消息,然后收到了2f+1条“收到B’”的消息但是并没有收到2f+1条“收到B”的消息,于是,c确认了区块B’,导致一致性失效。
在这里插入图片描述
于是,尽管诚实节点a收到了2f+1条B的消息,但,尽管a知道如果所有节点一直等下去的话,一定可以达成共识,但其他的诚实节点此刻并不知道这一点。他们仍旧认为出块节点可能失效(或者恶意)。于是,一致性和活性的矛盾再次出现——为了保证活性,他们必须冒着失去一致性的风险让下一个出块者出块,最终导致不一致。

那么,如何解决这个问题呢?其实症结还是在确认的规则上——
我们仔细看看情况4中的问题,其实在于a不该这么快就对B进行确认,而应该确认没有更换领袖之后再对B进行确认。
于是,我们才有了PBFT的两阶段确认。

两阶段确认
同样是在情况4,在两阶段确认之中,当a收到2f+1条关于B的消息时,并不是直接确认,而是进入“预备”状态,并且发一条“确认”消息表示自己正在“预备”。然后,如果一个诚实节点收到2f+1条“确认”消息之后,再进行确认。
同时,我们采用这样的节点轮换机制:
当节点达到time out之后,会请求出块节点轮换,相当于说:认为出块节点可能是恶意的,所以请求节点轮换。然后,当新的出块节点收到2f+1条请求之后,广播新的区块,并且广播这2f+1条请求,公告大家的确收到了这些请求。

于是,在情况4中,即便a节点已经收到2f+1条关于B的预备消息(第一轮的消息),但是仅仅进入预备状态,因为此时他还并不确定共识是不是一定能达成,因为出块节点仍旧可能是恶意的,所以其他节点还有节点轮换的可能。而这个时候另一个诚实节点b,如果在time out之前只收到了不到2f+1条关于B的预备消息,会发出领袖轮换请求。而如果有2f+1个节点都发出了这种请求的话,下一个出块的诚实节点c将会发出新的区块B’。这个时候,a节点由于并没有确认B,而由于2f+1个节点都已经请求节点轮换了,那么,a一定收不到2f+1个B的确认消息,于是无法对B进行确认,于是,如果他收到2f+1条B’的预备消息,他就会抛弃对于B的预备,而切换到B’上,这样,我们同时保证了一致性和活性。
在这里插入图片描述

然而,虽然我们解决了情况4,却带来了新的问题——
情况5:
诚实节点a顺利通过了预备阶段,并且,收到2f+1条区块B的确认消息(第二轮),这个时候认为不会有另一个诚实节点确认不同的区块B’,于是,对B进行了确认。然而,在这个时候出现了大规模的网络异常,导致其他诚实节点没有在timeout之前成功收到2f+1个关于区块B的确认消息,于是大部分节点还没有对B进行确认的情况下,发了轮换出块节点请求。然后,新节点在B的高度出块B’,再次造成不一致。
在这里插入图片描述
情况5其实在纠结的是这样一件事:虽然我们在中间加了一轮,但是,为了保证活性,我们还是需要在某个条件下对于区块进行确认,而同时,我们必须允许在timeout的时候进行出块节点轮换。但是,怎么保证出块节点轮换的时候没有任何一个诚实节点已经确认呢?反过来说,我们回到了老问题,诚实节点在什么时候才知道自己确认的这个区块不会再被改动了呢?

因为其实两轮共识已经提供了可以解决–为了保证活性,我们不能吊死在一个出块节点上,需要进行节点轮换;但是另一方面,一旦轮换了节点,新出的块又会和之前达成的一致相冲突–问题的基础——PBFT做了如下的改动,解决了一致性与活性的矛盾:

所有节点在广播更换出块节点的申请的时候,需要同时广播自己的状态,同时给出自己处于这个状态的证据(例如,如果处于准备状态,就提交2f+1条来自不同节点的准备信息)。然后,新的出块节点根据收到的2f+1条信息中的状态决定是否替换掉B:
1,如果没有任何一个节点处于预备状态,则,至少有f+1个诚实节点还没有预备,于是不会有2f+1条确认消息,于是不会有任何一个诚实节点已经确认了区块B。于是,这个时候,新的出块节点会将自己的新区块B’放在同B一样的高度,替换掉B。然后,广播区块B’的同时,广播收到的所有轮换申请——向所有人说明这种情况。
2,如果有一个节点处于预备状态,则说明至少有f+1个诚实节点已经收到过B并且准备进入预备状态。这个时候我们可以确认,不可能有一些诚实节点由于不同步(例如一直处于timeout),以至于他们对于一条和B不一致的链进行了确认。那么,新的出块节点将新区块B’连在B之后,并且广播收到的所有轮换申请。这时,由于处于预备状态的节点的轮换申请里包括2f+1条预备消息,所有收到新区块B’的节点也同时收到了2f+1条预备消息,于是可以进入预备状态。换句话说,无论新的区块节点是谁,有没有恶意,或者是网络失活导致区块B’没有达成共识都没关系。只要新区快的发布者采用这种方法,我们可以在保证活性的同时,保证不会和已经达成共识的区块出现不一致。
在这里插入图片描述

然而,以上的方法有个非常严重的问题——每次节点轮换的时候有极高的消息复杂度。
首先,节点轮换的时候,每个节点需要广播一个轮换请求,这里就是O(N2)的消息复杂度,然后,轮换请求中还需要包括自己之前所收到的所有预备消息来说明现在的状态,于是,这里就有了O(N3)的消息复杂度。
不仅如此,当下一个出块节点收到这些消息之后,他需要在发布区块的时候包含这些消息并且广播,于是,这里又有了O(N^3)的消息复杂度。
这对网络资源造成极大的消耗,同时,对于出块节点而言也是极大的负担。

与两阶段共识和三阶段共识的区别如同一阶段和两阶段的共识得区别一样,其中加了一轮确认。其中的目的其实就是降低两阶段共识中节点轮换的复杂度——更确切地说,两阶段共识中关于“前一个区块B是否已经达成共识”这件事的共识需要依靠每个节点发给新的出块节点,然后再广播给所有节点,造成了O(N^3)的消息复杂度。那么,如果干脆用一轮共识来决定这个问题会怎么样呢?
于是,得到了如下的三阶段共识方案:
诚实出块者广播区块B,诚实节点收到后发送关于B的“预备消息”,然后,当节点收到2f+1条预备消息之后,节点进入预备状态并广播“预确认消息”;然后,当节点收到2f+1条预确认消息时,节点进入预确认状态并广播“确认消息”;最后,当节点收到2f+1条确认消息的时候,确认区块B。
然后,这里面的节点轮换机制是:当节点达到timeout还没有达成共识的时候,节点广播自己的状态并申请更换出块者。然后,下一个出块者收集这些状态来决定新区块的高度:如果没有2f+1个节点在预备状态,则换掉B,如果有2f+1个节点在预备(或更高)状态,则把新区块出在B之后。
实际上,这种方法和两阶段共识的结果是一致的:
1,如果没有2f+1个节点在预备状态,则不可能有一个诚实节点已经进入确认状态,所以我们可以替换掉B。
在这里插入图片描述
2,如果有2f+1个节点在预备(或更高)状态,则不可能有一个诚实节点确认了一条不包括B的链,所以这个时候新的区块出在B之后。
在这里插入图片描述

三阶段共识和两阶段共识的区别,在我看来可以这样理解——对于一个诚实节点,当他对于区块B达成了两轮共识,他就可以确认对于另一个冲突的区块B’达不成共识。于是在两阶段共识中,如果在两阶段共识过程中进行了节点轮换,我们无法仅通过节点现在的状态就判定:1)是否有节点已经对B进行了确认;2)是否有节点对另一个区块B’进行了共识。于是,我们需要在节点轮换的时候,让所有节点广播自己收到的所有消息,然后新的区块发布者再广播收到的消息。
但是,如果采用三阶段共识,那么我们可以仅通过节点现在所处的状态来判定这一点——1)如果2f+1个节点还没达成两轮共识,那么就不会有节点已经对B进行了确认;2)如果有2f+1个节点达成了两轮共识,那么就不会有节点对另一个区块B’达成了共识。

PBFT实用性拜占庭容错,假设集群节点数为N,有问题的节点为f,有问题的节点中,可以既是故障节点,也可以是作恶节点,容忍有f=(N-1)/3)的“恶意节点”存在。
PBFT容错性目的是达成所有节点的一致性。

实用拜占庭容错系统中服务节点分为两类:
在这里插入图片描述

主节点:全系统有且仅有一个主节点,负责将客户端的请求排序。
从节点:按照主节点提供的顺序执行请求引用

考虑以下问题

在网络中有n个节点,f个恶意节点
恶意节点可以发布不一致的信息,或者拒绝服务(不发送信息)
那么,最多等待n-f条消息达成一致性结果,否则会因为恶意节点拒绝发送消息而陷入等待
在网络中,目前有n-f个诚实节点,f个恶意节点
收到半数以上诚实节点的确认 即 (n-f+1)/2
加上收到的恶意节点的确认信息 (n-f+1)/2+f
总体确认应当小于n-f 即 (n-f+1)/2+f<n-f 即 n>= 3f+1

PBFT算法的运作步骤为:

(1)取一个副本作为主节点,其他的副本作为备份;
(2)用户端向主节点发送使用服务操作的请求;
(3)主节点通过广播将请求发送给其他副本;
(4)所有副本执行请求并将结果发回用户端;
(5)用户端需要等待F+1个不同副本节点发回相同的结果,作为整个操作的最终结果。

PBFT算法包含三个重要阶段,分别是预准备(pre-prepare)、准备(prepare)和确认(commit)pre-prepare阶段和prepare阶段用来把在同一个view里发送的请求给确定下序列,就是排好序,让各个replicas节点都认可这个序列,照序执行prepare阶段和commit阶段用来确保那些已经达到commit状态的请求即使在发生view change后在新的view里依然保持原有的序列不变

联盟链中的应用

在联盟链中采用的PBFT类共识算法较为高效的解决了多节点参与情况下的典型分布式一致性问题,如消息无序、参与方异常、网络分化等同时,在允许一定比例的拜占庭参与方的前提下,做到了最终一致性。

众所周知,fabric和tendermint的共识机制就是PBFT。联盟链之所以选择PBFT作为其共 识机制,主要是因为它既可以保证一定的去中心化程度,而且还能防止分叉。PBFT使得出块的速度只依赖于网络传输速度,所以瓶颈只在网络层。

fabric中会有多个client同时发送request给PBFT节点,这些请求都是相互独立不重复 不冲突的,所以PBFT节点中的primary需要对这些请求排序并加编号(checkpoint就是当 前节点处理的最新请求序号),按照先入先出的原则依次进行PBFT共识。大部分节点 (2f+1)已经共识完成的最大请求序号叫stable checkpoint,stable checkpoint存在的目的其实是为了减少内存的占用,不用一直缓存记录,序号在stable checkpoint之前的记录可以删除。当主节点挂了(超时无响应)或者从节点集体认为主节点是问题节点时,就会触发ViewChange事件,ViewChange完成后,视图编号将会加1,节点按照顺序轮流做primary。

联盟链中PBFT节点在充分信任的基础上不用设置太多数量,4个其实就已经可以完成实际需要,超过100个PBFT节点就不适用,因为节点越多反而会造成网络带宽压力以及更长的等待确认时间。

PBFT不能算是最好的共识算法,抛开信任问题,在性能上它比不上raft;在去中心化程度 上,它比不上POW和POS;但是在联盟链中,PBFT是目前为止最适合的共识算法,它既满足了一定的去中心化问题,又兼顾了一定的性能。

PBFT算法存在的问题:

· 计算效率依赖于参与协议的节点数量,不适用于节点数量过大的区块链系统,扩展性差。

· 系统节点是固定的,无法应对公有链的开放环境,只适用于联盟链或私有链环境。

· PBFT算法要求总节点数n>=3f+1(其中,f代表作恶节点数)。系统的失效节点数量不得超过全网节点的1/3,容错率相对较低。

另外PBFT算法有一个弱点,其不能很好的存贮记录其交易信息,黑客能够截取一些失效的副本,这会让信息外漏。

  • 5
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值