github.com/klaytn/klaytn@v1.12.1/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  // sendNextRoundChange sends the ROUND CHANGE message with current round + 1
    32  func (c *core) sendNextRoundChange(loc string) {
    33  	if c.backend.NodeType() != common.CONSENSUSNODE {
    34  		return
    35  	}
    36  	logger.Warn("[RC] sendNextRoundChange happened", "where", loc)
    37  	c.sendRoundChange(new(big.Int).Add(c.currentView().Round, common.Big1))
    38  }
    39  
    40  // sendRoundChange sends the ROUND CHANGE message with the given round
    41  func (c *core) sendRoundChange(round *big.Int) {
    42  	logger := c.logger.NewWith("state", c.state)
    43  
    44  	cv := c.currentView()
    45  	if cv.Round.Cmp(round) >= 0 {
    46  		logger.Warn("[RC] Skip sending out the round change message", "current round", cv.Round,
    47  			"target round", round)
    48  		return
    49  	}
    50  
    51  	logger.Warn("[RC] Prepare messages received before catchUpRound",
    52  		"len(prepares)", c.current.Prepares.Size(), "messages", c.current.Prepares.GetMessages())
    53  	logger.Warn("[RC] Commit messages received before catchUpRound",
    54  		"len(commits)", c.current.Commits.Size(), "messages", c.current.Commits.GetMessages())
    55  
    56  	c.catchUpRound(&istanbul.View{
    57  		// The round number we'd like to transfer to.
    58  		Round:    new(big.Int).Set(round),
    59  		Sequence: new(big.Int).Set(cv.Sequence),
    60  	})
    61  
    62  	lastProposal, _ := c.backend.LastProposal()
    63  
    64  	// Now we have the new round number and sequence number
    65  	cv = c.currentView()
    66  	rc := &istanbul.Subject{
    67  		View:     cv,
    68  		Digest:   common.Hash{},
    69  		PrevHash: lastProposal.Hash(),
    70  	}
    71  
    72  	payload, err := Encode(rc)
    73  	if err != nil {
    74  		logger.Error("Failed to encode ROUND CHANGE", "rc", rc, "err", err)
    75  		return
    76  	}
    77  
    78  	c.broadcast(&message{
    79  		Hash: rc.PrevHash,
    80  		Code: msgRoundChange,
    81  		Msg:  payload,
    82  	})
    83  }
    84  
    85  func (c *core) handleRoundChange(msg *message, src istanbul.Validator) error {
    86  	logger := c.logger.NewWith("state", c.state, "from", src.Address().Hex())
    87  
    88  	// Decode ROUND CHANGE message
    89  	var rc *istanbul.Subject
    90  	if err := msg.Decode(&rc); err != nil {
    91  		logger.Error("Failed to decode message", "code", msg.Code, "err", err)
    92  		return errInvalidMessage
    93  	}
    94  
    95  	// TODO-Klaytn-Istanbul: establish round change messaging policy and then apply it
    96  	//if !c.valSet.CheckInSubList(msg.Hash, rc.View, src.Address()) {
    97  	//	return errNotFromCommittee
    98  	//}
    99  
   100  	if err := c.checkMessage(msgRoundChange, rc.View); err != nil {
   101  		return err
   102  	}
   103  
   104  	cv := c.currentView()
   105  	roundView := rc.View
   106  
   107  	// Add the ROUND CHANGE message to its message set and return how many
   108  	// messages we've got with the same round number and sequence number.
   109  	num, err := c.roundChangeSet.Add(roundView.Round, msg)
   110  	if err != nil {
   111  		logger.Warn("Failed to add round change message", "from", src, "msg", msg, "err", err)
   112  		return err
   113  	}
   114  
   115  	var numCatchUp, numStartNewRound int
   116  	n := RequiredMessageCount(c.valSet)
   117  	f := int(c.valSet.F())
   118  	// N ROUND CHANGE messages can start new round.
   119  	numStartNewRound = n
   120  	// F + 1 ROUND CHANGE messages can start catch up the round.
   121  	numCatchUp = f + 1
   122  
   123  	if num == numStartNewRound && (c.waitingForRoundChange || cv.Round.Cmp(roundView.Round) < 0) {
   124  		// We've received enough ROUND CHANGE messages, start a new round immediately.
   125  		logger.Warn("[RC] Prepare messages received before startNewRound", "round", cv.Round.String(),
   126  			"len(prepares)", c.current.Prepares.Size(), "messages", c.current.Prepares.GetMessages())
   127  		logger.Warn("[RC] Commit messages received before startNewRound", "round", cv.Round.String(),
   128  			"len(commits)", c.current.Commits.Size(), "messages", c.current.Commits.GetMessages())
   129  		logger.Warn("[RC] Received 2f+1 Round Change Messages. Starting new round",
   130  			"currentRound", cv.Round.String(), "newRound", roundView.Round.String())
   131  		c.startNewRound(roundView.Round)
   132  		return nil
   133  	} else if c.waitingForRoundChange && num == numCatchUp {
   134  		// Once we received enough ROUND CHANGE messages, those messages form a weak certificate.
   135  		// If our round number is smaller than the certificate's round number, we would
   136  		// try to catch up the round number.
   137  		if cv.Round.Cmp(roundView.Round) < 0 {
   138  			logger.Warn("[RC] Send round change because we have f+1 round change messages",
   139  				"currentRound", cv.Round.String(), "newRound", roundView.Round.String())
   140  			c.sendRoundChange(roundView.Round)
   141  		}
   142  		return nil
   143  	} else if cv.Round.Cmp(roundView.Round) < 0 {
   144  		// Only gossip the message with current round to other validators.
   145  		logger.Warn("[RC] Received round is bigger but not enough number of messages. Message ignored",
   146  			"currentRound", cv.Round.String(), "newRound", roundView.Round.String(), "numRC", num)
   147  		return errIgnored
   148  	}
   149  	return nil
   150  }
   151  
   152  // ----------------------------------------------------------------------------
   153  
   154  func newRoundChangeSet(valSet istanbul.ValidatorSet) *roundChangeSet {
   155  	return &roundChangeSet{
   156  		validatorSet: valSet,
   157  		roundChanges: make(map[uint64]*messageSet),
   158  		mu:           new(sync.Mutex),
   159  	}
   160  }
   161  
   162  type roundChangeSet struct {
   163  	validatorSet istanbul.ValidatorSet
   164  	roundChanges map[uint64]*messageSet
   165  	mu           *sync.Mutex
   166  }
   167  
   168  // Add adds the round and message into round change set
   169  func (rcs *roundChangeSet) Add(r *big.Int, msg *message) (int, error) {
   170  	rcs.mu.Lock()
   171  	defer rcs.mu.Unlock()
   172  
   173  	round := r.Uint64()
   174  	if rcs.roundChanges[round] == nil {
   175  		rcs.roundChanges[round] = newMessageSet(rcs.validatorSet)
   176  	}
   177  	err := rcs.roundChanges[round].Add(msg)
   178  	if err != nil {
   179  		return 0, err
   180  	}
   181  	return rcs.roundChanges[round].Size(), nil
   182  }
   183  
   184  // Clear deletes the messages with smaller round
   185  func (rcs *roundChangeSet) Clear(round *big.Int) {
   186  	rcs.mu.Lock()
   187  	defer rcs.mu.Unlock()
   188  
   189  	for k, rms := range rcs.roundChanges {
   190  		if len(rms.Values()) == 0 || k < round.Uint64() {
   191  			delete(rcs.roundChanges, k)
   192  		}
   193  	}
   194  }
   195  
   196  // MaxRound returns the max round which the number of messages is equal or larger than num
   197  func (rcs *roundChangeSet) MaxRound(num int) *big.Int {
   198  	rcs.mu.Lock()
   199  	defer rcs.mu.Unlock()
   200  
   201  	var maxRound *big.Int
   202  	for k, rms := range rcs.roundChanges {
   203  		if rms.Size() < num {
   204  			continue
   205  		}
   206  		r := big.NewInt(int64(k))
   207  		if maxRound == nil || maxRound.Cmp(r) < 0 {
   208  			maxRound = r
   209  		}
   210  	}
   211  	return maxRound
   212  }