对于公有区块链来说,由于委员会成员会更迭,自然会有成员的退出和更新,本文介绍的是一种POS的实现,委员会之间的通信使用
Tendermint
,普通节点使用devp2p
会存在两个p2p连接。
这是基于上一篇基于以太坊实现Tendermint POS的细节 (一):选举写的,没看的可以了解下。
PBFT Server初始化
初始化本地配置
- 端口
- IP
- 私钥
agent
为Tendermint
和应用协议的桥梁。
func (s *Taiyuechain) startPbftServer() error {
priv, err := crypto.ToECDSA(s.config.CommitteeKey)
cfg := config.DefaultConfig()
cfg.P2P.ListenAddress1 = "tcp://0.0.0.0:" + strconv.Itoa(s.config.Port)
cfg.P2P.ListenAddress2 = "tcp://0.0.0.0:" + strconv.Itoa(s.config.StandbyPort)
n1, err := tbft.NewNode(cfg, "1", priv, s.agent)
s.pbftServer = n1
return n1.Start()
}
PBFT的几种事件
委员会启动
需要传递给Server
界数,委员会成员,本届开始高度。
e.electionFeed.Send(types.ElectionEvent{
Option: types.CommitteeStart,
CommitteeID: e.committee.id,
CommitteeMembers: members,
BeginFastNumber: e.committee.beginFastNumber,
})
选举点
订阅区块上链的chainHead事件,获取当前上链高度。
func (e *Election) subScribeEvent() {
e.chainHeadSub = e.fastchain.SubscribeChainHeadEvent(e.chainHeadCh)
}
当块高度等于选举点
,计算本届结束高度,和下届委员会成员。
- 本届结束高度
计算出本届结束高度,通知server。e.electionFeed.Send(types.ElectionEvent{ Option: types.CommitteeOver, CommitteeID: e.committee.id, CommitteeMembers: e.committee.Members(), BeginFastNumber: e.committee.beginFastNumber, EndFastNumber: e.committee.endFastNumber, })
- 下届选举
需要计算下届委员会
成员,下届开始高度e.electionFeed.Send(types.ElectionEvent{ Option: types.CommitteeSwitchover, CommitteeID: e.nextCommittee.id, CommitteeMembers: e.nextCommittee.Members(), BeginFastNumber: e.nextCommittee.beginFastNumber, })
换届点
当块高度等于换届点
,本届委员会停止工作,启动下届委员会。
- 本届结束
通知server退出,结束端口监听e.electionFeed.Send(types.ElectionEvent{ Option: types.CommitteeOver, CommitteeID: e.committee.id, CommitteeMembers: e.committee.Members(), BeginFastNumber: e.committee.beginFastNumber, EndFastNumber: e.committee.endFastNumber, })
- 下届开始工作
设置下届委员会成员为本届成员,启动委员会。e.committee = e.nextCommittee e.electionFeed.Send(types.ElectionEvent{ Option: types.CommitteeStart, CommitteeID: e.committee.id, CommitteeMembers: e.committee.Members(), BeginFastNumber: e.committee.beginFastNumber, })
同步中的问题
当一个新节点启动的时候,会向其他节点同步数据,这时候计算委员会会有一些问题。因为当有一批数据上链时,只有最后一个区块发出chainhead事件,这时候上面的流程会有跳过。
计算最新高度的委员会成员,并赋值给当前届,这样区块中签名的验证就可以通过了。func (e *Election) getCommitteeInfoByCommitteeId(committeeId *big.Int) *committee { begin, end := types.GetEpochHeigth(committeeId) committee := &committee{ id: committeeId, beginFastNumber: new(big.Int).Set(begin), endFastNumber: new(big.Int).Set(end), } caCertPubkeyList := e.getCACertList() committee.members = e.assignmentCommitteeMember(caCertPubkeyList, committeeId) return committee }