Added preprepareSent variable to roundstate so that preprepare message is sent only once and updated justifyRoundChange to not return false, if prepared value or round does not match the values in prepared messages part of round change messages

This commit is contained in:
Jitendra Bhurat 2020-06-02 11:45:37 -04:00
parent 614e3d5b94
commit 1d8a952fa5
5 changed files with 26 additions and 20 deletions

View File

@ -47,7 +47,7 @@ func newBlockChain(n int) (*core.BlockChain, *backend) {
// Use the first key as private key
b, _ := New(config, nodeKeys[0], memDB).(*backend)
// set the qibft consensus enabled to true
b.qibftConsensusEnabled = true
b.qibftConsensusEnabled = false
genesis.MustCommit(memDB)
blockchain, err := core.NewBlockChain(memDB, nil, genesis.Config, b, vm.Config{}, nil)
if err != nil {

View File

@ -29,10 +29,7 @@ func (c *core) sendPreprepare(request *Request) {
if rcMessages == nil {
rcMessages = newMessageSet(c.valSet)
}
preparedMessages := request.PrepareMessages
if preparedMessages == nil {
preparedMessages = newMessageSet(c.valSet)
}
// If I'm the proposer and I have the same sequence with the proposal
if c.current.Sequence().Cmp(request.Proposal.Number()) == 0 && c.IsProposer() {
curView := c.currentView()
@ -40,7 +37,7 @@ func (c *core) sendPreprepare(request *Request) {
View: curView,
Proposal: request.Proposal,
RCMessages: rcMessages,
PreparedMessages: preparedMessages,
PreparedMessages: request.PrepareMessages,
})
if err != nil {
logger.Error("Failed to encode", "view", curView)
@ -50,6 +47,8 @@ func (c *core) sendPreprepare(request *Request) {
Code: msgPreprepare,
Msg: preprepare,
})
// Set the preprepareSent to the current round
c.current.preprepareSent = curView.Round
}
}

View File

@ -126,16 +126,23 @@ func (c *core) handleRoundChange(msg *message, src istanbul.Validator) error {
c.newRoundChangeTimer()
c.sendRoundChange(newRound)
} else if currentRoundMessages == c.QuorumSize() && c.IsProposer() && c.justifyRoundChange(cv.Round) {
} else if currentRoundMessages >= c.QuorumSize() && c.IsProposer() && c.justifyRoundChange(cv.Round) && c.current.preprepareSent.Cmp(cv.Round) < 0 {
preparedRound, proposal := c.highestPrepared(cv.Round)
if proposal == nil {
proposal = c.current.pendingRequest.Proposal
}
var preparedMessages *messageSet
if preparedRound == nil {
preparedMessages = newMessageSet(c.valSet)
} else {
preparedMessages = c.roundChangeSet.preparedMessages[preparedRound.Uint64()]
}
r := &Request{
Proposal: proposal,
RCMessages: c.roundChangeSet.roundChanges[cv.Round.Uint64()],
PrepareMessages: c.roundChangeSet.preparedMessages[preparedRound.Uint64()],
PrepareMessages: preparedMessages,
}
c.sendPreprepare(r)
@ -153,19 +160,19 @@ func (c *core) justifyRoundChange(round *big.Int) bool {
// Check if the block in each prepared message is the one that is being proposed
// To handle the case where a byzantine node can send an empty prepared block, check atleast Quorum of prepared blocks match the condition and not all
i := 0
for addr, msg := range c.roundChangeSet.preparedMessages[pr.Uint64()].messages {
for addr, msg := range c.roundChangeSet.preparedMessages[round.Uint64()].messages {
var prepare *Subject
if err := msg.Decode(&prepare); err != nil {
c.logger.Error("Failed to decode Prepared Message", "err", err)
return false
continue
}
if prepare.Digest.Hash() != pv.Hash() {
c.logger.Error("Highest Prepared Block does not match the Proposal", "Address", addr)
return false
continue
}
if prepare.View.Round.Uint64() != pr.Uint64() {
c.logger.Error("Round in Prepared Block does not match the Highest Prepared Round", "Address", addr)
return false
continue
}
i++
if i == c.QuorumSize() {
@ -214,8 +221,6 @@ func (rcs *roundChangeSet) Add(r *big.Int, msg *message, preparedRound *big.Int,
return err
}
rcs.preparedMessages[round] = preparedMessages
if rcs.preparedRounds == nil {
rcs.preparedRounds = make(map[uint64]*big.Int)
}
@ -223,12 +228,10 @@ func (rcs *roundChangeSet) Add(r *big.Int, msg *message, preparedRound *big.Int,
rcs.preparedBlocks = make(map[uint64]istanbul.Proposal)
}
if rcs.preparedRounds[round] == nil {
rcs.preparedRounds[round] = preparedRound
rcs.preparedBlocks[round] = preparedBlock
} else if preparedRound.Cmp(rcs.preparedRounds[round]) > 0 {
if rcs.preparedRounds[round] == nil || preparedRound.Cmp(rcs.preparedRounds[round]) > 0 {
rcs.preparedRounds[round] = preparedRound
rcs.preparedBlocks[round] = preparedBlock
rcs.preparedMessages[round] = preparedMessages
}
return nil

View File

@ -47,7 +47,7 @@ func TestRoundChangeSet(t *testing.T) {
Msg: m,
Address: v.Address(),
}
rc.Add(view.Round, msg, r.PreparedRound, r.PreparedBlock)
rc.Add(view.Round, msg, r.PreparedRound, r.PreparedBlock, newMessageSet(vset))
if rc.roundChanges[view.Round.Uint64()].Size() != i+1 {
t.Errorf("the size of round change messages mismatch: have %v, want %v", rc.roundChanges[view.Round.Uint64()].Size(), i+1)
}
@ -60,7 +60,7 @@ func TestRoundChangeSet(t *testing.T) {
Msg: m,
Address: v.Address(),
}
rc.Add(view.Round, msg, r.PreparedRound, r.PreparedBlock)
rc.Add(view.Round, msg, r.PreparedRound, r.PreparedBlock, newMessageSet(vset))
if rc.roundChanges[view.Round.Uint64()].Size() != vset.Size() {
t.Errorf("the size of round change messages mismatch: have %v, want %v", rc.roundChanges[view.Round.Uint64()].Size(), vset.Size())
}

View File

@ -38,6 +38,7 @@ func newRoundState(view *View, validatorSet istanbul.ValidatorSet, preprepare *P
pendingRequest: pendingRequest,
hasBadProposal: hasBadProposal,
rcMsgSentInRound: big.NewInt(0),
preprepareSent: big.NewInt(0),
}
}
@ -55,6 +56,9 @@ type roundState struct {
mu *sync.RWMutex
hasBadProposal func(hash common.Hash) bool
// Keep track of preprepare sent messages
preprepareSent *big.Int
}
func (s *roundState) GetPrepareOrCommitSize() int {