github.com/line/ostracon@v1.0.10-0.20230328032236-7f20145f065d/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/line/ostracon/libs/cmap" 14 tmmath "github.com/line/ostracon/libs/math" 15 tmrand "github.com/line/ostracon/libs/rand" 16 "github.com/line/ostracon/libs/service" 17 "github.com/line/ostracon/p2p" 18 "github.com/line/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 }, 199 } 200 } 201 202 // AddPeer implements Reactor by adding peer to the address book (if inbound) 203 // or by requesting more addresses (if outbound). 204 func (r *Reactor) AddPeer(p Peer) { 205 if p.IsOutbound() { 206 // For outbound peers, the address is already in the books - 207 // either via DialPeersAsync or r.Receive. 208 // Ask it for more peers if we need. 209 if r.book.NeedMoreAddrs() { 210 r.RequestAddrs(p) 211 } 212 } else { 213 // inbound peer is its own source 214 addr, err := p.NodeInfo().NetAddress() 215 if err != nil { 216 r.Logger.Error("Failed to get peer NetAddress", "err", err, "peer", p) 217 return 218 } 219 220 // Make it explicit that addr and src are the same for an inbound peer. 221 src := addr 222 223 // add to book. dont RequestAddrs right away because 224 // we don't trust inbound as much - let ensurePeersRoutine handle it. 225 err = r.book.AddAddress(addr, src) 226 r.logErrAddrBook(err) 227 } 228 } 229 230 // RemovePeer implements Reactor by resetting peer's requests info. 231 func (r *Reactor) RemovePeer(p Peer, reason interface{}) { 232 id := string(p.ID()) 233 r.requestsSent.Delete(id) 234 r.lastReceivedRequests.Delete(id) 235 } 236 237 func (r *Reactor) logErrAddrBook(err error) { 238 if err != nil { 239 switch err.(type) { 240 case ErrAddrBookNilAddr: 241 r.Logger.Error("Failed to add new address", "err", err) 242 default: 243 // non-routable, self, full book, private, etc. 244 r.Logger.Debug("Failed to add new address", "err", err) 245 } 246 } 247 } 248 249 // Receive implements Reactor by handling incoming PEX messages. 250 func (r *Reactor) Receive(chID byte, src Peer, msgBytes []byte) { 251 msg, err := decodeMsg(msgBytes) 252 if err != nil { 253 r.Logger.Error("Error decoding message", "src", src, "chId", chID, "err", err) 254 r.Switch.StopPeerForError(src, err) 255 return 256 } 257 r.Logger.Debug("Received message", "src", src, "chId", chID, "msg", msg) 258 259 switch msg := msg.(type) { 260 case *tmp2p.PexRequest: 261 262 // NOTE: this is a prime candidate for amplification attacks, 263 // so it's important we 264 // 1) restrict how frequently peers can request 265 // 2) limit the output size 266 267 // If we're a seed and this is an inbound peer, 268 // respond once and disconnect. 269 if r.config.SeedMode && !src.IsOutbound() { 270 id := string(src.ID()) 271 v := r.lastReceivedRequests.Get(id) 272 if v != nil { 273 // FlushStop/StopPeer are already 274 // running in a go-routine. 275 return 276 } 277 r.lastReceivedRequests.Set(id, time.Now()) 278 279 // Send addrs and disconnect 280 r.SendAddrs(src, r.book.GetSelectionWithBias(biasToSelectNewPeers)) 281 go func() { 282 // In a go-routine so it doesn't block .Receive. 283 src.FlushStop() 284 r.Switch.StopPeerGracefully(src) 285 }() 286 287 } else { 288 // Check we're not receiving requests too frequently. 289 if err := r.receiveRequest(src); err != nil { 290 r.Switch.StopPeerForError(src, err) 291 r.book.MarkBad(src.SocketAddr(), defaultBanTime) 292 return 293 } 294 r.SendAddrs(src, r.book.GetSelection()) 295 } 296 297 case *tmp2p.PexAddrs: 298 // If we asked for addresses, add them to the book 299 addrs, err := p2p.NetAddressesFromProto(msg.Addrs) 300 if err != nil { 301 r.Switch.StopPeerForError(src, err) 302 r.book.MarkBad(src.SocketAddr(), defaultBanTime) 303 return 304 } 305 err = r.ReceiveAddrs(addrs, src) 306 if err != nil { 307 r.Switch.StopPeerForError(src, err) 308 if err == ErrUnsolicitedList { 309 r.book.MarkBad(src.SocketAddr(), defaultBanTime) 310 } 311 return 312 } 313 314 default: 315 r.Logger.Error(fmt.Sprintf("Unknown message type %T", msg)) 316 } 317 } 318 319 // enforces a minimum amount of time between requests 320 func (r *Reactor) receiveRequest(src Peer) error { 321 id := string(src.ID()) 322 v := r.lastReceivedRequests.Get(id) 323 if v == nil { 324 // initialize with empty time 325 lastReceived := time.Time{} 326 r.lastReceivedRequests.Set(id, lastReceived) 327 return nil 328 } 329 330 lastReceived := v.(time.Time) 331 if lastReceived.Equal(time.Time{}) { 332 // first time gets a free pass. then we start tracking the time 333 lastReceived = time.Now() 334 r.lastReceivedRequests.Set(id, lastReceived) 335 return nil 336 } 337 338 now := time.Now() 339 minInterval := r.minReceiveRequestInterval() 340 if now.Sub(lastReceived) < minInterval { 341 return fmt.Errorf( 342 "peer (%v) sent next PEX request too soon. lastReceived: %v, now: %v, minInterval: %v. Disconnecting", 343 src.ID(), 344 lastReceived, 345 now, 346 minInterval, 347 ) 348 } 349 r.lastReceivedRequests.Set(id, now) 350 return nil 351 } 352 353 // RequestAddrs asks peer for more addresses if we do not already have a 354 // request out for this peer. 355 func (r *Reactor) RequestAddrs(p Peer) { 356 id := string(p.ID()) 357 if r.requestsSent.Has(id) { 358 return 359 } 360 r.Logger.Debug("Request addrs", "from", p) 361 r.requestsSent.Set(id, struct{}{}) 362 p.Send(PexChannel, mustEncode(&tmp2p.PexRequest{})) 363 } 364 365 // ReceiveAddrs adds the given addrs to the addrbook if theres an open 366 // request for this peer and deletes the open request. 367 // If there's no open request for the src peer, it returns an error. 368 func (r *Reactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error { 369 id := string(src.ID()) 370 if !r.requestsSent.Has(id) { 371 return ErrUnsolicitedList 372 } 373 r.requestsSent.Delete(id) 374 375 srcAddr, err := src.NodeInfo().NetAddress() 376 if err != nil { 377 return err 378 } 379 380 srcIsSeed := false 381 for _, seedAddr := range r.seedAddrs { 382 if seedAddr.Equals(srcAddr) { 383 srcIsSeed = true 384 break 385 } 386 } 387 388 for _, netAddr := range addrs { 389 // NOTE: we check netAddr validity and routability in book#AddAddress. 390 err = r.book.AddAddress(netAddr, srcAddr) 391 if err != nil { 392 r.logErrAddrBook(err) 393 // XXX: should we be strict about incoming data and disconnect from a 394 // peer here too? 395 continue 396 } 397 398 // If this address came from a seed node, try to connect to it without 399 // waiting (#2093) 400 if srcIsSeed { 401 go func(addr *p2p.NetAddress) { 402 err := r.dialPeer(addr) 403 if err != nil { 404 switch err.(type) { 405 case errMaxAttemptsToDial, errTooEarlyToDial, p2p.ErrCurrentlyDialingOrExistingAddress: 406 r.Logger.Debug(err.Error(), "addr", addr) 407 default: 408 r.Logger.Debug(err.Error(), "addr", addr) 409 } 410 } 411 }(netAddr) 412 } 413 } 414 415 return nil 416 } 417 418 // SendAddrs sends addrs to the peer. 419 func (r *Reactor) SendAddrs(p Peer, netAddrs []*p2p.NetAddress) { 420 p.Send(PexChannel, mustEncode(&tmp2p.PexAddrs{Addrs: p2p.NetAddressesToProto(netAddrs)})) 421 } 422 423 // SetEnsurePeersPeriod sets period to ensure peers connected. 424 func (r *Reactor) SetEnsurePeersPeriod(d time.Duration) { 425 r.ensurePeersPeriod = d 426 } 427 428 // Ensures that sufficient peers are connected. (continuous) 429 func (r *Reactor) ensurePeersRoutine() { 430 var ( 431 seed = tmrand.NewRand() 432 jitter = seed.Int63n(r.ensurePeersPeriod.Nanoseconds()) 433 ) 434 435 // Randomize first round of communication to avoid thundering herd. 436 // If no peers are present directly start connecting so we guarantee swift 437 // setup with the help of configured seeds. 438 if r.nodeHasSomePeersOrDialingAny() { 439 time.Sleep(time.Duration(jitter)) 440 } 441 442 // fire once immediately. 443 // ensures we dial the seeds right away if the book is empty 444 r.ensurePeers() 445 446 // fire periodically 447 ticker := time.NewTicker(r.ensurePeersPeriod) 448 for { 449 select { 450 case <-ticker.C: 451 r.ensurePeers() 452 case <-r.Quit(): 453 ticker.Stop() 454 return 455 } 456 } 457 } 458 459 // ensurePeers ensures that sufficient peers are connected. (once) 460 // 461 // heuristic that we haven't perfected yet, or, perhaps is manually edited by 462 // the node operator. It should not be used to compute what addresses are 463 // already connected or not. 464 func (r *Reactor) ensurePeers() { 465 var ( 466 out, in, dial = r.Switch.NumPeers() 467 numToDial = r.Switch.MaxNumOutboundPeers() - (out + dial) 468 ) 469 r.Logger.Info( 470 "Ensure peers", 471 "numOutPeers", out, 472 "numInPeers", in, 473 "numDialing", dial, 474 "numToDial", numToDial, 475 ) 476 477 if numToDial <= 0 { 478 return 479 } 480 481 // bias to prefer more vetted peers when we have fewer connections. 482 // not perfect, but somewhate ensures that we prioritize connecting to more-vetted 483 // NOTE: range here is [10, 90]. Too high ? 484 newBias := tmmath.MinInt(out, 8)*10 + 10 485 486 toDial := make(map[p2p.ID]*p2p.NetAddress) 487 // Try maxAttempts times to pick numToDial addresses to dial 488 maxAttempts := numToDial * 3 489 490 for i := 0; i < maxAttempts && len(toDial) < numToDial; i++ { 491 try := r.book.PickAddress(newBias) 492 if try == nil { 493 continue 494 } 495 if _, selected := toDial[try.ID]; selected { 496 continue 497 } 498 if r.Switch.IsDialingOrExistingAddress(try) { 499 continue 500 } 501 // TODO: consider moving some checks from toDial into here 502 // so we don't even consider dialing peers that we want to wait 503 // before dialling again, or have dialed too many times already 504 toDial[try.ID] = try 505 } 506 507 // Dial picked addresses 508 for _, addr := range toDial { 509 go func(addr *p2p.NetAddress) { 510 err := r.dialPeer(addr) 511 if err != nil { 512 switch err.(type) { 513 case errMaxAttemptsToDial, errTooEarlyToDial: 514 r.Logger.Debug(err.Error(), "addr", addr) 515 default: 516 r.Logger.Debug(err.Error(), "addr", addr) 517 } 518 } 519 }(addr) 520 } 521 522 if r.book.NeedMoreAddrs() { 523 // Check if banned nodes can be reinstated 524 r.book.ReinstateBadPeers() 525 } 526 527 if r.book.NeedMoreAddrs() { 528 529 // 1) Pick a random peer and ask for more. 530 peers := r.Switch.Peers().List() 531 peersCount := len(peers) 532 if peersCount > 0 { 533 peer := peers[tmrand.Int()%peersCount] 534 r.Logger.Info("We need more addresses. Sending pexRequest to random peer", "peer", peer) 535 r.RequestAddrs(peer) 536 } 537 538 // 2) Dial seeds if we are not dialing anyone. 539 // This is done in addition to asking a peer for addresses to work-around 540 // peers not participating in PEX. 541 if len(toDial) == 0 { 542 r.Logger.Info("No addresses to dial. Falling back to seeds") 543 r.dialSeeds() 544 } 545 } 546 } 547 548 func (r *Reactor) dialAttemptsInfo(addr *p2p.NetAddress) (attempts int, lastDialed time.Time) { 549 _attempts, ok := r.attemptsToDial.Load(addr.DialString()) 550 if !ok { 551 return 552 } 553 atd := _attempts.(_attemptsToDial) 554 return atd.number, atd.lastDialed 555 } 556 557 func (r *Reactor) dialPeer(addr *p2p.NetAddress) error { 558 attempts, lastDialed := r.dialAttemptsInfo(addr) 559 if !r.Switch.IsPeerPersistent(addr) && attempts > maxAttemptsToDial { 560 r.book.MarkBad(addr, defaultBanTime) 561 return errMaxAttemptsToDial{} 562 } 563 564 // exponential backoff if it's not our first attempt to dial given address 565 if attempts > 0 { 566 jitter := time.Duration(tmrand.Float64() * float64(time.Second)) // 1s == (1e9 ns) 567 backoffDuration := jitter + ((1 << uint(attempts)) * time.Second) 568 backoffDuration = r.maxBackoffDurationForPeer(addr, backoffDuration) 569 sinceLastDialed := time.Since(lastDialed) 570 if sinceLastDialed < backoffDuration { 571 return errTooEarlyToDial{backoffDuration, lastDialed} 572 } 573 } 574 575 err := r.Switch.DialPeerWithAddress(addr) 576 if err != nil { 577 if _, ok := err.(p2p.ErrCurrentlyDialingOrExistingAddress); ok { 578 return err 579 } 580 581 markAddrInBookBasedOnErr(addr, r.book, err) 582 switch err.(type) { 583 case p2p.ErrSwitchAuthenticationFailure: 584 // NOTE: addr is removed from addrbook in markAddrInBookBasedOnErr 585 r.attemptsToDial.Delete(addr.DialString()) 586 default: 587 r.attemptsToDial.Store(addr.DialString(), _attemptsToDial{attempts + 1, time.Now()}) 588 } 589 return fmt.Errorf("dialing failed (attempts: %d): %w", attempts+1, err) 590 } 591 592 // cleanup any history 593 r.attemptsToDial.Delete(addr.DialString()) 594 return nil 595 } 596 597 // maxBackoffDurationForPeer caps the backoff duration for persistent peers. 598 func (r *Reactor) maxBackoffDurationForPeer(addr *p2p.NetAddress, planned time.Duration) time.Duration { 599 if r.config.PersistentPeersMaxDialPeriod > 0 && 600 planned > r.config.PersistentPeersMaxDialPeriod && 601 r.Switch.IsPeerPersistent(addr) { 602 return r.config.PersistentPeersMaxDialPeriod 603 } 604 return planned 605 } 606 607 // checkSeeds checks that addresses are well formed. 608 // Returns number of seeds we can connect to, along with all seeds addrs. 609 // return err if user provided any badly formatted seed addresses. 610 // Doesn't error if the seed node can't be reached. 611 // numOnline returns -1 if no seed nodes were in the initial configuration. 612 func (r *Reactor) checkSeeds() (numOnline int, netAddrs []*p2p.NetAddress, err error) { 613 lSeeds := len(r.config.Seeds) 614 if lSeeds == 0 { 615 return -1, nil, nil 616 } 617 netAddrs, errs := p2p.NewNetAddressStrings(r.config.Seeds) 618 numOnline = lSeeds - len(errs) 619 for _, err := range errs { 620 switch e := err.(type) { 621 case p2p.ErrNetAddressLookup: 622 r.Logger.Error("Connecting to seed failed", "err", e) 623 default: 624 return 0, nil, fmt.Errorf("seed node configuration has error: %w", e) 625 } 626 } 627 return numOnline, netAddrs, nil 628 } 629 630 // randomly dial seeds until we connect to one or exhaust them 631 func (r *Reactor) dialSeeds() { 632 perm := tmrand.Perm(len(r.seedAddrs)) 633 // perm := r.Switch.rng.Perm(lSeeds) 634 for _, i := range perm { 635 // dial a random seed 636 seedAddr := r.seedAddrs[i] 637 err := r.Switch.DialPeerWithAddress(seedAddr) 638 639 switch err.(type) { 640 case nil, p2p.ErrCurrentlyDialingOrExistingAddress: 641 return 642 } 643 r.Switch.Logger.Error("Error dialing seed", "err", err, "seed", seedAddr) 644 } 645 // do not write error message if there were no seeds specified in config 646 if len(r.seedAddrs) > 0 { 647 r.Switch.Logger.Error("Couldn't connect to any seeds") 648 } 649 } 650 651 // AttemptsToDial returns the number of attempts to dial specific address. It 652 // returns 0 if never attempted or successfully connected. 653 func (r *Reactor) AttemptsToDial(addr *p2p.NetAddress) int { 654 lAttempts, attempted := r.attemptsToDial.Load(addr.DialString()) 655 if attempted { 656 return lAttempts.(_attemptsToDial).number 657 } 658 return 0 659 } 660 661 //---------------------------------------------------------- 662 663 // Explores the network searching for more peers. (continuous) 664 // Seed/Crawler Mode causes this node to quickly disconnect 665 // from peers, except other seed nodes. 666 func (r *Reactor) crawlPeersRoutine() { 667 // If we have any seed nodes, consult them first 668 if len(r.seedAddrs) > 0 { 669 r.dialSeeds() 670 } else { 671 // Do an initial crawl 672 r.crawlPeers(r.book.GetSelection()) 673 } 674 675 // Fire periodically 676 ticker := time.NewTicker(crawlPeerPeriod) 677 678 for { 679 select { 680 case <-ticker.C: 681 r.attemptDisconnects() 682 r.crawlPeers(r.book.GetSelection()) 683 r.cleanupCrawlPeerInfos() 684 case <-r.Quit(): 685 return 686 } 687 } 688 } 689 690 // nodeHasSomePeersOrDialingAny returns true if the node is connected to some 691 // peers or dialing them currently. 692 func (r *Reactor) nodeHasSomePeersOrDialingAny() bool { 693 out, in, dial := r.Switch.NumPeers() 694 return out+in+dial > 0 695 } 696 697 // crawlPeerInfo handles temporary data needed for the network crawling 698 // performed during seed/crawler mode. 699 type crawlPeerInfo struct { 700 Addr *p2p.NetAddress `json:"addr"` 701 // The last time we crawled the peer or attempted to do so. 702 LastCrawled time.Time `json:"last_crawled"` 703 } 704 705 // crawlPeers will crawl the network looking for new peer addresses. 706 func (r *Reactor) crawlPeers(addrs []*p2p.NetAddress) { 707 now := time.Now() 708 709 for _, addr := range addrs { 710 peerInfo, ok := r.crawlPeerInfos[addr.ID] 711 712 // Do not attempt to connect with peers we recently crawled. 713 if ok && now.Sub(peerInfo.LastCrawled) < minTimeBetweenCrawls { 714 continue 715 } 716 717 // Record crawling attempt. 718 r.crawlPeerInfos[addr.ID] = crawlPeerInfo{ 719 Addr: addr, 720 LastCrawled: now, 721 } 722 723 err := r.dialPeer(addr) 724 if err != nil { 725 switch err.(type) { 726 case errMaxAttemptsToDial, errTooEarlyToDial, p2p.ErrCurrentlyDialingOrExistingAddress: 727 r.Logger.Debug(err.Error(), "addr", addr) 728 default: 729 r.Logger.Debug(err.Error(), "addr", addr) 730 } 731 continue 732 } 733 734 peer := r.Switch.Peers().Get(addr.ID) 735 if peer != nil { 736 r.RequestAddrs(peer) 737 } 738 } 739 } 740 741 func (r *Reactor) cleanupCrawlPeerInfos() { 742 for id, info := range r.crawlPeerInfos { 743 // If we did not crawl a peer for 24 hours, it means the peer was removed 744 // from the addrbook => remove 745 // 746 // 10000 addresses / maxGetSelection = 40 cycles to get all addresses in 747 // the ideal case, 748 // 40 * crawlPeerPeriod ~ 20 minutes 749 if time.Since(info.LastCrawled) > 24*time.Hour { 750 delete(r.crawlPeerInfos, id) 751 } 752 } 753 } 754 755 // attemptDisconnects checks if we've been with each peer long enough to disconnect 756 func (r *Reactor) attemptDisconnects() { 757 for _, peer := range r.Switch.Peers().List() { 758 if peer.Status().Duration < r.config.SeedDisconnectWaitPeriod { 759 continue 760 } 761 if peer.IsPersistent() { 762 continue 763 } 764 r.Switch.StopPeerGracefully(peer) 765 } 766 } 767 768 func markAddrInBookBasedOnErr(addr *p2p.NetAddress, book AddrBook, err error) { 769 // TODO: detect more "bad peer" scenarios 770 switch err.(type) { 771 case p2p.ErrSwitchAuthenticationFailure: 772 book.MarkBad(addr, defaultBanTime) 773 default: 774 book.MarkAttempt(addr) 775 } 776 } 777 778 //----------------------------------------------------------------------------- 779 // Messages 780 781 // mustEncode proto encodes a tmp2p.Message 782 func mustEncode(pb proto.Message) []byte { 783 msg := tmp2p.Message{} 784 switch pb := pb.(type) { 785 case *tmp2p.PexRequest: 786 msg.Sum = &tmp2p.Message_PexRequest{PexRequest: pb} 787 case *tmp2p.PexAddrs: 788 msg.Sum = &tmp2p.Message_PexAddrs{PexAddrs: pb} 789 default: 790 panic(fmt.Sprintf("Unknown message type %T", pb)) 791 } 792 793 bz, err := msg.Marshal() 794 if err != nil { 795 panic(fmt.Errorf("unable to marshal %T: %w", pb, err)) 796 } 797 return bz 798 } 799 800 func decodeMsg(bz []byte) (proto.Message, error) { 801 pb := &tmp2p.Message{} 802 803 err := pb.Unmarshal(bz) 804 if err != nil { 805 return nil, err 806 } 807 808 switch msg := pb.Sum.(type) { 809 case *tmp2p.Message_PexRequest: 810 return msg.PexRequest, nil 811 case *tmp2p.Message_PexAddrs: 812 return msg.PexAddrs, nil 813 default: 814 return nil, fmt.Errorf("unknown message: %T", msg) 815 } 816 }