github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/core/handler.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  // Start implements core.Engine.Start
    27  func (c *core) Start() error {
    28  
    29  	roundState, err := c.createRoundState()
    30  	if err != nil {
    31  		return err
    32  	}
    33  
    34  	c.current = roundState
    35  	c.roundChangeSet = newRoundChangeSet(c.current.ValidatorSet())
    36  
    37  	// Reset the Round Change timer for the current round to timeout.
    38  	// (If we've restored RoundState such that we are in StateWaitingForRoundChange,
    39  	// this may also start a timer to send a repeat round change message.)
    40  	c.resetRoundChangeTimer()
    41  
    42  	// Process backlog
    43  	c.processPendingRequests()
    44  	c.backlog.updateState(c.CurrentView(), c.current.State())
    45  
    46  	// Tests will handle events itself, so we have to make subscribeEvents()
    47  	// be able to call in test.
    48  	c.subscribeEvents()
    49  	go c.handleEvents()
    50  
    51  	return nil
    52  }
    53  
    54  // Stop implements core.Engine.Stop
    55  func (c *core) Stop() error {
    56  	c.stopAllTimers()
    57  	c.unsubscribeEvents()
    58  
    59  	// Make sure the handler goroutine exits
    60  	c.handlerWg.Wait()
    61  
    62  	c.current = nil
    63  	return nil
    64  }
    65  
    66  // ----------------------------------------------------------------------------
    67  
    68  // Subscribe both internal and external events
    69  func (c *core) subscribeEvents() {
    70  	c.events = c.backend.EventMux().Subscribe(
    71  		// external events
    72  		istanbul.RequestEvent{},
    73  		istanbul.MessageEvent{},
    74  		// internal events
    75  		backlogEvent{},
    76  	)
    77  	c.timeoutSub = c.backend.EventMux().Subscribe(
    78  		timeoutAndMoveToNextRoundEvent{},
    79  		resendRoundChangeEvent{},
    80  	)
    81  	c.finalCommittedSub = c.backend.EventMux().Subscribe(
    82  		istanbul.FinalCommittedEvent{},
    83  	)
    84  }
    85  
    86  // Unsubscribe all events
    87  func (c *core) unsubscribeEvents() {
    88  	c.events.Unsubscribe()
    89  	c.timeoutSub.Unsubscribe()
    90  	c.finalCommittedSub.Unsubscribe()
    91  }
    92  
    93  func (c *core) handleEvents() {
    94  	// Clear state
    95  	defer func() {
    96  		c.handlerWg.Done()
    97  	}()
    98  
    99  	c.handlerWg.Add(1)
   100  
   101  	for {
   102  		logger := c.newLogger("func", "handleEvents")
   103  		select {
   104  		case event, ok := <-c.events.Chan():
   105  			if !ok {
   106  				return
   107  			}
   108  			// A real event arrived, process interesting content
   109  			switch ev := event.Data.(type) {
   110  			case istanbul.RequestEvent:
   111  				r := &istanbul.Request{
   112  					Proposal: ev.Proposal,
   113  				}
   114  				err := c.handleRequest(r)
   115  				if err == errFutureMessage {
   116  					c.storeRequestMsg(r)
   117  				}
   118  			case istanbul.MessageEvent:
   119  				if err := c.handleMsg(ev.Payload); err != nil {
   120  					logger.Debug("Error in handling istanbul message", "err", err)
   121  				}
   122  			case backlogEvent:
   123  				if payload, err := ev.msg.Payload(); err != nil {
   124  					logger.Error("Error in retrieving payload from istanbul message that was sent from a backlog event", "err", err)
   125  				} else {
   126  					if err := c.handleMsg(payload); err != nil {
   127  						logger.Debug("Error in handling istanbul message that was sent from a backlog event", "err", err)
   128  					}
   129  				}
   130  			}
   131  		case event, ok := <-c.timeoutSub.Chan():
   132  			if !ok {
   133  				return
   134  			}
   135  			switch ev := event.Data.(type) {
   136  			case timeoutAndMoveToNextRoundEvent:
   137  				if err := c.handleTimeoutAndMoveToNextRound(ev.view); err != nil {
   138  					logger.Error("Error on handleTimeoutAndMoveToNextRound", "err", err)
   139  				}
   140  			case resendRoundChangeEvent:
   141  				if err := c.handleResendRoundChangeEvent(ev.view); err != nil {
   142  					logger.Error("Error on handleResendRoundChangeEvent", "err", err)
   143  				}
   144  			}
   145  		case event, ok := <-c.finalCommittedSub.Chan():
   146  			if !ok {
   147  				return
   148  			}
   149  			switch event.Data.(type) {
   150  			case istanbul.FinalCommittedEvent:
   151  				if err := c.handleFinalCommitted(); err != nil {
   152  					logger.Error("Error on handleFinalCommit", "err", err)
   153  				}
   154  			}
   155  		}
   156  	}
   157  }
   158  
   159  // sendEvent sends events to mux
   160  func (c *core) sendEvent(ev interface{}) {
   161  	c.backend.EventMux().Post(ev)
   162  }
   163  
   164  func (c *core) handleMsg(payload []byte) error {
   165  	logger := c.newLogger("func", "handleMsg")
   166  
   167  	// Decode message and check its signature
   168  	msg := new(istanbul.Message)
   169  	if err := msg.FromPayload(payload, c.validateFn); err != nil {
   170  		logger.Debug("Failed to decode message from payload", "err", err)
   171  		return err
   172  	}
   173  
   174  	// Only accept message if the address is valid
   175  	_, src := c.current.ValidatorSet().GetByAddress(msg.Address)
   176  	if src == nil {
   177  		logger.Error("Invalid address in message", "m", msg)
   178  		return istanbul.ErrUnauthorizedAddress
   179  	}
   180  
   181  	return c.handleCheckedMsg(msg, src)
   182  }
   183  
   184  func (c *core) handleCheckedMsg(msg *istanbul.Message, src istanbul.Validator) error {
   185  	logger := c.newLogger("func", "handleCheckedMsg", "from", msg.Address)
   186  
   187  	// Store the message if it's a future message
   188  	catchFutureMessages := func(err error) error {
   189  		if err == errFutureMessage {
   190  			// Store in backlog (if it's not from self)
   191  			if msg.Address != c.address {
   192  				c.backlog.store(msg)
   193  			}
   194  		}
   195  		return err
   196  	}
   197  
   198  	switch msg.Code {
   199  	case istanbul.MsgPreprepare:
   200  		return catchFutureMessages(c.handlePreprepare(msg))
   201  	case istanbul.MsgPrepare:
   202  		return catchFutureMessages(c.handlePrepare(msg))
   203  	case istanbul.MsgCommit:
   204  		return catchFutureMessages(c.handleCommit(msg))
   205  	case istanbul.MsgRoundChange:
   206  		return catchFutureMessages(c.handleRoundChange(msg))
   207  	default:
   208  		logger.Error("Invalid message", "m", msg)
   209  	}
   210  
   211  	return errInvalidMessage
   212  }
   213  
   214  func (c *core) handleTimeoutAndMoveToNextRound(timedOutView *istanbul.View) error {
   215  	logger := c.newLogger("func", "handleTimeoutAndMoveToNextRound", "timed_out_seq", timedOutView.Sequence, "timed_out_round", timedOutView.Round)
   216  
   217  	// Avoid races where message is enqueued then a later event advances sequence or desired round.
   218  	if c.current.Sequence().Cmp(timedOutView.Sequence) != 0 || c.current.DesiredRound().Cmp(timedOutView.Round) != 0 {
   219  		logger.Trace("Timed out but now on a different view")
   220  		return nil
   221  	}
   222  
   223  	logger.Debug("Timed out, trying to wait for next round")
   224  	nextRound := new(big.Int).Add(timedOutView.Round, common.Big1)
   225  	return c.waitForDesiredRound(nextRound)
   226  }
   227  
   228  func (c *core) handleResendRoundChangeEvent(desiredView *istanbul.View) error {
   229  	logger := c.newLogger("func", "handleResendRoundChangeEvent", "set_at_seq", desiredView.Sequence, "set_at_desiredRound", desiredView.Round)
   230  
   231  	// Avoid races where message is enqueued then a later event advances sequence or desired round.
   232  	if c.current.Sequence().Cmp(desiredView.Sequence) != 0 || c.current.DesiredRound().Cmp(desiredView.Round) != 0 {
   233  		logger.Trace("Timed out but now on a different view")
   234  		return nil
   235  	}
   236  
   237  	c.resendRoundChangeMessage()
   238  	return nil
   239  }