github.com/hyperion-hyn/go-ethereum@v2.4.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  
    82  	for {
    83  		select {
    84  		case event, ok := <-c.events.Chan():
    85  			if !ok {
    86  				return
    87  			}
    88  			// A real event arrived, process interesting content
    89  			switch ev := event.Data.(type) {
    90  			case istanbul.RequestEvent:
    91  				r := &istanbul.Request{
    92  					Proposal: ev.Proposal,
    93  				}
    94  				err := c.handleRequest(r)
    95  				if err == errFutureMessage {
    96  					c.storeRequestMsg(r)
    97  				}
    98  			case istanbul.MessageEvent:
    99  				if err := c.handleMsg(ev.Payload); err == nil {
   100  					c.backend.Gossip(c.valSet, ev.Payload)
   101  				}
   102  			case backlogEvent:
   103  				// No need to check signature for internal messages
   104  				if err := c.handleCheckedMsg(ev.msg, ev.src); err == nil {
   105  					p, err := ev.msg.Payload()
   106  					if err != nil {
   107  						c.logger.Warn("Get message payload failed", "err", err)
   108  						continue
   109  					}
   110  					c.backend.Gossip(c.valSet, p)
   111  				}
   112  			}
   113  		case _, ok := <-c.timeoutSub.Chan():
   114  			if !ok {
   115  				return
   116  			}
   117  			c.handleTimeoutMsg()
   118  		case event, ok := <-c.finalCommittedSub.Chan():
   119  			if !ok {
   120  				return
   121  			}
   122  			switch event.Data.(type) {
   123  			case istanbul.FinalCommittedEvent:
   124  				c.handleFinalCommitted()
   125  			}
   126  		}
   127  	}
   128  }
   129  
   130  // sendEvent sends events to mux
   131  func (c *core) sendEvent(ev interface{}) {
   132  	c.backend.EventMux().Post(ev)
   133  }
   134  
   135  func (c *core) handleMsg(payload []byte) error {
   136  	logger := c.logger.New()
   137  
   138  	// Decode message and check its signature
   139  	msg := new(message)
   140  	if err := msg.FromPayload(payload, c.validateFn); err != nil {
   141  		logger.Error("Failed to decode message from payload", "err", err)
   142  		return err
   143  	}
   144  
   145  	// Only accept message if the address is valid
   146  	_, src := c.valSet.GetByAddress(msg.Address)
   147  	if src == nil {
   148  		logger.Error("Invalid address in message", "msg", msg)
   149  		return istanbul.ErrUnauthorizedAddress
   150  	}
   151  
   152  	return c.handleCheckedMsg(msg, src)
   153  }
   154  
   155  func (c *core) handleCheckedMsg(msg *message, src istanbul.Validator) error {
   156  	logger := c.logger.New("address", c.address, "from", src)
   157  
   158  	// Store the message if it's a future message
   159  	testBacklog := func(err error) error {
   160  		if err == errFutureMessage {
   161  			c.storeBacklog(msg, src)
   162  		}
   163  
   164  		return err
   165  	}
   166  
   167  	switch msg.Code {
   168  	case msgPreprepare:
   169  		return testBacklog(c.handlePreprepare(msg, src))
   170  	case msgPrepare:
   171  		return testBacklog(c.handlePrepare(msg, src))
   172  	case msgCommit:
   173  		return testBacklog(c.handleCommit(msg, src))
   174  	case msgRoundChange:
   175  		return testBacklog(c.handleRoundChange(msg, src))
   176  	default:
   177  		logger.Error("Invalid message", "msg", msg)
   178  	}
   179  
   180  	return errInvalidMessage
   181  }
   182  
   183  func (c *core) handleTimeoutMsg() {
   184  	// If we're not waiting for round change yet, we can try to catch up
   185  	// the max round with F+1 round change message. We only need to catch up
   186  	// if the max round is larger than current round.
   187  	if !c.waitingForRoundChange {
   188  		maxRound := c.roundChangeSet.MaxRound(c.valSet.F() + 1)
   189  		if maxRound != nil && maxRound.Cmp(c.current.Round()) > 0 {
   190  			c.sendRoundChange(maxRound)
   191  			return
   192  		}
   193  	}
   194  
   195  	lastProposal, _ := c.backend.LastProposal()
   196  	if lastProposal != nil && lastProposal.Number().Cmp(c.current.Sequence()) >= 0 {
   197  		c.logger.Trace("round change timeout, catch up latest sequence", "number", lastProposal.Number().Uint64())
   198  		c.startNewRound(common.Big0)
   199  	} else {
   200  		c.sendNextRoundChange()
   201  	}
   202  }