github.com/theQRL/go-zond@v0.1.1/zond/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 zond 18 19 import ( 20 "errors" 21 "math" 22 "math/big" 23 "sync" 24 "sync/atomic" 25 "time" 26 27 "github.com/theQRL/go-zond/common" 28 "github.com/theQRL/go-zond/consensus" 29 "github.com/theQRL/go-zond/consensus/beacon" 30 "github.com/theQRL/go-zond/core" 31 "github.com/theQRL/go-zond/core/forkid" 32 "github.com/theQRL/go-zond/core/rawdb" 33 "github.com/theQRL/go-zond/core/txpool" 34 "github.com/theQRL/go-zond/core/types" 35 "github.com/theQRL/go-zond/event" 36 "github.com/theQRL/go-zond/log" 37 "github.com/theQRL/go-zond/metrics" 38 "github.com/theQRL/go-zond/p2p" 39 "github.com/theQRL/go-zond/trie/triedb/pathdb" 40 "github.com/theQRL/go-zond/zond/downloader" 41 "github.com/theQRL/go-zond/zond/fetcher" 42 "github.com/theQRL/go-zond/zond/protocols/snap" 43 "github.com/theQRL/go-zond/zond/protocols/zond" 44 "github.com/theQRL/go-zond/zonddb" 45 ) 46 47 const ( 48 // txChanSize is the size of channel listening to NewTxsEvent. 49 // The number is referenced from the size of tx pool. 50 txChanSize = 4096 51 52 // txMaxBroadcastSize is the max size of a transaction that will be broadcasted. 53 // All transactions with a higher size will be announced and need to be fetched 54 // by the peer. 55 txMaxBroadcastSize = 4096 56 ) 57 58 var ( 59 syncChallengeTimeout = 15 * time.Second // Time allowance for a node to reply to the sync progress challenge 60 ) 61 62 // txPool defines the methods needed from a transaction pool implementation to 63 // support all the operations needed by the Ethereum chain protocols. 64 type txPool interface { 65 // Has returns an indicator whether txpool has a transaction 66 // cached with the given hash. 67 Has(hash common.Hash) bool 68 69 // Get retrieves the transaction from local txpool with given 70 // tx hash. 71 Get(hash common.Hash) *types.Transaction 72 73 // Add should add the given transactions to the pool. 74 Add(txs []*types.Transaction, local bool, sync bool) []error 75 76 // Pending should return pending transactions. 77 // The slice should be modifiable by the caller. 78 Pending(enforceTips bool) map[common.Address][]*txpool.LazyTransaction 79 80 // SubscribeNewTxsEvent should return an event subscription of 81 // NewTxsEvent and send events to the given channel. 82 SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription 83 } 84 85 // handlerConfig is the collection of initialization parameters to create a full 86 // node network handler. 87 type handlerConfig struct { 88 Database zonddb.Database // Database for direct sync insertions 89 Chain *core.BlockChain // Blockchain to serve data from 90 TxPool txPool // Transaction pool to propagate from 91 Merger *consensus.Merger // The manager for eth1/2 transition 92 Network uint64 // Network identifier to adfvertise 93 Sync downloader.SyncMode // Whether to snap or full sync 94 BloomCache uint64 // Megabytes to alloc for snap sync bloom 95 EventMux *event.TypeMux // Legacy event mux, deprecate for `feed` 96 RequiredBlocks map[uint64]common.Hash // Hard coded map of required block hashes for sync challenges 97 } 98 99 type handler struct { 100 networkID uint64 101 forkFilter forkid.Filter // Fork ID filter, constant across the lifetime of the node 102 103 snapSync atomic.Bool // Flag whether snap sync is enabled (gets disabled if we already have blocks) 104 acceptTxs atomic.Bool // Flag whether we're considered synchronised (enables transaction processing) 105 106 database zonddb.Database 107 txpool txPool 108 chain *core.BlockChain 109 maxPeers int 110 111 downloader *downloader.Downloader 112 blockFetcher *fetcher.BlockFetcher 113 txFetcher *fetcher.TxFetcher 114 peers *peerSet 115 merger *consensus.Merger 116 117 eventMux *event.TypeMux 118 txsCh chan core.NewTxsEvent 119 txsSub event.Subscription 120 minedBlockSub *event.TypeMuxSubscription 121 122 requiredBlocks map[uint64]common.Hash 123 124 // channels for fetcher, syncer, txsyncLoop 125 quitSync chan struct{} 126 127 chainSync *chainSyncer 128 wg sync.WaitGroup 129 130 handlerStartCh chan struct{} 131 handlerDoneCh chan struct{} 132 } 133 134 // newHandler returns a handler for all Ethereum chain management protocol. 135 func newHandler(config *handlerConfig) (*handler, error) { 136 // Create the protocol manager with the base fields 137 if config.EventMux == nil { 138 config.EventMux = new(event.TypeMux) // Nicety initialization for tests 139 } 140 h := &handler{ 141 networkID: config.Network, 142 forkFilter: forkid.NewFilter(config.Chain), 143 eventMux: config.EventMux, 144 database: config.Database, 145 txpool: config.TxPool, 146 chain: config.Chain, 147 peers: newPeerSet(), 148 merger: config.Merger, 149 requiredBlocks: config.RequiredBlocks, 150 quitSync: make(chan struct{}), 151 handlerDoneCh: make(chan struct{}), 152 handlerStartCh: make(chan struct{}), 153 } 154 if config.Sync == downloader.FullSync { 155 // The database seems empty as the current block is the genesis. Yet the snap 156 // block is ahead, so snap sync was enabled for this node at a certain point. 157 // The scenarios where this can happen is 158 // * if the user manually (or via a bad block) rolled back a snap sync node 159 // below the sync point. 160 // * the last snap sync is not finished while user specifies a full sync this 161 // time. But we don't have any recent state for full sync. 162 // In these cases however it's safe to reenable snap sync. 163 fullBlock, snapBlock := h.chain.CurrentBlock(), h.chain.CurrentSnapBlock() 164 if fullBlock.Number.Uint64() == 0 && snapBlock.Number.Uint64() > 0 { 165 h.snapSync.Store(true) 166 log.Warn("Switch sync mode from full sync to snap sync") 167 } 168 } else { 169 if h.chain.CurrentBlock().Number.Uint64() > 0 { 170 // Print warning log if database is not empty to run snap sync. 171 log.Warn("Switch sync mode from snap sync to full sync") 172 } else { 173 // If snap sync was requested and our database is empty, grant it 174 h.snapSync.Store(true) 175 } 176 } 177 // If sync succeeds, pass a callback to potentially disable snap sync mode 178 // and enable transaction propagation. 179 success := func() { 180 // If we were running snap sync and it finished, disable doing another 181 // round on next sync cycle 182 if h.snapSync.Load() { 183 log.Info("Snap sync complete, auto disabling") 184 h.snapSync.Store(false) 185 } 186 // If we've successfully finished a sync cycle, accept transactions from 187 // the network 188 h.enableSyncedFeatures() 189 } 190 // Construct the downloader (long sync) 191 h.downloader = downloader.New(config.Database, h.eventMux, h.chain, nil, h.removePeer, success) 192 if ttd := h.chain.Config().TerminalTotalDifficulty; ttd != nil { 193 if h.chain.Config().TerminalTotalDifficultyPassed { 194 log.Info("Chain post-merge, sync via beacon client") 195 } else { 196 head := h.chain.CurrentBlock() 197 if td := h.chain.GetTd(head.Hash(), head.Number.Uint64()); td.Cmp(ttd) >= 0 { 198 log.Info("Chain post-TTD, sync via beacon client") 199 } else { 200 log.Warn("Chain pre-merge, sync via PoW (ensure beacon client is ready)") 201 } 202 } 203 } else if h.chain.Config().TerminalTotalDifficultyPassed { 204 log.Error("Chain configured post-merge, but without TTD. Are you debugging sync?") 205 } 206 // Construct the fetcher (short sync) 207 validator := func(header *types.Header) error { 208 // All the block fetcher activities should be disabled 209 // after the transition. Print the warning log. 210 if h.merger.PoSFinalized() { 211 log.Warn("Unexpected validation activity", "hash", header.Hash(), "number", header.Number) 212 return errors.New("unexpected behavior after transition") 213 } 214 // Reject all the PoS style headers in the first place. No matter 215 // the chain has finished the transition or not, the PoS headers 216 // should only come from the trusted consensus layer instead of 217 // p2p network. 218 if beacon, ok := h.chain.Engine().(*beacon.Beacon); ok { 219 if beacon.IsPoSHeader(header) { 220 return errors.New("unexpected post-merge header") 221 } 222 } 223 return h.chain.Engine().VerifyHeader(h.chain, header) 224 } 225 heighter := func() uint64 { 226 return h.chain.CurrentBlock().Number.Uint64() 227 } 228 inserter := func(blocks types.Blocks) (int, error) { 229 // All the block fetcher activities should be disabled 230 // after the transition. Print the warning log. 231 if h.merger.PoSFinalized() { 232 var ctx []interface{} 233 ctx = append(ctx, "blocks", len(blocks)) 234 if len(blocks) > 0 { 235 ctx = append(ctx, "firsthash", blocks[0].Hash()) 236 ctx = append(ctx, "firstnumber", blocks[0].Number()) 237 ctx = append(ctx, "lasthash", blocks[len(blocks)-1].Hash()) 238 ctx = append(ctx, "lastnumber", blocks[len(blocks)-1].Number()) 239 } 240 log.Warn("Unexpected insertion activity", ctx...) 241 return 0, errors.New("unexpected behavior after transition") 242 } 243 // If snap sync is running, deny importing weird blocks. This is a problematic 244 // clause when starting up a new network, because snap-syncing miners might not 245 // accept each others' blocks until a restart. Unfortunately we haven't figured 246 // out a way yet where nodes can decide unilaterally whether the network is new 247 // or not. This should be fixed if we figure out a solution. 248 if h.snapSync.Load() { 249 log.Warn("Snap syncing, discarded propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash()) 250 return 0, nil 251 } 252 if h.merger.TDDReached() { 253 // The blocks from the p2p network is regarded as untrusted 254 // after the transition. In theory block gossip should be disabled 255 // entirely whenever the transition is started. But in order to 256 // handle the transition boundary reorg in the consensus-layer, 257 // the legacy blocks are still accepted, but only for the terminal 258 // pow blocks. Spec: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3675.md#halt-the-importing-of-pow-blocks 259 for i, block := range blocks { 260 ptd := h.chain.GetTd(block.ParentHash(), block.NumberU64()-1) 261 if ptd == nil { 262 return 0, nil 263 } 264 td := new(big.Int).Add(ptd, block.Difficulty()) 265 if !h.chain.Config().IsTerminalPoWBlock(ptd, td) { 266 log.Info("Filtered out non-termimal pow block", "number", block.NumberU64(), "hash", block.Hash()) 267 return 0, nil 268 } 269 if err := h.chain.InsertBlockWithoutSetHead(block); err != nil { 270 return i, err 271 } 272 } 273 return 0, nil 274 } 275 n, err := h.chain.InsertChain(blocks) 276 if err == nil { 277 h.enableSyncedFeatures() // Mark initial sync done on any fetcher import 278 } 279 return n, err 280 } 281 h.blockFetcher = fetcher.NewBlockFetcher(false, nil, h.chain.GetBlockByHash, validator, h.BroadcastBlock, heighter, nil, inserter, h.removePeer) 282 283 fetchTx := func(peer string, hashes []common.Hash) error { 284 p := h.peers.peer(peer) 285 if p == nil { 286 return errors.New("unknown peer") 287 } 288 return p.RequestTxs(hashes) 289 } 290 addTxs := func(txs []*types.Transaction) []error { 291 return h.txpool.Add(txs, false, false) 292 } 293 h.txFetcher = fetcher.NewTxFetcher(h.txpool.Has, addTxs, fetchTx) 294 h.chainSync = newChainSyncer(h) 295 return h, nil 296 } 297 298 // protoTracker tracks the number of active protocol handlers. 299 func (h *handler) protoTracker() { 300 defer h.wg.Done() 301 var active int 302 for { 303 select { 304 case <-h.handlerStartCh: 305 active++ 306 case <-h.handlerDoneCh: 307 active-- 308 case <-h.quitSync: 309 // Wait for all active handlers to finish. 310 for ; active > 0; active-- { 311 <-h.handlerDoneCh 312 } 313 return 314 } 315 } 316 } 317 318 // incHandlers signals to increment the number of active handlers if not 319 // quitting. 320 func (h *handler) incHandlers() bool { 321 select { 322 case h.handlerStartCh <- struct{}{}: 323 return true 324 case <-h.quitSync: 325 return false 326 } 327 } 328 329 // decHandlers signals to decrement the number of active handlers. 330 func (h *handler) decHandlers() { 331 h.handlerDoneCh <- struct{}{} 332 } 333 334 // runEthPeer registers an eth peer into the joint eth/snap peerset, adds it to 335 // various subsystems and starts handling messages. 336 func (h *handler) runEthPeer(peer *zond.Peer, handler zond.Handler) error { 337 if !h.incHandlers() { 338 return p2p.DiscQuitting 339 } 340 defer h.decHandlers() 341 342 // If the peer has a `snap` extension, wait for it to connect so we can have 343 // a uniform initialization/teardown mechanism 344 snap, err := h.peers.waitSnapExtension(peer) 345 if err != nil { 346 peer.Log().Error("Snapshot extension barrier failed", "err", err) 347 return err 348 } 349 350 // Execute the Ethereum handshake 351 var ( 352 genesis = h.chain.Genesis() 353 head = h.chain.CurrentHeader() 354 hash = head.Hash() 355 number = head.Number.Uint64() 356 td = h.chain.GetTd(hash, number) 357 ) 358 forkID := forkid.NewID(h.chain.Config(), genesis, number, head.Time) 359 if err := peer.Handshake(h.networkID, td, hash, genesis.Hash(), forkID, h.forkFilter); err != nil { 360 peer.Log().Debug("Ethereum handshake failed", "err", err) 361 return err 362 } 363 reject := false // reserved peer slots 364 if h.snapSync.Load() { 365 if snap == nil { 366 // If we are running snap-sync, we want to reserve roughly half the peer 367 // slots for peers supporting the snap protocol. 368 // The logic here is; we only allow up to 5 more non-snap peers than snap-peers. 369 if all, snp := h.peers.len(), h.peers.snapLen(); all-snp > snp+5 { 370 reject = true 371 } 372 } 373 } 374 // Ignore maxPeers if this is a trusted peer 375 if !peer.Peer.Info().Network.Trusted { 376 if reject || h.peers.len() >= h.maxPeers { 377 return p2p.DiscTooManyPeers 378 } 379 } 380 peer.Log().Debug("Ethereum peer connected", "name", peer.Name()) 381 382 // Register the peer locally 383 if err := h.peers.registerPeer(peer, snap); err != nil { 384 peer.Log().Error("Ethereum peer registration failed", "err", err) 385 return err 386 } 387 defer h.unregisterPeer(peer.ID()) 388 389 p := h.peers.peer(peer.ID()) 390 if p == nil { 391 return errors.New("peer dropped during handling") 392 } 393 // Register the peer in the downloader. If the downloader considers it banned, we disconnect 394 if err := h.downloader.RegisterPeer(peer.ID(), peer.Version(), peer); err != nil { 395 peer.Log().Error("Failed to register peer in zond syncer", "err", err) 396 return err 397 } 398 if snap != nil { 399 if err := h.downloader.SnapSyncer.Register(snap); err != nil { 400 peer.Log().Error("Failed to register peer in snap syncer", "err", err) 401 return err 402 } 403 } 404 h.chainSync.handlePeerEvent() 405 406 // Propagate existing transactions. new transactions appearing 407 // after this will be sent via broadcasts. 408 h.syncTransactions(peer) 409 410 // Create a notification channel for pending requests if the peer goes down 411 dead := make(chan struct{}) 412 defer close(dead) 413 414 // If we have any explicit peer required block hashes, request them 415 for number, hash := range h.requiredBlocks { 416 resCh := make(chan *zond.Response) 417 418 req, err := peer.RequestHeadersByNumber(number, 1, 0, false, resCh) 419 if err != nil { 420 return err 421 } 422 go func(number uint64, hash common.Hash, req *zond.Request) { 423 // Ensure the request gets cancelled in case of error/drop 424 defer req.Close() 425 426 timeout := time.NewTimer(syncChallengeTimeout) 427 defer timeout.Stop() 428 429 select { 430 case res := <-resCh: 431 headers := ([]*types.Header)(*res.Res.(*zond.BlockHeadersPacket)) 432 if len(headers) == 0 { 433 // Required blocks are allowed to be missing if the remote 434 // node is not yet synced 435 res.Done <- nil 436 return 437 } 438 // Validate the header and either drop the peer or continue 439 if len(headers) > 1 { 440 res.Done <- errors.New("too many headers in required block response") 441 return 442 } 443 if headers[0].Number.Uint64() != number || headers[0].Hash() != hash { 444 peer.Log().Info("Required block mismatch, dropping peer", "number", number, "hash", headers[0].Hash(), "want", hash) 445 res.Done <- errors.New("required block mismatch") 446 return 447 } 448 peer.Log().Debug("Peer required block verified", "number", number, "hash", hash) 449 res.Done <- nil 450 case <-timeout.C: 451 peer.Log().Warn("Required block challenge timed out, dropping", "addr", peer.RemoteAddr(), "type", peer.Name()) 452 h.removePeer(peer.ID()) 453 } 454 }(number, hash, req) 455 } 456 // Handle incoming messages until the connection is torn down 457 return handler(peer) 458 } 459 460 // runSnapExtension registers a `snap` peer into the joint zond/snap peerset and 461 // starts handling inbound messages. As `snap` is only a satellite protocol to 462 // `zond`, all subsystem registrations and lifecycle management will be done by 463 // the main `zond` handler to prevent strange races. 464 func (h *handler) runSnapExtension(peer *snap.Peer, handler snap.Handler) error { 465 if !h.incHandlers() { 466 return p2p.DiscQuitting 467 } 468 defer h.decHandlers() 469 470 if err := h.peers.registerSnapExtension(peer); err != nil { 471 if metrics.Enabled { 472 if peer.Inbound() { 473 snap.IngressRegistrationErrorMeter.Mark(1) 474 } else { 475 snap.EgressRegistrationErrorMeter.Mark(1) 476 } 477 } 478 peer.Log().Warn("Snapshot extension registration failed", "err", err) 479 return err 480 } 481 return handler(peer) 482 } 483 484 // removePeer requests disconnection of a peer. 485 func (h *handler) removePeer(id string) { 486 peer := h.peers.peer(id) 487 if peer != nil { 488 peer.Peer.Disconnect(p2p.DiscUselessPeer) 489 } 490 } 491 492 // unregisterPeer removes a peer from the downloader, fetchers and main peer set. 493 func (h *handler) unregisterPeer(id string) { 494 // Create a custom logger to avoid printing the entire id 495 var logger log.Logger 496 if len(id) < 16 { 497 // Tests use short IDs, don't choke on them 498 logger = log.New("peer", id) 499 } else { 500 logger = log.New("peer", id[:8]) 501 } 502 // Abort if the peer does not exist 503 peer := h.peers.peer(id) 504 if peer == nil { 505 logger.Error("Ethereum peer removal failed", "err", errPeerNotRegistered) 506 return 507 } 508 // Remove the `zond` peer if it exists 509 logger.Debug("Removing Ethereum peer", "snap", peer.snapExt != nil) 510 511 // Remove the `snap` extension if it exists 512 if peer.snapExt != nil { 513 h.downloader.SnapSyncer.Unregister(id) 514 } 515 h.downloader.UnregisterPeer(id) 516 h.txFetcher.Drop(id) 517 518 if err := h.peers.unregisterPeer(id); err != nil { 519 logger.Error("Ethereum peer removal failed", "err", err) 520 } 521 } 522 523 func (h *handler) Start(maxPeers int) { 524 h.maxPeers = maxPeers 525 526 // broadcast transactions 527 h.wg.Add(1) 528 h.txsCh = make(chan core.NewTxsEvent, txChanSize) 529 h.txsSub = h.txpool.SubscribeNewTxsEvent(h.txsCh) 530 go h.txBroadcastLoop() 531 532 // broadcast mined blocks 533 h.wg.Add(1) 534 h.minedBlockSub = h.eventMux.Subscribe(core.NewMinedBlockEvent{}) 535 go h.minedBroadcastLoop() 536 537 // start sync handlers 538 h.wg.Add(1) 539 go h.chainSync.loop() 540 541 // start peer handler tracker 542 h.wg.Add(1) 543 go h.protoTracker() 544 } 545 546 func (h *handler) Stop() { 547 h.txsSub.Unsubscribe() // quits txBroadcastLoop 548 h.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop 549 550 // Quit chainSync and txsync64. 551 // After this is done, no new peers will be accepted. 552 close(h.quitSync) 553 554 // Disconnect existing sessions. 555 // This also closes the gate for any new registrations on the peer set. 556 // sessions which are already established but not added to h.peers yet 557 // will exit when they try to register. 558 h.peers.close() 559 h.wg.Wait() 560 561 log.Info("Ethereum protocol stopped") 562 } 563 564 // BroadcastBlock will either propagate a block to a subset of its peers, or 565 // will only announce its availability (depending what's requested). 566 func (h *handler) BroadcastBlock(block *types.Block, propagate bool) { 567 // Disable the block propagation if the chain has already entered the PoS 568 // stage. The block propagation is delegated to the consensus layer. 569 if h.merger.PoSFinalized() { 570 return 571 } 572 // Disable the block propagation if it's the post-merge block. 573 if beacon, ok := h.chain.Engine().(*beacon.Beacon); ok { 574 if beacon.IsPoSHeader(block.Header()) { 575 return 576 } 577 } 578 hash := block.Hash() 579 peers := h.peers.peersWithoutBlock(hash) 580 581 // If propagation is requested, send to a subset of the peer 582 if propagate { 583 // Calculate the TD of the block (it's not imported yet, so block.Td is not valid) 584 var td *big.Int 585 if parent := h.chain.GetBlock(block.ParentHash(), block.NumberU64()-1); parent != nil { 586 td = new(big.Int).Add(block.Difficulty(), h.chain.GetTd(block.ParentHash(), block.NumberU64()-1)) 587 } else { 588 log.Error("Propagating dangling block", "number", block.Number(), "hash", hash) 589 return 590 } 591 // Send the block to a subset of our peers 592 transfer := peers[:int(math.Sqrt(float64(len(peers))))] 593 for _, peer := range transfer { 594 peer.AsyncSendNewBlock(block, td) 595 } 596 log.Trace("Propagated block", "hash", hash, "recipients", len(transfer), "duration", common.PrettyDuration(time.Since(block.ReceivedAt))) 597 return 598 } 599 // Otherwise if the block is indeed in out own chain, announce it 600 if h.chain.HasBlock(hash, block.NumberU64()) { 601 for _, peer := range peers { 602 peer.AsyncSendNewBlockHash(block) 603 } 604 log.Trace("Announced block", "hash", hash, "recipients", len(peers), "duration", common.PrettyDuration(time.Since(block.ReceivedAt))) 605 } 606 } 607 608 // BroadcastTransactions will propagate a batch of transactions 609 // - To a square root of all peers 610 // - And, separately, as announcements to all peers which are not known to 611 // already have the given transaction. 612 func (h *handler) BroadcastTransactions(txs types.Transactions) { 613 var ( 614 annoCount int // Count of announcements made 615 annoPeers int 616 directCount int // Count of the txs sent directly to peers 617 directPeers int // Count of the peers that were sent transactions directly 618 619 txset = make(map[*ethPeer][]common.Hash) // Set peer->hash to transfer directly 620 annos = make(map[*ethPeer][]common.Hash) // Set peer->hash to announce 621 622 ) 623 // Broadcast transactions to a batch of peers not knowing about it 624 for _, tx := range txs { 625 peers := h.peers.peersWithoutTransaction(tx.Hash()) 626 627 var numDirect int 628 if tx.Size() <= txMaxBroadcastSize { 629 numDirect = int(math.Sqrt(float64(len(peers)))) 630 } 631 // Send the tx unconditionally to a subset of our peers 632 for _, peer := range peers[:numDirect] { 633 txset[peer] = append(txset[peer], tx.Hash()) 634 } 635 // For the remaining peers, send announcement only 636 for _, peer := range peers[numDirect:] { 637 annos[peer] = append(annos[peer], tx.Hash()) 638 } 639 } 640 for peer, hashes := range txset { 641 directPeers++ 642 directCount += len(hashes) 643 peer.AsyncSendTransactions(hashes) 644 } 645 for peer, hashes := range annos { 646 annoPeers++ 647 annoCount += len(hashes) 648 peer.AsyncSendPooledTransactionHashes(hashes) 649 } 650 log.Debug("Transaction broadcast", "txs", len(txs), 651 "announce packs", annoPeers, "announced hashes", annoCount, 652 "tx packs", directPeers, "broadcast txs", directCount) 653 } 654 655 // minedBroadcastLoop sends mined blocks to connected peers. 656 func (h *handler) minedBroadcastLoop() { 657 defer h.wg.Done() 658 659 for obj := range h.minedBlockSub.Chan() { 660 if ev, ok := obj.Data.(core.NewMinedBlockEvent); ok { 661 h.BroadcastBlock(ev.Block, true) // First propagate block to peers 662 h.BroadcastBlock(ev.Block, false) // Only then announce to the rest 663 } 664 } 665 } 666 667 // txBroadcastLoop announces new transactions to connected peers. 668 func (h *handler) txBroadcastLoop() { 669 defer h.wg.Done() 670 for { 671 select { 672 case event := <-h.txsCh: 673 h.BroadcastTransactions(event.Txs) 674 case <-h.txsSub.Err(): 675 return 676 } 677 } 678 } 679 680 // enableSyncedFeatures enables the post-sync functionalities when the initial 681 // sync is finished. 682 func (h *handler) enableSyncedFeatures() { 683 h.acceptTxs.Store(true) 684 if h.chain.TrieDB().Scheme() == rawdb.PathScheme { 685 h.chain.TrieDB().SetBufferSize(pathdb.DefaultBufferSize) 686 } 687 }