mirror of https://github.com/poanetwork/quorum.git
185 lines
5.4 KiB
Go
185 lines
5.4 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 := &roundChange{
|
|
Round: new(big.Int).Set(cv.Round),
|
|
Sequence: new(big.Int).Set(cv.Sequence),
|
|
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 *roundChange
|
|
if err := msg.Decode(&rc); err != nil {
|
|
logger.Error("Failed to decode ROUND CHANGE", "err", err)
|
|
return errInvalidMessage
|
|
}
|
|
|
|
cv := c.currentView()
|
|
|
|
// We never accept ROUND CHANGE message with different sequence number
|
|
if rc.Sequence.Cmp(cv.Sequence) != 0 {
|
|
logger.Warn("Inconsistent sequence number", "expected", cv.Sequence, "got", rc.Sequence)
|
|
return errInvalidMessage
|
|
}
|
|
|
|
// We never accept ROUND CHANGE message with smaller round number
|
|
if rc.Round.Cmp(cv.Round) < 0 {
|
|
logger.Warn("Old round change", "from", src, "expected", cv.Round, "got", rc.Round)
|
|
return errOldMessage
|
|
}
|
|
|
|
// 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(rc.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(rc.Round) < 0 {
|
|
c.sendRoundChange(rc.Round)
|
|
}
|
|
return nil
|
|
} else if num == int(2*c.valSet.F()+1) && (c.waitingForRoundChange || cv.Round.Cmp(rc.Round) < 0) {
|
|
// We've received 2f+1 ROUND CHANGE messages, start a new round immediately.
|
|
c.startNewRound(&istanbul.View{
|
|
Round: new(big.Int).Set(rc.Round),
|
|
Sequence: new(big.Int).Set(rc.Sequence),
|
|
}, nil, common.Address{}, true)
|
|
return nil
|
|
} else if cv.Round.Cmp(rc.Round) < 0 {
|
|
// We consider the message with larger round as future messages and not
|
|
// gossip it to other validators.
|
|
return errFutureMessage
|
|
}
|
|
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
|
|
}
|