github.com/Finschia/ostracon@v1.1.5/p2p/pex/pex_reactor.go (about) 1 package pex 2 3 import ( 4 "errors" 5 "fmt" 6 "sync" 7 "time" 8 9 "github.com/gogo/protobuf/proto" 10 11 tmp2p "github.com/tendermint/tendermint/proto/tendermint/p2p" 12 13 "github.com/Finschia/ostracon/libs/cmap" 14 tmmath "github.com/Finschia/ostracon/libs/math" 15 tmrand "github.com/Finschia/ostracon/libs/rand" 16 "github.com/Finschia/ostracon/libs/service" 17 "github.com/Finschia/ostracon/p2p" 18 "github.com/Finschia/ostracon/p2p/conn" 19 ) 20 21 type Peer = p2p.Peer 22 23 const ( 24 // PexChannel is a channel for PEX messages 25 PexChannel = byte(0x00) 26 27 // over-estimate of max NetAddress size 28 // hexID (40) + IP (16) + Port (2) + Name (100) ... 29 // NOTE: dont use massive DNS name .. 30 maxAddressSize = 256 31 32 // NOTE: amplificaiton factor! 33 // small request results in up to maxMsgSize response 34 maxMsgSize = maxAddressSize * maxGetSelection 35 36 // ensure we have enough peers 37 defaultEnsurePeersPeriod = 30 * time.Second 38 39 // Seed/Crawler constants 40 41 // minTimeBetweenCrawls is a minimum time between attempts to crawl a peer. 42 minTimeBetweenCrawls = 2 * time.Minute 43 44 // check some peers every this 45 crawlPeerPeriod = 30 * time.Second 46 47 maxAttemptsToDial = 16 // ~ 35h in total (last attempt - 18h) 48 49 // if node connects to seed, it does not have any trusted peers. 50 // Especially in the beginning, node should have more trusted peers than 51 // untrusted. 52 biasToSelectNewPeers = 30 // 70 to select good peers 53 54 // if a peer is marked bad, it will be banned for at least this time period 55 defaultBanTime = 24 * time.Hour 56 ) 57 58 type errMaxAttemptsToDial struct { 59 } 60 61 func (e errMaxAttemptsToDial) Error() string { 62 return fmt.Sprintf("reached max attempts %d to dial", maxAttemptsToDial) 63 } 64 65 type errTooEarlyToDial struct { 66 backoffDuration time.Duration 67 lastDialed time.Time 68 } 69 70 func (e errTooEarlyToDial) Error() string { 71 return fmt.Sprintf( 72 "too early to dial (backoff duration: %d, last dialed: %v, time since: %v)", 73 e.backoffDuration, e.lastDialed, time.Since(e.lastDialed)) 74 } 75 76 // Reactor handles PEX (peer exchange) and ensures that an 77 // adequate number of peers are connected to the switch. 78 // 79 // It uses `AddrBook` (address book) to store `NetAddress`es of the peers. 80 // 81 // ## Preventing abuse 82 // 83 // Only accept pexAddrsMsg from peers we sent a corresponding pexRequestMsg too. 84 // Only accept one pexRequestMsg every ~defaultEnsurePeersPeriod. 85 type Reactor struct { 86 p2p.BaseReactor 87 88 book AddrBook 89 config *ReactorConfig 90 ensurePeersPeriod time.Duration // TODO: should go in the config 91 92 // maps to prevent abuse 93 requestsSent *cmap.CMap // ID->struct{}: unanswered send requests 94 lastReceivedRequests *cmap.CMap // ID->time.Time: last time peer requested from us 95 96 seedAddrs []*p2p.NetAddress 97 98 attemptsToDial sync.Map // address (string) -> {number of attempts (int), last time dialed (time.Time)} 99 100 // seed/crawled mode fields 101 crawlPeerInfos map[p2p.ID]crawlPeerInfo 102 } 103 104 func (r *Reactor) minReceiveRequestInterval() time.Duration { 105 // NOTE: must be less than ensurePeersPeriod, otherwise we'll request 106 // peers too quickly from others and they'll think we're bad! 107 return r.ensurePeersPeriod / 3 108 } 109 110 // ReactorConfig holds reactor specific configuration data. 111 type ReactorConfig struct { 112 // Seed/Crawler mode 113 SeedMode bool 114 115 // We want seeds to only advertise good peers. Therefore they should wait at 116 // least as long as we expect it to take for a peer to become good before 117 // disconnecting. 118 SeedDisconnectWaitPeriod time.Duration 119 120 // Maximum pause when redialing a persistent peer (if zero, exponential backoff is used) 121 PersistentPeersMaxDialPeriod time.Duration 122 123 // Seeds is a list of addresses reactor may use 124 // if it can't connect to peers in the addrbook. 125 Seeds []string 126 127 // Receive channel buffer size 128 RecvBufSize int 129 } 130 131 type _attemptsToDial struct { 132 number int 133 lastDialed time.Time 134 } 135 136 // NewReactor creates new PEX reactor. 137 func NewReactor(b AddrBook, async bool, config *ReactorConfig) *Reactor { 138 r := &Reactor{ 139 book: b, 140 config: config, 141 ensurePeersPeriod: defaultEnsurePeersPeriod, 142 requestsSent: cmap.NewCMap(), 143 lastReceivedRequests: cmap.NewCMap(), 144 crawlPeerInfos: make(map[p2p.ID]crawlPeerInfo), 145 } 146 r.BaseReactor = *p2p.NewBaseReactor("PEX", r, async, config.RecvBufSize) 147 return r 148 } 149 150 // OnStart implements BaseService 151 func (r *Reactor) OnStart() error { 152 // call BaseReactor's OnStart() 153 err := r.BaseReactor.OnStart() 154 if err != nil { 155 return err 156 } 157 158 err = r.book.Start() 159 if err != nil && err != service.ErrAlreadyStarted { 160 return err 161 } 162 163 numOnline, seedAddrs, err := r.checkSeeds() 164 if err != nil { 165 return err 166 } else if numOnline == 0 && r.book.Empty() { 167 return errors.New("address book is empty and couldn't resolve any seed nodes") 168 } 169 170 r.seedAddrs = seedAddrs 171 172 // Check if this node should run 173 // in seed/crawler mode 174 if r.config.SeedMode { 175 go r.crawlPeersRoutine() 176 } else { 177 go r.ensurePeersRoutine() 178 } 179 180 return nil 181 } 182 183 // OnStop implements BaseService 184 func (r *Reactor) OnStop() { 185 if err := r.book.Stop(); err != nil { 186 r.Logger.Error("Error stopping address book", "err", err) 187 } 188 } 189 190 // GetChannels implements Reactor 191 func (r *Reactor) GetChannels() []*conn.ChannelDescriptor { 192 return []*conn.ChannelDescriptor{ 193 { 194 ID: PexChannel, 195 Priority: 1, 196 SendQueueCapacity: 10, 197 RecvMessageCapacity: maxMsgSize, 198 MessageType: &tmp2p.Message{}, 199 }, 200 } 201 } 202 203 // AddPeer implements Reactor by adding peer to the address book (if inbound) 204 // or by requesting more addresses (if outbound). 205 func (r *Reactor) AddPeer(p Peer) { 206 if p.IsOutbound() { 207 // For outbound peers, the address is already in the books - 208 // either via DialPeersAsync or r.Receive. 209 // Ask it for more peers if we need. 210 if r.book.NeedMoreAddrs() { 211 r.RequestAddrs(p) 212 } 213 } else { 214 // inbound peer is its own source 215 addr, err := p.NodeInfo().NetAddress() 216 if err != nil { 217 r.Logger.Error("Failed to get peer NetAddress", "err", err, "peer", p) 218 return 219 } 220 221 // Make it explicit that addr and src are the same for an inbound peer. 222 src := addr 223 224 // add to book. dont RequestAddrs right away because 225 // we don't trust inbound as much - let ensurePeersRoutine handle it. 226 err = r.book.AddAddress(addr, src) 227 r.logErrAddrBook(err) 228 } 229 } 230 231 // RemovePeer implements Reactor by resetting peer's requests info. 232 func (r *Reactor) RemovePeer(p Peer, reason interface{}) { 233 id := string(p.ID()) 234 r.requestsSent.Delete(id) 235 r.lastReceivedRequests.Delete(id) 236 } 237 238 func (r *Reactor) logErrAddrBook(err error) { 239 if err != nil { 240 switch err.(type) { 241 case ErrAddrBookNilAddr: 242 r.Logger.Error("Failed to add new address", "err", err) 243 default: 244 // non-routable, self, full book, private, etc. 245 r.Logger.Debug("Failed to add new address", "err", err) 246 } 247 } 248 } 249 250 // Receive implements Reactor by handling incoming PEX messages. 251 func (r *Reactor) ReceiveEnvelope(e p2p.Envelope) { 252 r.Logger.Debug("Received message", "src", e.Src, "chId", e.ChannelID, "msg", e.Message) 253 254 switch msg := e.Message.(type) { 255 case *tmp2p.PexRequest: 256 257 // NOTE: this is a prime candidate for amplification attacks, 258 // so it's important we 259 // 1) restrict how frequently peers can request 260 // 2) limit the output size 261 262 // If we're a seed and this is an inbound peer, 263 // respond once and disconnect. 264 if r.config.SeedMode && !e.Src.IsOutbound() { 265 id := string(e.Src.ID()) 266 v := r.lastReceivedRequests.Get(id) 267 if v != nil { 268 // FlushStop/StopPeer are already 269 // running in a go-routine. 270 return 271 } 272 r.lastReceivedRequests.Set(id, time.Now()) 273 274 // Send addrs and disconnect 275 r.SendAddrs(e.Src, r.book.GetSelectionWithBias(biasToSelectNewPeers)) 276 go func() { 277 // In a go-routine so it doesn't block .Receive. 278 e.Src.FlushStop() 279 r.Switch.StopPeerGracefully(e.Src) 280 }() 281 282 } else { 283 // Check we're not receiving requests too frequently. 284 if err := r.receiveRequest(e.Src); err != nil { 285 r.Switch.StopPeerForError(e.Src, err) 286 r.book.MarkBad(e.Src.SocketAddr(), defaultBanTime) 287 return 288 } 289 r.SendAddrs(e.Src, r.book.GetSelection()) 290 } 291 292 case *tmp2p.PexAddrs: 293 // If we asked for addresses, add them to the book 294 addrs, err := p2p.NetAddressesFromProto(msg.Addrs) 295 if err != nil { 296 r.Switch.StopPeerForError(e.Src, err) 297 r.book.MarkBad(e.Src.SocketAddr(), defaultBanTime) 298 return 299 } 300 err = r.ReceiveAddrs(addrs, e.Src) 301 if err != nil { 302 r.Switch.StopPeerForError(e.Src, err) 303 if err == ErrUnsolicitedList { 304 r.book.MarkBad(e.Src.SocketAddr(), defaultBanTime) 305 } 306 return 307 } 308 309 default: 310 r.Logger.Error(fmt.Sprintf("Unknown message type %T", msg)) 311 } 312 } 313 314 func (r *Reactor) Receive(chID byte, peer p2p.Peer, msgBytes []byte) { 315 msg := &tmp2p.Message{} 316 err := proto.Unmarshal(msgBytes, msg) 317 if err != nil { 318 panic(err) 319 } 320 um, err := msg.Unwrap() 321 if err != nil { 322 panic(err) 323 } 324 r.ReceiveEnvelope(p2p.Envelope{ 325 ChannelID: chID, 326 Src: peer, 327 Message: um, 328 }) 329 } 330 331 // enforces a minimum amount of time between requests 332 func (r *Reactor) receiveRequest(src Peer) error { 333 id := string(src.ID()) 334 v := r.lastReceivedRequests.Get(id) 335 if v == nil { 336 // initialize with empty time 337 lastReceived := time.Time{} 338 r.lastReceivedRequests.Set(id, lastReceived) 339 return nil 340 } 341 342 lastReceived := v.(time.Time) 343 if lastReceived.Equal(time.Time{}) { 344 // first time gets a free pass. then we start tracking the time 345 lastReceived = time.Now() 346 r.lastReceivedRequests.Set(id, lastReceived) 347 return nil 348 } 349 350 now := time.Now() 351 minInterval := r.minReceiveRequestInterval() 352 if now.Sub(lastReceived) < minInterval { 353 return fmt.Errorf( 354 "peer (%v) sent next PEX request too soon. lastReceived: %v, now: %v, minInterval: %v. Disconnecting", 355 src.ID(), 356 lastReceived, 357 now, 358 minInterval, 359 ) 360 } 361 r.lastReceivedRequests.Set(id, now) 362 return nil 363 } 364 365 // RequestAddrs asks peer for more addresses if we do not already have a 366 // request out for this peer. 367 func (r *Reactor) RequestAddrs(p Peer) { 368 id := string(p.ID()) 369 if r.requestsSent.Has(id) { 370 return 371 } 372 r.Logger.Debug("Request addrs", "from", p) 373 r.requestsSent.Set(id, struct{}{}) 374 p2p.SendEnvelopeShim(p, p2p.Envelope{ //nolint: staticcheck 375 ChannelID: PexChannel, 376 Message: &tmp2p.PexRequest{}, 377 }, r.Logger) 378 } 379 380 // ReceiveAddrs adds the given addrs to the addrbook if theres an open 381 // request for this peer and deletes the open request. 382 // If there's no open request for the src peer, it returns an error. 383 func (r *Reactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error { 384 id := string(src.ID()) 385 if !r.requestsSent.Has(id) { 386 return ErrUnsolicitedList 387 } 388 r.requestsSent.Delete(id) 389 390 srcAddr, err := src.NodeInfo().NetAddress() 391 if err != nil { 392 return err 393 } 394 395 srcIsSeed := false 396 for _, seedAddr := range r.seedAddrs { 397 if seedAddr.Equals(srcAddr) { 398 srcIsSeed = true 399 break 400 } 401 } 402 403 for _, netAddr := range addrs { 404 // NOTE: we check netAddr validity and routability in book#AddAddress. 405 err = r.book.AddAddress(netAddr, srcAddr) 406 if err != nil { 407 r.logErrAddrBook(err) 408 // XXX: should we be strict about incoming data and disconnect from a 409 // peer here too? 410 continue 411 } 412 413 // If this address came from a seed node, try to connect to it without 414 // waiting (#2093) 415 if srcIsSeed { 416 go func(addr *p2p.NetAddress) { 417 err := r.dialPeer(addr) 418 if err != nil { 419 switch err.(type) { 420 case errMaxAttemptsToDial, errTooEarlyToDial, p2p.ErrCurrentlyDialingOrExistingAddress: 421 r.Logger.Debug(err.Error(), "addr", addr) 422 default: 423 r.Logger.Debug(err.Error(), "addr", addr) 424 } 425 } 426 }(netAddr) 427 } 428 } 429 430 return nil 431 } 432 433 // SendAddrs sends addrs to the peer. 434 func (r *Reactor) SendAddrs(p Peer, netAddrs []*p2p.NetAddress) { 435 e := p2p.Envelope{ 436 ChannelID: PexChannel, 437 Message: &tmp2p.PexAddrs{Addrs: p2p.NetAddressesToProto(netAddrs)}, 438 } 439 p2p.SendEnvelopeShim(p, e, r.Logger) //nolint: staticcheck 440 } 441 442 // SetEnsurePeersPeriod sets period to ensure peers connected. 443 func (r *Reactor) SetEnsurePeersPeriod(d time.Duration) { 444 r.ensurePeersPeriod = d 445 } 446 447 // Ensures that sufficient peers are connected. (continuous) 448 func (r *Reactor) ensurePeersRoutine() { 449 var ( 450 seed = tmrand.NewRand() 451 jitter = seed.Int63n(r.ensurePeersPeriod.Nanoseconds()) 452 ) 453 454 // Randomize first round of communication to avoid thundering herd. 455 // If no peers are present directly start connecting so we guarantee swift 456 // setup with the help of configured seeds. 457 if r.nodeHasSomePeersOrDialingAny() { 458 time.Sleep(time.Duration(jitter)) 459 } 460 461 // fire once immediately. 462 // ensures we dial the seeds right away if the book is empty 463 r.ensurePeers() 464 465 // fire periodically 466 ticker := time.NewTicker(r.ensurePeersPeriod) 467 for { 468 select { 469 case <-ticker.C: 470 r.ensurePeers() 471 case <-r.Quit(): 472 ticker.Stop() 473 return 474 } 475 } 476 } 477 478 // ensurePeers ensures that sufficient peers are connected. (once) 479 // 480 // heuristic that we haven't perfected yet, or, perhaps is manually edited by 481 // the node operator. It should not be used to compute what addresses are 482 // already connected or not. 483 func (r *Reactor) ensurePeers() { 484 var ( 485 out, in, dial = r.Switch.NumPeers() 486 numToDial = r.Switch.MaxNumOutboundPeers() - (out + dial) 487 ) 488 r.Logger.Info( 489 "Ensure peers", 490 "numOutPeers", out, 491 "numInPeers", in, 492 "numDialing", dial, 493 "numToDial", numToDial, 494 ) 495 496 if numToDial <= 0 { 497 return 498 } 499 500 // bias to prefer more vetted peers when we have fewer connections. 501 // not perfect, but somewhate ensures that we prioritize connecting to more-vetted 502 // NOTE: range here is [10, 90]. Too high ? 503 newBias := tmmath.MinInt(out, 8)*10 + 10 504 505 toDial := make(map[p2p.ID]*p2p.NetAddress) 506 // Try maxAttempts times to pick numToDial addresses to dial 507 maxAttempts := numToDial * 3 508 509 for i := 0; i < maxAttempts && len(toDial) < numToDial; i++ { 510 try := r.book.PickAddress(newBias) 511 if try == nil { 512 continue 513 } 514 if _, selected := toDial[try.ID]; selected { 515 continue 516 } 517 if r.Switch.IsDialingOrExistingAddress(try) { 518 continue 519 } 520 // TODO: consider moving some checks from toDial into here 521 // so we don't even consider dialing peers that we want to wait 522 // before dialling again, or have dialed too many times already 523 toDial[try.ID] = try 524 } 525 526 // Dial picked addresses 527 for _, addr := range toDial { 528 go func(addr *p2p.NetAddress) { 529 err := r.dialPeer(addr) 530 if err != nil { 531 switch err.(type) { 532 case errMaxAttemptsToDial, errTooEarlyToDial: 533 r.Logger.Debug(err.Error(), "addr", addr) 534 default: 535 r.Logger.Debug(err.Error(), "addr", addr) 536 } 537 } 538 }(addr) 539 } 540 541 if r.book.NeedMoreAddrs() { 542 // Check if banned nodes can be reinstated 543 r.book.ReinstateBadPeers() 544 } 545 546 if r.book.NeedMoreAddrs() { 547 548 // 1) Pick a random peer and ask for more. 549 peers := r.Switch.Peers().List() 550 peersCount := len(peers) 551 if peersCount > 0 { 552 peer := peers[tmrand.Int()%peersCount] 553 r.Logger.Info("We need more addresses. Sending pexRequest to random peer", "peer", peer) 554 r.RequestAddrs(peer) 555 } 556 557 // 2) Dial seeds if we are not dialing anyone. 558 // This is done in addition to asking a peer for addresses to work-around 559 // peers not participating in PEX. 560 if len(toDial) == 0 { 561 r.Logger.Info("No addresses to dial. Falling back to seeds") 562 r.dialSeeds() 563 } 564 } 565 } 566 567 func (r *Reactor) dialAttemptsInfo(addr *p2p.NetAddress) (attempts int, lastDialed time.Time) { 568 _attempts, ok := r.attemptsToDial.Load(addr.DialString()) 569 if !ok { 570 return 571 } 572 atd := _attempts.(_attemptsToDial) 573 return atd.number, atd.lastDialed 574 } 575 576 func (r *Reactor) dialPeer(addr *p2p.NetAddress) error { 577 attempts, lastDialed := r.dialAttemptsInfo(addr) 578 if !r.Switch.IsPeerPersistent(addr) && attempts > maxAttemptsToDial { 579 r.book.MarkBad(addr, defaultBanTime) 580 return errMaxAttemptsToDial{} 581 } 582 583 // exponential backoff if it's not our first attempt to dial given address 584 if attempts > 0 { 585 jitter := time.Duration(tmrand.Float64() * float64(time.Second)) // 1s == (1e9 ns) 586 backoffDuration := jitter + ((1 << uint(attempts)) * time.Second) 587 backoffDuration = r.maxBackoffDurationForPeer(addr, backoffDuration) 588 sinceLastDialed := time.Since(lastDialed) 589 if sinceLastDialed < backoffDuration { 590 return errTooEarlyToDial{backoffDuration, lastDialed} 591 } 592 } 593 594 err := r.Switch.DialPeerWithAddress(addr) 595 if err != nil { 596 if _, ok := err.(p2p.ErrCurrentlyDialingOrExistingAddress); ok { 597 return err 598 } 599 600 markAddrInBookBasedOnErr(addr, r.book, err) 601 switch err.(type) { 602 case p2p.ErrSwitchAuthenticationFailure: 603 // NOTE: addr is removed from addrbook in markAddrInBookBasedOnErr 604 r.attemptsToDial.Delete(addr.DialString()) 605 default: 606 r.attemptsToDial.Store(addr.DialString(), _attemptsToDial{attempts + 1, time.Now()}) 607 } 608 return fmt.Errorf("dialing failed (attempts: %d): %w", attempts+1, err) 609 } 610 611 // cleanup any history 612 r.attemptsToDial.Delete(addr.DialString()) 613 return nil 614 } 615 616 // maxBackoffDurationForPeer caps the backoff duration for persistent peers. 617 func (r *Reactor) maxBackoffDurationForPeer(addr *p2p.NetAddress, planned time.Duration) time.Duration { 618 if r.config.PersistentPeersMaxDialPeriod > 0 && 619 planned > r.config.PersistentPeersMaxDialPeriod && 620 r.Switch.IsPeerPersistent(addr) { 621 return r.config.PersistentPeersMaxDialPeriod 622 } 623 return planned 624 } 625 626 // checkSeeds checks that addresses are well formed. 627 // Returns number of seeds we can connect to, along with all seeds addrs. 628 // return err if user provided any badly formatted seed addresses. 629 // Doesn't error if the seed node can't be reached. 630 // numOnline returns -1 if no seed nodes were in the initial configuration. 631 func (r *Reactor) checkSeeds() (numOnline int, netAddrs []*p2p.NetAddress, err error) { 632 lSeeds := len(r.config.Seeds) 633 if lSeeds == 0 { 634 return -1, nil, nil 635 } 636 netAddrs, errs := p2p.NewNetAddressStrings(r.config.Seeds) 637 numOnline = lSeeds - len(errs) 638 for _, err := range errs { 639 switch e := err.(type) { 640 case p2p.ErrNetAddressLookup: 641 r.Logger.Error("Connecting to seed failed", "err", e) 642 default: 643 return 0, nil, fmt.Errorf("seed node configuration has error: %w", e) 644 } 645 } 646 return numOnline, netAddrs, nil 647 } 648 649 // randomly dial seeds until we connect to one or exhaust them 650 func (r *Reactor) dialSeeds() { 651 perm := tmrand.Perm(len(r.seedAddrs)) 652 // perm := r.Switch.rng.Perm(lSeeds) 653 for _, i := range perm { 654 // dial a random seed 655 seedAddr := r.seedAddrs[i] 656 err := r.Switch.DialPeerWithAddress(seedAddr) 657 658 switch err.(type) { 659 case nil, p2p.ErrCurrentlyDialingOrExistingAddress: 660 return 661 } 662 r.Switch.Logger.Error("Error dialing seed", "err", err, "seed", seedAddr) 663 } 664 // do not write error message if there were no seeds specified in config 665 if len(r.seedAddrs) > 0 { 666 r.Switch.Logger.Error("Couldn't connect to any seeds") 667 } 668 } 669 670 // AttemptsToDial returns the number of attempts to dial specific address. It 671 // returns 0 if never attempted or successfully connected. 672 func (r *Reactor) AttemptsToDial(addr *p2p.NetAddress) int { 673 lAttempts, attempted := r.attemptsToDial.Load(addr.DialString()) 674 if attempted { 675 return lAttempts.(_attemptsToDial).number 676 } 677 return 0 678 } 679 680 //---------------------------------------------------------- 681 682 // Explores the network searching for more peers. (continuous) 683 // Seed/Crawler Mode causes this node to quickly disconnect 684 // from peers, except other seed nodes. 685 func (r *Reactor) crawlPeersRoutine() { 686 // If we have any seed nodes, consult them first 687 if len(r.seedAddrs) > 0 { 688 r.dialSeeds() 689 } else { 690 // Do an initial crawl 691 r.crawlPeers(r.book.GetSelection()) 692 } 693 694 // Fire periodically 695 ticker := time.NewTicker(crawlPeerPeriod) 696 697 for { 698 select { 699 case <-ticker.C: 700 r.attemptDisconnects() 701 r.crawlPeers(r.book.GetSelection()) 702 r.cleanupCrawlPeerInfos() 703 case <-r.Quit(): 704 return 705 } 706 } 707 } 708 709 // nodeHasSomePeersOrDialingAny returns true if the node is connected to some 710 // peers or dialing them currently. 711 func (r *Reactor) nodeHasSomePeersOrDialingAny() bool { 712 out, in, dial := r.Switch.NumPeers() 713 return out+in+dial > 0 714 } 715 716 // crawlPeerInfo handles temporary data needed for the network crawling 717 // performed during seed/crawler mode. 718 type crawlPeerInfo struct { 719 Addr *p2p.NetAddress `json:"addr"` 720 // The last time we crawled the peer or attempted to do so. 721 LastCrawled time.Time `json:"last_crawled"` 722 } 723 724 // crawlPeers will crawl the network looking for new peer addresses. 725 func (r *Reactor) crawlPeers(addrs []*p2p.NetAddress) { 726 now := time.Now() 727 728 for _, addr := range addrs { 729 peerInfo, ok := r.crawlPeerInfos[addr.ID] 730 731 // Do not attempt to connect with peers we recently crawled. 732 if ok && now.Sub(peerInfo.LastCrawled) < minTimeBetweenCrawls { 733 continue 734 } 735 736 // Record crawling attempt. 737 r.crawlPeerInfos[addr.ID] = crawlPeerInfo{ 738 Addr: addr, 739 LastCrawled: now, 740 } 741 742 err := r.dialPeer(addr) 743 if err != nil { 744 switch err.(type) { 745 case errMaxAttemptsToDial, errTooEarlyToDial, p2p.ErrCurrentlyDialingOrExistingAddress: 746 r.Logger.Debug(err.Error(), "addr", addr) 747 default: 748 r.Logger.Debug(err.Error(), "addr", addr) 749 } 750 continue 751 } 752 753 peer := r.Switch.Peers().Get(addr.ID) 754 if peer != nil { 755 r.RequestAddrs(peer) 756 } 757 } 758 } 759 760 func (r *Reactor) cleanupCrawlPeerInfos() { 761 for id, info := range r.crawlPeerInfos { 762 // If we did not crawl a peer for 24 hours, it means the peer was removed 763 // from the addrbook => remove 764 // 765 // 10000 addresses / maxGetSelection = 40 cycles to get all addresses in 766 // the ideal case, 767 // 40 * crawlPeerPeriod ~ 20 minutes 768 if time.Since(info.LastCrawled) > 24*time.Hour { 769 delete(r.crawlPeerInfos, id) 770 } 771 } 772 } 773 774 // attemptDisconnects checks if we've been with each peer long enough to disconnect 775 func (r *Reactor) attemptDisconnects() { 776 for _, peer := range r.Switch.Peers().List() { 777 if peer.Status().Duration < r.config.SeedDisconnectWaitPeriod { 778 continue 779 } 780 if peer.IsPersistent() { 781 continue 782 } 783 r.Switch.StopPeerGracefully(peer) 784 } 785 } 786 787 func markAddrInBookBasedOnErr(addr *p2p.NetAddress, book AddrBook, err error) { 788 // TODO: detect more "bad peer" scenarios 789 switch err.(type) { 790 case p2p.ErrSwitchAuthenticationFailure: 791 book.MarkBad(addr, defaultBanTime) 792 default: 793 book.MarkAttempt(addr) 794 } 795 }