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