github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/consensus/istanbul/qbft/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  	"sort"
    22  	"sync"
    23  
    24  	"github.com/kisexp/xdchain/common"
    25  	"github.com/kisexp/xdchain/common/hexutil"
    26  	"github.com/kisexp/xdchain/consensus/istanbul"
    27  	qbfttypes "github.com/kisexp/xdchain/consensus/istanbul/qbft/types"
    28  	"github.com/kisexp/xdchain/core/types"
    29  	"github.com/kisexp/xdchain/rlp"
    30  )
    31  
    32  // broadcastNextRoundChange sends the ROUND CHANGE message with current round + 1
    33  func (c *core) broadcastNextRoundChange() {
    34  	cv := c.currentView()
    35  	c.broadcastRoundChange(new(big.Int).Add(cv.Round, common.Big1))
    36  }
    37  
    38  // broadcastRoundChange is called when either
    39  // - ROUND-CHANGE timeout expires (meaning either we have not received PRE-PREPARE message or we have not received a quorum of COMMIT messages)
    40  // -
    41  
    42  // It
    43  // - Creates and sign ROUND-CHANGE message
    44  // - broadcast the ROUND-CHANGE message with the given round
    45  func (c *core) broadcastRoundChange(round *big.Int) {
    46  	logger := c.currentLogger(true, nil)
    47  
    48  	// Validates new round corresponds to current view
    49  	cv := c.currentView()
    50  	if cv.Round.Cmp(round) > 0 {
    51  		logger.Error("QBFT: invalid past target round", "target", round)
    52  		return
    53  	}
    54  
    55  	roundChange := qbfttypes.NewRoundChange(c.current.Sequence(), round, c.current.preparedRound, c.current.preparedBlock)
    56  
    57  	// Sign message
    58  	encodedPayload, err := roundChange.EncodePayloadForSigning()
    59  	if err != nil {
    60  		withMsg(logger, roundChange).Error("QBFT: failed to encode ROUND-CHANGE message", "err", err)
    61  		return
    62  	}
    63  	signature, err := c.backend.Sign(encodedPayload)
    64  	if err != nil {
    65  		withMsg(logger, roundChange).Error("QBFT: failed to sign ROUND-CHANGE message", "err", err)
    66  		return
    67  	}
    68  	roundChange.SetSignature(signature)
    69  
    70  	// Extend ROUND-CHANGE message with PREPARE justification
    71  	if c.QBFTPreparedPrepares != nil {
    72  		roundChange.Justification = c.QBFTPreparedPrepares
    73  		withMsg(logger, roundChange).Debug("QBFT: extended ROUND-CHANGE message with PREPARE justification", "justification", roundChange.Justification)
    74  	}
    75  
    76  	// RLP-encode message
    77  	data, err := rlp.EncodeToBytes(roundChange)
    78  	if err != nil {
    79  		withMsg(logger, roundChange).Error("QBFT: failed to encode ROUND-CHANGE message", "err", err)
    80  		return
    81  	}
    82  
    83  	withMsg(logger, roundChange).Info("QBFT: broadcast ROUND-CHANGE message", "payload", hexutil.Encode(data))
    84  
    85  	// Broadcast RLP-encoded message
    86  	if err = c.backend.Broadcast(c.valSet, roundChange.Code(), data); err != nil {
    87  		withMsg(logger, roundChange).Error("QBFT: failed to broadcast ROUND-CHANGE message", "err", err)
    88  		return
    89  	}
    90  }
    91  
    92  // handleRoundChange is called when receiving a ROUND-CHANGE message from another validator
    93  // - accumulates ROUND-CHANGE messages until reaching quorum for a given round
    94  // - when quorum of ROUND-CHANGE messages is reached then
    95  func (c *core) handleRoundChange(roundChange *qbfttypes.RoundChange) error {
    96  	logger := c.currentLogger(true, roundChange)
    97  
    98  	view := roundChange.View()
    99  	currentRound := c.currentView().Round
   100  
   101  	// number of validators we received ROUND-CHANGE from for a round higher than the current one
   102  	num := c.roundChangeSet.higherRoundMessages(currentRound)
   103  
   104  	// number of validators we received ROUND-CHANGE from for the current round
   105  	currentRoundMessages := c.roundChangeSet.getRCMessagesForGivenRound(currentRound)
   106  
   107  	logger.Info("QBFT: handle ROUND-CHANGE message", "higherRoundChanges.count", num, "currentRoundChanges.count", currentRoundMessages)
   108  
   109  	// Add ROUND-CHANGE message to message set
   110  	if view.Round.Cmp(currentRound) >= 0 {
   111  		var prepareMessages []*qbfttypes.Prepare = nil
   112  		var pr *big.Int = nil
   113  		var pb *types.Block = nil
   114  		if roundChange.PreparedRound != nil && roundChange.PreparedBlock != nil && roundChange.Justification != nil && len(roundChange.Justification) > 0 {
   115  			prepareMessages = roundChange.Justification
   116  			pr = roundChange.PreparedRound
   117  			pb = roundChange.PreparedBlock
   118  		}
   119  		err := c.roundChangeSet.Add(view.Round, roundChange, pr, pb, prepareMessages, c.QuorumSize())
   120  		if err != nil {
   121  			logger.Warn("QBFT: failed to add ROUND-CHANGE message", "err", err)
   122  			return err
   123  		}
   124  	}
   125  
   126  	// number of validators we received ROUND-CHANGE from for a round higher than the current one
   127  	num = c.roundChangeSet.higherRoundMessages(currentRound)
   128  
   129  	// number of validators we received ROUND-CHANGE from for the current round
   130  	currentRoundMessages = c.roundChangeSet.getRCMessagesForGivenRound(currentRound)
   131  
   132  	logger = logger.New("higherRoundChanges.count", num, "currentRoundChanges.count", currentRoundMessages)
   133  
   134  	if num == c.valSet.F()+1 {
   135  		// We received F+1 ROUND-CHANGE messages (this may happen before our timeout exprired)
   136  		// we start new round and broadcast ROUND-CHANGE message
   137  		newRound := c.roundChangeSet.getMinRoundChange(currentRound)
   138  
   139  		logger.Info("QBFT: received F+1 ROUND-CHANGE messages", "F", c.valSet.F())
   140  
   141  		c.startNewRound(newRound)
   142  		c.broadcastRoundChange(newRound)
   143  	} else if currentRoundMessages >= c.QuorumSize() && c.IsProposer() && c.current.preprepareSent.Cmp(currentRound) < 0 {
   144  		logger.Info("QBFT: received quorum of ROUND-CHANGE messages")
   145  
   146  		// We received quorum of ROUND-CHANGE for current round and we are proposer
   147  
   148  		// If we have received a quorum of PREPARE message
   149  		// then we propose the same block proposal again if not we
   150  		// propose the block proposal that we generated
   151  		_, proposal := c.highestPrepared(currentRound)
   152  		if proposal == nil {
   153  			proposal = c.current.pendingRequest.Proposal
   154  		}
   155  
   156  		// Prepare justification for ROUND-CHANGE messages
   157  		roundChangeMessages := c.roundChangeSet.roundChanges[currentRound.Uint64()]
   158  		rcSignedPayloads := make([]*qbfttypes.SignedRoundChangePayload, 0)
   159  		for _, m := range roundChangeMessages.Values() {
   160  			rcMsg := m.(*qbfttypes.RoundChange)
   161  			rcSignedPayloads = append(rcSignedPayloads, &rcMsg.SignedRoundChangePayload)
   162  		}
   163  
   164  		prepareMessages := c.roundChangeSet.prepareMessages[currentRound.Uint64()]
   165  		if err := isJustified(proposal, rcSignedPayloads, prepareMessages, c.QuorumSize()); err != nil {
   166  			logger.Error("QBFT: invalid ROUND-CHANGE message justification", "err", err)
   167  			return nil
   168  		}
   169  
   170  		r := &Request{
   171  			Proposal:        proposal,
   172  			RCMessages:      roundChangeMessages,
   173  			PrepareMessages: prepareMessages,
   174  		}
   175  		c.sendPreprepareMsg(r)
   176  	} else {
   177  		logger.Debug("QBFT: accepted ROUND-CHANGE messages")
   178  	}
   179  	return nil
   180  }
   181  
   182  // highestPrepared returns the highest Prepared Round and the corresponding Prepared Block
   183  func (c *core) highestPrepared(round *big.Int) (*big.Int, istanbul.Proposal) {
   184  	return c.roundChangeSet.highestPreparedRound[round.Uint64()], c.roundChangeSet.highestPreparedBlock[round.Uint64()]
   185  }
   186  
   187  // ----------------------------------------------------------------------------
   188  
   189  func newRoundChangeSet(valSet istanbul.ValidatorSet) *roundChangeSet {
   190  	return &roundChangeSet{
   191  		validatorSet:         valSet,
   192  		roundChanges:         make(map[uint64]*qbftMsgSet),
   193  		prepareMessages:      make(map[uint64][]*qbfttypes.Prepare),
   194  		highestPreparedRound: make(map[uint64]*big.Int),
   195  		highestPreparedBlock: make(map[uint64]istanbul.Proposal),
   196  		mu:                   new(sync.Mutex),
   197  	}
   198  }
   199  
   200  type roundChangeSet struct {
   201  	validatorSet         istanbul.ValidatorSet
   202  	roundChanges         map[uint64]*qbftMsgSet
   203  	prepareMessages      map[uint64][]*qbfttypes.Prepare
   204  	highestPreparedRound map[uint64]*big.Int
   205  	highestPreparedBlock map[uint64]istanbul.Proposal
   206  	mu                   *sync.Mutex
   207  }
   208  
   209  func (rcs *roundChangeSet) NewRound(r *big.Int) {
   210  	rcs.mu.Lock()
   211  	defer rcs.mu.Unlock()
   212  	round := r.Uint64()
   213  	if rcs.roundChanges[round] == nil {
   214  		rcs.roundChanges[round] = newQBFTMsgSet(rcs.validatorSet)
   215  	}
   216  	if rcs.prepareMessages[round] == nil {
   217  		rcs.prepareMessages[round] = make([]*qbfttypes.Prepare, 0)
   218  	}
   219  }
   220  
   221  // Add adds the round and message into round change set
   222  func (rcs *roundChangeSet) Add(r *big.Int, msg qbfttypes.QBFTMessage, preparedRound *big.Int, preparedBlock istanbul.Proposal, prepareMessages []*qbfttypes.Prepare, quorumSize int) error {
   223  	rcs.mu.Lock()
   224  	defer rcs.mu.Unlock()
   225  
   226  	round := r.Uint64()
   227  	if rcs.roundChanges[round] == nil {
   228  		rcs.roundChanges[round] = newQBFTMsgSet(rcs.validatorSet)
   229  	}
   230  	if err := rcs.roundChanges[round].Add(msg); err != nil {
   231  		return err
   232  	}
   233  
   234  	if preparedRound != nil && (rcs.highestPreparedRound[round] == nil || preparedRound.Cmp(rcs.highestPreparedRound[round]) > 0) {
   235  		roundChange := msg.(*qbfttypes.RoundChange)
   236  		if hasMatchingRoundChangeAndPrepares(roundChange, prepareMessages, quorumSize) == nil {
   237  			rcs.highestPreparedRound[round] = preparedRound
   238  			rcs.highestPreparedBlock[round] = preparedBlock
   239  			rcs.prepareMessages[round] = prepareMessages
   240  		}
   241  	}
   242  
   243  	return nil
   244  }
   245  
   246  // higherRoundMessages returns the count of validators we received a ROUND-CHANGE message from
   247  // for any round greater than the given round
   248  func (rcs *roundChangeSet) higherRoundMessages(round *big.Int) int {
   249  	rcs.mu.Lock()
   250  	defer rcs.mu.Unlock()
   251  
   252  	addresses := make(map[common.Address]struct{})
   253  	for k, rms := range rcs.roundChanges {
   254  		if k > round.Uint64() {
   255  			for addr := range rms.messages {
   256  				addresses[addr] = struct{}{}
   257  			}
   258  		}
   259  	}
   260  	return len(addresses)
   261  }
   262  
   263  // getRCMessagesForGivenRound return the count ROUND-CHANGE messages
   264  // received for a given round
   265  func (rcs *roundChangeSet) getRCMessagesForGivenRound(round *big.Int) int {
   266  	rcs.mu.Lock()
   267  	defer rcs.mu.Unlock()
   268  
   269  	if rms := rcs.roundChanges[round.Uint64()]; rms != nil {
   270  		return len(rms.messages)
   271  	}
   272  	return 0
   273  }
   274  
   275  // getMinRoundChange returns the minimum round greater than the given round
   276  func (rcs *roundChangeSet) getMinRoundChange(round *big.Int) *big.Int {
   277  	rcs.mu.Lock()
   278  	defer rcs.mu.Unlock()
   279  
   280  	var keys []int
   281  	for k := range rcs.roundChanges {
   282  		if k > round.Uint64() {
   283  			keys = append(keys, int(k))
   284  		}
   285  	}
   286  	sort.Ints(keys)
   287  	if len(keys) == 0 {
   288  		return round
   289  	}
   290  	return big.NewInt(int64(keys[0]))
   291  }
   292  
   293  // ClearLowerThan deletes the messages for round earlier than the given round
   294  func (rcs *roundChangeSet) ClearLowerThan(round *big.Int) {
   295  	rcs.mu.Lock()
   296  	defer rcs.mu.Unlock()
   297  
   298  	for k, rms := range rcs.roundChanges {
   299  		if len(rms.Values()) == 0 || k < round.Uint64() {
   300  			delete(rcs.roundChanges, k)
   301  			delete(rcs.highestPreparedRound, k)
   302  			delete(rcs.highestPreparedBlock, k)
   303  			delete(rcs.prepareMessages, k)
   304  		}
   305  	}
   306  }
   307  
   308  // MaxRound returns the max round which the number of messages is equal or larger than num
   309  func (rcs *roundChangeSet) MaxRound(num int) *big.Int {
   310  	rcs.mu.Lock()
   311  	defer rcs.mu.Unlock()
   312  
   313  	var maxRound *big.Int
   314  	for k, rms := range rcs.roundChanges {
   315  		if rms.Size() < num {
   316  			continue
   317  		}
   318  		r := big.NewInt(int64(k))
   319  		if maxRound == nil || maxRound.Cmp(r) < 0 {
   320  			maxRound = r
   321  		}
   322  	}
   323  	return maxRound
   324  }