github.com/electroneum/electroneum-sc@v0.0.0-20230105223411-3bc1d078281e/eth/handler.go (about)

     1  // Copyright 2015 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 eth
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"math"
    23  	"math/big"
    24  	"sync"
    25  	"sync/atomic"
    26  	"time"
    27  
    28  	"github.com/electroneum/electroneum-sc/common"
    29  	"github.com/electroneum/electroneum-sc/consensus"
    30  	"github.com/electroneum/electroneum-sc/consensus/beacon"
    31  	"github.com/electroneum/electroneum-sc/consensus/clique"
    32  	"github.com/electroneum/electroneum-sc/consensus/ethash"
    33  	"github.com/electroneum/electroneum-sc/core"
    34  	"github.com/electroneum/electroneum-sc/core/forkid"
    35  	"github.com/electroneum/electroneum-sc/core/types"
    36  	"github.com/electroneum/electroneum-sc/crypto"
    37  	"github.com/electroneum/electroneum-sc/eth/downloader"
    38  	"github.com/electroneum/electroneum-sc/eth/fetcher"
    39  	"github.com/electroneum/electroneum-sc/eth/protocols/eth"
    40  	"github.com/electroneum/electroneum-sc/eth/protocols/snap"
    41  	"github.com/electroneum/electroneum-sc/ethdb"
    42  	"github.com/electroneum/electroneum-sc/event"
    43  	"github.com/electroneum/electroneum-sc/log"
    44  	"github.com/electroneum/electroneum-sc/p2p"
    45  	"github.com/electroneum/electroneum-sc/p2p/enode"
    46  	"github.com/electroneum/electroneum-sc/params"
    47  )
    48  
    49  const (
    50  	// txChanSize is the size of channel listening to NewTxsEvent.
    51  	// The number is referenced from the size of tx pool.
    52  	txChanSize         = 4096
    53  	protocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message
    54  )
    55  
    56  var (
    57  	syncChallengeTimeout = 15 * time.Second // Time allowance for a node to reply to the sync progress challenge
    58  	errMsgTooLarge       = errors.New("message too long")
    59  )
    60  
    61  // txPool defines the methods needed from a transaction pool implementation to
    62  // support all the operations needed by the Ethereum chain protocols.
    63  type txPool interface {
    64  	// Has returns an indicator whether txpool has a transaction
    65  	// cached with the given hash.
    66  	Has(hash common.Hash) bool
    67  
    68  	// Get retrieves the transaction from local txpool with given
    69  	// tx hash.
    70  	Get(hash common.Hash) *types.Transaction
    71  
    72  	// AddRemotes should add the given transactions to the pool.
    73  	AddRemotes([]*types.Transaction) []error
    74  
    75  	// Pending should return pending transactions.
    76  	// The slice should be modifiable by the caller.
    77  	Pending(enforceTips bool) map[common.Address]types.Transactions
    78  
    79  	// SubscribeNewTxsEvent should return an event subscription of
    80  	// NewTxsEvent and send events to the given channel.
    81  	SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
    82  }
    83  
    84  // handlerConfig is the collection of initialization parameters to create a full
    85  // node network handler.
    86  type handlerConfig struct {
    87  	Database   ethdb.Database            // Database for direct sync insertions
    88  	Chain      *core.BlockChain          // Blockchain to serve data from
    89  	TxPool     txPool                    // Transaction pool to propagate from
    90  	Merger     *consensus.Merger         // The manager for eth1/2 transition
    91  	Network    uint64                    // Network identifier to adfvertise
    92  	Sync       downloader.SyncMode       // Whether to snap or full sync
    93  	BloomCache uint64                    // Megabytes to alloc for snap sync bloom
    94  	EventMux   *event.TypeMux            // Legacy event mux, deprecate for `feed`
    95  	Checkpoint *params.TrustedCheckpoint // Hard coded checkpoint for sync challenges
    96  
    97  	RequiredBlocks map[uint64]common.Hash // Hard coded map of required block hashes for sync challenges
    98  
    99  	Engine consensus.Engine
   100  }
   101  
   102  type handler struct {
   103  	networkID  uint64
   104  	forkFilter forkid.Filter // Fork ID filter, constant across the lifetime of the node
   105  
   106  	snapSync  uint32 // Flag whether snap sync is enabled (gets disabled if we already have blocks)
   107  	acceptTxs uint32 // Flag whether we're considered synchronised (enables transaction processing)
   108  
   109  	checkpointNumber uint64      // Block number for the sync progress validator to cross reference
   110  	checkpointHash   common.Hash // Block hash for the sync progress validator to cross reference
   111  
   112  	database ethdb.Database
   113  	txpool   txPool
   114  	chain    *core.BlockChain
   115  	maxPeers int
   116  
   117  	downloader   *downloader.Downloader
   118  	blockFetcher *fetcher.BlockFetcher
   119  	txFetcher    *fetcher.TxFetcher
   120  	peers        *peerSet
   121  	merger       *consensus.Merger
   122  
   123  	eventMux      *event.TypeMux
   124  	txsCh         chan core.NewTxsEvent
   125  	txsSub        event.Subscription
   126  	minedBlockSub *event.TypeMuxSubscription
   127  
   128  	requiredBlocks map[uint64]common.Hash
   129  
   130  	// channels for fetcher, syncer, txsyncLoop
   131  	quitSync chan struct{}
   132  
   133  	chainSync *chainSyncer
   134  	wg        sync.WaitGroup
   135  	peerWG    sync.WaitGroup
   136  
   137  	engine consensus.Engine
   138  }
   139  
   140  // newHandler returns a handler for all Ethereum chain management protocol.
   141  func newHandler(config *handlerConfig) (*handler, error) {
   142  	// Create the protocol manager with the base fields
   143  	if config.EventMux == nil {
   144  		config.EventMux = new(event.TypeMux) // Nicety initialization for tests
   145  	}
   146  	h := &handler{
   147  		networkID:      config.Network,
   148  		forkFilter:     forkid.NewFilter(config.Chain),
   149  		eventMux:       config.EventMux,
   150  		database:       config.Database,
   151  		txpool:         config.TxPool,
   152  		chain:          config.Chain,
   153  		peers:          newPeerSet(),
   154  		merger:         config.Merger,
   155  		requiredBlocks: config.RequiredBlocks,
   156  		quitSync:       make(chan struct{}),
   157  		engine:         config.Engine,
   158  	}
   159  
   160  	if handler, ok := h.engine.(consensus.Handler); ok {
   161  		handler.SetBroadcaster(h)
   162  	}
   163  
   164  	if config.Sync == downloader.FullSync {
   165  		// The database seems empty as the current block is the genesis. Yet the snap
   166  		// block is ahead, so snap sync was enabled for this node at a certain point.
   167  		// The scenarios where this can happen is
   168  		// * if the user manually (or via a bad block) rolled back a snap sync node
   169  		//   below the sync point.
   170  		// * the last snap sync is not finished while user specifies a full sync this
   171  		//   time. But we don't have any recent state for full sync.
   172  		// In these cases however it's safe to reenable snap sync.
   173  		fullBlock, fastBlock := h.chain.CurrentBlock(), h.chain.CurrentFastBlock()
   174  		if fullBlock.NumberU64() == 0 && fastBlock.NumberU64() > 0 {
   175  			h.snapSync = uint32(1)
   176  			log.Warn("Switch sync mode from full sync to snap sync")
   177  		}
   178  	} else {
   179  		if h.chain.CurrentBlock().NumberU64() > 0 {
   180  			// Print warning log if database is not empty to run snap sync.
   181  			log.Warn("Switch sync mode from snap sync to full sync")
   182  		} else {
   183  			// If snap sync was requested and our database is empty, grant it
   184  			h.snapSync = uint32(1)
   185  		}
   186  	}
   187  	// If we have trusted checkpoints, enforce them on the chain
   188  	if config.Checkpoint != nil {
   189  		h.checkpointNumber = (config.Checkpoint.SectionIndex+1)*params.CHTFrequency - 1
   190  		h.checkpointHash = config.Checkpoint.SectionHead
   191  	}
   192  	// If sync succeeds, pass a callback to potentially disable snap sync mode
   193  	// and enable transaction propagation.
   194  	success := func() {
   195  		// If we were running snap sync and it finished, disable doing another
   196  		// round on next sync cycle
   197  		if atomic.LoadUint32(&h.snapSync) == 1 {
   198  			log.Info("Snap sync complete, auto disabling")
   199  			atomic.StoreUint32(&h.snapSync, 0)
   200  		}
   201  		// If we've successfully finished a sync cycle and passed any required
   202  		// checkpoint, enable accepting transactions from the network
   203  		head := h.chain.CurrentBlock()
   204  		if head.NumberU64() >= h.checkpointNumber {
   205  			// Checkpoint passed, sanity check the timestamp to have a fallback mechanism
   206  			// for non-checkpointed (number = 0) private networks.
   207  			if head.Time() >= uint64(time.Now().AddDate(0, -1, 0).Unix()) {
   208  				atomic.StoreUint32(&h.acceptTxs, 1)
   209  			}
   210  		}
   211  	}
   212  	// Construct the downloader (long sync) and its backing state bloom if snap
   213  	// sync is requested. The downloader is responsible for deallocating the state
   214  	// bloom when it's done.
   215  	h.downloader = downloader.New(h.checkpointNumber, config.Database, h.eventMux, h.chain, nil, h.removePeer, success)
   216  
   217  	// Construct the fetcher (short sync)
   218  	validator := func(header *types.Header) error {
   219  		// All the block fetcher activities should be disabled
   220  		// after the transition. Print the warning log.
   221  		if h.merger.PoSFinalized() {
   222  			log.Warn("Unexpected validation activity", "hash", header.Hash(), "number", header.Number)
   223  			return errors.New("unexpected behavior after transition")
   224  		}
   225  		// Reject all the PoS style headers in the first place. No matter
   226  		// the chain has finished the transition or not, the PoS headers
   227  		// should only come from the trusted consensus layer instead of
   228  		// p2p network.
   229  		if beacon, ok := h.chain.Engine().(*beacon.Beacon); ok {
   230  			if beacon.IsPoSHeader(header) {
   231  				return errors.New("unexpected post-merge header")
   232  			}
   233  		}
   234  		return h.chain.Engine().VerifyHeader(h.chain, header, true)
   235  	}
   236  	heighter := func() uint64 {
   237  		return h.chain.CurrentBlock().NumberU64()
   238  	}
   239  	inserter := func(blocks types.Blocks) (int, error) {
   240  		// All the block fetcher activities should be disabled
   241  		// after the transition. Print the warning log.
   242  		if h.merger.PoSFinalized() {
   243  			var ctx []interface{}
   244  			ctx = append(ctx, "blocks", len(blocks))
   245  			if len(blocks) > 0 {
   246  				ctx = append(ctx, "firsthash", blocks[0].Hash())
   247  				ctx = append(ctx, "firstnumber", blocks[0].Number())
   248  				ctx = append(ctx, "lasthash", blocks[len(blocks)-1].Hash())
   249  				ctx = append(ctx, "lastnumber", blocks[len(blocks)-1].Number())
   250  			}
   251  			log.Warn("Unexpected insertion activity", ctx...)
   252  			return 0, errors.New("unexpected behavior after transition")
   253  		}
   254  		// If sync hasn't reached the checkpoint yet, deny importing weird blocks.
   255  		//
   256  		// Ideally we would also compare the head block's timestamp and similarly reject
   257  		// the propagated block if the head is too old. Unfortunately there is a corner
   258  		// case when starting new networks, where the genesis might be ancient (0 unix)
   259  		// which would prevent full nodes from accepting it.
   260  		if h.chain.CurrentBlock().NumberU64() < h.checkpointNumber {
   261  			log.Warn("Unsynced yet, discarded propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash())
   262  			return 0, nil
   263  		}
   264  		// If snap sync is running, deny importing weird blocks. This is a problematic
   265  		// clause when starting up a new network, because snap-syncing miners might not
   266  		// accept each others' blocks until a restart. Unfortunately we haven't figured
   267  		// out a way yet where nodes can decide unilaterally whether the network is new
   268  		// or not. This should be fixed if we figure out a solution.
   269  		if atomic.LoadUint32(&h.snapSync) == 1 {
   270  			log.Warn("Fast syncing, discarded propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash())
   271  			return 0, nil
   272  		}
   273  		if h.merger.TDDReached() {
   274  			// The blocks from the p2p network is regarded as untrusted
   275  			// after the transition. In theory block gossip should be disabled
   276  			// entirely whenever the transition is started. But in order to
   277  			// handle the transition boundary reorg in the consensus-layer,
   278  			// the legacy blocks are still accepted, but only for the terminal
   279  			// pow blocks. Spec: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3675.md#halt-the-importing-of-pow-blocks
   280  			for i, block := range blocks {
   281  				ptd := h.chain.GetTd(block.ParentHash(), block.NumberU64()-1)
   282  				if ptd == nil {
   283  					return 0, nil
   284  				}
   285  				td := new(big.Int).Add(ptd, block.Difficulty())
   286  				if !h.chain.Config().IsTerminalPoWBlock(ptd, td) {
   287  					log.Info("Filtered out non-termimal pow block", "number", block.NumberU64(), "hash", block.Hash())
   288  					return 0, nil
   289  				}
   290  				if err := h.chain.InsertBlockWithoutSetHead(block); err != nil {
   291  					return i, err
   292  				}
   293  			}
   294  			return 0, nil
   295  		}
   296  		n, err := h.chain.InsertChain(blocks)
   297  		if err == nil {
   298  			atomic.StoreUint32(&h.acceptTxs, 1) // Mark initial sync done on any fetcher import
   299  		}
   300  		return n, err
   301  	}
   302  	h.blockFetcher = fetcher.NewBlockFetcher(false, nil, h.chain.GetBlockByHash, validator, h.BroadcastBlock, heighter, nil, inserter, h.removePeer)
   303  
   304  	fetchTx := func(peer string, hashes []common.Hash) error {
   305  		p := h.peers.peer(peer)
   306  		if p == nil {
   307  			return errors.New("unknown peer")
   308  		}
   309  		return p.RequestTxs(hashes)
   310  	}
   311  	h.txFetcher = fetcher.NewTxFetcher(h.txpool.Has, h.txpool.AddRemotes, fetchTx)
   312  	h.chainSync = newChainSyncer(h)
   313  	return h, nil
   314  }
   315  
   316  // runEthPeer registers an eth peer into the joint eth/snap peerset, adds it to
   317  // various subsistems and starts handling messages.
   318  func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error {
   319  	// If the peer has a `snap` extension, wait for it to connect so we can have
   320  	// a uniform initialization/teardown mechanism
   321  	snap, err := h.peers.waitSnapExtension(peer)
   322  	if err != nil {
   323  		peer.Log().Error("Snapshot extension barrier failed", "err", err)
   324  		return err
   325  	}
   326  	// TODO(karalabe): Not sure why this is needed
   327  	if !h.chainSync.handlePeerEvent(peer) {
   328  		return p2p.DiscQuitting
   329  	}
   330  	h.peerWG.Add(1)
   331  	defer h.peerWG.Done()
   332  
   333  	// Execute the Ethereum handshake
   334  	var (
   335  		genesis = h.chain.Genesis()
   336  		head    = h.chain.CurrentHeader()
   337  		hash    = head.Hash()
   338  		number  = head.Number.Uint64()
   339  		td      = h.chain.GetTd(hash, number)
   340  	)
   341  	forkID := forkid.NewID(h.chain.Config(), h.chain.Genesis().Hash(), h.chain.CurrentHeader().Number.Uint64())
   342  	if err := peer.Handshake(h.networkID, td, hash, genesis.Hash(), forkID, h.forkFilter); err != nil {
   343  		peer.Log().Debug("Ethereum handshake failed", "err", err)
   344  
   345  		// When the Handshake() returns an error, the Run method corresponding to `eth` protocol returns with the error, causing the peer to drop, signal subprotocol as well to exit the `Run` method
   346  		peer.EthPeerDisconnected <- struct{}{}
   347  		return err
   348  	}
   349  	reject := false // reserved peer slots
   350  	if atomic.LoadUint32(&h.snapSync) == 1 {
   351  		if snap == nil {
   352  			// If we are running snap-sync, we want to reserve roughly half the peer
   353  			// slots for peers supporting the snap protocol.
   354  			// The logic here is; we only allow up to 5 more non-snap peers than snap-peers.
   355  			if all, snp := h.peers.len(), h.peers.snapLen(); all-snp > snp+5 {
   356  				reject = true
   357  			}
   358  		}
   359  	}
   360  	// Ignore maxPeers if this is a trusted peer
   361  	if !peer.Peer.Info().Network.Trusted {
   362  		if reject || h.peers.len() >= h.maxPeers {
   363  			return p2p.DiscTooManyPeers
   364  		}
   365  	}
   366  	peer.Log().Debug("Ethereum peer connected", "name", peer.Name())
   367  
   368  	// Register the peer locally
   369  	if err := h.peers.registerPeer(peer, snap); err != nil {
   370  		peer.Log().Error("Ethereum peer registration failed", "err", err)
   371  
   372  		// When the Register() returns an error, the Run method corresponding to `eth` protocol returns with the error, causing the peer to drop, signal subprotocol as well to exit the `Run` method
   373  		peer.EthPeerDisconnected <- struct{}{}
   374  		return err
   375  	}
   376  	defer h.unregisterPeer(peer.ID())
   377  
   378  	p := h.peers.peer(peer.ID())
   379  	if p == nil {
   380  		return errors.New("peer dropped during handling")
   381  	}
   382  	// Register the peer in the downloader. If the downloader considers it banned, we disconnect
   383  	if err := h.downloader.RegisterPeer(peer.ID(), peer.Version(), peer); err != nil {
   384  		peer.Log().Error("Failed to register peer in eth syncer", "err", err)
   385  		return err
   386  	}
   387  	if snap != nil {
   388  		if err := h.downloader.SnapSyncer.Register(snap); err != nil {
   389  			peer.Log().Error("Failed to register peer in snap syncer", "err", err)
   390  			return err
   391  		}
   392  	}
   393  	h.chainSync.handlePeerEvent(peer)
   394  
   395  	// Propagate existing transactions. new transactions appearing
   396  	// after this will be sent via broadcasts.
   397  	h.syncTransactions(peer)
   398  
   399  	// Create a notification channel for pending requests if the peer goes down
   400  	dead := make(chan struct{})
   401  	defer close(dead)
   402  
   403  	// If we have a trusted CHT, reject all peers below that (avoid fast sync eclipse)
   404  	if h.checkpointHash != (common.Hash{}) {
   405  		// Request the peer's checkpoint header for chain height/weight validation
   406  		resCh := make(chan *eth.Response)
   407  		if _, err := peer.RequestHeadersByNumber(h.checkpointNumber, 1, 0, false, resCh); err != nil {
   408  			return err
   409  		}
   410  		// Start a timer to disconnect if the peer doesn't reply in time
   411  		go func() {
   412  			timeout := time.NewTimer(syncChallengeTimeout)
   413  			defer timeout.Stop()
   414  
   415  			select {
   416  			case res := <-resCh:
   417  				headers := ([]*types.Header)(*res.Res.(*eth.BlockHeadersPacket))
   418  				if len(headers) == 0 {
   419  					// If we're doing a snap sync, we must enforce the checkpoint
   420  					// block to avoid eclipse attacks. Unsynced nodes are welcome
   421  					// to connect after we're done joining the network.
   422  					if atomic.LoadUint32(&h.snapSync) == 1 {
   423  						peer.Log().Warn("Dropping unsynced node during sync", "addr", peer.RemoteAddr(), "type", peer.Name())
   424  						res.Done <- errors.New("unsynced node cannot serve sync")
   425  						return
   426  					}
   427  					res.Done <- nil
   428  					return
   429  				}
   430  				// Validate the header and either drop the peer or continue
   431  				if len(headers) > 1 {
   432  					res.Done <- errors.New("too many headers in checkpoint response")
   433  					return
   434  				}
   435  				if headers[0].Hash() != h.checkpointHash {
   436  					res.Done <- errors.New("checkpoint hash mismatch")
   437  					return
   438  				}
   439  				res.Done <- nil
   440  
   441  			case <-timeout.C:
   442  				peer.Log().Warn("Checkpoint challenge timed out, dropping", "addr", peer.RemoteAddr(), "type", peer.Name())
   443  				h.removePeer(peer.ID())
   444  
   445  			case <-dead:
   446  				// Peer handler terminated, abort all goroutines
   447  			}
   448  		}()
   449  	}
   450  	// If we have any explicit peer required block hashes, request them
   451  	for number, hash := range h.requiredBlocks {
   452  		resCh := make(chan *eth.Response)
   453  		if _, err := peer.RequestHeadersByNumber(number, 1, 0, false, resCh); err != nil {
   454  			return err
   455  		}
   456  		go func(number uint64, hash common.Hash) {
   457  			timeout := time.NewTimer(syncChallengeTimeout)
   458  			defer timeout.Stop()
   459  
   460  			select {
   461  			case res := <-resCh:
   462  				headers := ([]*types.Header)(*res.Res.(*eth.BlockHeadersPacket))
   463  				if len(headers) == 0 {
   464  					// Required blocks are allowed to be missing if the remote
   465  					// node is not yet synced
   466  					res.Done <- nil
   467  					return
   468  				}
   469  				// Validate the header and either drop the peer or continue
   470  				if len(headers) > 1 {
   471  					res.Done <- errors.New("too many headers in required block response")
   472  					return
   473  				}
   474  				if headers[0].Number.Uint64() != number || headers[0].Hash() != hash {
   475  					peer.Log().Info("Required block mismatch, dropping peer", "number", number, "hash", headers[0].Hash(), "want", hash)
   476  					res.Done <- errors.New("required block mismatch")
   477  					return
   478  				}
   479  				peer.Log().Debug("Peer required block verified", "number", number, "hash", hash)
   480  				res.Done <- nil
   481  			case <-timeout.C:
   482  				peer.Log().Warn("Required block challenge timed out, dropping", "addr", peer.RemoteAddr(), "type", peer.Name())
   483  				h.removePeer(peer.ID())
   484  			}
   485  		}(number, hash)
   486  	}
   487  
   488  	p.EthPeerRegistered <- struct{}{}
   489  
   490  	// Handle incoming messages until the connection is torn down
   491  	return handler(peer)
   492  }
   493  
   494  // runSnapExtension registers a `snap` peer into the joint eth/snap peerset and
   495  // starts handling inbound messages. As `snap` is only a satellite protocol to
   496  // `eth`, all subsystem registrations and lifecycle management will be done by
   497  // the main `eth` handler to prevent strange races.
   498  func (h *handler) runSnapExtension(peer *snap.Peer, handler snap.Handler) error {
   499  	h.peerWG.Add(1)
   500  	defer h.peerWG.Done()
   501  
   502  	if err := h.peers.registerSnapExtension(peer); err != nil {
   503  		peer.Log().Warn("Snapshot extension registration failed", "err", err)
   504  		return err
   505  	}
   506  	return handler(peer)
   507  }
   508  
   509  // removePeer requests disconnection of a peer.
   510  func (h *handler) removePeer(id string) {
   511  	peer := h.peers.peer(id)
   512  	if peer != nil {
   513  		peer.Peer.Disconnect(p2p.DiscUselessPeer)
   514  	}
   515  }
   516  
   517  // unregisterPeer removes a peer from the downloader, fetchers and main peer set.
   518  func (h *handler) unregisterPeer(id string) {
   519  	// Create a custom logger to avoid printing the entire id
   520  	var logger log.Logger
   521  	if len(id) < 16 {
   522  		// Tests use short IDs, don't choke on them
   523  		logger = log.New("peer", id)
   524  	} else {
   525  		logger = log.New("peer", id[:8])
   526  	}
   527  	// Abort if the peer does not exist
   528  	peer := h.peers.peer(id)
   529  	if peer == nil {
   530  		logger.Error("Ethereum peer removal failed", "err", errPeerNotRegistered)
   531  		return
   532  	}
   533  	// Remove the `eth` peer if it exists
   534  	logger.Debug("Removing Ethereum peer", "snap", peer.snapExt != nil)
   535  
   536  	// Remove the `snap` extension if it exists
   537  	if peer.snapExt != nil {
   538  		h.downloader.SnapSyncer.Unregister(id)
   539  	}
   540  	h.downloader.UnregisterPeer(id)
   541  	h.txFetcher.Drop(id)
   542  
   543  	if err := h.peers.unregisterPeer(id); err != nil {
   544  		logger.Error("Ethereum peer removal failed", "err", err)
   545  	}
   546  }
   547  
   548  func (h *handler) Start(maxPeers int) {
   549  	h.maxPeers = maxPeers
   550  
   551  	// broadcast transactions
   552  	h.wg.Add(1)
   553  	h.txsCh = make(chan core.NewTxsEvent, txChanSize)
   554  	h.txsSub = h.txpool.SubscribeNewTxsEvent(h.txsCh)
   555  	go h.txBroadcastLoop()
   556  
   557  	// broadcast mined blocks
   558  	h.wg.Add(1)
   559  	h.minedBlockSub = h.eventMux.Subscribe(core.NewMinedBlockEvent{})
   560  	go h.minedBroadcastLoop()
   561  
   562  	// start sync handlers
   563  	h.wg.Add(1)
   564  	go h.chainSync.loop()
   565  }
   566  
   567  func (h *handler) Stop() {
   568  	h.txsSub.Unsubscribe()        // quits txBroadcastLoop
   569  	h.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop
   570  
   571  	// Quit chainSync and txsync64.
   572  	// After this is done, no new peers will be accepted.
   573  	close(h.quitSync)
   574  	h.wg.Wait()
   575  
   576  	// Disconnect existing sessions.
   577  	// This also closes the gate for any new registrations on the peer set.
   578  	// sessions which are already established but not added to h.peers yet
   579  	// will exit when they try to register.
   580  	h.peers.close()
   581  	h.peerWG.Wait()
   582  
   583  	log.Info("Electroneum protocol stopped")
   584  }
   585  
   586  func (h *handler) Enqueue(id string, block *types.Block) {
   587  	h.blockFetcher.Enqueue(id, block)
   588  }
   589  
   590  // BroadcastBlock will either propagate a block to a subset of its peers, or
   591  // will only announce its availability (depending what's requested).
   592  func (h *handler) BroadcastBlock(block *types.Block, propagate bool) {
   593  	// Disable the block propagation if the chain has already entered the PoS
   594  	// stage. The block propagation is delegated to the consensus layer.
   595  	if h.merger.PoSFinalized() {
   596  		return
   597  	}
   598  	// Disable the block propagation if it's the post-merge block.
   599  	if beacon, ok := h.chain.Engine().(*beacon.Beacon); ok {
   600  		if beacon.IsPoSHeader(block.Header()) {
   601  			return
   602  		}
   603  	}
   604  	hash := block.Hash()
   605  	peers := h.peers.peersWithoutBlock(hash)
   606  
   607  	// If propagation is requested, send to a subset of the peer
   608  	if propagate {
   609  		// Calculate the TD of the block (it's not imported yet, so block.Td is not valid)
   610  		var td *big.Int
   611  		if parent := h.chain.GetBlock(block.ParentHash(), block.NumberU64()-1); parent != nil {
   612  			td = new(big.Int).Add(block.Difficulty(), h.chain.GetTd(block.ParentHash(), block.NumberU64()-1))
   613  		} else {
   614  			log.Error("Propagating dangling block", "number", block.Number(), "hash", hash)
   615  			return
   616  		}
   617  		// Send the block to a subset of our peers
   618  		transfer := peers[:int(math.Sqrt(float64(len(peers))))]
   619  		for _, peer := range transfer {
   620  			peer.AsyncSendNewBlock(block, td)
   621  		}
   622  		log.Trace("Propagated block", "hash", hash, "recipients", len(transfer), "duration", common.PrettyDuration(time.Since(block.ReceivedAt)))
   623  		return
   624  	}
   625  	// Otherwise if the block is indeed in out own chain, announce it
   626  	if h.chain.HasBlock(hash, block.NumberU64()) {
   627  		for _, peer := range peers {
   628  			peer.AsyncSendNewBlockHash(block)
   629  		}
   630  		log.Trace("Announced block", "hash", hash, "recipients", len(peers), "duration", common.PrettyDuration(time.Since(block.ReceivedAt)))
   631  	}
   632  }
   633  
   634  // BroadcastTransactions will propagate a batch of transactions
   635  // - To a square root of all peers
   636  // - And, separately, as announcements to all peers which are not known to
   637  // already have the given transaction.
   638  func (h *handler) BroadcastTransactions(txs types.Transactions) {
   639  	var (
   640  		annoCount   int // Count of announcements made
   641  		annoPeers   int
   642  		directCount int // Count of the txs sent directly to peers
   643  		directPeers int // Count of the peers that were sent transactions directly
   644  
   645  		txset = make(map[*ethPeer][]common.Hash) // Set peer->hash to transfer directly
   646  		annos = make(map[*ethPeer][]common.Hash) // Set peer->hash to announce
   647  
   648  	)
   649  	// Broadcast transactions to a batch of peers not knowing about it
   650  	for _, tx := range txs {
   651  		peers := h.peers.peersWithoutTransaction(tx.Hash())
   652  		// Send the tx unconditionally to a subset of our peers
   653  		// Ibft protocol changes for broadcasting to all peers not only Sqrt
   654  		//numDirect := int(math.Sqrt(float64(len(peers))))
   655  		for _, peer := range peers {
   656  			txset[peer] = append(txset[peer], tx.Hash())
   657  		}
   658  		// For the remaining peers, send announcement only
   659  		//for _, peer := range peers[numDirect:] {
   660  		//	annos[peer] = append(annos[peer], tx.Hash())
   661  		//}
   662  		log.Trace("Broadcast transaction", "hash", tx.Hash(), "recipients", len(peers))
   663  	}
   664  	for peer, hashes := range txset {
   665  		directPeers++
   666  		directCount += len(hashes)
   667  		peer.AsyncSendTransactions(hashes)
   668  	}
   669  	for peer, hashes := range annos {
   670  		annoPeers++
   671  		annoCount += len(hashes)
   672  		peer.AsyncSendPooledTransactionHashes(hashes)
   673  	}
   674  	log.Debug("Transaction broadcast", "txs", len(txs),
   675  		"announce packs", annoPeers, "announced hashes", annoCount,
   676  		"tx packs", directPeers, "broadcast txs", directCount)
   677  }
   678  
   679  // minedBroadcastLoop sends mined blocks to connected peers.
   680  func (h *handler) minedBroadcastLoop() {
   681  	defer h.wg.Done()
   682  
   683  	for obj := range h.minedBlockSub.Chan() {
   684  		if ev, ok := obj.Data.(core.NewMinedBlockEvent); ok {
   685  			h.BroadcastBlock(ev.Block, true)  // First propagate block to peers
   686  			h.BroadcastBlock(ev.Block, false) // Only then announce to the rest
   687  		}
   688  	}
   689  }
   690  
   691  // txBroadcastLoop announces new transactions to connected peers.
   692  func (h *handler) txBroadcastLoop() {
   693  	defer h.wg.Done()
   694  	for {
   695  		select {
   696  		case event := <-h.txsCh:
   697  			h.BroadcastTransactions(event.Txs)
   698  		case <-h.txsSub.Err():
   699  			return
   700  		}
   701  	}
   702  }
   703  
   704  // NodeInfo represents a short summary of the Ethereum sub-protocol metadata
   705  // known about the host peer.
   706  type NodeInfo struct {
   707  	Network    uint64              `json:"network"`    // Ethereum network ID (1=Frontier, 2=Morden, Ropsten=3, Rinkeby=4)
   708  	Difficulty *big.Int            `json:"difficulty"` // Total difficulty of the host's blockchain
   709  	Genesis    common.Hash         `json:"genesis"`    // SHA3 hash of the host's genesis block
   710  	Config     *params.ChainConfig `json:"config"`     // Chain configuration for the fork rules
   711  	Head       common.Hash         `json:"head"`       // SHA3 hash of the host's best owned block
   712  	Consensus  string              `json:"consensus"`  // Consensus mechanism in use
   713  }
   714  
   715  // NodeInfo retrieves some protocol metadata about the running host node.
   716  func (h *handler) NodeInfo() *NodeInfo {
   717  	currentBlock := h.chain.CurrentBlock()
   718  	chainConfig := h.chain.Config()
   719  
   720  	return &NodeInfo{
   721  		Network:    h.networkID,
   722  		Difficulty: h.chain.GetTd(currentBlock.Hash(), currentBlock.NumberU64()),
   723  		Genesis:    h.chain.Genesis().Hash(),
   724  		Config:     chainConfig,
   725  		Head:       currentBlock.Hash(),
   726  		Consensus:  h.getConsensusAlgorithm(),
   727  	}
   728  }
   729  
   730  func (h *handler) getConsensusAlgorithm() string {
   731  	var consensusAlgo string
   732  	switch h.engine.(type) {
   733  	case consensus.Istanbul:
   734  		consensusAlgo = "IBFT"
   735  	case *clique.Clique:
   736  		consensusAlgo = "clique"
   737  	case *ethash.Ethash:
   738  		consensusAlgo = "ethash"
   739  	default:
   740  		consensusAlgo = "unknown"
   741  	}
   742  	return consensusAlgo
   743  }
   744  
   745  func (h *handler) FindPeers(targets map[common.Address]bool) map[common.Address]consensus.Peer {
   746  	m := make(map[common.Address]consensus.Peer)
   747  	for _, p := range h.peers.peers {
   748  		pubKey := p.Node().Pubkey()
   749  		addr := crypto.PubkeyToAddress(*pubKey)
   750  		if targets[addr] {
   751  			m[addr] = p
   752  		}
   753  	}
   754  	return m
   755  }
   756  
   757  // makeIbftConsensusProtocol is similar to eth/handler.go -> makeProtocol. Called from eth/handler.go -> Protocols.
   758  // returns the supported subprotocol to the p2p server.
   759  // The Run method starts the protocol and is called by the p2p server. The ibft consensus subprotocol,
   760  // leverages the peer created and managed by the "eth" subprotocol.
   761  // The ibft consensus protocol requires that the "eth" protocol is running as well.
   762  func (h *handler) makeIbftConsensusProtocol(ProtoName string, version uint, length uint64) p2p.Protocol {
   763  	return p2p.Protocol{
   764  		Name:    ProtoName,
   765  		Version: version,
   766  		Length:  length,
   767  		// no new peer created, uses the "eth" peer, so no peer management needed.
   768  		Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
   769  			/*
   770  			* 1. wait for the eth protocol to create and register an eth peer.
   771  			* 2. get the associate eth peer that was registered by he "eth" protocol.
   772  			* 3. add the rw protocol for the ibft subprotocol to the eth peer.
   773  			* 4. start listening for incoming messages.
   774  			* 5. the incoming message will be sent on the ibft specific subprotocol, e.g. "istanbul/100".
   775  			* 6. send messages to the consensus engine handler.
   776  			* 7. messages to other to other peers listening to the subprotocol can be sent using the
   777  			*    (eth)peer.ConsensusSend() which will write to the protoRW.
   778  			 */
   779  			// wait for the "eth" protocol to create and register the peer (added to peerset)
   780  			select {
   781  			case <-p.EthPeerRegistered:
   782  				// the ethpeer should be registered, try to retrieve it and start the consensus handler.
   783  				p2pPeerId := fmt.Sprintf("%x", p.ID().Bytes()[:8])
   784  				ethPeer := h.peers.peer(p2pPeerId)
   785  				if ethPeer == nil {
   786  					p2pPeerId = fmt.Sprintf("%x", p.ID().Bytes()) //TODO:BBO
   787  					ethPeer = h.peers.peer(p2pPeerId)
   788  					log.Warn("full p2p peer", "id", p2pPeerId, "etnPeer", ethPeer)
   789  				}
   790  				if ethPeer != nil {
   791  					p.Log().Debug("consensus subprotocol retrieved eth peer from peerset", "etnPeer.id", p2pPeerId, "ProtoName", ProtoName)
   792  					// add the rw protocol for the ibft subprotocol to the eth peer.
   793  					ethPeer.AddConsensusProtoRW(rw)
   794  					return h.handleConsensusLoop(p, rw)
   795  				}
   796  				p.Log().Error("consensus subprotocol retrieved nil eth peer from peerset", "etnPeer.id", p2pPeerId)
   797  				return errEthPeerNil
   798  			case <-p.EthPeerDisconnected:
   799  				return errEthPeerNotRegistered
   800  			}
   801  		},
   802  		NodeInfo: func() interface{} {
   803  			return h.NodeInfo()
   804  		},
   805  		PeerInfo: func(id enode.ID) interface{} {
   806  			if p := h.peers.peer(fmt.Sprintf("%x", id[:8])); p != nil {
   807  				return p.Info()
   808  			}
   809  			if p := h.peers.peer(fmt.Sprintf("%x", id)); p != nil { // TODO:BBO
   810  				return p.Info()
   811  			}
   812  			return nil
   813  		},
   814  	}
   815  }
   816  
   817  func (h *handler) handleConsensusLoop(p *p2p.Peer, protoRW p2p.MsgReadWriter) error {
   818  	// Handle incoming messages until the connection is torn down
   819  	for {
   820  		if err := h.handleConsensus(p, protoRW); err != nil {
   821  			p.Log().Debug("Ethereum ibft message handling failed", "err", err)
   822  			return err
   823  		}
   824  	}
   825  }
   826  
   827  // This is a no-op because the eth handleMsg main loop handle ibf message as well.
   828  func (h *handler) handleConsensus(p *p2p.Peer, protoRW p2p.MsgReadWriter) error {
   829  	// Read the next message from the remote peer (in protoRW), and ensure it's fully consumed
   830  	msg, err := protoRW.ReadMsg()
   831  	if err != nil {
   832  		return err
   833  	}
   834  	if msg.Size > protocolMaxMsgSize {
   835  		return fmt.Errorf("%w: %v > %v", errMsgTooLarge, msg.Size, protocolMaxMsgSize)
   836  	}
   837  	defer msg.Discard()
   838  
   839  	// See if the consensus engine protocol can handle this message, e.g. istanbul will check for message is
   840  	// istanbulMsg = 0x11, and NewBlockMsg = 0x07.
   841  	handled, err := h.handleConsensusMsg(p, msg)
   842  	if handled {
   843  		p.Log().Debug("consensus message was handled by consensus engine", "handled", handled,
   844  			"ibftConsensusProtocolName", ibftConsensusProtocolName, "err", err)
   845  		return err
   846  	}
   847  
   848  	return nil
   849  }
   850  
   851  func (h *handler) handleConsensusMsg(p *p2p.Peer, msg p2p.Msg) (bool, error) {
   852  	if handler, ok := h.engine.(consensus.Handler); ok {
   853  		pubKey := p.Node().Pubkey()
   854  		addr := crypto.PubkeyToAddress(*pubKey)
   855  		handled, err := handler.HandleMsg(addr, msg)
   856  		return handled, err
   857  	}
   858  	return false, nil
   859  }