github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/core/request.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  
    22  	"github.com/ethereum/go-ethereum/common"
    23  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    24  )
    25  
    26  func (c *core) handleRequest(request *istanbul.Request) error {
    27  	logger := c.newLogger("func", "handleRequest")
    28  
    29  	err := c.checkRequestMsg(request)
    30  	if err == errInvalidMessage {
    31  		logger.Warn("invalid request")
    32  		return err
    33  	} else if err != nil {
    34  		logger.Warn("unexpected request", "err", err, "number", request.Proposal.Number(), "hash", request.Proposal.Hash())
    35  		return err
    36  	}
    37  
    38  	logger.Trace("handleRequest", "number", request.Proposal.Number(), "hash", request.Proposal.Hash())
    39  
    40  	if err = c.current.SetPendingRequest(request); err != nil {
    41  		return err
    42  	}
    43  
    44  	// Must go through startNewRound to send proposals for round > 0 to ensure a round change certificate is generated.
    45  	if c.current.State() == StateAcceptRequest && c.current.Round().Cmp(common.Big0) == 0 {
    46  		c.sendPreprepare(request, istanbul.RoundChangeCertificate{})
    47  	}
    48  	return nil
    49  }
    50  
    51  // check request state
    52  // return errInvalidMessage if the message is invalid
    53  // return errFutureMessage if the sequence of proposal is larger than current sequence
    54  // return errOldMessage if the sequence of proposal is smaller than current sequence
    55  func (c *core) checkRequestMsg(request *istanbul.Request) error {
    56  	if request == nil || request.Proposal == nil {
    57  		return errInvalidMessage
    58  	}
    59  
    60  	if c := c.current.Sequence().Cmp(request.Proposal.Number()); c > 0 {
    61  		return errOldMessage
    62  	} else if c < 0 {
    63  		return errFutureMessage
    64  	} else {
    65  		return nil
    66  	}
    67  }
    68  
    69  var (
    70  	maxNumberForRequestsQueue = big.NewInt(2 << (63 - 2))
    71  )
    72  
    73  func (c *core) storeRequestMsg(request *istanbul.Request) {
    74  	logger := c.newLogger("func", "storeRequestMsg")
    75  
    76  	if request.Proposal.Number().Cmp(maxNumberForRequestsQueue) >= 0 {
    77  		logger.Debug("Dropping future request", "number", request.Proposal.Number(), "hash", request.Proposal.Hash())
    78  		return
    79  	}
    80  
    81  	logger.Trace("Store future request", "number", request.Proposal.Number(), "hash", request.Proposal.Hash())
    82  
    83  	c.pendingRequestsMu.Lock()
    84  	defer c.pendingRequestsMu.Unlock()
    85  
    86  	c.pendingRequests.Push(request, -request.Proposal.Number().Int64())
    87  }
    88  
    89  func (c *core) processPendingRequests() {
    90  	c.pendingRequestsMu.Lock()
    91  	defer c.pendingRequestsMu.Unlock()
    92  
    93  	for !(c.pendingRequests.Empty()) {
    94  		m, prio := c.pendingRequests.Pop()
    95  		r, ok := m.(*istanbul.Request)
    96  		if !ok {
    97  			c.logger.Warn("Malformed request, skip", "m", m)
    98  			continue
    99  		}
   100  
   101  		// Push back if it's a future message
   102  		err := c.checkRequestMsg(r)
   103  		if err == nil {
   104  			c.logger.Trace("Post pending request", "number", r.Proposal.Number(), "hash", r.Proposal.Hash())
   105  
   106  			go c.sendEvent(istanbul.RequestEvent{
   107  				Proposal: r.Proposal,
   108  			})
   109  		} else if err == errFutureMessage {
   110  			c.logger.Trace("Stop processing request", "number", r.Proposal.Number(), "hash", r.Proposal.Hash())
   111  			c.pendingRequests.Push(m, prio)
   112  			break
   113  		} else if err != nil {
   114  			c.logger.Trace("Skip the pending request", "number", r.Proposal.Number(), "hash", r.Proposal.Hash(), "err", err)
   115  		}
   116  
   117  	}
   118  }