github.com/frankli-dev/go-ethereum@v1.1.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 "math" 22 "math/big" 23 "sync" 24 "sync/atomic" 25 "time" 26 27 "github.com/frankli-dev/go-ethereum/common" 28 "github.com/frankli-dev/go-ethereum/core" 29 "github.com/frankli-dev/go-ethereum/core/forkid" 30 "github.com/frankli-dev/go-ethereum/core/types" 31 "github.com/frankli-dev/go-ethereum/eth/downloader" 32 "github.com/frankli-dev/go-ethereum/eth/fetcher" 33 "github.com/frankli-dev/go-ethereum/eth/protocols/eth" 34 "github.com/frankli-dev/go-ethereum/eth/protocols/snap" 35 "github.com/frankli-dev/go-ethereum/ethdb" 36 "github.com/frankli-dev/go-ethereum/event" 37 "github.com/frankli-dev/go-ethereum/log" 38 "github.com/frankli-dev/go-ethereum/p2p" 39 "github.com/frankli-dev/go-ethereum/params" 40 "github.com/frankli-dev/go-ethereum/trie" 41 ) 42 43 const ( 44 // txChanSize is the size of channel listening to NewTxsEvent. 45 // The number is referenced from the size of tx pool. 46 txChanSize = 4096 47 ) 48 49 var ( 50 syncChallengeTimeout = 15 * time.Second // Time allowance for a node to reply to the sync progress challenge 51 ) 52 53 // txPool defines the methods needed from a transaction pool implementation to 54 // support all the operations needed by the Ethereum chain protocols. 55 type txPool interface { 56 // Has returns an indicator whether txpool has a transaction 57 // cached with the given hash. 58 Has(hash common.Hash) bool 59 60 // Get retrieves the transaction from local txpool with given 61 // tx hash. 62 Get(hash common.Hash) *types.Transaction 63 64 // AddRemotes should add the given transactions to the pool. 65 AddRemotes([]*types.Transaction) []error 66 67 // Pending should return pending transactions. 68 // The slice should be modifiable by the caller. 69 Pending() (map[common.Address]types.Transactions, error) 70 71 // SubscribeNewTxsEvent should return an event subscription of 72 // NewTxsEvent and send events to the given channel. 73 SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription 74 } 75 76 // handlerConfig is the collection of initialization parameters to create a full 77 // node network handler. 78 type handlerConfig struct { 79 Database ethdb.Database // Database for direct sync insertions 80 Chain *core.BlockChain // Blockchain to serve data from 81 TxPool txPool // Transaction pool to propagate from 82 Network uint64 // Network identifier to adfvertise 83 Sync downloader.SyncMode // Whether to fast or full sync 84 BloomCache uint64 // Megabytes to alloc for fast sync bloom 85 EventMux *event.TypeMux // Legacy event mux, deprecate for `feed` 86 Checkpoint *params.TrustedCheckpoint // Hard coded checkpoint for sync challenges 87 Whitelist map[uint64]common.Hash // Hard coded whitelist for sync challenged 88 DirectBroadcast bool 89 } 90 91 type handler struct { 92 networkID uint64 93 forkFilter forkid.Filter // Fork ID filter, constant across the lifetime of the node 94 95 fastSync uint32 // Flag whether fast sync is enabled (gets disabled if we already have blocks) 96 snapSync uint32 // Flag whether fast sync should operate on top of the snap protocol 97 acceptTxs uint32 // Flag whether we're considered synchronised (enables transaction processing) 98 directBroadcast bool 99 100 checkpointNumber uint64 // Block number for the sync progress validator to cross reference 101 checkpointHash common.Hash // Block hash for the sync progress validator to cross reference 102 103 database ethdb.Database 104 txpool txPool 105 chain *core.BlockChain 106 maxPeers int 107 108 downloader *downloader.Downloader 109 stateBloom *trie.SyncBloom 110 blockFetcher *fetcher.BlockFetcher 111 txFetcher *fetcher.TxFetcher 112 peers *peerSet 113 114 eventMux *event.TypeMux 115 txsCh chan core.NewTxsEvent 116 txsSub event.Subscription 117 minedBlockSub *event.TypeMuxSubscription 118 119 whitelist map[uint64]common.Hash 120 121 // channels for fetcher, syncer, txsyncLoop 122 txsyncCh chan *txsync 123 quitSync chan struct{} 124 125 chainSync *chainSyncer 126 wg sync.WaitGroup 127 peerWG sync.WaitGroup 128 } 129 130 // newHandler returns a handler for all Ethereum chain management protocol. 131 func newHandler(config *handlerConfig) (*handler, error) { 132 // Create the protocol manager with the base fields 133 if config.EventMux == nil { 134 config.EventMux = new(event.TypeMux) // Nicety initialization for tests 135 } 136 h := &handler{ 137 networkID: config.Network, 138 forkFilter: forkid.NewFilter(config.Chain), 139 eventMux: config.EventMux, 140 database: config.Database, 141 txpool: config.TxPool, 142 chain: config.Chain, 143 peers: newPeerSet(), 144 whitelist: config.Whitelist, 145 directBroadcast: config.DirectBroadcast, 146 txsyncCh: make(chan *txsync), 147 quitSync: make(chan struct{}), 148 } 149 if config.Sync == downloader.FullSync { 150 // The database seems empty as the current block is the genesis. Yet the fast 151 // block is ahead, so fast sync was enabled for this node at a certain point. 152 // The scenarios where this can happen is 153 // * if the user manually (or via a bad block) rolled back a fast sync node 154 // below the sync point. 155 // * the last fast sync is not finished while user specifies a full sync this 156 // time. But we don't have any recent state for full sync. 157 // In these cases however it's safe to reenable fast sync. 158 fullBlock, fastBlock := h.chain.CurrentBlock(), h.chain.CurrentFastBlock() 159 if fullBlock.NumberU64() == 0 && fastBlock.NumberU64() > 0 { 160 h.fastSync = uint32(1) 161 log.Warn("Switch sync mode from full sync to fast sync") 162 } 163 } else { 164 if h.chain.CurrentBlock().NumberU64() > 0 { 165 // Print warning log if database is not empty to run fast sync. 166 log.Warn("Switch sync mode from fast sync to full sync") 167 } else { 168 // If fast sync was requested and our database is empty, grant it 169 h.fastSync = uint32(1) 170 if config.Sync == downloader.SnapSync { 171 h.snapSync = uint32(1) 172 } 173 } 174 } 175 // If we have trusted checkpoints, enforce them on the chain 176 if config.Checkpoint != nil { 177 h.checkpointNumber = (config.Checkpoint.SectionIndex+1)*params.CHTFrequency - 1 178 h.checkpointHash = config.Checkpoint.SectionHead 179 } 180 // Construct the downloader (long sync) and its backing state bloom if fast 181 // sync is requested. The downloader is responsible for deallocating the state 182 // bloom when it's done. 183 // Note: we don't enable it if snap-sync is performed, since it's very heavy 184 // and the heal-portion of the snap sync is much lighter than fast. What we particularly 185 // want to avoid, is a 90%-finished (but restarted) snap-sync to begin 186 // indexing the entire trie 187 if atomic.LoadUint32(&h.fastSync) == 1 && atomic.LoadUint32(&h.snapSync) == 0 { 188 h.stateBloom = trie.NewSyncBloom(config.BloomCache, config.Database) 189 } 190 h.downloader = downloader.New(h.checkpointNumber, config.Database, h.stateBloom, h.eventMux, h.chain, nil, h.removePeer) 191 192 // Construct the fetcher (short sync) 193 validator := func(header *types.Header) error { 194 return h.chain.Engine().VerifyHeader(h.chain, header, true) 195 } 196 heighter := func() uint64 { 197 return h.chain.CurrentBlock().NumberU64() 198 } 199 inserter := func(blocks types.Blocks) (int, error) { 200 // If sync hasn't reached the checkpoint yet, deny importing weird blocks. 201 // 202 // Ideally we would also compare the head block's timestamp and similarly reject 203 // the propagated block if the head is too old. Unfortunately there is a corner 204 // case when starting new networks, where the genesis might be ancient (0 unix) 205 // which would prevent full nodes from accepting it. 206 if h.chain.CurrentBlock().NumberU64() < h.checkpointNumber { 207 log.Warn("Unsynced yet, discarded propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash()) 208 return 0, nil 209 } 210 // If fast sync is running, deny importing weird blocks. This is a problematic 211 // clause when starting up a new network, because fast-syncing miners might not 212 // accept each others' blocks until a restart. Unfortunately we haven't figured 213 // out a way yet where nodes can decide unilaterally whether the network is new 214 // or not. This should be fixed if we figure out a solution. 215 if atomic.LoadUint32(&h.fastSync) == 1 { 216 log.Warn("Fast syncing, discarded propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash()) 217 return 0, nil 218 } 219 n, err := h.chain.InsertChain(blocks) 220 if err == nil { 221 atomic.StoreUint32(&h.acceptTxs, 1) // Mark initial sync done on any fetcher import 222 } 223 return n, err 224 } 225 h.blockFetcher = fetcher.NewBlockFetcher(false, nil, h.chain.GetBlockByHash, validator, h.BroadcastBlock, heighter, nil, inserter, h.removePeer) 226 227 fetchTx := func(peer string, hashes []common.Hash) error { 228 p := h.peers.peer(peer) 229 if p == nil { 230 return errors.New("unknown peer") 231 } 232 return p.RequestTxs(hashes) 233 } 234 h.txFetcher = fetcher.NewTxFetcher(h.txpool.Has, h.txpool.AddRemotes, fetchTx) 235 h.chainSync = newChainSyncer(h) 236 return h, nil 237 } 238 239 // runEthPeer registers an eth peer into the joint eth/snap peerset, adds it to 240 // various subsistems and starts handling messages. 241 func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error { 242 // If the peer has a `snap` extension, wait for it to connect so we can have 243 // a uniform initialization/teardown mechanism 244 snap, err := h.peers.waitSnapExtension(peer) 245 if err != nil { 246 peer.Log().Error("Snapshot extension barrier failed", "err", err) 247 return err 248 } 249 // TODO(karalabe): Not sure why this is needed 250 if !h.chainSync.handlePeerEvent(peer) { 251 return p2p.DiscQuitting 252 } 253 h.peerWG.Add(1) 254 defer h.peerWG.Done() 255 256 // Execute the Ethereum handshake 257 var ( 258 genesis = h.chain.Genesis() 259 head = h.chain.CurrentHeader() 260 hash = head.Hash() 261 number = head.Number.Uint64() 262 td = h.chain.GetTd(hash, number) 263 ) 264 forkID := forkid.NewID(h.chain.Config(), h.chain.Genesis().Hash(), h.chain.CurrentHeader().Number.Uint64()) 265 if err := peer.Handshake(h.networkID, td, hash, genesis.Hash(), forkID, h.forkFilter); err != nil { 266 peer.Log().Debug("Ethereum handshake failed", "err", err) 267 return err 268 } 269 reject := false // reserved peer slots 270 if atomic.LoadUint32(&h.snapSync) == 1 { 271 if snap == nil { 272 // If we are running snap-sync, we want to reserve roughly half the peer 273 // slots for peers supporting the snap protocol. 274 // The logic here is; we only allow up to 5 more non-snap peers than snap-peers. 275 if all, snp := h.peers.len(), h.peers.snapLen(); all-snp > snp+5 { 276 reject = true 277 } 278 } 279 } 280 // Ignore maxPeers if this is a trusted peer 281 if !peer.Peer.Info().Network.Trusted { 282 if reject || h.peers.len() >= h.maxPeers { 283 return p2p.DiscTooManyPeers 284 } 285 } 286 peer.Log().Debug("Ethereum peer connected", "name", peer.Name()) 287 288 // Register the peer locally 289 if err := h.peers.registerPeer(peer, snap); err != nil { 290 peer.Log().Error("Ethereum peer registration failed", "err", err) 291 return err 292 } 293 defer h.removePeer(peer.ID()) 294 295 p := h.peers.peer(peer.ID()) 296 if p == nil { 297 return errors.New("peer dropped during handling") 298 } 299 // Register the peer in the downloader. If the downloader considers it banned, we disconnect 300 if err := h.downloader.RegisterPeer(peer.ID(), peer.Version(), peer); err != nil { 301 peer.Log().Error("Failed to register peer in eth syncer", "err", err) 302 return err 303 } 304 if snap != nil { 305 if err := h.downloader.SnapSyncer.Register(snap); err != nil { 306 peer.Log().Error("Failed to register peer in snap syncer", "err", err) 307 return err 308 } 309 } 310 h.chainSync.handlePeerEvent(peer) 311 312 // Propagate existing transactions. new transactions appearing 313 // after this will be sent via broadcasts. 314 h.syncTransactions(peer) 315 316 // If we have a trusted CHT, reject all peers below that (avoid fast sync eclipse) 317 if h.checkpointHash != (common.Hash{}) { 318 // Request the peer's checkpoint header for chain height/weight validation 319 if err := peer.RequestHeadersByNumber(h.checkpointNumber, 1, 0, false); err != nil { 320 return err 321 } 322 // Start a timer to disconnect if the peer doesn't reply in time 323 p.syncDrop = time.AfterFunc(syncChallengeTimeout, func() { 324 peer.Log().Warn("Checkpoint challenge timed out, dropping", "addr", peer.RemoteAddr(), "type", peer.Name()) 325 h.removePeer(peer.ID()) 326 }) 327 // Make sure it's cleaned up if the peer dies off 328 defer func() { 329 if p.syncDrop != nil { 330 p.syncDrop.Stop() 331 p.syncDrop = nil 332 } 333 }() 334 } 335 // If we have any explicit whitelist block hashes, request them 336 for number := range h.whitelist { 337 if err := peer.RequestHeadersByNumber(number, 1, 0, false); err != nil { 338 return err 339 } 340 } 341 // Handle incoming messages until the connection is torn down 342 return handler(peer) 343 } 344 345 // runSnapExtension registers a `snap` peer into the joint eth/snap peerset and 346 // starts handling inbound messages. As `snap` is only a satellite protocol to 347 // `eth`, all subsystem registrations and lifecycle management will be done by 348 // the main `eth` handler to prevent strange races. 349 func (h *handler) runSnapExtension(peer *snap.Peer, handler snap.Handler) error { 350 h.peerWG.Add(1) 351 defer h.peerWG.Done() 352 353 if err := h.peers.registerSnapExtension(peer); err != nil { 354 peer.Log().Error("Snapshot extension registration failed", "err", err) 355 return err 356 } 357 return handler(peer) 358 } 359 360 // removePeer unregisters a peer from the downloader and fetchers, removes it from 361 // the set of tracked peers and closes the network connection to it. 362 func (h *handler) removePeer(id string) { 363 // Create a custom logger to avoid printing the entire id 364 var logger log.Logger 365 if len(id) < 16 { 366 // Tests use short IDs, don't choke on them 367 logger = log.New("peer", id) 368 } else { 369 logger = log.New("peer", id[:8]) 370 } 371 // Abort if the peer does not exist 372 peer := h.peers.peer(id) 373 if peer == nil { 374 logger.Error("Ethereum peer removal failed", "err", errPeerNotRegistered) 375 return 376 } 377 // Remove the `eth` peer if it exists 378 logger.Debug("Removing Ethereum peer", "snap", peer.snapExt != nil) 379 380 // Remove the `snap` extension if it exists 381 if peer.snapExt != nil { 382 h.downloader.SnapSyncer.Unregister(id) 383 } 384 h.downloader.UnregisterPeer(id) 385 h.txFetcher.Drop(id) 386 387 if err := h.peers.unregisterPeer(id); err != nil { 388 logger.Error("Ethereum peer removal failed", "err", err) 389 } 390 // Hard disconnect at the networking layer 391 peer.Peer.Disconnect(p2p.DiscUselessPeer) 392 } 393 394 func (h *handler) Start(maxPeers int) { 395 h.maxPeers = maxPeers 396 397 // broadcast transactions 398 h.wg.Add(1) 399 h.txsCh = make(chan core.NewTxsEvent, txChanSize) 400 h.txsSub = h.txpool.SubscribeNewTxsEvent(h.txsCh) 401 go h.txBroadcastLoop() 402 403 // broadcast mined blocks 404 h.wg.Add(1) 405 h.minedBlockSub = h.eventMux.Subscribe(core.NewMinedBlockEvent{}) 406 go h.minedBroadcastLoop() 407 408 // start sync handlers 409 h.wg.Add(2) 410 go h.chainSync.loop() 411 go h.txsyncLoop64() // TODO(karalabe): Legacy initial tx echange, drop with eth/64. 412 } 413 414 func (h *handler) Stop() { 415 h.txsSub.Unsubscribe() // quits txBroadcastLoop 416 h.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop 417 418 // Quit chainSync and txsync64. 419 // After this is done, no new peers will be accepted. 420 close(h.quitSync) 421 h.wg.Wait() 422 423 // Disconnect existing sessions. 424 // This also closes the gate for any new registrations on the peer set. 425 // sessions which are already established but not added to h.peers yet 426 // will exit when they try to register. 427 h.peers.close() 428 h.peerWG.Wait() 429 430 log.Info("Ethereum protocol stopped") 431 } 432 433 // BroadcastBlock will either propagate a block to a subset of its peers, or 434 // will only announce its availability (depending what's requested). 435 func (h *handler) BroadcastBlock(block *types.Block, propagate bool) { 436 hash := block.Hash() 437 peers := h.peers.peersWithoutBlock(hash) 438 439 // If propagation is requested, send to a subset of the peer 440 if propagate { 441 // Calculate the TD of the block (it's not imported yet, so block.Td is not valid) 442 var td *big.Int 443 if parent := h.chain.GetBlock(block.ParentHash(), block.NumberU64()-1); parent != nil { 444 td = new(big.Int).Add(block.Difficulty(), h.chain.GetTd(block.ParentHash(), block.NumberU64()-1)) 445 } else { 446 log.Error("Propagating dangling block", "number", block.Number(), "hash", hash) 447 return 448 } 449 // Send the block to a subset of our peers 450 var transfer []*ethPeer 451 if h.directBroadcast { 452 transfer = peers[:int(len(peers))] 453 } else { 454 transfer = peers[:int(math.Sqrt(float64(len(peers))))] 455 } 456 for _, peer := range transfer { 457 peer.AsyncSendNewBlock(block, td) 458 } 459 log.Trace("Propagated block", "hash", hash, "recipients", len(transfer), "duration", common.PrettyDuration(time.Since(block.ReceivedAt))) 460 return 461 } 462 // Otherwise if the block is indeed in out own chain, announce it 463 if h.chain.HasBlock(hash, block.NumberU64()) { 464 for _, peer := range peers { 465 peer.AsyncSendNewBlockHash(block) 466 } 467 log.Trace("Announced block", "hash", hash, "recipients", len(peers), "duration", common.PrettyDuration(time.Since(block.ReceivedAt))) 468 } 469 } 470 471 // BroadcastTransactions will propagate a batch of transactions 472 // - To a square root of all peers 473 // - And, separately, as announcements to all peers which are not known to 474 // already have the given transaction. 475 func (h *handler) BroadcastTransactions(txs types.Transactions) { 476 var ( 477 annoCount int // Count of announcements made 478 annoPeers int 479 directCount int // Count of the txs sent directly to peers 480 directPeers int // Count of the peers that were sent transactions directly 481 482 txset = make(map[*ethPeer][]common.Hash) // Set peer->hash to transfer directly 483 annos = make(map[*ethPeer][]common.Hash) // Set peer->hash to announce 484 485 ) 486 // Broadcast transactions to a batch of peers not knowing about it 487 for _, tx := range txs { 488 peers := h.peers.peersWithoutTransaction(tx.Hash()) 489 // Send the tx unconditionally to a subset of our peers 490 numDirect := int(math.Sqrt(float64(len(peers)))) 491 for _, peer := range peers[:numDirect] { 492 txset[peer] = append(txset[peer], tx.Hash()) 493 } 494 // For the remaining peers, send announcement only 495 for _, peer := range peers[numDirect:] { 496 annos[peer] = append(annos[peer], tx.Hash()) 497 } 498 } 499 for peer, hashes := range txset { 500 directPeers++ 501 directCount += len(hashes) 502 peer.AsyncSendTransactions(hashes) 503 } 504 for peer, hashes := range annos { 505 annoPeers++ 506 annoCount += len(hashes) 507 peer.AsyncSendPooledTransactionHashes(hashes) 508 } 509 log.Debug("Transaction broadcast", "txs", len(txs), 510 "announce packs", annoPeers, "announced hashes", annoCount, 511 "tx packs", directPeers, "broadcast txs", directCount) 512 } 513 514 // minedBroadcastLoop sends mined blocks to connected peers. 515 func (h *handler) minedBroadcastLoop() { 516 defer h.wg.Done() 517 518 for obj := range h.minedBlockSub.Chan() { 519 if ev, ok := obj.Data.(core.NewMinedBlockEvent); ok { 520 h.BroadcastBlock(ev.Block, true) // First propagate block to peers 521 h.BroadcastBlock(ev.Block, false) // Only then announce to the rest 522 } 523 } 524 } 525 526 // txBroadcastLoop announces new transactions to connected peers. 527 func (h *handler) txBroadcastLoop() { 528 defer h.wg.Done() 529 for { 530 select { 531 case event := <-h.txsCh: 532 h.BroadcastTransactions(event.Txs) 533 case <-h.txsSub.Err(): 534 return 535 } 536 } 537 }