github.com/klaytn/klaytn@v1.10.2/consensus/istanbul/core/roundchange.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2017 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from quorum/consensus/istanbul/core/roundchange.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package core
    22  
    23  import (
    24  	"math/big"
    25  	"sync"
    26  
    27  	"github.com/klaytn/klaytn/common"
    28  	"github.com/klaytn/klaytn/consensus/istanbul"
    29  )
    30  
    31  const (
    32  	// If the number of validators is 6 or less, the chain could be forked by round change if the required minimum consensus message is "2f+1".
    33  	// So, the exceptional case such as number of validator is 6, gather more messages than "2f+1". See requiredMessageCount for more specific information.
    34  	exceptionalValidatorsNumber = 6
    35  )
    36  
    37  // sendNextRoundChange sends the ROUND CHANGE message with current round + 1
    38  func (c *core) sendNextRoundChange(loc string) {
    39  	if c.backend.NodeType() != common.CONSENSUSNODE {
    40  		return
    41  	}
    42  	logger.Warn("[RC] sendNextRoundChange happened", "where", loc)
    43  	c.sendRoundChange(new(big.Int).Add(c.currentView().Round, common.Big1))
    44  }
    45  
    46  // sendRoundChange sends the ROUND CHANGE message with the given round
    47  func (c *core) sendRoundChange(round *big.Int) {
    48  	logger := c.logger.NewWith("state", c.state)
    49  
    50  	cv := c.currentView()
    51  	if cv.Round.Cmp(round) >= 0 {
    52  		logger.Warn("[RC] Skip sending out the round change message", "current round", cv.Round,
    53  			"target round", round)
    54  		return
    55  	}
    56  
    57  	logger.Warn("[RC] Prepare messages received before catchUpRound",
    58  		"len(prepares)", c.current.Prepares.Size(), "messages", c.current.Prepares.GetMessages())
    59  	logger.Warn("[RC] Commit messages received before catchUpRound",
    60  		"len(commits)", c.current.Commits.Size(), "messages", c.current.Commits.GetMessages())
    61  
    62  	c.catchUpRound(&istanbul.View{
    63  		// The round number we'd like to transfer to.
    64  		Round:    new(big.Int).Set(round),
    65  		Sequence: new(big.Int).Set(cv.Sequence),
    66  	})
    67  
    68  	lastProposal, _ := c.backend.LastProposal()
    69  
    70  	// Now we have the new round number and sequence number
    71  	cv = c.currentView()
    72  	rc := &istanbul.Subject{
    73  		View:     cv,
    74  		Digest:   common.Hash{},
    75  		PrevHash: lastProposal.Hash(),
    76  	}
    77  
    78  	payload, err := Encode(rc)
    79  	if err != nil {
    80  		logger.Error("Failed to encode ROUND CHANGE", "rc", rc, "err", err)
    81  		return
    82  	}
    83  
    84  	c.broadcast(&message{
    85  		Hash: rc.PrevHash,
    86  		Code: msgRoundChange,
    87  		Msg:  payload,
    88  	})
    89  }
    90  
    91  func (c *core) handleRoundChange(msg *message, src istanbul.Validator) error {
    92  	logger := c.logger.NewWith("state", c.state, "from", src.Address().Hex())
    93  
    94  	// Decode ROUND CHANGE message
    95  	var rc *istanbul.Subject
    96  	if err := msg.Decode(&rc); err != nil {
    97  		logger.Error("Failed to decode message", "code", msg.Code, "err", err)
    98  		return errInvalidMessage
    99  	}
   100  
   101  	// TODO-Klaytn-Istanbul: establish round change messaging policy and then apply it
   102  	//if !c.valSet.CheckInSubList(msg.Hash, rc.View, src.Address()) {
   103  	//	return errNotFromCommittee
   104  	//}
   105  
   106  	if err := c.checkMessage(msgRoundChange, rc.View); err != nil {
   107  		return err
   108  	}
   109  
   110  	cv := c.currentView()
   111  	roundView := rc.View
   112  
   113  	// Add the ROUND CHANGE message to its message set and return how many
   114  	// messages we've got with the same round number and sequence number.
   115  	num, err := c.roundChangeSet.Add(roundView.Round, msg)
   116  	if err != nil {
   117  		logger.Warn("Failed to add round change message", "from", src, "msg", msg, "err", err)
   118  		return err
   119  	}
   120  
   121  	var numCatchUp, numStartNewRound int
   122  	if c.valSet.Size() <= exceptionalValidatorsNumber {
   123  		n := requiredMessageCount(c.valSet)
   124  		// N ROUND CHANGE messages can start new round.
   125  		numStartNewRound = n
   126  		// N - 1 ROUND CHANGE messages can catch up the round.
   127  		numCatchUp = n - 1
   128  	} else {
   129  		f := int(c.valSet.F())
   130  		// 2*F + 1 ROUND CHANGE messages can start new round.
   131  		numStartNewRound = 2*f + 1
   132  		// F + 1 ROUND CHANGE messages can start catch up the round.
   133  		numCatchUp = f + 1
   134  	}
   135  
   136  	if num == numStartNewRound && (c.waitingForRoundChange || cv.Round.Cmp(roundView.Round) < 0) {
   137  		// We've received enough ROUND CHANGE messages, start a new round immediately.
   138  		logger.Warn("[RC] Prepare messages received before startNewRound", "round", cv.Round.String(),
   139  			"len(prepares)", c.current.Prepares.Size(), "messages", c.current.Prepares.GetMessages())
   140  		logger.Warn("[RC] Commit messages received before startNewRound", "round", cv.Round.String(),
   141  			"len(commits)", c.current.Commits.Size(), "messages", c.current.Commits.GetMessages())
   142  		logger.Warn("[RC] Received 2f+1 Round Change Messages. Starting new round",
   143  			"currentRound", cv.Round.String(), "newRound", roundView.Round.String())
   144  		c.startNewRound(roundView.Round)
   145  		return nil
   146  	} else if c.waitingForRoundChange && num == numCatchUp {
   147  		// Once we received enough ROUND CHANGE messages, those messages form a weak certificate.
   148  		// If our round number is smaller than the certificate's round number, we would
   149  		// try to catch up the round number.
   150  		if cv.Round.Cmp(roundView.Round) < 0 {
   151  			logger.Warn("[RC] Send round change because we have f+1 round change messages",
   152  				"currentRound", cv.Round.String(), "newRound", roundView.Round.String())
   153  			c.sendRoundChange(roundView.Round)
   154  		}
   155  		return nil
   156  	} else if cv.Round.Cmp(roundView.Round) < 0 {
   157  		// Only gossip the message with current round to other validators.
   158  		logger.Warn("[RC] Received round is bigger but not enough number of messages. Message ignored",
   159  			"currentRound", cv.Round.String(), "newRound", roundView.Round.String(), "numRC", num)
   160  		return errIgnored
   161  	}
   162  	return nil
   163  }
   164  
   165  // ----------------------------------------------------------------------------
   166  
   167  func newRoundChangeSet(valSet istanbul.ValidatorSet) *roundChangeSet {
   168  	return &roundChangeSet{
   169  		validatorSet: valSet,
   170  		roundChanges: make(map[uint64]*messageSet),
   171  		mu:           new(sync.Mutex),
   172  	}
   173  }
   174  
   175  type roundChangeSet struct {
   176  	validatorSet istanbul.ValidatorSet
   177  	roundChanges map[uint64]*messageSet
   178  	mu           *sync.Mutex
   179  }
   180  
   181  // Add adds the round and message into round change set
   182  func (rcs *roundChangeSet) Add(r *big.Int, msg *message) (int, error) {
   183  	rcs.mu.Lock()
   184  	defer rcs.mu.Unlock()
   185  
   186  	round := r.Uint64()
   187  	if rcs.roundChanges[round] == nil {
   188  		rcs.roundChanges[round] = newMessageSet(rcs.validatorSet)
   189  	}
   190  	err := rcs.roundChanges[round].Add(msg)
   191  	if err != nil {
   192  		return 0, err
   193  	}
   194  	return rcs.roundChanges[round].Size(), nil
   195  }
   196  
   197  // Clear deletes the messages with smaller round
   198  func (rcs *roundChangeSet) Clear(round *big.Int) {
   199  	rcs.mu.Lock()
   200  	defer rcs.mu.Unlock()
   201  
   202  	for k, rms := range rcs.roundChanges {
   203  		if len(rms.Values()) == 0 || k < round.Uint64() {
   204  			delete(rcs.roundChanges, k)
   205  		}
   206  	}
   207  }
   208  
   209  // MaxRound returns the max round which the number of messages is equal or larger than num
   210  func (rcs *roundChangeSet) MaxRound(num int) *big.Int {
   211  	rcs.mu.Lock()
   212  	defer rcs.mu.Unlock()
   213  
   214  	var maxRound *big.Int
   215  	for k, rms := range rcs.roundChanges {
   216  		if rms.Size() < num {
   217  			continue
   218  		}
   219  		r := big.NewInt(int64(k))
   220  		if maxRound == nil || maxRound.Cmp(r) < 0 {
   221  			maxRound = r
   222  		}
   223  	}
   224  	return maxRound
   225  }