github.com/electroneum/electroneum-sc@v0.0.0-20230105223411-3bc1d078281e/consensus/istanbul/core/backlog.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  	"github.com/electroneum/electroneum-sc/consensus/istanbul"
    21  	qbfttypes "github.com/electroneum/electroneum-sc/consensus/istanbul/types"
    22  	"gopkg.in/karalabe/cookiejar.v2/collections/prque"
    23  )
    24  
    25  var (
    26  	// msgPriority is defined for calculating processing priority to speedup consensus
    27  	// msgPreprepare > msgCommit > msgPrepare
    28  	msgPriority = map[uint64]int{
    29  		qbfttypes.PreprepareCode: 1,
    30  		qbfttypes.CommitCode:     2,
    31  		qbfttypes.PrepareCode:    3,
    32  	}
    33  )
    34  
    35  // checkMessage checks that a message matches our current QBFT state
    36  //
    37  // In particular it ensures that
    38  // - message has the expected round
    39  // - message has the expected sequence
    40  // - message type is expected given our current state
    41  
    42  // return errInvalidMessage if the message is invalid
    43  // return errFutureMessage if the message view is larger than current view
    44  // return errOldMessage if the message view is smaller than current view
    45  func (c *core) checkMessage(msgCode uint64, view *istanbul.View) error {
    46  	if view == nil || view.Sequence == nil || view.Round == nil {
    47  		return errInvalidMessage
    48  	}
    49  
    50  	if msgCode == qbfttypes.RoundChangeCode {
    51  		// if ROUND-CHANGE message
    52  		// check that
    53  		// - sequence matches our current sequence
    54  		// - round is in the future
    55  		if view.Sequence.Cmp(c.currentView().Sequence) > 0 {
    56  			return errFutureMessage
    57  		} else if view.Cmp(c.currentView()) < 0 {
    58  			return errOldMessage
    59  		}
    60  		return nil
    61  	}
    62  
    63  	// If not ROUND-CHANGE
    64  	// check that round and sequence equals our current round and sequence
    65  	if view.Cmp(c.currentView()) > 0 {
    66  		return errFutureMessage
    67  	}
    68  
    69  	if view.Cmp(c.currentView()) < 0 {
    70  		return errOldMessage
    71  	}
    72  
    73  	switch c.state {
    74  	case StateAcceptRequest:
    75  		// StateAcceptRequest only accepts msgPreprepare and msgRoundChange
    76  		// other messages are future messages
    77  		if msgCode > qbfttypes.PreprepareCode {
    78  			return errFutureMessage
    79  		}
    80  		return nil
    81  	case StatePreprepared:
    82  		// StatePreprepared only accepts msgPrepare and msgRoundChange
    83  		// message less than msgPrepare are invalid and greater are future messages
    84  		if msgCode < qbfttypes.PrepareCode {
    85  			return errInvalidMessage
    86  		} else if msgCode > qbfttypes.PrepareCode {
    87  			return errFutureMessage
    88  		}
    89  		return nil
    90  	case StatePrepared:
    91  		// StatePrepared only accepts msgCommit and msgRoundChange
    92  		// other messages are invalid messages
    93  		if msgCode < qbfttypes.CommitCode {
    94  			return errInvalidMessage
    95  		}
    96  		return nil
    97  	case StateCommitted:
    98  		// StateCommit rejects all messages other than msgRoundChange
    99  		return errInvalidMessage
   100  	}
   101  	return nil
   102  }
   103  
   104  // addToBacklog allows to postpone the processing of future messages
   105  
   106  // it adds the message to backlog which is read on every state change
   107  func (c *core) addToBacklog(msg qbfttypes.QBFTMessage) {
   108  	logger := c.currentLogger(true, msg)
   109  
   110  	src := msg.Source()
   111  	if src == c.Address() {
   112  		logger.Warn("IBFT: backlog from self")
   113  		return
   114  	}
   115  
   116  	logger.Trace("IBFT: new backlog message", "backlogs_size", len(c.backlogs))
   117  
   118  	c.backlogsMu.Lock()
   119  	defer c.backlogsMu.Unlock()
   120  
   121  	backlog := c.backlogs[src]
   122  	if backlog == nil {
   123  		backlog = prque.New()
   124  		c.backlogs[src] = backlog
   125  	}
   126  	view := msg.View()
   127  	backlog.Push(msg, toPriority(msg.Code(), &view))
   128  }
   129  
   130  // processBacklog lookup for future messages that have been backlogged and post it on
   131  // the event channel so main handler loop can handle it
   132  
   133  // It is called on every state change
   134  func (c *core) processBacklog() {
   135  	c.backlogsMu.Lock()
   136  	defer c.backlogsMu.Unlock()
   137  
   138  	for srcAddress, backlog := range c.backlogs {
   139  		if backlog == nil {
   140  			continue
   141  		}
   142  		_, src := c.valSet.GetByAddress(srcAddress)
   143  		if src == nil {
   144  			// validator is not available
   145  			delete(c.backlogs, srcAddress)
   146  			continue
   147  		}
   148  		logger := c.logger.New("from", src, "state", c.state)
   149  		isFuture := false
   150  
   151  		logger.Trace("IBFT: process backlog")
   152  
   153  		// We stop processing if
   154  		//   1. backlog is empty
   155  		//   2. The first message in queue is a future message
   156  		for !(backlog.Empty() || isFuture) {
   157  			m, prio := backlog.Pop()
   158  
   159  			var code uint64
   160  			var view istanbul.View
   161  			var event backlogEvent
   162  
   163  			msg := m.(qbfttypes.QBFTMessage)
   164  			code = msg.Code()
   165  			view = msg.View()
   166  			event.msg = msg
   167  
   168  			// Push back if it's a future message
   169  			err := c.checkMessage(code, &view)
   170  			if err != nil {
   171  				if err == errFutureMessage {
   172  					// this is still a future message
   173  					logger.Trace("IBFT: stop processing backlog", "msg", m)
   174  					backlog.Push(m, prio)
   175  					isFuture = true
   176  					break
   177  				}
   178  				logger.Trace("IBFT: skip backlog message", "msg", m, "err", err)
   179  				continue
   180  			}
   181  			logger.Trace("IBFT: post backlog event", "msg", m)
   182  
   183  			event.src = src
   184  			go c.sendEvent(event)
   185  		}
   186  	}
   187  }
   188  
   189  func toPriority(msgCode uint64, view *istanbul.View) float32 {
   190  	if msgCode == qbfttypes.RoundChangeCode {
   191  		// For msgRoundChange, set the message priority based on its sequence
   192  		return -float32(view.Sequence.Uint64() * 1000)
   193  	}
   194  	// FIXME: round will be reset as 0 while new sequence
   195  	// 10 * Round limits the range of message code is from 0 to 9
   196  	// 1000 * Sequence limits the range of round is from 0 to 99
   197  	return -float32(view.Sequence.Uint64()*1000 + view.Round.Uint64()*10 + uint64(msgPriority[msgCode]))
   198  }