github.com/klaytn/klaytn@v1.10.2/consensus/istanbul/core/backlog.go (about)

     1  // Modifications Copyright 2018 The klaytn Authors
     2  // Copyright 2017 The go-ethereum Authors
     3  // This file is part of the go-ethereum library.
     4  //
     5  // The go-ethereum library is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Lesser General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // The go-ethereum library is distributed in the hope that it will be useful,
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    13  // GNU Lesser General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Lesser General Public License
    16  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    17  //
    18  // This file is derived from quorum/consensus/istanbul/core/backlog.go (2018/06/04).
    19  // Modified and improved for the klaytn development.
    20  
    21  package core
    22  
    23  import (
    24  	"github.com/klaytn/klaytn/common"
    25  	"github.com/klaytn/klaytn/common/prque"
    26  	"github.com/klaytn/klaytn/consensus/istanbul"
    27  )
    28  
    29  // msgPriority is defined for calculating processing priority to speedup consensus
    30  // msgPreprepare > msgCommit > msgPrepare
    31  var msgPriority = map[uint64]int{
    32  	msgPreprepare: 1,
    33  	msgCommit:     2,
    34  	msgPrepare:    3,
    35  }
    36  
    37  // checkMessage checks the message state
    38  // return errInvalidMessage if the message is invalid
    39  // return errFutureMessage if the message view is larger than current view
    40  // return errOldMessage if the message view is smaller than current view
    41  func (c *core) checkMessage(msgCode uint64, view *istanbul.View) error {
    42  	if view == nil || view.Sequence == nil || view.Round == nil {
    43  		return errInvalidMessage
    44  	}
    45  
    46  	if msgCode == msgRoundChange {
    47  		if view.Sequence.Cmp(c.currentView().Sequence) > 0 {
    48  			return errFutureMessage
    49  		} else if view.Cmp(c.currentView()) < 0 {
    50  			return errOldMessage
    51  		}
    52  		return nil
    53  	}
    54  
    55  	if view.Cmp(c.currentView()) > 0 {
    56  		return errFutureMessage
    57  	}
    58  
    59  	if view.Cmp(c.currentView()) < 0 {
    60  		return errOldMessage
    61  	}
    62  
    63  	if c.waitingForRoundChange {
    64  		return errFutureMessage
    65  	}
    66  
    67  	// StateAcceptRequest only accepts msgPreprepare
    68  	// other messages are future messages
    69  	if c.state == StateAcceptRequest {
    70  		if msgCode > msgPreprepare {
    71  			return errFutureMessage
    72  		}
    73  		return nil
    74  	}
    75  
    76  	// For states(StatePreprepared, StatePrepared, StateCommitted),
    77  	// can accept all message types if processing with same view
    78  	return nil
    79  }
    80  
    81  func (c *core) storeBacklog(msg *message, src istanbul.Validator) {
    82  	logger := c.logger.NewWith("from", src, "state", c.state)
    83  
    84  	if src.Address() == c.Address() {
    85  		logger.Warn("Backlog from self")
    86  		return
    87  	}
    88  
    89  	logger.Trace("Store future message")
    90  
    91  	c.backlogsMu.Lock()
    92  	defer c.backlogsMu.Unlock()
    93  
    94  	backlog := c.backlogs[src.Address()]
    95  	if backlog == nil {
    96  		backlog = prque.New()
    97  	}
    98  	switch msg.Code {
    99  	case 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 src, backlog := range c.backlogs {
   121  		if backlog == nil {
   122  			continue
   123  		}
   124  
   125  		logger := c.logger.NewWith("from", src, "state", c.state)
   126  		isFuture := false
   127  
   128  		// We stop processing if
   129  		//   1. backlog is empty
   130  		//   2. The first message in queue is a future message
   131  		for !(backlog.Empty() || isFuture) {
   132  			m, prio := backlog.Pop()
   133  			msg := m.(*message)
   134  			var view *istanbul.View
   135  			var prevHash common.Hash
   136  			switch msg.Code {
   137  			case msgPreprepare:
   138  				var m *istanbul.Preprepare
   139  				err := msg.Decode(&m)
   140  				if err == nil {
   141  					view = m.View
   142  				}
   143  				prevHash = m.Proposal.ParentHash()
   144  				// for msgRoundChange, msgPrepare and msgCommit cases
   145  			default:
   146  				var sub *istanbul.Subject
   147  				err := msg.Decode(&sub)
   148  				if err == nil {
   149  					view = sub.View
   150  				}
   151  				prevHash = sub.PrevHash
   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  				Hash: prevHash,
   175  			})
   176  		}
   177  	}
   178  }
   179  
   180  func toPriority(msgCode uint64, view *istanbul.View) int64 {
   181  	if msgCode == msgRoundChange {
   182  		// For msgRoundChange, set the message priority based on its sequence
   183  		return -int64(view.Sequence.Uint64() * 1000)
   184  	}
   185  	// FIXME: round will be reset as 0 while new sequence
   186  	// 10 * Round limits the range of message code is from 0 to 9
   187  	// 1000 * Sequence limits the range of round is from 0 to 99
   188  	return -int64(view.Sequence.Uint64()*1000 + view.Round.Uint64()*10 + uint64(msgPriority[msgCode]))
   189  }