github.com/pfcoder/quorum@v2.0.3-0.20180501191142-d4a1b0958135+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  	backlog := c.backlogs[src]
    92  	if backlog == nil {
    93  		backlog = prque.New()
    94  	}
    95  	switch msg.Code {
    96  	case msgPreprepare:
    97  		var p *istanbul.Preprepare
    98  		err := msg.Decode(&p)
    99  		if err == nil {
   100  			backlog.Push(msg, toPriority(msg.Code, p.View))
   101  		}
   102  		// for msgRoundChange, msgPrepare and msgCommit cases
   103  	default:
   104  		var p *istanbul.Subject
   105  		err := msg.Decode(&p)
   106  		if err == nil {
   107  			backlog.Push(msg, toPriority(msg.Code, p.View))
   108  		}
   109  	}
   110  	c.backlogs[src] = backlog
   111  }
   112  
   113  func (c *core) processBacklog() {
   114  	c.backlogsMu.Lock()
   115  	defer c.backlogsMu.Unlock()
   116  
   117  	for src, backlog := range c.backlogs {
   118  		if backlog == nil {
   119  			continue
   120  		}
   121  
   122  		logger := c.logger.New("from", src, "state", c.state)
   123  		isFuture := false
   124  
   125  		// We stop processing if
   126  		//   1. backlog is empty
   127  		//   2. The first message in queue is a future message
   128  		for !(backlog.Empty() || isFuture) {
   129  			m, prio := backlog.Pop()
   130  			msg := m.(*message)
   131  			var view *istanbul.View
   132  			switch msg.Code {
   133  			case msgPreprepare:
   134  				var m *istanbul.Preprepare
   135  				err := msg.Decode(&m)
   136  				if err == nil {
   137  					view = m.View
   138  				}
   139  				// for msgRoundChange, msgPrepare and msgCommit cases
   140  			default:
   141  				var sub *istanbul.Subject
   142  				err := msg.Decode(&sub)
   143  				if err == nil {
   144  					view = sub.View
   145  				}
   146  			}
   147  			if view == nil {
   148  				logger.Debug("Nil view", "msg", msg)
   149  				continue
   150  			}
   151  			// Push back if it's a future message
   152  			err := c.checkMessage(msg.Code, view)
   153  			if err != nil {
   154  				if err == errFutureMessage {
   155  					logger.Trace("Stop processing backlog", "msg", msg)
   156  					backlog.Push(msg, prio)
   157  					isFuture = true
   158  					break
   159  				}
   160  				logger.Trace("Skip the backlog event", "msg", msg, "err", err)
   161  				continue
   162  			}
   163  			logger.Trace("Post backlog event", "msg", msg)
   164  
   165  			go c.sendEvent(backlogEvent{
   166  				src: src,
   167  				msg: msg,
   168  			})
   169  		}
   170  	}
   171  }
   172  
   173  func toPriority(msgCode uint64, view *istanbul.View) float32 {
   174  	if msgCode == msgRoundChange {
   175  		// For msgRoundChange, set the message priority based on its sequence
   176  		return -float32(view.Sequence.Uint64() * 1000)
   177  	}
   178  	// FIXME: round will be reset as 0 while new sequence
   179  	// 10 * Round limits the range of message code is from 0 to 9
   180  	// 1000 * Sequence limits the range of round is from 0 to 99
   181  	return -float32(view.Sequence.Uint64()*1000 + view.Round.Uint64()*10 + uint64(msgPriority[msgCode]))
   182  }