github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/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  	"math/big"
    21  	"sort"
    22  	"sync"
    23  
    24  	"github.com/ethereum/go-ethereum/common"
    25  	"github.com/ethereum/go-ethereum/common/prque"
    26  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    27  	"github.com/ethereum/go-ethereum/log"
    28  )
    29  
    30  var (
    31  	// msgPriority is defined for calculating processing priority to speedup consensus
    32  	// istanbul.MsgPreprepare > istanbul.MsgCommit > istanbul.MsgPrepare
    33  	msgPriority = map[uint64]int{
    34  		istanbul.MsgPreprepare: 1,
    35  		istanbul.MsgCommit:     2,
    36  		istanbul.MsgPrepare:    3,
    37  	}
    38  
    39  	// Do not accept messages for views more than this many sequences in the future.
    40  	acceptMaxFutureSequence             = big.NewInt(10)
    41  	acceptMaxFutureMsgsFromOneValidator = 1000
    42  	acceptMaxFutureMessages             = 10 * 1000
    43  	acceptMaxFutureMessagesPruneBatch   = 100
    44  )
    45  
    46  // checkMessage checks the message state
    47  // return errInvalidMessage if the message is invalid
    48  // return errFutureMessage if the message view is larger than current view
    49  // return errOldMessage if the message view is smaller than current view
    50  func (c *core) checkMessage(msgCode uint64, msgView *istanbul.View) error {
    51  	if msgView == nil || msgView.Sequence == nil || msgView.Round == nil {
    52  		return errInvalidMessage
    53  	}
    54  
    55  	if msgView.Cmp(c.current.View()) < 0 {
    56  		return errOldMessage
    57  	} else if msgView.Sequence.Cmp(c.current.Sequence()) > 0 {
    58  		// sequence is bigger, definitely future message
    59  		return errFutureMessage
    60  	} else {
    61  		// same sequence && msgRound >= currentRound
    62  
    63  		// Accept all RoundChange (also future rounds)
    64  		// but check again desired round
    65  		if msgCode == istanbul.MsgRoundChange {
    66  			if msgView.Round.Cmp(c.current.DesiredRound()) < 0 {
    67  				return errOldMessage
    68  			}
    69  			return nil
    70  		}
    71  
    72  		// TODO we should check directly against the desired round
    73  		// there's no sense in accepting (or storing) messages on the range [currentRound, desiredRound]
    74  
    75  		if msgView.Round.Cmp(c.current.View().Round) > 0 || c.current.State() == StateWaitingForNewRound {
    76  			return errFutureMessage
    77  		}
    78  
    79  		// StateAcceptRequest only accepts istanbul.MsgPreprepare
    80  		// other messages are future messages
    81  		if c.current.State() == StateAcceptRequest && msgCode != istanbul.MsgPreprepare {
    82  			return errFutureMessage
    83  		}
    84  
    85  		// For states(StatePreprepared, StatePrepared, StateCommitted),
    86  		// can accept all message types if processing with same view
    87  		return nil
    88  	}
    89  }
    90  
    91  // MsgBacklog represent a backlog of future messages
    92  // It works by:
    93  //     - allowing storing messages with "store()"
    94  //     - call eventListener when a backlog message becomes "present"
    95  //     - updates its notion of time/state with updateState()
    96  type MsgBacklog interface {
    97  	// store atttemps to store the message in the backlog
    98  	// it might not do so, if the message is too far in the future
    99  	store(msg *istanbul.Message)
   100  
   101  	// updateState updates the notion of time/state of the backlog,
   102  	// as a side effect it will call the eventListener for all backlog
   103  	// messages that belong to the current "state"
   104  	updateState(view *istanbul.View, state State)
   105  }
   106  
   107  type msgBacklogImpl struct {
   108  	backlogBySeq  map[uint64]*prque.Prque
   109  	msgCountBySrc map[common.Address]int
   110  	msgCount      int
   111  
   112  	currentView  *istanbul.View
   113  	currentState State
   114  
   115  	backlogsMu   *sync.Mutex
   116  	msgProcessor func(*istanbul.Message)
   117  	checkMessage func(msgCode uint64, msgView *istanbul.View) error
   118  	logger       log.Logger
   119  }
   120  
   121  func newMsgBacklog(msgProcessor func(*istanbul.Message), checkMessage func(msgCode uint64, msgView *istanbul.View) error) MsgBacklog {
   122  	initialView := &istanbul.View{
   123  		Round:    big.NewInt(0),
   124  		Sequence: big.NewInt(1),
   125  	}
   126  
   127  	return &msgBacklogImpl{
   128  		backlogBySeq:  make(map[uint64]*prque.Prque),
   129  		msgCountBySrc: make(map[common.Address]int),
   130  		msgCount:      0,
   131  
   132  		currentView:  initialView,
   133  		currentState: StateAcceptRequest,
   134  
   135  		msgProcessor: msgProcessor,
   136  		checkMessage: checkMessage,
   137  		backlogsMu:   new(sync.Mutex),
   138  		logger:       log.New("type", "MsgBacklog"),
   139  	}
   140  }
   141  
   142  func (c *msgBacklogImpl) store(msg *istanbul.Message) {
   143  	logger := c.logger.New("func", "store", "from", msg.Address)
   144  
   145  	view, err := extractMessageView(msg)
   146  
   147  	if err != nil {
   148  		return
   149  	}
   150  
   151  	c.backlogsMu.Lock()
   152  	defer c.backlogsMu.Unlock()
   153  
   154  	// Never accept messages too far into the future
   155  	if view.Sequence.Cmp(new(big.Int).Add(c.currentView.Sequence, acceptMaxFutureSequence)) > 0 {
   156  		logger.Debug("Dropping message", "reason", "too far in the future", "m", msg)
   157  		return
   158  	}
   159  
   160  	if view.Round.Cmp(maxRoundForPriorityQueue) >= 0 {
   161  		logger.Debug("Dropping message", "reason", "round exceeds PQ bounds check", "m", msg)
   162  		return
   163  	}
   164  
   165  	// Check and inc per-validator future message limit
   166  	if c.msgCountBySrc[msg.Address] > acceptMaxFutureMsgsFromOneValidator {
   167  		logger.Debug("Dropping message", "reason", "exceeds per-address cap")
   168  		return
   169  	}
   170  
   171  	logger.Trace("Store future message", "m", msg)
   172  	c.msgCountBySrc[msg.Address]++
   173  	c.msgCount++
   174  
   175  	// Add message to per-seq list
   176  	backlogForSeq := c.backlogBySeq[view.Sequence.Uint64()]
   177  	if backlogForSeq == nil {
   178  		backlogForSeq = prque.New(nil)
   179  		c.backlogBySeq[view.Sequence.Uint64()] = backlogForSeq
   180  	}
   181  
   182  	backlogForSeq.Push(msg, toPriority(msg.Code, view))
   183  
   184  	// After insert, remove messages if we have more than "acceptMaxFutureMessages"
   185  	c.removeMessagesOverflow()
   186  }
   187  
   188  // removeMessagesOverflow will remove messages if necessary to maintain the number of messages <= acceptMaxFutureMessages
   189  // For that, it will remove messages that further on the future
   190  func (c *msgBacklogImpl) removeMessagesOverflow() {
   191  	// Keep backlog below total max size by pruning future-most sequence first
   192  	// (we always leave one sequence's entire messages and rely on per-validator limits)
   193  	if c.msgCount > acceptMaxFutureMessages {
   194  		backlogSeqs := c.getSortedBacklogSeqs()
   195  		for i := len(backlogSeqs) - 1; i > 0; i-- {
   196  			seq := backlogSeqs[i]
   197  			if seq <= c.currentView.Sequence.Uint64() ||
   198  				c.msgCount < (acceptMaxFutureMessages-acceptMaxFutureMessagesPruneBatch) {
   199  				break
   200  			}
   201  			c.clearBacklogForSeq(seq)
   202  		}
   203  	}
   204  }
   205  
   206  // Return slice of sequences present in backlog sorted in ascending order
   207  // Call with backlogsMu held.
   208  func (c *msgBacklogImpl) getSortedBacklogSeqs() []uint64 {
   209  	backlogSeqs := make([]uint64, len(c.backlogBySeq))
   210  	i := 0
   211  	for k := range c.backlogBySeq {
   212  		backlogSeqs[i] = k
   213  		i++
   214  	}
   215  	sort.Slice(backlogSeqs, func(i, j int) bool {
   216  		return backlogSeqs[i] < backlogSeqs[j]
   217  	})
   218  	return backlogSeqs
   219  }
   220  
   221  // clearBacklogForSeq will remove all entries in the backlog
   222  // for the given seq
   223  func (c *msgBacklogImpl) clearBacklogForSeq(seq uint64) {
   224  	c.processBacklogForSeq(seq, func(_ *istanbul.Message) bool { return true })
   225  }
   226  
   227  // processBacklogForSeq will call process() with each entry of the backlog
   228  // for the given seq, until process return "false".
   229  // The entry on which process() returned false will remain in the backlog
   230  func (c *msgBacklogImpl) processBacklogForSeq(seq uint64, process func(*istanbul.Message) bool) {
   231  	backlogForSeq := c.backlogBySeq[seq]
   232  	if backlogForSeq == nil {
   233  		return
   234  	}
   235  
   236  	backlogSize := backlogForSeq.Size()
   237  	for i := 0; i < backlogSize; i++ {
   238  		m, priority := backlogForSeq.Pop()
   239  		msg := m.(*istanbul.Message)
   240  
   241  		shouldStop := process(msg)
   242  
   243  		if shouldStop {
   244  			backlogForSeq.Push(m, priority)
   245  			break
   246  		}
   247  
   248  		c.msgCountBySrc[msg.Address]--
   249  		if c.msgCountBySrc[msg.Address] == 0 {
   250  			delete(c.msgCountBySrc, msg.Address)
   251  		}
   252  		c.msgCount--
   253  	}
   254  
   255  	if backlogForSeq.Size() == 0 {
   256  		delete(c.backlogBySeq, seq)
   257  	}
   258  }
   259  
   260  func (c *msgBacklogImpl) updateState(view *istanbul.View, state State) {
   261  	c.backlogsMu.Lock()
   262  	defer c.backlogsMu.Unlock()
   263  
   264  	c.currentState = state
   265  	c.currentView = view
   266  
   267  	c.processBacklog()
   268  }
   269  
   270  func (c *msgBacklogImpl) processBacklog() {
   271  
   272  	logger := c.logger.New("func", "processBacklog", "cur_seq", c.currentView.Sequence, "cur_round", c.currentView.Round)
   273  	processedMsgsConsidered, processedMsgsEnqueued, processedMsgsFuture := 0, 0, 0
   274  
   275  	for _, seq := range c.getSortedBacklogSeqs() {
   276  
   277  		if seq < c.currentView.Sequence.Uint64() {
   278  			// Earlier sequence. Prune all messages.
   279  			c.clearBacklogForSeq(seq)
   280  		} else if seq == c.currentView.Sequence.Uint64() {
   281  			// Current sequence. Process all in order.
   282  			c.processBacklogForSeq(seq, func(msg *istanbul.Message) bool {
   283  				processedMsgsConsidered++
   284  
   285  				view, err := extractMessageView(msg)
   286  
   287  				if err != nil {
   288  					logger.Warn("Error decoding msg", "err", err)
   289  					return false
   290  				}
   291  				if view == nil {
   292  					logger.Warn("Nil view")
   293  					return false
   294  				}
   295  
   296  				logger := logger.New("m", msg, "msg_view", view)
   297  
   298  				err = c.checkMessage(msg.Code, view)
   299  
   300  				if err == errFutureMessage {
   301  					logger.Debug("Future message in backlog for seq, pushing back to the backlog")
   302  					processedMsgsFuture++
   303  					return true
   304  				}
   305  
   306  				if err == nil {
   307  					logger.Trace("Post backlog event")
   308  					processedMsgsEnqueued++
   309  					go c.msgProcessor(msg)
   310  				} else {
   311  					logger.Trace("Skip the backlog event", "err", err)
   312  				}
   313  				return false
   314  			})
   315  		}
   316  	}
   317  
   318  	if processedMsgsConsidered > 0 {
   319  		logger.Info("Processing istanbul backlog", "considered", processedMsgsConsidered, "future", processedMsgsFuture, "enqueued", processedMsgsEnqueued)
   320  	}
   321  }
   322  
   323  // A safe maximum for round that prevents overflow
   324  var (
   325  	maxRoundForPriorityQueue = big.NewInt(1 << (63 - 5))
   326  )
   327  
   328  func toPriority(msgCode uint64, view *istanbul.View) int64 {
   329  	if msgCode == istanbul.MsgRoundChange {
   330  		// msgRoundChange comes first
   331  		return 0
   332  	}
   333  	// 10 * Round limits the range possible message codes to [0, 9]
   334  	// Caller must check for integer overflow.
   335  	return -int64(view.Round.Uint64()*10 + uint64(msgPriority[msgCode]))
   336  }
   337  
   338  func extractMessageView(msg *istanbul.Message) (*istanbul.View, error) {
   339  	var v *istanbul.View
   340  	switch msg.Code {
   341  	case istanbul.MsgPreprepare:
   342  		var p *istanbul.Preprepare
   343  		err := msg.Decode(&p)
   344  		if err != nil {
   345  			return nil, err
   346  		}
   347  		v = p.View
   348  	case istanbul.MsgPrepare:
   349  		var p *istanbul.Subject
   350  		err := msg.Decode(&p)
   351  		if err != nil {
   352  			return nil, err
   353  		}
   354  		v = p.View
   355  	case istanbul.MsgCommit:
   356  		var cs *istanbul.CommittedSubject
   357  		err := msg.Decode(&cs)
   358  		if err != nil {
   359  			return nil, err
   360  		}
   361  		v = cs.Subject.View
   362  	case istanbul.MsgRoundChange:
   363  		var p *istanbul.RoundChange
   364  		err := msg.Decode(&p)
   365  		if err != nil {
   366  			return nil, err
   367  		}
   368  		v = p.View
   369  	}
   370  	return v, nil
   371  }