quorum/consensus/istanbul/core/roundchange.go

173 lines
4.9 KiB
Go

// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package core
import (
"math/big"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/istanbul"
)
// sendNextRoundChange sends the ROUND CHANGE message with current round + 1
func (c *core) sendNextRoundChange() {
cv := c.currentView()
c.sendRoundChange(new(big.Int).Add(cv.Round, common.Big1))
}
// sendRoundChange sends the ROUND CHANGE message with the given round
func (c *core) sendRoundChange(round *big.Int) {
logger := c.logger.New("state", c.state)
cv := c.currentView()
if cv.Round.Cmp(round) >= 0 {
logger.Error("Cannot send out the round change", "current round", cv.Round, "target round", round)
return
}
c.catchUpRound(&istanbul.View{
// The round number we'd like to transfer to.
Round: new(big.Int).Set(round),
Sequence: new(big.Int).Set(cv.Sequence),
})
// Now we have the new round number and sequence number
cv = c.currentView()
rc := &istanbul.Subject{
View: cv,
Digest: common.Hash{},
}
payload, err := Encode(rc)
if err != nil {
logger.Error("Failed to encode ROUND CHANGE", "rc", rc, "err", err)
return
}
c.broadcast(&message{
Code: msgRoundChange,
Msg: payload,
})
}
func (c *core) handleRoundChange(msg *message, src istanbul.Validator) error {
logger := c.logger.New("state", c.state, "from", src.Address().Hex())
// Decode ROUND CHANGE message
var rc *istanbul.Subject
if err := msg.Decode(&rc); err != nil {
logger.Error("Failed to decode ROUND CHANGE", "err", err)
return errInvalidMessage
}
if err := c.checkMessage(msgRoundChange, rc.View); err != nil {
return err
}
cv := c.currentView()
roundView := rc.View
// Add the ROUND CHANGE message to its message set and return how many
// messages we've got with the same round number and sequence number.
num, err := c.roundChangeSet.Add(roundView.Round, msg)
if err != nil {
logger.Warn("Failed to add round change message", "from", src, "msg", msg, "err", err)
return err
}
// Once we received f+1 ROUND CHANGE messages, those messages form a weak certificate.
// If our round number is smaller than the certificate's round number, we would
// try to catch up the round number.
if c.waitingForRoundChange && num == int(c.valSet.F()+1) {
if cv.Round.Cmp(roundView.Round) < 0 {
c.sendRoundChange(roundView.Round)
}
return nil
} else if num == c.QuorumSize() && (c.waitingForRoundChange || cv.Round.Cmp(roundView.Round) < 0) {
// We've received 2f+1/Ceil(2N/3) ROUND CHANGE messages, start a new round immediately.
c.startNewRound(roundView.Round)
return nil
} else if cv.Round.Cmp(roundView.Round) < 0 {
// Only gossip the message with current round to other validators.
return errIgnored
}
return nil
}
// ----------------------------------------------------------------------------
func newRoundChangeSet(valSet istanbul.ValidatorSet) *roundChangeSet {
return &roundChangeSet{
validatorSet: valSet,
roundChanges: make(map[uint64]*messageSet),
mu: new(sync.Mutex),
}
}
type roundChangeSet struct {
validatorSet istanbul.ValidatorSet
roundChanges map[uint64]*messageSet
mu *sync.Mutex
}
// Add adds the round and message into round change set
func (rcs *roundChangeSet) Add(r *big.Int, msg *message) (int, error) {
rcs.mu.Lock()
defer rcs.mu.Unlock()
round := r.Uint64()
if rcs.roundChanges[round] == nil {
rcs.roundChanges[round] = newMessageSet(rcs.validatorSet)
}
err := rcs.roundChanges[round].Add(msg)
if err != nil {
return 0, err
}
return rcs.roundChanges[round].Size(), nil
}
// Clear deletes the messages with smaller round
func (rcs *roundChangeSet) Clear(round *big.Int) {
rcs.mu.Lock()
defer rcs.mu.Unlock()
for k, rms := range rcs.roundChanges {
if len(rms.Values()) == 0 || k < round.Uint64() {
delete(rcs.roundChanges, k)
}
}
}
// MaxRound returns the max round which the number of messages is equal or larger than num
func (rcs *roundChangeSet) MaxRound(num int) *big.Int {
rcs.mu.Lock()
defer rcs.mu.Unlock()
var maxRound *big.Int
for k, rms := range rcs.roundChanges {
if rms.Size() < num {
continue
}
r := big.NewInt(int64(k))
if maxRound == nil || maxRound.Cmp(r) < 0 {
maxRound = r
}
}
return maxRound
}