为什么PBFT协议中需要Commit阶段

1. 写在前面:一个问题

我们首先来思考一个问题:对于BFT类型的共识,每个正确节点在执行一条交易(客户端发出的执行请求)之前中需要满足什么样的条件?或者说它应该在何时执行一条交易?

笔者的理解是:它需要利用一个确切的机制/算法,去检查一种时机/条件。该算法保证满足了该时机/条件后,其他正确节点也会做出相应的执行操作,此时其便可执行该交易。这便暗含着一个要求,即:只要某个正确节点执行了一个交易,其他正确节点必须也对该交易进行执行。

我们以PBFT中的场景为例,当编号为n的消息m在正确节点i中得到了执行,那么该消息在正确节点j中也必须得到执行,且执行编号也为n。更重要的是,即使节点i在执行了消息m之后发生了主节点的切换,节点j还是必须以n为编号执行消息m

有人可能会问:在后期,节点j执行了编号为n的消息p,并且在节点i也执行编号为n的消息m'(即对编号为n的消息进行覆盖执行),不也能保证所有节点的状态一致性嘛。但这里存在两个问题:

  • 如果真采用这种方式,那节点状态就永远得不到确认了,因为在后期永远存在被覆盖的可能性,这实际上就没有意义了;
  • PBFT中就没必要设计那么复杂的View Change机制了,每次换完主节点之后,丢弃之前的所有数据状态,重新开始不就行了吗?PBFT中复杂的View Change设计就是为了保证节点i中执行了的交易,即使在切换主节点之后也一定会被其他正确节点执行。

引用StackOverflow中的一个回答如下。简言之,该回答中表示:整个PBFT集群应该呈现出一种黑盒状态,其只要对client返回了reply,该reply就不应该再被否认/改写。该回答中还提到一个名词durable access,比较好地表达了该意思。

The system appears to a client as a black box. The whole idea of this box is to provide reliable access to some service, thus, it should mask the failures of a particular replica. Otherwise, if you discard everything at each view change, clients will constantly lose their data. So basically, your solution simply contradicts the specification.

2. Prepared数据作为可执行证明

PBFT论文中提到“Prepared原语保证了在同一个View中,不同节点中编号n对应的消息是一致的。”这一点的具体证明可以参考PBFT论文以及本博客最后列出的参考文献。
也就是说,如果不考虑主节点的切换,Preprepare阶段+Prepare阶段就能达到共识的目的。

因此,在不存在主节点切换时,我们可以将Prepared数据(包括对应的Preprepare消息和2f个Prepare消息)作为一条消息m的“可执行”证明。

3. 假设不存在Commit阶段

我们假设集群中共有10个节点,节点A到节点J,其中节点A到节点G是诚实节点,节点HIJ是恶意节点。
假设不存在Commit阶段,节点A在达到Prepared状态后便执行了编号n对应的消息m,但此时节点B到节点G都没有达到Prepared状态。同样在此时,网络开始执行主节点切换。若不采用任何机制,新的主节点可能不知道编号n已经对应了消息m(或者新的主节点是恶意节点,假装不知道编号n已经对应了消息m)。于是,新的主节点发起对应于编号n的消息m'。节点B到节点G将针对新消息m'进行共识,从而得到与节点A中不一样的执行结果。

有同学可能会有以下几点疑问:

  • 节点A不是有了Prepared数据了嘛。它可以直接把这份Prepared数据发送给其他节点,证明编号n对应着消息m。不就可以避免不一致了吗?
    **答:**由于PBFT是弱同步网络,节点A的消息不保证能及时发送到其他节点。可能等其他节点收到节点APrepared数据之前,已经完成了编号n对应消息m'的共识了。
  • 接着上一条,那就再定义一个机制嘛。即使其他节点达成了编号n对应消息m'的共识,如果它们又收到节点APrepared消息,那就对之前的共识进行回滚,重新采用节点APrepared消息作为共识。
    **答:**这不就和第1小节中的理解矛盾了嘛。其他节点已经达成了共识,却又在后期进行改写。并且在未改写之前,整个集群中的节点出现了两种状态,即分别执行了(n, m)和(n, m')。
  • 既然在主节点切换之前,节点A已经达到了Prepared状态了,说明其他至少6个节点已经发出了编号n对应消息mPrepare消息或PrePrepare消息。那么其他节点在主节点切换后,对编号n对应两种消息(mm')的情形进行特殊处理不就行了吗?比如说采用View编号小的消息m.
    答:这里有个问题:如果上一个View中的主节点是恶意节点,那么他发送的消息将一直影响着整个集群(如果采用View编号小的消息m,恶意主节点的消息将一直有效并废弃新主节点中的对应消息),使得在新View中也迟迟无法达成共识。这便失去了主节点切换的意义了:主节点切换就是为了解决当前主节点是恶意节点而阻塞共识的情况。

4. Commit阶段如何解决上述问题

通过以上三小节的分析,我们可以发现,BFT共识算法中最难的问题之一就是:如何在主节点切换后,还能保证消息执行的一致性。换句话说:若在View v中节点i以编号n执行了消息m,那么在View v+1中节点j也必须以编号n执行消息m
下面,我们来看看Commit阶段是如何解决该问题的(准确来说是配合View Change来解决该问题)。

一方面,PBFT中要求节点i只有接收到了2f+1个Commit消息后,才算是达到了Committed状态,也才能执行对应的消息m。这保证了: 在此时,PBFT集群中至少有2f+1个节点到达了消息m对应的Prepared状态,拥有了Prepared数据。

另一方面,PBFT中要求节点在发出的VIEW-CHANGE消息中携带Prepared数据,且在新主节点发出的NEW-VIEW消息中至少携带2f+1个VIEW-CHANGE消息。结合上一段中提到的2f+1个节点拥有了Prepared数据,可以得出:NEW-VIEW消息中至少包含f+1个Prepared数据。且又由于恶意节点最多f个,因此这f+1个Prepared数据保证了至少一个是从诚实节点发出的。

也就是说:PBFT通过CommitView Change的机制,强制要求新主节点在NEW-VIEW消息中包含所有在上一轮中已经被Committed了的消息对应的Prepared数据。

这里,读者可能会有几个疑惑:

  • 为什么一定要是包含Prepared数据呢?包含Prepare数据不行吗?只要表明了编号n对应于消息m就行了啊。
    答:包含Prepare数据不行,因为Prepare消息可能是错误的(准确来说,由于恶意主节点的误导,诚实节点发出了错误信息)。其实这与另外一个问题是等价的,即:节点i在到达Prepared状态后立即执行消息m,并在发出的VIEW-CHANGE消息中携带Prepare消息(注意是Prepare消息,不是Prepared数据)。这保证了NEW-VIEW消息中至少包含f+1个Prepare消息,且至少一条是从诚实节点发出的。
    但诚实节点发出的Prepare消息不一定是正确的。具体而言,假设一共有4个节点(ABCD,其中D是恶意节点),在View v中主节点是D,其向AB发送了a=1的消息,而向C发送了a=2的消息。节点A在收到BPrepare消息后到达了Prepared状态,此时发生了主节点切换。切换过程中的NEW-VIEW消息中包含了2条Prepare数据,一条是C发出的,一条是D发出的。也就是说在新View中,集群将对a=2进行共识。这就是我们在上一段所说的:NEW-VIEW消息中确实至少包含了一条诚实节点发出的Prepare消息,但由于恶意主节点的误导,这个Prepare消息本身就是错误的。
    那么如果NEW-VIEW消息中包含的至少一条诚实节点发出的消息是Prepared数据,会有什么不同呢?由第2节的分析可知,Prepared数据是经历过“简单共识”过的,也就不会受到恶意主节点的误导,从而保证了在View v中执行了的消息也一定还是会在View v+1中执行。

  • 既然Prepared数据是经过共识,其便是无法伪造的,为啥需要NEW-VIEW消息中至少携带2f+1个VIEW-CHANGE消息(从而至少包含f+1个Prepared数据)?反正Prepared无法伪造,有一个不就行了吗?
    答:Prepared数据虽然无法伪造,但恶意节点可能在发送VIEW-CHANGE消息时故意遗漏某个消息m对应的Prepared数据。为处理这种情况,PBFT要求NEW-VIEW中至少包含2f+1条VIEW-CHANGE消息。

综上,Commit阶段主要是用来与View Change机制进行配合,从而保证在上一个View中以编号n执行了的消息/交易m,在新的View中也会以相同的编号n执行。引用论文中的一句话来说便是:

The prepare and commit phases are used to ensure that requests that commit are totally ordered across views.

参考文献

  1. PBFT: Why cant the replicas perform the request after 2/3 have prepared? why do we need commit phase?
  2. Why is the commit phase in PBFT necessary?
  3. 实用拜占庭容错算法(PBFT)
  4. Why do we need total order across view changes in consensus protocols?
  5. 为什么pbft需要三个阶段?
  • 12
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 19
    评论
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值