拜占庭问题

拜占庭将军问题(Byzantine Generals Problem),是由Leslie Lamport在其同名论文中提出的分布式对等网络通信容错问题。在分布式计算中,不同的计算机通过通讯交换信息达成共识而按照同一套协作策略行动。但有时候,系统中的成员计算机可能出错而发送错误的信息,用于传递信息的通讯网络也可能导致信息损坏,使得网络中不同的成员关于全体协作的策略得出不同结论,从而破坏系统一致性。拜占庭将军问题被认为是容错性问题中最难的问题类型之一。

PBFT算法

介绍

  1. BFT从上世纪80年代开始被研究,目前已经是一个被研究得比较透彻的理论,具体实现都已经有现成的算法。其中,PBFT是当中最著名的算法,PBFT是Practical Byzantine Fault Tolerance的缩写,意为实用拜占庭容错算法。该算法是Miguel Castro和Barbara Liskov在1999年提出来的,解决了原始拜占庭容错算法效率不高的问题,将算法复杂度由指数级降低到多项式级,使得拜占庭容错算法在实际系统应用中变得可行。
  2. 为了保证pbft算法的正确性,节点总数量n和作恶节点数量f必须满足n > 3f。至于原因,我们接着往下看。

The resiliency of our algorithm is optimal: 3f + 1 is the minimum number of replicas that allow an asynchronous system to provide the safety and liveness properties when up to f replicas are faulty (see [2] for a proof). This many replicas are needed because it must be possible to proceed after communicating with n - f replicas, since f replicas might be faulty and not responding. However, it is possible that the f replicas that did not respond are not faulty and, therefore, f of those that responded might be faulty. Even so, there must still be enough responses that those from non-faulty replicas outnumber those from faulty ones, i.e., n - 2f > f. Therefore n > 3f.

PBFT算法三阶段

PBFT共识算法原理_客户端
算法的核心三个阶段分别是 pre-prepare 阶段(预准备阶段),prepare 阶段(准备阶段), commit 阶段(提交阶段)。图中的C代表客户端,0,1,2,3 代表节点的编号,打叉的3代表可能是故障节点或者是问题节点,这里表现的行为就是对其它节点的请求无响应。0 是主节点。整个过程大致是如下:

首先,客户端向主节点发起请求,主节点 0 收到客户端请求,会向其它节点发送 pre-prepare 消息,其它节点就收到了pre-prepare 消息,就开始了这个核心三阶段共识过程了。

Pre-prepare 阶段:节点收到 pre-prepare 消息后,会有两种选择,一种是接受,一种是不接受。什么时候才不接受主节点发来的 pre-prepare 消息呢?一种典型的情况就是如果一个节点接受到了一条 pre-pre 消息,消息里的 v 和 n 在之前收到里的消息是曾经出现过的,但是 d 和 m 却和之前的消息不一致,或者请求编号不在高低水位之间(高低水位的概念在下文会进行解释),这时候就会拒绝请求。拒绝的逻辑就是主节点不会发送两条具有相同的 v 和 n ,但 d 和 m 却不同的消息。

Prepare 阶段:节点同意请求后会向其它节点发送 prepare 消息。这里要注意一点,同一时刻不是只有一个节点在进行这个过程,可能有 n 个节点也在进行这个过程。因此节点是有可能收到其它节点发送的 prepare 消息的。在一定时间范围内,如果收到超过 2f 个不同节点的 prepare 消息,就代表 prepare 阶段已经完成。

Commit 阶段:于是进入 commit 阶段。向其它节点广播 commit 消息,同理,这个过程可能是有 n 个节点也在进行的。因此可能会收到其它节点发过来的 commit 消息,当收到 2f+1 个 commit 消息后(包括自己),代表大多数节点已经进入 commit 阶段,这一阶段已经达成共识,于是节点就会执行请求,写入数据。

相关问题

  1. prepare和commit阶段为何都要2f+1个节点反馈确认?(这2f+1并不一定是相同的)

对于prepare和commit来说,节点需要在2f+1个状态复制机的沟通内就要做出决定,这是刚好可以保证一致性的,考虑最坏的情况:我们假设收到的有f个是正常节点发过来的,也有f个是恶意节点发过来的,那么,第2f+1个只可能是正常节点发过来的。(因为我们限制了最多只有f个恶意节点)由此可知,“大多数”正常的节点还是可以让系统工作下去的。所以2f+1这个参数和n>3f+1的要求是逻辑自洽的。

  1. client为何只需要f+1个相同的回复就可确认?

之前我们说,prepare和commit阶段为何都要2f+1个节点反馈,才能确认。client只需要f+1个相同的reply就可以了呢?我们还是来考虑最坏的情况,我们假设这f+1个相同的reply中,有f个都是恶意节点。
所以至少有一个rely是正常节点发出来的,因为在prepare阶段,这个正常的节点已经可以保证prepared(m,v,n,i)为真,所以已经能代表大多数的意见,所以,client只需要f+1个相同的reply就能保证他拿到的是整个系统内“大多数正常节点“的意见,从而达到一致性。

  1. 如果primary是恶意节点呢?

对于一致性,我们可以这么看:如果prepared(m,v,n,i)为真,那么prepared(m’,v,n,j)一定是错误的,因为对于同一个提案我们不可能有两种结果,从而保证整个系统的一致性。
假设primary节点是恶意的,那么意味着在replicas节点中⾄多有f-1个恶意的节点,prepared(m,v,n,i)为真,则证明有f+1个善意节点达成了了⼀致,prepared(m’,v,n,j)为真,意味着另外f+1个善意节点达成了一致,因为系统中只有2f+1个善意节点,因此最少有⼀个善意节点发送了两个冲突的prepare消息,这是不可能的。所以prepared(m,v,n,i)为真,那么prepared(m’,v,n,j)是错误的。

总结

  1. 特征/优点:
    • 通信复杂度O(n^2)。
    • 首次提出在异步网络环境下使用状态机副本复制协议,该算法可以工作在异步环境中,并且通过优化在早期算法的基础上把响应性能提升了一个数量级以上。作者使用这个算法实现了拜占庭容错的网络文件系统(NFS),性能测试证明了该系统仅比无副本复制的标准NFS慢了3%。
    • 使用了加密技术来防止欺骗攻击和重播攻击,以及检测被破坏的消息。消息包含了公钥签名(其实就是RSA算法)、消息验证编码(MAC)和无碰撞哈希函数生成的消息摘要(message digest)。
    • 适用于permissioned systems (联盟链/私有链),能容纳故障节点,也能容纳作恶节点。要求所有节点数量至少为3f+1(f为作恶/故障不回应节点的数量),这样才能保证在异步系统中提供安全性和活性。
    • 解决了原始拜占庭容错(BFT)算法效率不高的问题,将算法复杂度由指数级降低到多项式级,使得拜占庭容错算法在实际系统应用中变得可行。
  2. 缺点:
    • 仅仅适用于permissioned systems (联盟链/私有链)。
    • 通信复杂度过高,可拓展性比较低,一般的系统在达到100左右的节点个数时,性能下降非常快。
    • PBFT在网络不稳定的情况下延迟很高。
Either Excellent or Rusty