对于公有区块链来说,由于委员会成员会更迭,自然会有成员的退出和更新,本文介绍的是一种POS的实现,委员会之间的通信使用
Tendermint
,普通节点使用devp2p
会存在两个p2p连接。
选举
- 成员的产生可以通过随机数生成如
ETH2.0
- 通过从POW矿工中寻找满足一定算力的成员
混合共识
- 通过
Balance
质押量选择排行前20的成员
获取委员会信息
选举出了委员会成员后,一般只知道它的公钥
,并不知道它的IP
和端口
,如何实现委员会成员之间的互联呢。
//CommitteeNode contains main info of committee node
type CommitteeNode struct {
IP string
Port uint32
Port2 uint32
Coinbase common.Address //reward address
Publickey []byte
}
发送节点nodeinfo信息
将本节点信息用所有委员会成员
的公钥加密一遍,
for _, member := range committeeInfo.GetAllMembers() {
pubkey, _ := crypto.UnmarshalPubkey(member.Publickey)
encryptNode, err := ecies.Encrypt(rand.Reader, ecies.ImportECDSAPublic(pubkey), nodeByte, nil, nil)
if err != nil {
log.Error("publickey encrypt node error ", "member.Publickey", common.Bytes2Hex(member.Publickey), "err", err)
}
encryptNodes = append(encryptNodes, encryptNode)
}
如果对方是委员会节点,则可解密出委员会成员的信息。
priKey := ecies.ImportECDSA(privateKey)
for _, encryptNode := range cryNodeInfo.Nodes {
decryptNode, err := priKey.Decrypt(encryptNode, nil, nil)
if err == nil { // can Decrypt by priKey
transportCommitteeNode := new(types.TransportCommitteeNode) //receive nodeInfo
rlp.DecodeBytes(decryptNode, transportCommitteeNode)
committeeNode := transportCommitteeNode.ConvertTransportToCommitteeNode(pubKey)
return committeeNode
}
}
此时节点可实现连接。
epoch
pos中一般都有界数
的概念,每一届都会有开始高度和结束高度,以太坊中的SLOT
可以当高度去处理。
type EpochIDInfo struct {
EpochID uint64
BeginHeight uint64
EndHeight uint64
}
创世成员
func (e *Election) getCommitteeByGenesis() *committee {
begin, end := types.GetEpochHeigth(new(big.Int).Set(common.Big0))
return &committee{
id: new(big.Int).Set(common.Big0),
beginFastNumber: begin,
endFastNumber: end,
members: e.genesisCommittee,
}
}
新选举出来的委员会成员都会在下一届生效,实现中新选出来的成员会在本界
开始高度之前的100
个块,提前
发送节点nodeinfo信息,使用Port2
启动tendermint server
。
if next == epoch.EndHeight-params.ElectionPoint+1 {
epoch := types.GetEpochFromHeight(next + params.NewEpochLength)
committee := &types.CommitteeInfo{
Id: new(big.Int).SetUint64(epoch.EpochID),
StartHeight: new(big.Int).SetUint64(epoch.BeginHeight),
EndHeight: new(big.Int).SetUint64(epoch.EndHeight),
}
validators := agent.getValidators(epoch.EpochID)
committee.Members = validators
// Switch to new epoch
agent.setCommitteeInfo(nextCommittee, committee)
if agent.IsUsedOrUnusedMember(committee, agent.committeeNode.Publickey) {
//是委员会成员 发送nodeinfo
agent.startSend(committee, true)
// 更新server节点信息,启动server
help.CheckAndPrintError(agent.server.PutCommittee(committee))
help.CheckAndPrintError(agent.server.PutNodes(committee.Id, []*types.CommitteeNode{agent.committeeNode}))
} else {
agent.startSend(committee, false)
}
}
这样在本届出块
之前,委员会成员已经实现了充分连接。