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