github.com/electroneum/electroneum-sc@v0.0.0-20230105223411-3bc1d078281e/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  	"bytes"
    21  	"errors"
    22  	"io"
    23  	"math/big"
    24  	"reflect"
    25  
    26  	"github.com/electroneum/electroneum-sc/common"
    27  	"github.com/electroneum/electroneum-sc/consensus"
    28  	"github.com/electroneum/electroneum-sc/consensus/istanbul"
    29  	qbfttypes "github.com/electroneum/electroneum-sc/consensus/istanbul/types"
    30  	"github.com/electroneum/electroneum-sc/core/types"
    31  	"github.com/electroneum/electroneum-sc/p2p"
    32  	lru "github.com/hashicorp/golang-lru"
    33  )
    34  
    35  const (
    36  	NewBlockMsg = 0x07
    37  	istanbulMsg = 0x11
    38  )
    39  
    40  var (
    41  	// errDecodeFailed is returned when decode message fails
    42  	errDecodeFailed = errors.New("fail to decode istanbul message")
    43  
    44  	// errPayloadReadFailed is returned when qbft message read fails
    45  	errPayloadReadFailed = errors.New("unable to read payload from message")
    46  )
    47  
    48  // Protocol implements consensus.Engine.Protocol
    49  func (sb *Backend) Protocol() consensus.Protocol {
    50  	return consensus.IstanbulProtocol
    51  }
    52  
    53  func (sb *Backend) decode(msg p2p.Msg) ([]byte, common.Hash, error) {
    54  	var data []byte = make([]byte, msg.Size)
    55  	if _, err := msg.Payload.Read(data); err != nil {
    56  		return nil, common.Hash{}, errPayloadReadFailed
    57  	}
    58  	return data, istanbul.RLPHash(data), nil
    59  }
    60  
    61  // HandleMsg implements consensus.Handler.HandleMsg
    62  func (sb *Backend) HandleMsg(addr common.Address, msg p2p.Msg) (bool, error) {
    63  	sb.coreMu.Lock()
    64  	defer sb.coreMu.Unlock()
    65  	if _, ok := qbfttypes.MessageCodes()[msg.Code]; ok || msg.Code == istanbulMsg {
    66  		if !sb.coreStarted {
    67  			return true, istanbul.ErrStoppedEngine
    68  		}
    69  
    70  		data, hash, err := sb.decode(msg)
    71  		if err != nil {
    72  			return true, errDecodeFailed
    73  		}
    74  		// Mark peer's message
    75  		ms, ok := sb.recentMessages.Get(addr)
    76  		var m *lru.ARCCache
    77  		if ok {
    78  			m, _ = ms.(*lru.ARCCache)
    79  		} else {
    80  			m, _ = lru.NewARC(inmemoryMessages)
    81  			sb.recentMessages.Add(addr, m)
    82  		}
    83  		m.Add(hash, true)
    84  
    85  		// Mark self known message
    86  		if _, ok := sb.knownMessages.Get(hash); ok {
    87  			return true, nil
    88  		}
    89  		sb.knownMessages.Add(hash, true)
    90  
    91  		go sb.istanbulEventMux.Post(istanbul.MessageEvent{
    92  			Code:    msg.Code,
    93  			Payload: data,
    94  		})
    95  		return true, nil
    96  	}
    97  	//https://github.com/ConsenSys/quorum/pull/539
    98  	//https://github.com/ConsenSys/quorum/issues/389
    99  	if msg.Code == NewBlockMsg && sb.core != nil && sb.core.IsProposer() { // eth.NewBlockMsg: import cycle
   100  		// this case is to safeguard the race of similar block which gets propagated from other node while this node is proposing
   101  		// as p2p.Msg can only be decoded once (get EOF for any subsequence read), we need to make sure the payload is restored after we decode it
   102  		sb.logger.Debug("IBFT: received NewBlockMsg", "size", msg.Size, "payload.type", reflect.TypeOf(msg.Payload), "sender", addr)
   103  		if reader, ok := msg.Payload.(*bytes.Reader); ok {
   104  			payload, err := io.ReadAll(reader)
   105  			if err != nil {
   106  				return true, err
   107  			}
   108  			reader.Reset(payload)       // ready to be decoded
   109  			defer reader.Reset(payload) // restore so main eth/handler can decode
   110  			var request struct {        // this has to be same as eth/protocol.go#newBlockData as we are reading NewBlockMsg
   111  				Block *types.Block
   112  				TD    *big.Int
   113  			}
   114  			if err := msg.Decode(&request); err != nil {
   115  				sb.logger.Error("IBFT: unable to decode the NewBlockMsg", "error", err)
   116  				return false, nil
   117  			}
   118  			newRequestedBlock := request.Block
   119  			if newRequestedBlock.Header().MixDigest == types.IstanbulDigest && sb.core.IsCurrentProposal(newRequestedBlock.Hash()) {
   120  				sb.logger.Debug("IBFT: block already proposed", "hash", newRequestedBlock.Hash(), "sender", addr)
   121  				return true, nil
   122  			}
   123  		}
   124  	}
   125  	return false, nil
   126  }
   127  
   128  // SetBroadcaster implements consensus.Handler.SetBroadcaster
   129  func (sb *Backend) SetBroadcaster(broadcaster consensus.Broadcaster) {
   130  	sb.broadcaster = broadcaster
   131  }
   132  
   133  func (sb *Backend) NewChainHead() error {
   134  	sb.coreMu.RLock()
   135  	defer sb.coreMu.RUnlock()
   136  	if !sb.coreStarted {
   137  		return istanbul.ErrStoppedEngine
   138  	}
   139  	go sb.istanbulEventMux.Post(istanbul.FinalCommittedEvent{})
   140  	return nil
   141  }