11.5 push汇总 - Agzs/geth-pbft-study GitHub Wiki
根据运行过程中preprepare的消息发送处理,进行了修改,push的是相关的修改部分。由于项目中导入了go-ethereum的rlp包,所以根据rlp包中的函数修改geth-pbft相关部分。
1、consensus/pbft/config.yaml 和 node/core.yaml
根据实际情况进行修改,比如config.yaml中的timeout属性值可扩大10倍,避免viewchange过早发生;core.yaml中路径的选择,用于存储rocksDB
2、consensus/pbft/pbft.go
2.1 注释掉recent相关检验授权部分
因为在pbft中不需要每挖一个块,就轮换一次signer,所以recent在pbft中不起作用。
同时,注释掉Signed recently, must wait for others"设置的时间机制
另外,后期发现未授权错误也和该部分有关。
2.2 修改lastExec
将c.pbft.lastExec = newBlock.Header().Number.Uint64()移到finishedChan<-struct{}{}后,并修改为commint.SeqNo,表示当前view中最后成功执行的消息的标号。
3、consensus/pbft/pbft-core.go
3.1 注释掉pbftMessage结构体
统一使用types.PbftMessage结构体,并在processEvent()中注释掉case *pbftMessage和case pbftMessageEvent,修改为case *types.PbftMessage
3.2 注释掉execDoneEvent分支
将该分支对应的操作移动到recvCommit()中产生execDoneEvent{}的地方,避免运行中的6、recvCommit()问题
3.3 修改hash显示问题
%x输出,用于logger.Debugf()、fmt.sprintf()...- 转换为common.hash类型,直接输出,用于
log.Info()...
主要涉及:common.StringToHash(digest)和common.BytesToHash(digest)
3.4 修改该文件中用到type.PbftMessage结构体定义,涉及prePrepare、prepare、commit
4、consensus/pbft/viewchange.go
4.1 添加ConvertMapToStruct()函数
将map类型转换为struct数组类型,主要用于NewView结构体的Xset成员变量,由原先的msgList改为XSet结构体,并修改相关部分,以解决rlp编码中没有map类型的decoder的问题,该函数如下:
func ConvertMapToStruct(msgList map[uint64]string) []*types.XSet {
var xset []*types.XSet
i := 0
for id, hash := range msgList {
xset[i] = &types.XSet{Seq: id, Hash: hash}
i++
}
return xset
}
4.2修改该文件中用到type.PbftMessage结构体定义,涉及ViewChange和NewView
5、core/types/pbft_messages.go
重新编写该文件,废除原先的protobuf。主要涉及PrePrepare、Prepare、Commit、Checkpoint、ViewChange、NewView,以解决rlp编解码问题。
5.1 结构体沿用原来定义,稍微变动
type PbftMessage struct {
// Types that are valid to be assigned to Payload:
// PrePrepare *PrePrepare //=> payload = 1
// Prepare *Prepare //=> payload = 2
// Commit *Commit //=> payload = 3
// Checkpoint *Checkpoint //=> payload = 4
// ViewChange *ViewChange //=> payload = 5
// NewView *NewView //=> payload = 6
Sender uint64 //=>add. --Agzs
PayloadCode uint64
Payload interface{}
}
const (
// PbftMessage payload
PrePrepareMsg = 0x01
PrepareMsg = 0x02
CommitMsg = 0x03
CheckpointMsg = 0x04
ViewChangeMsg = 0x05
NewViewMsg = 0x06
FetchBlockMsgMsg = 0x07
)
5.2 仿block,编写DecodeRLP()和EncodeRLP()
以实现ethereum/go-ethereum/rlp/decode.go中的Decoder接口,ethereum/go-ethereum/rlp/encode.go中的Encoder接口,解决rlp编码问题。
func (m *PbftMessage) DecodeRLP(s *rlp.Stream) error {
var pbftMsg pbftMessage
if err := s.Decode(&pbftMsg); err != nil {
return err
}
m.Sender = pbftMsg.Sender
m.PayloadCode = pbftMsg.PayloadCode
m.Payload = pbftMsg.Payload
return nil
}
// EncodeRLP serializes m into the PbftMessage RLP format.
func (m *PbftMessage) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, pbftMessage{
Sender: m.Sender,
PayloadCode: m.PayloadCode,
Payload: m.Payload,
})
}
5.3 payload接口转换为其他结构体
比如PrePrepare,其他消息类似:
func (m *PbftMessage) GetPrePrepare() *PrePrepare {
if x, ok := m.GetPayload().(*PrePrepare); ok && m.GetPayloadCode() == PrePrepareMsg {
return x
}
return nil
}
5.4 修改消息结构体成员变量的tag
比如PrePare,其他的类似
type Prepare struct {
View uint64 `json:"view" gencodec:"required"`
SequenceNumber uint64 `json:"sequenceNumber" gencodec:"required"`
BlockHash []byte `json:"blockHash" gencodec:"required"`
ReplicaId uint64 `json:"replicaId" gencodec:"required"`
}
6、eth/handler.go
6.1 修改handleMsg()中的分支
注释掉
case msg.Code == ConsensusMsg:
...
扩展为
case msg.Code == PrePrepareMsg:
...
case msg.Code == PrepareMsg:
...
case msg.Code == CommitMsg:
...
...
6.2 修改BroadcastMsg()
添加消息识别,即PeersWithoutMsg()函数,只向未发送过消息的peer发送消息。
func (pm *ProtocolManager) BroadcastMsg(msg *types.PbftMessage) {
/// TODO: May need to optimize it, only broadcast msg to peers without it. --Zhiguo
/// peers := pm.peers.PeersWithoutTx(hash)
//FIXME include this again: peers = peers[:int(math.Sqrt(float64(len(peers))))]
log.Info("pm.BroadcastMsg() start------------") //=>test. --Agzs
//=> add PeerWithoutMsg() start. --Agzs
hash := types.Hash(msg)
peers := pm.peers.PeersWithoutMsg(hash)
//=> add PeerWithoutMsg() end. --Agzs
for _, peer := range peers {
log.Info("peer broadcast msg", "peer", peer.id, "send msg's hash:", hash) //=>test. --Agzs
peer.SendMsg(msg)
}
log.Info("pm.BroadcastMsg() end------------") //=>test. --Agzs
log.Trace("Broadcast transaction", "hash", hash, "recipients", len(pm.peers.peers)) //=> peers -> pm.peers.peers --Agzs
}
7、eth/peer.go
7.1 修改peer结构体
仿block和transaction,在peer结构体中添加knownMsg成员变量,用于6中的消息识别。
相应的,需要编写MarkMsg()和func (ps *peerSet) PeersWithoutMsg(hash common.Hash) []*peer{}
7.2 修改SendMsg()
将之前的consensusMsg分解出来,单独发送相应的消息,以解决rlp中对接口类型成员变量无法解码的问题。各消息在读取后重新组装成types.PbftMessage,交PBFT.processEvent()进一步处理
func (p *peer) SendMsg(msg *types.PbftMessage) error {
log.Info("peer.SendMsg() start", "pbftMessageType", reflect.ValueOf(msg.GetPayload()).Type())
p.knownMsg.Add(types.Hash(msg)) //=> add for knowMsg. --Agzs
//=>return p2p.Send(p.rw, ConsensusMsg, msg)
//=> add --Agzs
if x, ok := msg.GetPayload().(*types.PrePrepare); ok {
return p2p.Send(p.rw, PrePrepareMsg, []interface{}{x, msg.Sender, msg.PayloadCode})
} else if x, ok := msg.GetPayload().(*types.Prepare); ok {
return p2p.Send(p.rw, PrepareMsg, []interface{}{x, msg.Sender, msg.PayloadCode})
} else if x, ok := msg.GetPayload().(*types.Commit); ok {
return p2p.Send(p.rw, CommitMsg, []interface{}{x, msg.Sender, msg.PayloadCode})
} else if x, ok := msg.GetPayload().(*types.Checkpoint); ok {
return p2p.Send(p.rw, CheckpointMsg, []interface{}{x, msg.Sender, msg.PayloadCode})
} else if x, ok := msg.GetPayload().(*types.ViewChange); ok {
return p2p.Send(p.rw, ViewChangeMsg, []interface{}{x, msg.Sender, msg.PayloadCode})
} else if x, ok := msg.GetPayload().(*types.NewView); ok {
return p2p.Send(p.rw, NewViewMsg, []interface{}{x, msg.Sender, msg.PayloadCode})
}
return fmt.Errorf("Invalid message: %v", msg)
}
8、eth/protocol.go
注释掉ConsensusMsg标识,添加其他六个标识,修改ProtocolLengths,和5、6、7一起组合,解决rlp编解码。
//=>var ProtocolLengths = []uint64{17, 8}
var ProtocolLengths = []uint64{23, 8} //=> +1, since add ConsensusMsg. --Agzs
// eth protocol message codes
const (
...
//=>ConsensusMsg = 0x11 /// for PBFT consensus --Zhiguo 04/10
PrePrepareMsg = 0x11 //=>add --Agzs
PrepareMsg = 0x12
CommitMsg = 0x13
CheckpointMsg = 0x14
ViewChangeMsg = 0x15
NewViewMsg = 0x16
)
定义各消息类型的结构体组装,用于从p2p中读取到的msg恢复数据,比如prePrepareData,其他类似:
type prePrepareData struct {
PrePrePare *types.PrePrepare
Sender uint64
PayloadCode uint64
}