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