github.com/ConsenSys/Quorum@v20.10.0+incompatible/consensus/istanbul/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/ethereum/go-ethereum/common"
    24  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    25  )
    26  
    27  // sendNextRoundChange sends the ROUND CHANGE message with current round + 1
    28  func (c *core) sendNextRoundChange() {
    29  	cv := c.currentView()
    30  	c.sendRoundChange(new(big.Int).Add(cv.Round, common.Big1))
    31  }
    32  
    33  // sendRoundChange sends the ROUND CHANGE message with the given round
    34  func (c *core) sendRoundChange(round *big.Int) {
    35  	logger := c.logger.New("state", c.state)
    36  
    37  	cv := c.currentView()
    38  	if cv.Round.Cmp(round) >= 0 {
    39  		logger.Error("Cannot send out the round change", "current round", cv.Round, "target round", round)
    40  		return
    41  	}
    42  
    43  	c.catchUpRound(&istanbul.View{
    44  		// The round number we'd like to transfer to.
    45  		Round:    new(big.Int).Set(round),
    46  		Sequence: new(big.Int).Set(cv.Sequence),
    47  	})
    48  
    49  	// Now we have the new round number and sequence number
    50  	cv = c.currentView()
    51  	rc := &istanbul.Subject{
    52  		View:   cv,
    53  		Digest: common.Hash{},
    54  	}
    55  
    56  	payload, err := Encode(rc)
    57  	if err != nil {
    58  		logger.Error("Failed to encode ROUND CHANGE", "rc", rc, "err", err)
    59  		return
    60  	}
    61  
    62  	c.broadcast(&message{
    63  		Code: msgRoundChange,
    64  		Msg:  payload,
    65  	})
    66  }
    67  
    68  func (c *core) handleRoundChange(msg *message, src istanbul.Validator) error {
    69  	logger := c.logger.New("state", c.state, "from", src.Address().Hex())
    70  
    71  	// Decode ROUND CHANGE message
    72  	var rc *istanbul.Subject
    73  	if err := msg.Decode(&rc); err != nil {
    74  		logger.Error("Failed to decode ROUND CHANGE", "err", err)
    75  		return errInvalidMessage
    76  	}
    77  
    78  	if err := c.checkMessage(msgRoundChange, rc.View); err != nil {
    79  		return err
    80  	}
    81  
    82  	cv := c.currentView()
    83  	roundView := rc.View
    84  
    85  	// Add the ROUND CHANGE message to its message set and return how many
    86  	// messages we've got with the same round number and sequence number.
    87  	num, err := c.roundChangeSet.Add(roundView.Round, msg)
    88  	if err != nil {
    89  		logger.Warn("Failed to add round change message", "from", src, "msg", msg, "err", err)
    90  		return err
    91  	}
    92  
    93  	// Once we received f+1 ROUND CHANGE messages, those messages form a weak certificate.
    94  	// If our round number is smaller than the certificate's round number, we would
    95  	// try to catch up the round number.
    96  	if c.waitingForRoundChange && num == c.valSet.F()+1 {
    97  		if cv.Round.Cmp(roundView.Round) < 0 {
    98  			c.sendRoundChange(roundView.Round)
    99  		}
   100  		return nil
   101  	} else if num == c.QuorumSize() && (c.waitingForRoundChange || cv.Round.Cmp(roundView.Round) < 0) {
   102  		// We've received 2f+1/Ceil(2N/3) ROUND CHANGE messages, start a new round immediately.
   103  		c.startNewRound(roundView.Round)
   104  		return nil
   105  	} else if cv.Round.Cmp(roundView.Round) < 0 {
   106  		// Only gossip the message with current round to other validators.
   107  		return errIgnored
   108  	}
   109  	return nil
   110  }
   111  
   112  // ----------------------------------------------------------------------------
   113  
   114  func newRoundChangeSet(valSet istanbul.ValidatorSet) *roundChangeSet {
   115  	return &roundChangeSet{
   116  		validatorSet: valSet,
   117  		roundChanges: make(map[uint64]*messageSet),
   118  		mu:           new(sync.Mutex),
   119  	}
   120  }
   121  
   122  type roundChangeSet struct {
   123  	validatorSet istanbul.ValidatorSet
   124  	roundChanges map[uint64]*messageSet
   125  	mu           *sync.Mutex
   126  }
   127  
   128  // Add adds the round and message into round change set
   129  func (rcs *roundChangeSet) Add(r *big.Int, msg *message) (int, error) {
   130  	rcs.mu.Lock()
   131  	defer rcs.mu.Unlock()
   132  
   133  	round := r.Uint64()
   134  	if rcs.roundChanges[round] == nil {
   135  		rcs.roundChanges[round] = newMessageSet(rcs.validatorSet)
   136  	}
   137  	err := rcs.roundChanges[round].Add(msg)
   138  	if err != nil {
   139  		return 0, err
   140  	}
   141  	return rcs.roundChanges[round].Size(), nil
   142  }
   143  
   144  // Clear deletes the messages with smaller round
   145  func (rcs *roundChangeSet) Clear(round *big.Int) {
   146  	rcs.mu.Lock()
   147  	defer rcs.mu.Unlock()
   148  
   149  	for k, rms := range rcs.roundChanges {
   150  		if len(rms.Values()) == 0 || k < round.Uint64() {
   151  			delete(rcs.roundChanges, k)
   152  		}
   153  	}
   154  }
   155  
   156  // MaxRound returns the max round which the number of messages is equal or larger than num
   157  func (rcs *roundChangeSet) MaxRound(num int) *big.Int {
   158  	rcs.mu.Lock()
   159  	defer rcs.mu.Unlock()
   160  
   161  	var maxRound *big.Int
   162  	for k, rms := range rcs.roundChanges {
   163  		if rms.Size() < num {
   164  			continue
   165  		}
   166  		r := big.NewInt(int64(k))
   167  		if maxRound == nil || maxRound.Cmp(r) < 0 {
   168  			maxRound = r
   169  		}
   170  	}
   171  	return maxRound
   172  }