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(ð.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 }