github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/consensus/istanbul/ibft/core/roundchange.go (about)

     1  // Copyright 2017 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package core
    18  
    19  import (
    20  	"math/big"
    21  	"sync"
    22  
    23  	"github.com/kisexp/xdchain/common"
    24  	"github.com/kisexp/xdchain/consensus/istanbul"
    25  	istanbulcommon "github.com/kisexp/xdchain/consensus/istanbul/common"
    26  	ibfttypes "github.com/kisexp/xdchain/consensus/istanbul/ibft/types"
    27  )
    28  
    29  // sendNextRoundChange sends the ROUND CHANGE message with current round + 1
    30  func (c *core) sendNextRoundChange() {
    31  	cv := c.currentView()
    32  	c.sendRoundChange(new(big.Int).Add(cv.Round, common.Big1))
    33  }
    34  
    35  // sendRoundChange sends the ROUND CHANGE message with the given round
    36  func (c *core) sendRoundChange(round *big.Int) {
    37  	logger := c.logger.New("state", c.state)
    38  
    39  	cv := c.currentView()
    40  	if cv.Round.Cmp(round) >= 0 {
    41  		logger.Error("Cannot send out the round change", "current round", cv.Round, "target round", round)
    42  		return
    43  	}
    44  
    45  	c.catchUpRound(&istanbul.View{
    46  		// The round number we'd like to transfer to.
    47  		Round:    new(big.Int).Set(round),
    48  		Sequence: new(big.Int).Set(cv.Sequence),
    49  	})
    50  
    51  	// Now we have the new round number and sequence number
    52  	cv = c.currentView()
    53  	rc := &istanbul.Subject{
    54  		View:   cv,
    55  		Digest: common.Hash{},
    56  	}
    57  
    58  	payload, err := ibfttypes.Encode(rc)
    59  	if err != nil {
    60  		logger.Error("Failed to encode ROUND CHANGE", "rc", rc, "err", err)
    61  		return
    62  	}
    63  
    64  	c.broadcast(&ibfttypes.Message{
    65  		Code: ibfttypes.MsgRoundChange,
    66  		Msg:  payload,
    67  	})
    68  }
    69  
    70  func (c *core) handleRoundChange(msg *ibfttypes.Message, src istanbul.Validator) error {
    71  	logger := c.logger.New("state", c.state, "from", src.Address().Hex())
    72  
    73  	// Decode ROUND CHANGE message
    74  	var rc *istanbul.Subject
    75  	if err := msg.Decode(&rc); err != nil {
    76  		logger.Error("Failed to decode ROUND CHANGE", "err", err)
    77  		return istanbulcommon.ErrInvalidMessage
    78  	}
    79  
    80  	if err := c.checkMessage(ibfttypes.MsgRoundChange, rc.View); err != nil {
    81  		return err
    82  	}
    83  
    84  	cv := c.currentView()
    85  	roundView := rc.View
    86  
    87  	// Add the ROUND CHANGE message to its message set and return how many
    88  	// messages we've got with the same round number and sequence number.
    89  	num, err := c.roundChangeSet.Add(roundView.Round, msg)
    90  	if err != nil {
    91  		logger.Warn("Failed to add round change message", "from", src, "msg", msg, "err", err)
    92  		return err
    93  	}
    94  
    95  	// Once we received f+1 ROUND CHANGE messages, those messages form a weak certificate.
    96  	// If our round number is smaller than the certificate's round number, we would
    97  	// try to catch up the round number.
    98  	if c.waitingForRoundChange && num == c.valSet.F()+1 {
    99  		if cv.Round.Cmp(roundView.Round) < 0 {
   100  			c.sendRoundChange(roundView.Round)
   101  		}
   102  		return nil
   103  	} else if num == c.QuorumSize() && (c.waitingForRoundChange || cv.Round.Cmp(roundView.Round) < 0) {
   104  		// We've received 2f+1/Ceil(2N/3) ROUND CHANGE messages, start a new round immediately.
   105  		c.startNewRound(roundView.Round)
   106  		return nil
   107  	} else if cv.Round.Cmp(roundView.Round) < 0 {
   108  		// Only gossip the message with current round to other validators.
   109  		return istanbulcommon.ErrIgnored
   110  	}
   111  	return nil
   112  }
   113  
   114  // ----------------------------------------------------------------------------
   115  
   116  func newRoundChangeSet(valSet istanbul.ValidatorSet) *roundChangeSet {
   117  	return &roundChangeSet{
   118  		validatorSet: valSet,
   119  		roundChanges: make(map[uint64]*messageSet),
   120  		mu:           new(sync.Mutex),
   121  	}
   122  }
   123  
   124  type roundChangeSet struct {
   125  	validatorSet istanbul.ValidatorSet
   126  	roundChanges map[uint64]*messageSet
   127  	mu           *sync.Mutex
   128  }
   129  
   130  // Add adds the round and message into round change set
   131  func (rcs *roundChangeSet) Add(r *big.Int, msg *ibfttypes.Message) (int, error) {
   132  	rcs.mu.Lock()
   133  	defer rcs.mu.Unlock()
   134  
   135  	round := r.Uint64()
   136  	if rcs.roundChanges[round] == nil {
   137  		rcs.roundChanges[round] = newMessageSet(rcs.validatorSet)
   138  	}
   139  	err := rcs.roundChanges[round].Add(msg)
   140  	if err != nil {
   141  		return 0, err
   142  	}
   143  	return rcs.roundChanges[round].Size(), nil
   144  }
   145  
   146  // Clear deletes the messages with smaller round
   147  func (rcs *roundChangeSet) Clear(round *big.Int) {
   148  	rcs.mu.Lock()
   149  	defer rcs.mu.Unlock()
   150  
   151  	for k, rms := range rcs.roundChanges {
   152  		if len(rms.Values()) == 0 || k < round.Uint64() {
   153  			delete(rcs.roundChanges, k)
   154  		}
   155  	}
   156  }
   157  
   158  // MaxRound returns the max round which the number of messages is equal or larger than num
   159  func (rcs *roundChangeSet) MaxRound(num int) *big.Int {
   160  	rcs.mu.Lock()
   161  	defer rcs.mu.Unlock()
   162  
   163  	var maxRound *big.Int
   164  	for k, rms := range rcs.roundChanges {
   165  		if rms.Size() < num {
   166  			continue
   167  		}
   168  		r := big.NewInt(int64(k))
   169  		if maxRound == nil || maxRound.Cmp(r) < 0 {
   170  			maxRound = r
   171  		}
   172  	}
   173  	return maxRound
   174  }