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 *pbftMessagecase 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结构体定义,涉及ViewChangeNewView

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
}