PBFT原理及go语言代码实现
PBFT
前提
客户端
正常情况下的操作
预准备阶段
准备阶段
确认阶段
垃圾回收
视图切换
算法的安全性与活性
前提
在pbft算法中,用R表示副本节点集合,每个副本用{0,...,|R|-1}来表示。假设|R|=3f+1,f为出错节点的数量。
副本节点通过一系列称为“视图”(view)的配置来移动。在每一个视图中,包含一个primary(主节点),其他做为backups(备份节点)。算法的步骤如下:
step1:客户端向主节点发送请求以调用服务器操作。
step2:主节点向备份节点广播该请求。
step3:副本节点执行该请求并回复到客户端。
step4:客户端收到来自不同副本节点的f+1个相同的结果。
由于pbft算法是基于状态机复制的算法,因此副本节点必须满足以下两点:
(1)它们必须是确定性的。(在给定状态与给定参数集下,执行操作后产生的结果是相同的)
(2)它们必须以相同的状态开始。
客户端
在客户端执行的操作有两个:
客户端向主节点发送请求信息<REQUEST,o,t,c> 其中,o为请求状态复制机的操作,t为时间戳。
副本节点向客户端返回回复信息<REPLY,v,t,c,i,r> 其中,v为当前的视图编号,t为时间戳,i为副本节点编号,r为执行请求后的操作结果。
正常情况下的操作
“三阶段协议”
当主节点p收到客户端请求m,它将会启动三阶段协议自动广播请求信息给其他副本节点。三阶段协议分别是“pre-prepare预准备阶段”,“prepare准备阶段”,“commit确认阶段”。
pre-prepare预准备阶段
预准备阶段主节点向所有的副本节点发送pre-prepare消息,<<PRE-PREPARE,v,n,d>,m>
但这个消息要满足以下四点,副本节点才会接收:
(1)请求消息m和pre-prepare消息的数字签名都是正确的,且d是消息m的摘要。
(2)它存在于当前的视图v中。
(3)该备份节点从未在当前视图v中接收过包含不同摘要的序号为n的预准备消息。
(4)预准备消息中的序号n位于消息量下限h和上限H之间。
prepare准备阶段
当备份节点i接收了预准备消息,它将进入准备阶段,将准备消息<PREPARE,v,n,d,i>发送给其他副本节点,并将预准备消息和准备消息都写入消息日志中。
同上,prepare消息满足以下三个要求,才会被其他副本节点接收:
(1)准备消息的签名是正确的。
(2)视图编号与副本节点当前的视图编号一致。
(3)准备消息的序列号位于消息量下限h和上限H之间。
准备阶段完成的标志,有以下内容插入到日志中就代表完成:
(1)请求消息m
(2)在视图v中,序列号为n的请求m的预准备消息
(3)2f个与预准备消息匹配的且来自不同备份节点的准备消息。(检查视图编号、序列号、消息摘要,如果都一致则代表匹配)
在PBFT算法中,pre-prepare和prepare阶段保证了非故障副本节点对在同一个视图中的请求排序达成一致。
commit确认阶段
准备阶段完成进入确认阶段,同上,副本节点接收确认信息<COMMIT,v,n,D(m),i>,确认消息也要满足以下条件:
(1)确认消息的签名是正确的。
(2)视图编号与副本节点当前的视图编号一致。
(3)确认消息的序列号位于消息量下限h和上限H之间。
垃圾回收
为了保证系统的安全性,副本节点在删除自己的消息日志前,需确保至少f+1个正常副本节点执行了消息所对应的请求,并且在视图变更时向其他副本节点证明。另外,如果某些副本节点错过了部分消息,且这些消息已经被非故障副本节点删除了,则需要通过转移全部或部分服务状态来更新,因此在执行每个阶段后,副本节点需要对状态的正确性进行证明。
因此在PBFT中定义了检查点协议checkpoint,如果已经进行证明了,则转换成stable checkout 。当一个副本节点i生成了"checkpoint",它向其他副本节点广播<CHECKPOINT,n,d,i>消息。每个副本节点在日志中收集checkpoint信息,直到收集到2f+1个来自不同副本节点的具有相同序列号n和摘要d的检查点消息,就代表已经进行证明了。
另外,checkpoint协议还可以用来更新水线的高低值h和H,即代表了可以被接收的消息量大小。
视图变更
视图变更机制保证了系统的活性。
当在视图v中,副本节点i计时器超时后,则会触发view-change,视图由v变成v+1,并且停止接收消息(检查点协议、视图变更、新消息视图除外),并向所有副本节点播报<VIEW-CHANGE,v+1,n,c,p,i>,当视图编号为v+1中的主节点从其他副本节点收到2f个有效的view-change消息后,则广播<NEW-VIEW,v+1,v,o>。其中v是有效的VIEW-CHANGE消息集合,o是主节点重新发起的未经完成的pre-prepare消息集合,主节点将o集合中的消息放入日志中。接下来就进入到视图v+1中,并开始o集合中的pre-prepare及后续处理流程。
算法的正确性
在PBFT中,视图变更和垃圾回收机制保证了算法的安全性safety和活性liveness。
代码实现
首先定义消息类型结构体
//请求消息结构体
type RequestMsg struct{
TimeStamp int64 'json:"timestamp"'
ClientID string 'json:"clientID"'
Operation string 'json:"operation"'
SequenceID int64 'json:"sequenceID"'
}
//回复消息结构体
type ReplyMsg struct{
ViewID int64 'json:"viewID"'
TimeStamp int64 'json:"timestamp"'
ClientID string 'json:"clientID"'
NodeID string 'json:"nodeID"'
Result string 'json:"result"'
}
//预准备消息结构体
type PrePrepareMsg struct{
ViewID int64 'json:"viewID"'
SequenceID int64 'json:"sequenceID"'
Digest string 'json:"digest"'
RequestMsg *RequestMsg 'json:"requestMsg"'
}
//投票消息结构体<