PBFT基础流程

PBFT(Practical Byzantine Fault Tolerance)共识算法可以在少数节点作恶(如伪造消息)场景中达成共识,它采用签名、签名验证、哈希等密码学算法确保消息传递过程中的防篡改性、防伪造性、不可抵赖性,并优化了前人工作,将拜占庭容错算法复杂度从指数级降低到多项式级别,在一个由(3*f+1)个节点构成的系统中,只要有不少于(2*f+1)个非恶意节点正常工作,该系统就能达成一致性,如:7个节点的系统中允许2个节点出现拜占庭错误。

FISCO BCOS区块链系统实现了PBFT共识算法。

1. 重要概念

节点类型、节点ID、节点索引和视图是PBFT共识算法的关键概念。区块链系统基本概念请参考关键概念

1.1 节点类型

其中Leader和Replica统称为共识节点。

1.2 节点ID && 节点索引

为了防止节点作恶,PBFT共识过程中每个共识节点均对其发送的消息进行签名,对收到的消息包进行验签名,因此每个节点均维护一份公私钥对,私钥用于对发送的消息进行签名,公钥作为节点ID,用于标识和验签。

节点ID : 共识节点签名公钥和共识节点唯一标识, 一般是64字节二进制串,其他节点使用消息包发送者的节点ID对消息包进行验签

考虑到节点ID很长,在共识消息中包含该字段会耗费部分网络带宽,FISCO BCOS引入了节点索引,每个共识节点维护一份公共的共识节点列表,节点索引记录了每个共识节点ID在这个列表中的位置,发送网络消息包时,只需要带上节点索引,其他节点即可以从公共的共识节点列表中索引出节点的ID,进而对消息进行验签:

节点索引 : 每个共识节点ID在这个公共节点ID列表中的位置

1.3 视图(view)

PBFT共识算法使用视图view记录每个节点的共识状态,相同视图节点维护相同的Leader和Replicas节点列表。当Leader出现故障,会发生视图切换,若视图切换成功(至少2*f+1个节点达到相同视图),则根据新的视图选出新leader,新leader开始出块,否则继续进行视图切换,直至全网大部分节点(大于等于2*f+1)达到一致视图。

FISCO BCOS系统中,leader索引的计算公式如下:

leader_idx = (view + block_number) % node_num

下图简单展示了4(3*f+1, f=1)节点FISCO BCOS系统中,第三个节点(node3)为拜占庭节点情况下,视图切换过程:

1.4 共识消息

PBFT模块主要包括PrepareReq、SignReq、CommitReq和ViewChangeReq四种共识消息:

这四类消息包包含的字段大致相同,所有消息包共有的字段如下:

字段名 字段含义
字段名 字段含义
idx 当前节点索引
packetType 请求包类型(包括PrepareReqPacket/SignReqPacket/CommitReqPacket/ViewChangeReqPacket)
height 当前正在处理的区块高度(一般是本地区块高度加一)
blockHash 当前正在处理的区块哈希
view 当前节点所处的视图
sig 当前节点对blockHash的签名

PrepareReqPacket类型消息包包含了正在处理的区块信息:

消息包类型 字段名 字段含义
PrepareReqPacket block 所有共识节点正在共识的区块数据

2. 系统框架

系统框架如下图:

PBFT共识主要包括两个线程:

3. 核心流程

PBFT共识主要包括Pre-prepare、Prepare和Commit三个阶段:

下图详细介绍了PBFT各个阶段的具体流程:

.. mermaid::

   graph TB
        classDef blue fill:#4C84FF,stroke:#4C84FF,stroke-width:4px, font:#1D263F, text-align:center;

        classDef yellow fill:#FFEEB8,stroke:#FFEEB8,stroke-width:4px, font:#1D263F, text-align:center;

        classDef light fill:#EBF5FF,stroke:#1D263F,stroke-width:2px,  font:#1D263F, text-align:center;

        subgraph 共识处理流程
        A((开始))-->B
        B(获取PBFT请求类型)-->|Prepare请求|C
        B-->|Sign请求|D
        B-->|Commit请求|F
        C(Prepare是否有效?)-->|是|G
        C-->|否|B

        G(addRawPrepare<br/>缓存Prepare请求)-->H
        H(Prepare内区块是空块?)-->|否|I
        H-->|是|T
        T(视图切换)

        I(execBlock<br/>执行Prepare内区块)-->J
        J(generateSignPacket<br/>产生签名请求)-->K
        K(addPrepareCache<br/>缓存执行后区块)-->L
        L(broadcacstSignReq<br/>广播签名请求)

        D(isSignReqValid<br/>签名请求是否有效?)-->|是|M
        D-->|否|B
        M(addSignReq<br/>缓存收到的签名请求)-->N
        N(checkSignEnough<br/>签名请求是否达到2*f+1?)-->|是|O
        N-->|否|B
        O(updateLocalPrepare<br/>备份Prepare请求)-->P
        P(broadcastCommitReq<br/>广播Commit请求, 表明节点已达到可提交区块状态)

        F(isCommitReqValid <br/> Commit请求是否有效?)-->|是|Q
        Q(addCommitReq <br/> 缓存Commit请求)-->R
        R(checkCommitEnough <br/> Commit请求是否达到2*f+1?)-->|是|S
        R-->|否|B
        S(CommitBlock<br> 将缓存的执行后区块提交到DB)

        class A,B light
        class C,G,H,I,J,K,L,T light
        class D,M,N,O,P light
        class Q,F,R,S light
        end

3.1 leader打包区块

PBFT共识算法中,共识节点轮流出块,每一轮共识仅有一个leader打包区块,leader索引通过公式(block_number + current_view) % consensus_node_num计算得出。

节点计算当前leader索引与自己索引相同后,就开始打包区块。区块打包主要由PBFTSealer线程完成,Sealer线程的主要工作如下图所示:

3.2 pre-prepare阶段

共识节点收到Prepare包后,进入pre-prepare阶段,此阶段的主要工作流程包括:

3.3 Prepare阶段

共识节点收到签名包后,进入Prepare阶段,此阶段的主要工作流程包括:

3.4 Commit阶段

共识节点收到Commit包后,进入Commit阶段,此阶段工作流程包括:

3.5 视图切换处理流程

当PBFT三阶段共识超时或节点收到空块时,PBFTEngine会试图切换到更高的视图(将要切换到的视图toView加一),并触发ViewChange处理流程;节点收到ViewChange包时,也会触发ViewChange处理流程: