pbft 的启动及配置
当节点是 vp 时,server 方法 -> GetEngine 设置 engine.consenter 为 obcBatch,obcBatch 有一个成员 externalEventReceiver 实现了 RecvMsg
方法。
id:由 getValidatorID 可得 (vpX..id 就为 X)
当收到消息时(和 noops 消息类型一样),调用 externalEventReceiver.RecvMsg:
// RecvMsg is called by the stack when a new message is received
func (eer *externalEventReceiver) RecvMsg(ocMsg *pb.Message, senderHandle *pb.PeerID) error {
eer.manager.Queue() <- batchMessageEvent{
msg: ocMsg,
sender: senderHandle,
}
return nil
}
由于 externalEventReceiver 设置的 Receiver 是 obcbatch,所以外面所有事件交由 obcbatch.ProcessEvent 处理,obcbatch.ProcessEvent 转
发给 obcbatch.processMessage,然后调用 op.submitToLeader(req)
submitToLeader 存下该请求(storeOutstanding(req)),并广播(防止自己处于错误的 view),然后如果自己是主节点的话,执行 leaderProcReq,它的策略主要是
在请求数达到 op.batchSize 的时候才返回 events.Event,否则返回空。当返回不为空时,processMessage 会返回一个非空事件,然后传递到 ProcessEvent
进入 default 分支,交给 pbft-core 的 ProcessEvent 模块处理(RequestBatch 事件)
pbft 协议过程
主节点收到 RequestBatch 过程
- 存入 <hash(req), req> 到 reqBatchStore,outstandingReqBatches,并持久化( instance.consumer.StoreState(k,v) )
- 打开一个与 hash(req)相关的计时器
- 判断当前节点是不是主节点,如果是 sendPrePrepare
主节点发送 PrePrepare 过程
- 先得到序列号 n(instance.seqNo + 1)
- 如果发现已经收到其他摘要相同 view 相同而序列号 n 不同的,则返回
- 当 n 不在 watermarks 间,或 n > h + L/2 返回
- 当 n 大于 viewChangeSeqNo,因为将要 view-change 主节点,返回
func (instance *pbftCore) updateViewChangeSeqNo() {
if instance.viewChangePeriod <= 0 {
return
}
// Ensure the view change always occurs at a checkpoint boundary
instance.viewChangeSeqNo = instance.seqNo + instance.viewChangePeriod*instance.K - instance.seqNo%instance.K
logger.Debugf("Replica %d updating view change sequence number to %d", instance.id, instance.viewChangeSeqNo)
}
如果 viewchangeperiod 配置为 0 则不会改变 viewChangeSeqNo
- 以上条件都不满足。构造 pre-prepare 消息
preprep := &PrePrepare{
View: instance.view, // 当前view
SequenceNumber: n, // 消息号
BatchDigest: digest,
RequestBatch: reqBatch,
ReplicaId: instance.id, //主节点id
}
将 prePrepare, digest 存入 cert 里,并持久化 qset(?)
6. 广播 pre-Prepare 消息
7. maybeSendCommit (目前不是特别明白)
recvPrePrepare 处理流程
-
如果正在 view-change,忽略这个 Pre-Prepare 消息
-
如果收到的 Pre-Prepare 发送 id 不是当前 view 的主节点,忽略这个 Pre-Prepare 消息(view 一定,主节点一定 pbftCore.primary 函数)
-
当收到的 view 与 pbft-core 的 view 不一致,或 n 不在 watermarks 间的话,丢弃这个消息
-
当 n 大于 viewChangeSeqNo,发送 viewChange 消息,返回
-
当收到相同序列号的消息时,如果消息体不同,进行 view-change
否则,将收到的 prePrepare, digest 存入 cert 里 -
当 reqBatchStore 不存在当前 PrePrepare 消息的摘要时,首先计算摘要,如果摘要与收到的摘要一致,将摘要记录
到 reqBatchStore 与 outstandingReqBatches 中,并持久化该 reqBatch
7.当前节点不是主节点并且这个 Pre-Prepare 消息之前没有发送 Prepare 消息的话,构造 prepare 消息:
prep := &Prepare{
View: preprep.View,
SequenceNumber: preprep.SequenceNumber,
BatchDigest: preprep.BatchDigest,
ReplicaId: instance.id,
}
自己调用 recvPrepare(相当于自己收到了 Prepare 消息)
然后持久化 qset(?)
调用 instance.innerBroadcast(&Message{Payload: &Message_Prepare{Prepare: prep}})广播消息
节点收到 prepare 消息
- 如果收到的是主节点的消息,丢弃(prepare 不可能由主节点发送)
- 当收到的 view 与 pbft-core 的 view 不一致,或 n 不在 watermarks 间的话,丢弃这个消息
- 获取 cert,如果收到同一节点同一 view 的同一序号消息,则退出
- 将 prepare 放入 cert,并持久化 pset
- maybeSendCommit(目前不是特别明白)
节点收到 commit 消息 recvCommit
- 当收到的 view 与 pbft-core 的 view 不一致,或 n 不在 watermarks 间的话,丢弃这个消息
- 获取 cert,如果收到同一节点同一 view 的同一序号存在该 commit 消息,丢弃这个消息
- 添加 commit 到 cert 里
- 看该 msg 是否满足 commited 函数,若满足,将先前放入的 outstandingReqBatches 删除
执行未完成的 batch,执行完后,如果序列号等于 viewChangeSeqNo,进行 view-change
执行未完成的 batch executeOutstanding()
大概思路是每来一个 commit,执行所有没执行完的,当中任意一个执行成功则返回,否则继续执行
- 如果 instance.currentExec 当前正在执行,退出
- 执行所有没执行完的,执行成功一次就返回
for idx := range instance.certStore {
if instance.executeOne(idx) {
break
}
}
执行某一个 commit executeOne(n)
- 如果 n 不等于 lastExec+1,返回 false(为了保证按序执行)
这里的 lastExec 初始化由 getLastSeqNo 获得 - 如果设置了 skipInProgress,则返回 false
skipInProgress : 在发现了一个落后的情况到我们选择一个新的起点间设置 - 如果该 req 不满足 commited 函数,则返回 false
- 如果是空请求 instance.execDoneSync()
否则 instance.consumer.execute(idx.n, reqBatch)进入执行状态
obcbatch execute
- 把即将执行的交易放入 txs
- 存入最近执行交易的节点和时间戳
- 调用 op.stack.Execute(meta, txs) (后台执行,执行完会收到 executedEvent)
广播消息:
消息 | 函数 |
---|---|
Message_PrePrepare | sendPrePrepare |
Message_Prepare | recvPrePrepare |
Message_Commit | maybeSendCommit |
Message_Checkpoint | Checkpoint |
Message_FetchRequestBatch | fetchRequestBatches |
Message_ViewChange | sendViewChange |
Message_NewView | sendNewView |
Message_Prepare | processNewView2 |
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于