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