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