github.com/ethereum/go-ethereum@v1.16.1/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  	"maps"
    22  	"math"
    23  	"math/big"
    24  	"slices"
    25  	"sync"
    26  	"sync/atomic"
    27  	"time"
    28  
    29  	"github.com/ethereum/go-ethereum/common"
    30  	"github.com/ethereum/go-ethereum/core"
    31  	"github.com/ethereum/go-ethereum/core/rawdb"
    32  	"github.com/ethereum/go-ethereum/core/txpool"
    33  	"github.com/ethereum/go-ethereum/core/types"
    34  	"github.com/ethereum/go-ethereum/crypto"
    35  	"github.com/ethereum/go-ethereum/eth/downloader"
    36  	"github.com/ethereum/go-ethereum/eth/ethconfig"
    37  	"github.com/ethereum/go-ethereum/eth/fetcher"
    38  	"github.com/ethereum/go-ethereum/eth/protocols/eth"
    39  	"github.com/ethereum/go-ethereum/eth/protocols/snap"
    40  	"github.com/ethereum/go-ethereum/ethdb"
    41  	"github.com/ethereum/go-ethereum/event"
    42  	"github.com/ethereum/go-ethereum/log"
    43  	"github.com/ethereum/go-ethereum/metrics"
    44  	"github.com/ethereum/go-ethereum/p2p"
    45  	"github.com/ethereum/go-ethereum/p2p/enode"
    46  )
    47  
    48  const (
    49  	// txChanSize is the size of channel listening to NewTxsEvent.
    50  	// The number is referenced from the size of tx pool.
    51  	txChanSize = 4096
    52  
    53  	// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
    54  	chainHeadChanSize = 128
    55  
    56  	// txMaxBroadcastSize is the max size of a transaction that will be broadcasted.
    57  	// All transactions with a higher size will be announced and need to be fetched
    58  	// by the peer.
    59  	txMaxBroadcastSize = 4096
    60  )
    61  
    62  var syncChallengeTimeout = 15 * time.Second // Time allowance for a node to reply to the sync progress challenge
    63  
    64  // txPool defines the methods needed from a transaction pool implementation to
    65  // support all the operations needed by the Ethereum chain protocols.
    66  type txPool interface {
    67  	// Has returns an indicator whether txpool has a transaction
    68  	// cached with the given hash.
    69  	Has(hash common.Hash) bool
    70  
    71  	// Get retrieves the transaction from local txpool with given
    72  	// tx hash.
    73  	Get(hash common.Hash) *types.Transaction
    74  
    75  	// GetRLP retrieves the RLP-encoded transaction from local txpool
    76  	// with given tx hash.
    77  	GetRLP(hash common.Hash) []byte
    78  
    79  	// GetMetadata returns the transaction type and transaction size with the
    80  	// given transaction hash.
    81  	GetMetadata(hash common.Hash) *txpool.TxMetadata
    82  
    83  	// Add should add the given transactions to the pool.
    84  	Add(txs []*types.Transaction, sync bool) []error
    85  
    86  	// Pending should return pending transactions.
    87  	// The slice should be modifiable by the caller.
    88  	Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction
    89  
    90  	// SubscribeTransactions subscribes to new transaction events. The subscriber
    91  	// can decide whether to receive notifications only for newly seen transactions
    92  	// or also for reorged out ones.
    93  	SubscribeTransactions(ch chan<- core.NewTxsEvent, reorgs bool) event.Subscription
    94  }
    95  
    96  // handlerConfig is the collection of initialization parameters to create a full
    97  // node network handler.
    98  type handlerConfig struct {
    99  	NodeID         enode.ID               // P2P node ID used for tx propagation topology
   100  	Database       ethdb.Database         // Database for direct sync insertions
   101  	Chain          *core.BlockChain       // Blockchain to serve data from
   102  	TxPool         txPool                 // Transaction pool to propagate from
   103  	Network        uint64                 // Network identifier to advertise
   104  	Sync           ethconfig.SyncMode     // Whether to snap or full sync
   105  	BloomCache     uint64                 // Megabytes to alloc for snap sync bloom
   106  	EventMux       *event.TypeMux         // Legacy event mux, deprecate for `feed`
   107  	RequiredBlocks map[uint64]common.Hash // Hard coded map of required block hashes for sync challenges
   108  }
   109  
   110  type handler struct {
   111  	nodeID    enode.ID
   112  	networkID uint64
   113  
   114  	snapSync atomic.Bool // Flag whether snap sync is enabled (gets disabled if we already have blocks)
   115  	synced   atomic.Bool // Flag whether we're considered synchronised (enables transaction processing)
   116  
   117  	database ethdb.Database
   118  	txpool   txPool
   119  	chain    *core.BlockChain
   120  	maxPeers int
   121  
   122  	downloader *downloader.Downloader
   123  	txFetcher  *fetcher.TxFetcher
   124  	peers      *peerSet
   125  
   126  	eventMux   *event.TypeMux
   127  	txsCh      chan core.NewTxsEvent
   128  	txsSub     event.Subscription
   129  	blockRange *blockRangeState
   130  
   131  	requiredBlocks map[uint64]common.Hash
   132  
   133  	// channels for fetcher, syncer, txsyncLoop
   134  	quitSync chan struct{}
   135  
   136  	wg sync.WaitGroup
   137  
   138  	handlerStartCh chan struct{}
   139  	handlerDoneCh  chan struct{}
   140  }
   141  
   142  // newHandler returns a handler for all Ethereum chain management protocol.
   143  func newHandler(config *handlerConfig) (*handler, error) {
   144  	// Create the protocol manager with the base fields
   145  	if config.EventMux == nil {
   146  		config.EventMux = new(event.TypeMux) // Nicety initialization for tests
   147  	}
   148  	h := &handler{
   149  		nodeID:         config.NodeID,
   150  		networkID:      config.Network,
   151  		eventMux:       config.EventMux,
   152  		database:       config.Database,
   153  		txpool:         config.TxPool,
   154  		chain:          config.Chain,
   155  		peers:          newPeerSet(),
   156  		requiredBlocks: config.RequiredBlocks,
   157  		quitSync:       make(chan struct{}),
   158  		handlerDoneCh:  make(chan struct{}),
   159  		handlerStartCh: make(chan struct{}),
   160  	}
   161  	if config.Sync == ethconfig.FullSync {
   162  		// The database seems empty as the current block is the genesis. Yet the snap
   163  		// block is ahead, so snap sync was enabled for this node at a certain point.
   164  		// The scenarios where this can happen is
   165  		// * if the user manually (or via a bad block) rolled back a snap sync node
   166  		//   below the sync point.
   167  		// * the last snap sync is not finished while user specifies a full sync this
   168  		//   time. But we don't have any recent state for full sync.
   169  		// In these cases however it's safe to reenable snap sync.
   170  		fullBlock, snapBlock := h.chain.CurrentBlock(), h.chain.CurrentSnapBlock()
   171  		if fullBlock.Number.Uint64() == 0 && snapBlock.Number.Uint64() > 0 {
   172  			h.snapSync.Store(true)
   173  			log.Warn("Switch sync mode from full sync to snap sync", "reason", "snap sync incomplete")
   174  		} else if !h.chain.HasState(fullBlock.Root) {
   175  			h.snapSync.Store(true)
   176  			log.Warn("Switch sync mode from full sync to snap sync", "reason", "head state missing")
   177  		}
   178  	} else {
   179  		head := h.chain.CurrentBlock()
   180  		if head.Number.Uint64() > 0 && h.chain.HasState(head.Root) {
   181  			// Print warning log if database is not empty to run snap sync.
   182  			log.Warn("Switch sync mode from snap sync to full sync", "reason", "snap sync complete")
   183  		} else {
   184  			// If snap sync was requested and our database is empty, grant it
   185  			h.snapSync.Store(true)
   186  			log.Info("Enabled snap sync", "head", head.Number, "hash", head.Hash())
   187  		}
   188  	}
   189  	// If snap sync is requested but snapshots are disabled, fail loudly
   190  	if h.snapSync.Load() && (config.Chain.Snapshots() == nil && config.Chain.TrieDB().Scheme() == rawdb.HashScheme) {
   191  		return nil, errors.New("snap sync not supported with snapshots disabled")
   192  	}
   193  	// Construct the downloader (long sync)
   194  	h.downloader = downloader.New(config.Database, h.eventMux, h.chain, h.removePeer, h.enableSyncedFeatures)
   195  
   196  	fetchTx := func(peer string, hashes []common.Hash) error {
   197  		p := h.peers.peer(peer)
   198  		if p == nil {
   199  			return errors.New("unknown peer")
   200  		}
   201  		return p.RequestTxs(hashes)
   202  	}
   203  	addTxs := func(txs []*types.Transaction) []error {
   204  		return h.txpool.Add(txs, false)
   205  	}
   206  	h.txFetcher = fetcher.NewTxFetcher(h.txpool.Has, addTxs, fetchTx, h.removePeer)
   207  	return h, nil
   208  }
   209  
   210  // protoTracker tracks the number of active protocol handlers.
   211  func (h *handler) protoTracker() {
   212  	defer h.wg.Done()
   213  	var active int
   214  	for {
   215  		select {
   216  		case <-h.handlerStartCh:
   217  			active++
   218  		case <-h.handlerDoneCh:
   219  			active--
   220  		case <-h.quitSync:
   221  			// Wait for all active handlers to finish.
   222  			for ; active > 0; active-- {
   223  				<-h.handlerDoneCh
   224  			}
   225  			return
   226  		}
   227  	}
   228  }
   229  
   230  // incHandlers signals to increment the number of active handlers if not
   231  // quitting.
   232  func (h *handler) incHandlers() bool {
   233  	select {
   234  	case h.handlerStartCh <- struct{}{}:
   235  		return true
   236  	case <-h.quitSync:
   237  		return false
   238  	}
   239  }
   240  
   241  // decHandlers signals to decrement the number of active handlers.
   242  func (h *handler) decHandlers() {
   243  	h.handlerDoneCh <- struct{}{}
   244  }
   245  
   246  // runEthPeer registers an eth peer into the joint eth/snap peerset, adds it to
   247  // various subsystems and starts handling messages.
   248  func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error {
   249  	if !h.incHandlers() {
   250  		return p2p.DiscQuitting
   251  	}
   252  	defer h.decHandlers()
   253  
   254  	// If the peer has a `snap` extension, wait for it to connect so we can have
   255  	// a uniform initialization/teardown mechanism
   256  	snap, err := h.peers.waitSnapExtension(peer)
   257  	if err != nil {
   258  		peer.Log().Error("Snapshot extension barrier failed", "err", err)
   259  		return err
   260  	}
   261  
   262  	// Execute the Ethereum handshake
   263  	if err := peer.Handshake(h.networkID, h.chain, h.blockRange.currentRange()); err != nil {
   264  		peer.Log().Debug("Ethereum handshake failed", "err", err)
   265  		return err
   266  	}
   267  	reject := false // reserved peer slots
   268  	if h.snapSync.Load() {
   269  		if snap == nil {
   270  			// If we are running snap-sync, we want to reserve roughly half the peer
   271  			// slots for peers supporting the snap protocol.
   272  			// The logic here is; we only allow up to 5 more non-snap peers than snap-peers.
   273  			if all, snp := h.peers.len(), h.peers.snapLen(); all-snp > snp+5 {
   274  				reject = true
   275  			}
   276  		}
   277  	}
   278  	// Ignore maxPeers if this is a trusted peer
   279  	if !peer.Peer.Info().Network.Trusted {
   280  		if reject || h.peers.len() >= h.maxPeers {
   281  			return p2p.DiscTooManyPeers
   282  		}
   283  	}
   284  	peer.Log().Debug("Ethereum peer connected", "name", peer.Name())
   285  
   286  	// Register the peer locally
   287  	if err := h.peers.registerPeer(peer, snap); err != nil {
   288  		peer.Log().Error("Ethereum peer registration failed", "err", err)
   289  		return err
   290  	}
   291  	defer h.unregisterPeer(peer.ID())
   292  
   293  	p := h.peers.peer(peer.ID())
   294  	if p == nil {
   295  		return errors.New("peer dropped during handling")
   296  	}
   297  	// Register the peer in the downloader. If the downloader considers it banned, we disconnect
   298  	if err := h.downloader.RegisterPeer(peer.ID(), peer.Version(), peer); err != nil {
   299  		peer.Log().Error("Failed to register peer in eth syncer", "err", err)
   300  		return err
   301  	}
   302  	if snap != nil {
   303  		if err := h.downloader.SnapSyncer.Register(snap); err != nil {
   304  			peer.Log().Error("Failed to register peer in snap syncer", "err", err)
   305  			return err
   306  		}
   307  	}
   308  	// Propagate existing transactions. new transactions appearing
   309  	// after this will be sent via broadcasts.
   310  	h.syncTransactions(peer)
   311  
   312  	// Create a notification channel for pending requests if the peer goes down
   313  	dead := make(chan struct{})
   314  	defer close(dead)
   315  
   316  	// If we have any explicit peer required block hashes, request them
   317  	for number, hash := range h.requiredBlocks {
   318  		resCh := make(chan *eth.Response)
   319  
   320  		req, err := peer.RequestHeadersByNumber(number, 1, 0, false, resCh)
   321  		if err != nil {
   322  			return err
   323  		}
   324  		go func(number uint64, hash common.Hash, req *eth.Request) {
   325  			// Ensure the request gets cancelled in case of error/drop
   326  			defer req.Close()
   327  
   328  			timeout := time.NewTimer(syncChallengeTimeout)
   329  			defer timeout.Stop()
   330  
   331  			select {
   332  			case res := <-resCh:
   333  				headers := ([]*types.Header)(*res.Res.(*eth.BlockHeadersRequest))
   334  				if len(headers) == 0 {
   335  					// Required blocks are allowed to be missing if the remote
   336  					// node is not yet synced
   337  					res.Done <- nil
   338  					return
   339  				}
   340  				// Validate the header and either drop the peer or continue
   341  				if len(headers) > 1 {
   342  					res.Done <- errors.New("too many headers in required block response")
   343  					return
   344  				}
   345  				if headers[0].Number.Uint64() != number || headers[0].Hash() != hash {
   346  					peer.Log().Info("Required block mismatch, dropping peer", "number", number, "hash", headers[0].Hash(), "want", hash)
   347  					res.Done <- errors.New("required block mismatch")
   348  					return
   349  				}
   350  				peer.Log().Debug("Peer required block verified", "number", number, "hash", hash)
   351  				res.Done <- nil
   352  			case <-timeout.C:
   353  				peer.Log().Warn("Required block challenge timed out, dropping", "addr", peer.RemoteAddr(), "type", peer.Name())
   354  				h.removePeer(peer.ID())
   355  			}
   356  		}(number, hash, req)
   357  	}
   358  	// Handle incoming messages until the connection is torn down
   359  	return handler(peer)
   360  }
   361  
   362  // runSnapExtension registers a `snap` peer into the joint eth/snap peerset and
   363  // starts handling inbound messages. As `snap` is only a satellite protocol to
   364  // `eth`, all subsystem registrations and lifecycle management will be done by
   365  // the main `eth` handler to prevent strange races.
   366  func (h *handler) runSnapExtension(peer *snap.Peer, handler snap.Handler) error {
   367  	if !h.incHandlers() {
   368  		return p2p.DiscQuitting
   369  	}
   370  	defer h.decHandlers()
   371  
   372  	if err := h.peers.registerSnapExtension(peer); err != nil {
   373  		if metrics.Enabled() {
   374  			if peer.Inbound() {
   375  				snap.IngressRegistrationErrorMeter.Mark(1)
   376  			} else {
   377  				snap.EgressRegistrationErrorMeter.Mark(1)
   378  			}
   379  		}
   380  		peer.Log().Debug("Snapshot extension registration failed", "err", err)
   381  		return err
   382  	}
   383  	return handler(peer)
   384  }
   385  
   386  // removePeer requests disconnection of a peer.
   387  func (h *handler) removePeer(id string) {
   388  	peer := h.peers.peer(id)
   389  	if peer != nil {
   390  		peer.Peer.Disconnect(p2p.DiscUselessPeer)
   391  	}
   392  }
   393  
   394  // unregisterPeer removes a peer from the downloader, fetchers and main peer set.
   395  func (h *handler) unregisterPeer(id string) {
   396  	// Create a custom logger to avoid printing the entire id
   397  	var logger log.Logger
   398  	if len(id) < 16 {
   399  		// Tests use short IDs, don't choke on them
   400  		logger = log.New("peer", id)
   401  	} else {
   402  		logger = log.New("peer", id[:8])
   403  	}
   404  	// Abort if the peer does not exist
   405  	peer := h.peers.peer(id)
   406  	if peer == nil {
   407  		logger.Warn("Ethereum peer removal failed", "err", errPeerNotRegistered)
   408  		return
   409  	}
   410  	// Remove the `eth` peer if it exists
   411  	logger.Debug("Removing Ethereum peer", "snap", peer.snapExt != nil)
   412  
   413  	// Remove the `snap` extension if it exists
   414  	if peer.snapExt != nil {
   415  		h.downloader.SnapSyncer.Unregister(id)
   416  	}
   417  	h.downloader.UnregisterPeer(id)
   418  	h.txFetcher.Drop(id)
   419  
   420  	if err := h.peers.unregisterPeer(id); err != nil {
   421  		logger.Error("Ethereum peer removal failed", "err", err)
   422  	}
   423  }
   424  
   425  func (h *handler) Start(maxPeers int) {
   426  	h.maxPeers = maxPeers
   427  
   428  	// broadcast and announce transactions (only new ones, not resurrected ones)
   429  	h.wg.Add(1)
   430  	h.txsCh = make(chan core.NewTxsEvent, txChanSize)
   431  	h.txsSub = h.txpool.SubscribeTransactions(h.txsCh, false)
   432  	go h.txBroadcastLoop()
   433  
   434  	// broadcast block range
   435  	h.wg.Add(1)
   436  	h.blockRange = newBlockRangeState(h.chain, h.eventMux)
   437  	go h.blockRangeLoop(h.blockRange)
   438  
   439  	// start sync handlers
   440  	h.txFetcher.Start()
   441  
   442  	// start peer handler tracker
   443  	h.wg.Add(1)
   444  	go h.protoTracker()
   445  }
   446  
   447  func (h *handler) Stop() {
   448  	h.txsSub.Unsubscribe() // quits txBroadcastLoop
   449  	h.blockRange.stop()
   450  	h.txFetcher.Stop()
   451  	h.downloader.Terminate()
   452  
   453  	// Quit chainSync and txsync64.
   454  	// After this is done, no new peers will be accepted.
   455  	close(h.quitSync)
   456  
   457  	// Disconnect existing sessions.
   458  	// This also closes the gate for any new registrations on the peer set.
   459  	// sessions which are already established but not added to h.peers yet
   460  	// will exit when they try to register.
   461  	h.peers.close()
   462  	h.wg.Wait()
   463  
   464  	log.Info("Ethereum protocol stopped")
   465  }
   466  
   467  // BroadcastTransactions will propagate a batch of transactions
   468  // - To a square root of all peers for non-blob transactions
   469  // - And, separately, as announcements to all peers which are not known to
   470  // already have the given transaction.
   471  func (h *handler) BroadcastTransactions(txs types.Transactions) {
   472  	var (
   473  		blobTxs  int // Number of blob transactions to announce only
   474  		largeTxs int // Number of large transactions to announce only
   475  
   476  		directCount int // Number of transactions sent directly to peers (duplicates included)
   477  		annCount    int // Number of transactions announced across all peers (duplicates included)
   478  
   479  		txset = make(map[*ethPeer][]common.Hash) // Set peer->hash to transfer directly
   480  		annos = make(map[*ethPeer][]common.Hash) // Set peer->hash to announce
   481  	)
   482  	// Broadcast transactions to a batch of peers not knowing about it
   483  	direct := big.NewInt(int64(math.Sqrt(float64(h.peers.len())))) // Approximate number of peers to broadcast to
   484  	if direct.BitLen() == 0 {
   485  		direct = big.NewInt(1)
   486  	}
   487  	total := new(big.Int).Exp(direct, big.NewInt(2), nil) // Stabilise total peer count a bit based on sqrt peers
   488  
   489  	var (
   490  		signer = types.LatestSigner(h.chain.Config()) // Don't care about chain status, we just need *a* sender
   491  		hasher = crypto.NewKeccakState()
   492  		hash   = make([]byte, 32)
   493  	)
   494  	for _, tx := range txs {
   495  		var maybeDirect bool
   496  		switch {
   497  		case tx.Type() == types.BlobTxType:
   498  			blobTxs++
   499  		case tx.Size() > txMaxBroadcastSize:
   500  			largeTxs++
   501  		default:
   502  			maybeDirect = true
   503  		}
   504  		// Send the transaction (if it's small enough) directly to a subset of
   505  		// the peers that have not received it yet, ensuring that the flow of
   506  		// transactions is grouped by account to (try and) avoid nonce gaps.
   507  		//
   508  		// To do this, we hash the local enode IW with together with a peer's
   509  		// enode ID together with the transaction sender and broadcast if
   510  		// `sha(self, peer, sender) mod peers < sqrt(peers)`.
   511  		for _, peer := range h.peers.peersWithoutTransaction(tx.Hash()) {
   512  			var broadcast bool
   513  			if maybeDirect {
   514  				hasher.Reset()
   515  				hasher.Write(h.nodeID.Bytes())
   516  				hasher.Write(peer.Node().ID().Bytes())
   517  
   518  				from, _ := types.Sender(signer, tx) // Ignore error, we only use the addr as a propagation target splitter
   519  				hasher.Write(from.Bytes())
   520  
   521  				hasher.Read(hash)
   522  				if new(big.Int).Mod(new(big.Int).SetBytes(hash), total).Cmp(direct) < 0 {
   523  					broadcast = true
   524  				}
   525  			}
   526  			if broadcast {
   527  				txset[peer] = append(txset[peer], tx.Hash())
   528  			} else {
   529  				annos[peer] = append(annos[peer], tx.Hash())
   530  			}
   531  		}
   532  	}
   533  	for peer, hashes := range txset {
   534  		directCount += len(hashes)
   535  		peer.AsyncSendTransactions(hashes)
   536  	}
   537  	for peer, hashes := range annos {
   538  		annCount += len(hashes)
   539  		peer.AsyncSendPooledTransactionHashes(hashes)
   540  	}
   541  	log.Debug("Distributed transactions", "plaintxs", len(txs)-blobTxs-largeTxs, "blobtxs", blobTxs, "largetxs", largeTxs,
   542  		"bcastpeers", len(txset), "bcastcount", directCount, "annpeers", len(annos), "anncount", annCount)
   543  }
   544  
   545  // txBroadcastLoop announces new transactions to connected peers.
   546  func (h *handler) txBroadcastLoop() {
   547  	defer h.wg.Done()
   548  	for {
   549  		select {
   550  		case event := <-h.txsCh:
   551  			h.BroadcastTransactions(event.Txs)
   552  		case <-h.txsSub.Err():
   553  			return
   554  		}
   555  	}
   556  }
   557  
   558  // enableSyncedFeatures enables the post-sync functionalities when the initial
   559  // sync is finished.
   560  func (h *handler) enableSyncedFeatures() {
   561  	// Mark the local node as synced.
   562  	h.synced.Store(true)
   563  
   564  	// If we were running snap sync and it finished, disable doing another
   565  	// round on next sync cycle
   566  	if h.snapSync.Load() {
   567  		log.Info("Snap sync complete, auto disabling")
   568  		h.snapSync.Store(false)
   569  	}
   570  }
   571  
   572  // blockRangeState holds the state of the block range update broadcasting mechanism.
   573  type blockRangeState struct {
   574  	prev    eth.BlockRangeUpdatePacket
   575  	next    atomic.Pointer[eth.BlockRangeUpdatePacket]
   576  	headCh  chan core.ChainHeadEvent
   577  	headSub event.Subscription
   578  	syncSub *event.TypeMuxSubscription
   579  }
   580  
   581  func newBlockRangeState(chain *core.BlockChain, typeMux *event.TypeMux) *blockRangeState {
   582  	headCh := make(chan core.ChainHeadEvent, chainHeadChanSize)
   583  	headSub := chain.SubscribeChainHeadEvent(headCh)
   584  	syncSub := typeMux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
   585  	st := &blockRangeState{
   586  		headCh:  headCh,
   587  		headSub: headSub,
   588  		syncSub: syncSub,
   589  	}
   590  	st.update(chain, chain.CurrentBlock())
   591  	st.prev = *st.next.Load()
   592  	return st
   593  }
   594  
   595  // blockRangeBroadcastLoop announces changes in locally-available block range to peers.
   596  // The range to announce is the range that is available in the store, so it's not just
   597  // about imported blocks.
   598  func (h *handler) blockRangeLoop(st *blockRangeState) {
   599  	defer h.wg.Done()
   600  
   601  	for {
   602  		select {
   603  		case ev := <-st.syncSub.Chan():
   604  			if ev == nil {
   605  				continue
   606  			}
   607  			if _, ok := ev.Data.(downloader.StartEvent); ok && h.snapSync.Load() {
   608  				h.blockRangeWhileSnapSyncing(st)
   609  			}
   610  		case <-st.headCh:
   611  			st.update(h.chain, h.chain.CurrentBlock())
   612  			if st.shouldSend() {
   613  				h.broadcastBlockRange(st)
   614  			}
   615  		case <-st.headSub.Err():
   616  			return
   617  		}
   618  	}
   619  }
   620  
   621  // blockRangeWhileSnapSyncing announces block range updates during snap sync.
   622  // Here we poll the CurrentSnapBlock on a timer and announce updates to it.
   623  func (h *handler) blockRangeWhileSnapSyncing(st *blockRangeState) {
   624  	tick := time.NewTicker(1 * time.Minute)
   625  	defer tick.Stop()
   626  
   627  	for {
   628  		select {
   629  		case <-tick.C:
   630  			st.update(h.chain, h.chain.CurrentSnapBlock())
   631  			if st.shouldSend() {
   632  				h.broadcastBlockRange(st)
   633  			}
   634  		// back to processing head block updates when sync is done
   635  		case ev := <-st.syncSub.Chan():
   636  			if ev == nil {
   637  				continue
   638  			}
   639  			switch ev.Data.(type) {
   640  			case downloader.FailedEvent, downloader.DoneEvent:
   641  				return
   642  			}
   643  		// ignore head updates, but exit when the subscription ends
   644  		case <-st.headCh:
   645  		case <-st.headSub.Err():
   646  			return
   647  		}
   648  	}
   649  }
   650  
   651  // broadcastBlockRange sends a range update when one is due.
   652  func (h *handler) broadcastBlockRange(state *blockRangeState) {
   653  	h.peers.lock.Lock()
   654  	peerlist := slices.Collect(maps.Values(h.peers.peers))
   655  	h.peers.lock.Unlock()
   656  	if len(peerlist) == 0 {
   657  		return
   658  	}
   659  	msg := state.currentRange()
   660  	log.Debug("Sending BlockRangeUpdate", "peers", len(peerlist), "earliest", msg.EarliestBlock, "latest", msg.LatestBlock)
   661  	for _, p := range peerlist {
   662  		p.SendBlockRangeUpdate(msg)
   663  	}
   664  	state.prev = *state.next.Load()
   665  }
   666  
   667  // update assigns the values of the next block range update from the chain.
   668  func (st *blockRangeState) update(chain *core.BlockChain, latest *types.Header) {
   669  	earliest, _ := chain.HistoryPruningCutoff()
   670  	st.next.Store(&eth.BlockRangeUpdatePacket{
   671  		EarliestBlock:   min(latest.Number.Uint64(), earliest),
   672  		LatestBlock:     latest.Number.Uint64(),
   673  		LatestBlockHash: latest.Hash(),
   674  	})
   675  }
   676  
   677  // shouldSend decides whether it is time to send a block range update. We don't want to
   678  // send these updates constantly, so they will usually only be sent every 32 blocks.
   679  // However, there is a special case: if the range would move back, i.e. due to SetHead, we
   680  // want to send it immediately.
   681  func (st *blockRangeState) shouldSend() bool {
   682  	next := st.next.Load()
   683  	return next.LatestBlock < st.prev.LatestBlock ||
   684  		next.LatestBlock-st.prev.LatestBlock >= 32
   685  }
   686  
   687  func (st *blockRangeState) stop() {
   688  	st.syncSub.Unsubscribe()
   689  	st.headSub.Unsubscribe()
   690  }
   691  
   692  // currentRange returns the current block range.
   693  // This is safe to call from any goroutine.
   694  func (st *blockRangeState) currentRange() eth.BlockRangeUpdatePacket {
   695  	return *st.next.Load()
   696  }