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