github.com/ConsenSys/Quorum@v20.10.0+incompatible/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/ethereum/go-ethereum/consensus/istanbul"
    21  	"gopkg.in/karalabe/cookiejar.v2/collections/prque"
    22  )
    23  
    24  var (
    25  	// msgPriority is defined for calculating processing priority to speedup consensus
    26  	// msgPreprepare > msgCommit > msgPrepare
    27  	msgPriority = map[uint64]int{
    28  		msgPreprepare: 1,
    29  		msgCommit:     2,
    30  		msgPrepare:    3,
    31  	}
    32  )
    33  
    34  // checkMessage checks the message state
    35  // return errInvalidMessage if the message is invalid
    36  // return errFutureMessage if the message view is larger than current view
    37  // return errOldMessage if the message view is smaller than current view
    38  func (c *core) checkMessage(msgCode uint64, view *istanbul.View) error {
    39  	if view == nil || view.Sequence == nil || view.Round == nil {
    40  		return errInvalidMessage
    41  	}
    42  
    43  	if msgCode == msgRoundChange {
    44  		if view.Sequence.Cmp(c.currentView().Sequence) > 0 {
    45  			return errFutureMessage
    46  		} else if view.Cmp(c.currentView()) < 0 {
    47  			return errOldMessage
    48  		}
    49  		return nil
    50  	}
    51  
    52  	if view.Cmp(c.currentView()) > 0 {
    53  		return errFutureMessage
    54  	}
    55  
    56  	if view.Cmp(c.currentView()) < 0 {
    57  		return errOldMessage
    58  	}
    59  
    60  	if c.waitingForRoundChange {
    61  		return errFutureMessage
    62  	}
    63  
    64  	// StateAcceptRequest only accepts msgPreprepare
    65  	// other messages are future messages
    66  	if c.state == StateAcceptRequest {
    67  		if msgCode > msgPreprepare {
    68  			return errFutureMessage
    69  		}
    70  		return nil
    71  	}
    72  
    73  	// For states(StatePreprepared, StatePrepared, StateCommitted),
    74  	// can accept all message types if processing with same view
    75  	return nil
    76  }
    77  
    78  func (c *core) storeBacklog(msg *message, src istanbul.Validator) {
    79  	logger := c.logger.New("from", src, "state", c.state)
    80  
    81  	if src.Address() == c.Address() {
    82  		logger.Warn("Backlog from self")
    83  		return
    84  	}
    85  
    86  	logger.Trace("Store future message")
    87  
    88  	c.backlogsMu.Lock()
    89  	defer c.backlogsMu.Unlock()
    90  
    91  	logger.Debug("Retrieving backlog queue", "for", src.Address(), "backlogs_size", len(c.backlogs))
    92  	backlog := c.backlogs[src.Address()]
    93  	if backlog == nil {
    94  		backlog = prque.New()
    95  	}
    96  	switch msg.Code {
    97  	case msgPreprepare:
    98  		var p *istanbul.Preprepare
    99  		err := msg.Decode(&p)
   100  		if err == nil {
   101  			backlog.Push(msg, toPriority(msg.Code, p.View))
   102  		}
   103  		// for msgRoundChange, msgPrepare and msgCommit cases
   104  	default:
   105  		var p *istanbul.Subject
   106  		err := msg.Decode(&p)
   107  		if err == nil {
   108  			backlog.Push(msg, toPriority(msg.Code, p.View))
   109  		}
   110  	}
   111  	c.backlogs[src.Address()] = backlog
   112  }
   113  
   114  func (c *core) processBacklog() {
   115  	c.backlogsMu.Lock()
   116  	defer c.backlogsMu.Unlock()
   117  
   118  	for srcAddress, backlog := range c.backlogs {
   119  		if backlog == nil {
   120  			continue
   121  		}
   122  		_, src := c.valSet.GetByAddress(srcAddress)
   123  		if src == nil {
   124  			// validator is not available
   125  			delete(c.backlogs, srcAddress)
   126  			continue
   127  		}
   128  		logger := c.logger.New("from", src, "state", c.state)
   129  		isFuture := false
   130  
   131  		// We stop processing if
   132  		//   1. backlog is empty
   133  		//   2. The first message in queue is a future message
   134  		for !(backlog.Empty() || isFuture) {
   135  			m, prio := backlog.Pop()
   136  			msg := m.(*message)
   137  			var view *istanbul.View
   138  			switch msg.Code {
   139  			case msgPreprepare:
   140  				var m *istanbul.Preprepare
   141  				err := msg.Decode(&m)
   142  				if err == nil {
   143  					view = m.View
   144  				}
   145  				// for msgRoundChange, msgPrepare and msgCommit cases
   146  			default:
   147  				var sub *istanbul.Subject
   148  				err := msg.Decode(&sub)
   149  				if err == nil {
   150  					view = sub.View
   151  				}
   152  			}
   153  			if view == nil {
   154  				logger.Debug("Nil view", "msg", msg)
   155  				continue
   156  			}
   157  			// Push back if it's a future message
   158  			err := c.checkMessage(msg.Code, view)
   159  			if err != nil {
   160  				if err == errFutureMessage {
   161  					logger.Trace("Stop processing backlog", "msg", msg)
   162  					backlog.Push(msg, prio)
   163  					isFuture = true
   164  					break
   165  				}
   166  				logger.Trace("Skip the backlog event", "msg", msg, "err", err)
   167  				continue
   168  			}
   169  			logger.Trace("Post backlog event", "msg", msg)
   170  
   171  			go c.sendEvent(backlogEvent{
   172  				src: src,
   173  				msg: msg,
   174  			})
   175  		}
   176  	}
   177  }
   178  
   179  func toPriority(msgCode uint64, view *istanbul.View) float32 {
   180  	if msgCode == msgRoundChange {
   181  		// For msgRoundChange, set the message priority based on its sequence
   182  		return -float32(view.Sequence.Uint64() * 1000)
   183  	}
   184  	// FIXME: round will be reset as 0 while new sequence
   185  	// 10 * Round limits the range of message code is from 0 to 9
   186  	// 1000 * Sequence limits the range of round is from 0 to 99
   187  	return -float32(view.Sequence.Uint64()*1000 + view.Round.Uint64()*10 + uint64(msgPriority[msgCode]))
   188  }