github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/backend/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 backend
    18  
    19  import (
    20  	"errors"
    21  	"reflect"
    22  
    23  	"github.com/ethereum/go-ethereum/common"
    24  	"github.com/ethereum/go-ethereum/consensus"
    25  	"github.com/ethereum/go-ethereum/consensus/istanbul"
    26  	"github.com/ethereum/go-ethereum/core/types"
    27  	"github.com/ethereum/go-ethereum/event"
    28  	"github.com/ethereum/go-ethereum/p2p"
    29  	lru "github.com/hashicorp/golang-lru"
    30  )
    31  
    32  // If you want to add a code, you need to increment the Lengths Array size!
    33  const (
    34  	istanbulConsensusMsg      = 0x11
    35  	istanbulAnnounceMsg       = 0x12
    36  	istanbulValEnodesShareMsg = 0x13
    37  	istanbulFwdMsg            = 0x14
    38  	istanbulDelegateSign      = 0x15
    39  )
    40  
    41  var (
    42  	// errDecodeFailed is returned when decode message fails
    43  	errDecodeFailed = errors.New("fail to decode istanbul message")
    44  )
    45  
    46  // Protocol implements consensus.Engine.Protocol
    47  func (sb *Backend) Protocol() consensus.Protocol {
    48  	return consensus.Protocol{
    49  		Name:     "istanbul",
    50  		Versions: []uint{64},
    51  		Lengths:  []uint64{22},
    52  		Primary:  true,
    53  	}
    54  }
    55  
    56  func (sb *Backend) isIstanbulMsg(msg p2p.Msg) bool {
    57  	return (msg.Code == istanbulConsensusMsg) || (msg.Code == istanbulAnnounceMsg) || (msg.Code == istanbulValEnodesShareMsg) || (msg.Code == istanbulFwdMsg) || (msg.Code == istanbulDelegateSign)
    58  }
    59  
    60  // HandleMsg implements consensus.Handler.HandleMsg
    61  func (sb *Backend) HandleMsg(addr common.Address, msg p2p.Msg, peer consensus.Peer) (bool, error) {
    62  	sb.coreMu.Lock()
    63  	defer sb.coreMu.Unlock()
    64  
    65  	sb.logger.Trace("HandleMsg called", "address", addr, "m", msg, "peer", peer.Node())
    66  
    67  	if sb.isIstanbulMsg(msg) {
    68  		if (!sb.coreStarted && !sb.config.Proxy) && (msg.Code == istanbulConsensusMsg) {
    69  			return true, istanbul.ErrStoppedEngine
    70  		}
    71  
    72  		var data []byte
    73  		if err := msg.Decode(&data); err != nil {
    74  			if err == errUnauthorized {
    75  				sb.logger.Debug("Failed to decode message payload", "err", err)
    76  			} else {
    77  				sb.logger.Error("Failed to decode message payload", "err", err)
    78  			}
    79  			return true, errDecodeFailed
    80  		}
    81  
    82  		if msg.Code == istanbulDelegateSign {
    83  			if sb.shouldHandleDelegateSign() {
    84  				go sb.delegateSignFeed.Send(istanbul.MessageEvent{Payload: data})
    85  				return true, nil
    86  			}
    87  
    88  			return true, errors.New("No proxy or proxied validator found")
    89  		}
    90  
    91  		hash := istanbul.RLPHash(data)
    92  
    93  		// Mark peer's message
    94  		ms, ok := sb.recentMessages.Get(addr)
    95  		var m *lru.ARCCache
    96  		if ok {
    97  			m, _ = ms.(*lru.ARCCache)
    98  		} else {
    99  			m, _ = lru.NewARC(inmemoryMessages)
   100  			sb.recentMessages.Add(addr, m)
   101  		}
   102  		m.Add(hash, true)
   103  
   104  		// Mark self known message
   105  		if _, ok := sb.knownMessages.Get(hash); ok {
   106  			return true, nil
   107  		}
   108  		sb.knownMessages.Add(hash, true)
   109  
   110  		if msg.Code == istanbulConsensusMsg {
   111  			err := sb.handleConsensusMsg(peer, data)
   112  			return true, err
   113  		} else if msg.Code == istanbulFwdMsg {
   114  			err := sb.handleFwdMsg(peer, data)
   115  			return true, err
   116  		} else if msg.Code == istanbulAnnounceMsg {
   117  			go sb.handleIstAnnounce(data)
   118  		} else if msg.Code == istanbulValEnodesShareMsg {
   119  			go sb.handleValEnodesShareMsg(data)
   120  		}
   121  
   122  		return true, nil
   123  	}
   124  	return false, nil
   125  }
   126  
   127  // Handle an incoming consensus msg
   128  func (sb *Backend) handleConsensusMsg(peer consensus.Peer, payload []byte) error {
   129  	if sb.config.Proxy {
   130  		// Verify that this message is not from the proxied peer
   131  		if reflect.DeepEqual(peer, sb.proxiedPeer) {
   132  			sb.logger.Warn("Got a consensus message from the proxied validator.  Ignoring it")
   133  			return nil
   134  		}
   135  
   136  		// Need to forward the message to the proxied validator
   137  		sb.logger.Trace("Forwarding consensus message to proxied validator")
   138  		if sb.proxiedPeer != nil {
   139  			go sb.proxiedPeer.Send(istanbulConsensusMsg, payload)
   140  		}
   141  	} else { // The case when this node is a validator
   142  		go sb.istanbulEventMux.Post(istanbul.MessageEvent{
   143  			Payload: payload,
   144  		})
   145  	}
   146  
   147  	return nil
   148  }
   149  
   150  // Handle an incoming forward msg
   151  func (sb *Backend) handleFwdMsg(peer consensus.Peer, payload []byte) error {
   152  	// Ignore the message if this node it not a proxy
   153  	if !sb.config.Proxy {
   154  		sb.logger.Warn("Got a forward consensus message and this node is not a proxy.  Ignoring it")
   155  		return nil
   156  	}
   157  
   158  	// Verify that it's coming from the proxied peer
   159  	if !reflect.DeepEqual(peer, sb.proxiedPeer) {
   160  		sb.logger.Warn("Got a forward consensus message from a non proxied valiator.  Ignoring it")
   161  		return nil
   162  	}
   163  
   164  	istMsg := new(istanbul.Message)
   165  
   166  	// An Istanbul FwdMsg doesn't have a signature since it's coming from a trusted peer and
   167  	// the wrapped message is already signed by the proxied validator.
   168  	if err := istMsg.FromPayload(payload, nil); err != nil {
   169  		sb.logger.Error("Failed to decode message from payload", "err", err)
   170  		return err
   171  	}
   172  
   173  	var fwdMsg *istanbul.ForwardMessage
   174  	err := istMsg.Decode(&fwdMsg)
   175  	if err != nil {
   176  		sb.logger.Error("Failed to decode a ForwardMessage", "err", err)
   177  		return err
   178  	}
   179  
   180  	sb.logger.Debug("Forwarding a consensus message")
   181  	go sb.Gossip(fwdMsg.DestAddresses, fwdMsg.Msg, istanbulConsensusMsg, false)
   182  	return nil
   183  }
   184  
   185  func (sb *Backend) shouldHandleDelegateSign() bool {
   186  	return sb.IsProxy() || sb.IsProxiedValidator()
   187  }
   188  
   189  // SubscribeNewDelegateSignEvent subscribes a channel to any new delegate sign messages
   190  func (sb *Backend) SubscribeNewDelegateSignEvent(ch chan<- istanbul.MessageEvent) event.Subscription {
   191  	return sb.delegateSignScope.Track(sb.delegateSignFeed.Subscribe(ch))
   192  }
   193  
   194  // SetBroadcaster implements consensus.Handler.SetBroadcaster
   195  func (sb *Backend) SetBroadcaster(broadcaster consensus.Broadcaster) {
   196  	sb.broadcaster = broadcaster
   197  }
   198  
   199  // SetP2PServer implements consensus.Handler.SetP2PServer
   200  func (sb *Backend) SetP2PServer(p2pserver consensus.P2PServer) {
   201  	sb.p2pserver = p2pserver
   202  }
   203  
   204  // This function is called by miner/worker.go whenever it's mainLoop gets a newWork event.
   205  func (sb *Backend) NewWork() error {
   206  	sb.coreMu.RLock()
   207  	defer sb.coreMu.RUnlock()
   208  	if !sb.coreStarted {
   209  		return istanbul.ErrStoppedEngine
   210  	}
   211  
   212  	go sb.istanbulEventMux.Post(istanbul.FinalCommittedEvent{})
   213  	return nil
   214  }
   215  
   216  // This function is called by all nodes.
   217  // At the end of each epoch, this function will
   218  //    1)  Output if it is or isn't an elected validator if it has mining turned on.
   219  //    2)  Refresh the validator connections if it's a proxy or non proxied validator
   220  func (sb *Backend) NewChainHead(newBlock *types.Block) {
   221  	if istanbul.IsLastBlockOfEpoch(newBlock.Number().Uint64(), sb.config.Epoch) {
   222  		sb.coreMu.RLock()
   223  		defer sb.coreMu.RUnlock()
   224  
   225  		valset := sb.getValidators(newBlock.Number().Uint64(), newBlock.Hash())
   226  
   227  		// Output whether this validator was or wasn't elected for the
   228  		// new epoch's validator set
   229  		if sb.coreStarted {
   230  			_, val := valset.GetByAddress(sb.ValidatorAddress())
   231  			sb.logger.Info("Validator Election Results", "address", sb.ValidatorAddress(), "elected", (val != nil), "number", newBlock.Number().Uint64())
   232  
   233  			sb.newEpochCh <- struct{}{}
   234  		}
   235  
   236  		// If this is a proxy or a non proxied validator and a
   237  		// new epoch just started, then refresh the validator enode table
   238  		sb.logger.Trace("At end of epoch and going to refresh validator peers", "new block number", newBlock.Number().Uint64())
   239  		sb.RefreshValPeers(valset)
   240  	}
   241  }
   242  
   243  func (sb *Backend) RegisterPeer(peer consensus.Peer, isProxiedPeer bool) {
   244  	// TODO - For added security, we may want the node keys of the proxied validators to be
   245  	//        registered with the proxy, and verify that all newly connected proxied peer has
   246  	//        the correct node key
   247  	if sb.config.Proxy && isProxiedPeer {
   248  		sb.proxiedPeer = peer
   249  	} else if sb.config.Proxied {
   250  		if sb.proxyNode != nil && peer.Node().ID() == sb.proxyNode.node.ID() {
   251  			sb.proxyNode.peer = peer
   252  		} else {
   253  			sb.logger.Error("Unauthorized connected peer to the proxied validator", "peer node", peer.Node().String())
   254  		}
   255  	}
   256  }
   257  
   258  func (sb *Backend) UnregisterPeer(peer consensus.Peer, isProxiedPeer bool) {
   259  	if sb.config.Proxy && isProxiedPeer && reflect.DeepEqual(sb.proxiedPeer, peer) {
   260  		sb.proxiedPeer = nil
   261  	} else if sb.config.Proxied {
   262  		if sb.proxyNode != nil && peer.Node().ID() == sb.proxyNode.node.ID() {
   263  			sb.proxyNode.peer = nil
   264  		}
   265  	}
   266  }