PBFT算法的背景及算法流程请阅读:PBFT:实用拜占庭容错算法
在PBFT算法流程中,两个阶段(pre-prepare和prepare阶段)以后所有节点即可对消息的序号达成共识,但为何还需要第三个阶段(commit阶段)之后共识结果才能提交呢?
究极答案:避免因主节点恶意赋予两个消息同一个序号而导致换主后节点提交的消息不一致。
主节点作恶
主节点在收到客户端的消息请求m和m'时,企图赋予它们同一个序号n,故在转发给其它节点的pre-prepare消息中,有的为m-n,有的为m'-n,还可能同时把m-n和m'-n发送给其它恶意节点。
假设有7个节点,f=2,主节点(0号节点)和6号节点为恶意节点。下面分别讨论两阶段提交和三阶段提交时会遇到的情况。
两阶段提交
假设主节点(0号节点)是恶意节点,它接收到来自客户端(C)的两个消息m和m'后,将赋予m序号n的pre-prepare消息发送给1号2号3号节点(f+1个诚实节点),将赋予m'序号n的pre-prepare消息发送给4号5号(f个诚实节点),将上述两个消息均发给6号节点(恶意节点)。在prepare阶段,各节点收到来自主节点的pre-prepare消息后,向其他节点转发prepare消息,其中6号节点可以随机选择转发m和m',假设6号节点只向2号(少于f+1个诚实节点)转发了包含m的prepare消息,向其他节点发送的是包含m'的prepare消息,则2号节点总共收到的prepare消息为{m,m,m,m,m',m',m}(主节点无需再发送prepare消息给其他节点,默认为m,自己当前的prepare消息也为m)。此时要求节点若收到2f+1=5个相同的prepare消息即可提交。因此2号节点在prepare阶段后提交m。其它节点由于无法收到2f+1=5个相同的prepare消息,因此不会提交。
假设此时主节点超时未响应或超时未发起请求或被其他节点发现为拜占庭节点,则其他节点将发起view-change请求。节点在发起view-change请求时将停止接收来自其他节点的pre-prepare请求和prepare请求。新的主节点会收集2f+1=5个view-change消息以判断消息m的提交情况。而除了少于f+1=3个节点提交了m外,其他大于或等于2f+1=5个节点都没有提交m,因此如果新的主收到的是来自后面2f+1=5个节点的view-change,它会认为m没被任何一个节点提交,从而会重新发起一个新消息的共识,或对m赋予新的序号,系统的安全性因此遭到破坏。
三阶段提交
若prepare阶段之后有节点发现无法收起2f+1个相同的prepare消息(如图1所示),则断定主节点为拜占庭节点,因而直接发起view-change换主,此时并未有m或m'实现了提交,因此不影响新view下的重新共识。
若对赋予m序号n的共识到达了commit阶段,则表明所有节点都收到了至少2f+1=5个包含m的prepare消息。
反证:假设由于最多f个恶意节点的存在,使得1号节点收到了至少2f+1个包含m的prepare消息,2号节点收到了至少2f+1个包含m'的prepare消息,则同时发出包含m和m'的prepare消息的节点数量至少为2f+1+2f+1-(3f+1)=f+1个,然而系统中最多存在f个拜占庭节点,因此与假设矛盾。
若某个节点在commit阶段后提交了m,则表明该节点收到了至少2f+1=5个包含m的commit消息,这意味着至少有2f+1=5个节点已经达到prepared状态(指收到至少2f+1=5个包含m的prepare消息),其中至少有f+1=3个节点是诚实节点(称为H)。
假设此时发生view-change,新的主节点会在收集view-change消息的同时收集节点在检查点前已经prepared的消息,并重新对它们发起共识。在新主节点收集的2f+1=5个view-change请求中,必然有来自H的view-change,因此也必然包含当前已经提交的m的prepared消息。
证明:系统中总的节点数是3f+1,H(已经prepared的诚实节点)中的节点数为f+1,除了H以外的节点数为2f,因此为了够2f+1个view-change消息,必然有至少一个H中的诚实节点向新主发起 view-change消息,其中必然包含m的prepared消息。
特别鸣谢:华东师范大学区块链实验室张召教授和佟兴博士