github.com/ConsenSys/Quorum@v20.10.0+incompatible/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  	"github.com/ethereum/go-ethereum/common"
    21  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    22  )
    23  
    24  // Start implements core.Engine.Start
    25  func (c *core) Start() error {
    26  	// Start a new round from last sequence + 1
    27  	c.startNewRound(common.Big0)
    28  
    29  	// Tests will handle events itself, so we have to make subscribeEvents()
    30  	// be able to call in test.
    31  	c.subscribeEvents()
    32  	go c.handleEvents()
    33  
    34  	return nil
    35  }
    36  
    37  // Stop implements core.Engine.Stop
    38  func (c *core) Stop() error {
    39  	c.stopTimer()
    40  	c.unsubscribeEvents()
    41  
    42  	// Make sure the handler goroutine exits
    43  	c.handlerWg.Wait()
    44  	return nil
    45  }
    46  
    47  // ----------------------------------------------------------------------------
    48  
    49  // Subscribe both internal and external events
    50  func (c *core) subscribeEvents() {
    51  	c.events = c.backend.EventMux().Subscribe(
    52  		// external events
    53  		istanbul.RequestEvent{},
    54  		istanbul.MessageEvent{},
    55  		// internal events
    56  		backlogEvent{},
    57  	)
    58  	c.timeoutSub = c.backend.EventMux().Subscribe(
    59  		timeoutEvent{},
    60  	)
    61  	c.finalCommittedSub = c.backend.EventMux().Subscribe(
    62  		istanbul.FinalCommittedEvent{},
    63  	)
    64  }
    65  
    66  // Unsubscribe all events
    67  func (c *core) unsubscribeEvents() {
    68  	c.events.Unsubscribe()
    69  	c.timeoutSub.Unsubscribe()
    70  	c.finalCommittedSub.Unsubscribe()
    71  }
    72  
    73  func (c *core) handleEvents() {
    74  	// Clear state
    75  	defer func() {
    76  		c.current = nil
    77  		c.handlerWg.Done()
    78  	}()
    79  
    80  	c.handlerWg.Add(1)
    81  	for {
    82  		select {
    83  		case event, ok := <-c.events.Chan():
    84  			if !ok {
    85  				return
    86  			}
    87  			// A real event arrived, process interesting content
    88  			switch ev := event.Data.(type) {
    89  			case istanbul.RequestEvent:
    90  				r := &istanbul.Request{
    91  					Proposal: ev.Proposal,
    92  				}
    93  				err := c.handleRequest(r)
    94  				if err == errFutureMessage {
    95  					c.storeRequestMsg(r)
    96  				}
    97  			case istanbul.MessageEvent:
    98  				if err := c.handleMsg(ev.Payload); err == nil {
    99  					c.backend.Gossip(c.valSet, ev.Payload)
   100  				}
   101  			case backlogEvent:
   102  				// No need to check signature for internal messages
   103  				if err := c.handleCheckedMsg(ev.msg, ev.src); err == nil {
   104  					p, err := ev.msg.Payload()
   105  					if err != nil {
   106  						c.logger.Warn("Get message payload failed", "err", err)
   107  						continue
   108  					}
   109  					c.backend.Gossip(c.valSet, p)
   110  				}
   111  			}
   112  		case _, ok := <-c.timeoutSub.Chan():
   113  			if !ok {
   114  				return
   115  			}
   116  			c.handleTimeoutMsg()
   117  		case event, ok := <-c.finalCommittedSub.Chan():
   118  			if !ok {
   119  				return
   120  			}
   121  			switch event.Data.(type) {
   122  			case istanbul.FinalCommittedEvent:
   123  				c.handleFinalCommitted()
   124  			}
   125  		}
   126  	}
   127  }
   128  
   129  // sendEvent sends events to mux
   130  func (c *core) sendEvent(ev interface{}) {
   131  	c.backend.EventMux().Post(ev)
   132  }
   133  
   134  func (c *core) handleMsg(payload []byte) error {
   135  	logger := c.logger.New()
   136  
   137  	// Decode message and check its signature
   138  	msg := new(message)
   139  	if err := msg.FromPayload(payload, c.validateFn); err != nil {
   140  		logger.Error("Failed to decode message from payload", "err", err)
   141  		return err
   142  	}
   143  
   144  	// Only accept message if the address is valid
   145  	_, src := c.valSet.GetByAddress(msg.Address)
   146  	if src == nil {
   147  		logger.Error("Invalid address in message", "msg", msg)
   148  		return istanbul.ErrUnauthorizedAddress
   149  	}
   150  
   151  	return c.handleCheckedMsg(msg, src)
   152  }
   153  
   154  func (c *core) handleCheckedMsg(msg *message, src istanbul.Validator) error {
   155  	logger := c.logger.New("address", c.address, "from", src)
   156  
   157  	// Store the message if it's a future message
   158  	testBacklog := func(err error) error {
   159  		if err == errFutureMessage {
   160  			c.storeBacklog(msg, src)
   161  		}
   162  
   163  		return err
   164  	}
   165  
   166  	switch msg.Code {
   167  	case msgPreprepare:
   168  		return testBacklog(c.handlePreprepare(msg, src))
   169  	case msgPrepare:
   170  		return testBacklog(c.handlePrepare(msg, src))
   171  	case msgCommit:
   172  		return testBacklog(c.handleCommit(msg, src))
   173  	case msgRoundChange:
   174  		return testBacklog(c.handleRoundChange(msg, src))
   175  	default:
   176  		logger.Error("Invalid message", "msg", msg)
   177  	}
   178  
   179  	return errInvalidMessage
   180  }
   181  
   182  func (c *core) handleTimeoutMsg() {
   183  	// If we're not waiting for round change yet, we can try to catch up
   184  	// the max round with F+1 round change message. We only need to catch up
   185  	// if the max round is larger than current round.
   186  	if !c.waitingForRoundChange {
   187  		maxRound := c.roundChangeSet.MaxRound(c.valSet.F() + 1)
   188  		if maxRound != nil && maxRound.Cmp(c.current.Round()) > 0 {
   189  			c.sendRoundChange(maxRound)
   190  			return
   191  		}
   192  	}
   193  
   194  	lastProposal, _ := c.backend.LastProposal()
   195  	if lastProposal != nil && lastProposal.Number().Cmp(c.current.Sequence()) >= 0 {
   196  		c.logger.Trace("round change timeout, catch up latest sequence", "number", lastProposal.Number().Uint64())
   197  		c.startNewRound(common.Big0)
   198  	} else {
   199  		c.sendNextRoundChange()
   200  	}
   201  }