You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

674 lines
19 KiB

9 years ago
9 years ago
8 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
8 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
7 years ago
9 years ago
9 years ago
8 years ago
7 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
8 years ago
9 years ago
7 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
8 years ago
9 years ago
8 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  1. package p2p
  2. import (
  3. "bytes"
  4. "fmt"
  5. "math/rand"
  6. "reflect"
  7. "sort"
  8. "time"
  9. "github.com/pkg/errors"
  10. wire "github.com/tendermint/go-wire"
  11. cmn "github.com/tendermint/tmlibs/common"
  12. )
  13. const (
  14. // PexChannel is a channel for PEX messages
  15. PexChannel = byte(0x00)
  16. // period to ensure peers connected
  17. defaultEnsurePeersPeriod = 30 * time.Second
  18. minNumOutboundPeers = 10
  19. maxPexMessageSize = 1048576 // 1MB
  20. )
  21. // PEXReactor handles PEX (peer exchange) and ensures that an
  22. // adequate number of peers are connected to the switch.
  23. //
  24. // It uses `AddrBook` (address book) to store `NetAddress`es of the peers.
  25. //
  26. // ## Preventing abuse
  27. //
  28. // Only accept pexAddrsMsg from peers we sent a corresponding pexRequestMsg too.
  29. // Only accept one pexRequestMsg every ~defaultEnsurePeersPeriod.
  30. type PEXReactor struct {
  31. BaseReactor
  32. book *AddrBook
  33. config *PEXReactorConfig
  34. ensurePeersPeriod time.Duration
  35. // maps to prevent abuse
  36. requestsSent *cmn.CMap // ID->struct{}: unanswered send requests
  37. lastReceivedRequests *cmn.CMap // ID->time.Time: last time peer requested from us
  38. }
  39. // PEXReactorConfig holds reactor specific configuration data.
  40. type PEXReactorConfig struct {
  41. // Seeds is a list of addresses reactor may use if it can't connect to peers
  42. // in the addrbook.
  43. Seeds []string
  44. }
  45. // NewPEXReactor creates new PEX reactor.
  46. func NewPEXReactor(b *AddrBook, config *PEXReactorConfig) *PEXReactor {
  47. r := &PEXReactor{
  48. book: b,
  49. config: config,
  50. ensurePeersPeriod: defaultEnsurePeersPeriod,
  51. requestsSent: cmn.NewCMap(),
  52. lastReceivedRequests: cmn.NewCMap(),
  53. }
  54. r.BaseReactor = *NewBaseReactor("PEXReactor", r)
  55. return r
  56. }
  57. // OnStart implements BaseService
  58. func (r *PEXReactor) OnStart() error {
  59. if err := r.BaseReactor.OnStart(); err != nil {
  60. return err
  61. }
  62. err := r.book.Start()
  63. if err != nil && err != cmn.ErrAlreadyStarted {
  64. return err
  65. }
  66. // return err if user provided a bad seed address
  67. if err := r.checkSeeds(); err != nil {
  68. return err
  69. }
  70. go r.ensurePeersRoutine()
  71. return nil
  72. }
  73. // OnStop implements BaseService
  74. func (r *PEXReactor) OnStop() {
  75. r.BaseReactor.OnStop()
  76. r.book.Stop()
  77. }
  78. // GetChannels implements Reactor
  79. func (r *PEXReactor) GetChannels() []*ChannelDescriptor {
  80. return []*ChannelDescriptor{
  81. {
  82. ID: PexChannel,
  83. Priority: 1,
  84. SendQueueCapacity: 10,
  85. },
  86. }
  87. }
  88. // AddPeer implements Reactor by adding peer to the address book (if inbound)
  89. // or by requesting more addresses (if outbound).
  90. func (r *PEXReactor) AddPeer(p Peer) {
  91. if p.IsOutbound() {
  92. // For outbound peers, the address is already in the books -
  93. // either via DialPeersAsync or r.Receive.
  94. // Ask it for more peers if we need.
  95. if r.book.NeedMoreAddrs() {
  96. r.RequestPEX(p)
  97. }
  98. } else {
  99. // For inbound peers, the peer is its own source,
  100. // and its NodeInfo has already been validated.
  101. // Let the ensurePeersRoutine handle asking for more
  102. // peers when we need - we don't trust inbound peers as much.
  103. addr := p.NodeInfo().NetAddress()
  104. r.book.AddAddress(addr, addr)
  105. }
  106. }
  107. // RemovePeer implements Reactor.
  108. func (r *PEXReactor) RemovePeer(p Peer, reason interface{}) {
  109. id := string(p.ID())
  110. r.requestsSent.Delete(id)
  111. r.lastReceivedRequests.Delete(id)
  112. }
  113. // Receive implements Reactor by handling incoming PEX messages.
  114. func (r *PEXReactor) Receive(chID byte, src Peer, msgBytes []byte) {
  115. _, msg, err := DecodeMessage(msgBytes)
  116. if err != nil {
  117. r.Logger.Error("Error decoding message", "err", err)
  118. return
  119. }
  120. r.Logger.Debug("Received message", "src", src, "chId", chID, "msg", msg)
  121. switch msg := msg.(type) {
  122. case *pexRequestMessage:
  123. // We received a request for peers from src.
  124. if err := r.receiveRequest(src); err != nil {
  125. r.Switch.StopPeerForError(src, err)
  126. return
  127. }
  128. r.SendAddrs(src, r.book.GetSelection())
  129. case *pexAddrsMessage:
  130. // We received some peer addresses from src.
  131. if err := r.ReceivePEX(msg.Addrs, src); err != nil {
  132. r.Switch.StopPeerForError(src, err)
  133. return
  134. }
  135. default:
  136. r.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
  137. }
  138. }
  139. func (r *PEXReactor) receiveRequest(src Peer) error {
  140. id := string(src.ID())
  141. v := r.lastReceivedRequests.Get(id)
  142. if v == nil {
  143. // initialize with empty time
  144. lastReceived := time.Time{}
  145. r.lastReceivedRequests.Set(id, lastReceived)
  146. return nil
  147. }
  148. lastReceived := v.(time.Time)
  149. if lastReceived.Equal(time.Time{}) {
  150. // first time gets a free pass. then we start tracking the time
  151. lastReceived = time.Now()
  152. r.lastReceivedRequests.Set(id, lastReceived)
  153. return nil
  154. }
  155. now := time.Now()
  156. if now.Sub(lastReceived) < r.ensurePeersPeriod/3 {
  157. return fmt.Errorf("Peer (%v) is sending too many PEX requests. Disconnecting", src.ID())
  158. }
  159. r.lastReceivedRequests.Set(id, now)
  160. return nil
  161. }
  162. // RequestPEX asks peer for more addresses if we do not already
  163. // have a request out for this peer.
  164. func (r *PEXReactor) RequestPEX(p Peer) {
  165. id := string(p.ID())
  166. if r.requestsSent.Has(id) {
  167. return
  168. }
  169. r.requestsSent.Set(id, struct{}{})
  170. p.Send(PexChannel, struct{ PexMessage }{&pexRequestMessage{}})
  171. }
  172. // ReceivePEX adds the given addrs to the addrbook if theres an open
  173. // request for this peer and deletes the open request.
  174. // If there's no open request for the src peer, it returns an error.
  175. func (r *PEXReactor) ReceivePEX(addrs []*NetAddress, src Peer) error {
  176. id := string(src.ID())
  177. if !r.requestsSent.Has(id) {
  178. return errors.New("Received unsolicited pexAddrsMessage")
  179. }
  180. r.requestsSent.Delete(id)
  181. srcAddr := src.NodeInfo().NetAddress()
  182. for _, netAddr := range addrs {
  183. if netAddr != nil {
  184. r.book.AddAddress(netAddr, srcAddr)
  185. }
  186. }
  187. return nil
  188. }
  189. // SendAddrs sends addrs to the peer.
  190. func (r *PEXReactor) SendAddrs(p Peer, netAddrs []*NetAddress) {
  191. p.Send(PexChannel, struct{ PexMessage }{&pexAddrsMessage{Addrs: netAddrs}})
  192. }
  193. // SetEnsurePeersPeriod sets period to ensure peers connected.
  194. func (r *PEXReactor) SetEnsurePeersPeriod(d time.Duration) {
  195. r.ensurePeersPeriod = d
  196. }
  197. // Ensures that sufficient peers are connected. (continuous)
  198. func (r *PEXReactor) ensurePeersRoutine() {
  199. // Randomize when routine starts
  200. ensurePeersPeriodMs := r.ensurePeersPeriod.Nanoseconds() / 1e6
  201. time.Sleep(time.Duration(rand.Int63n(ensurePeersPeriodMs)) * time.Millisecond)
  202. // fire once immediately.
  203. // ensures we dial the seeds right away if the book is empty
  204. r.ensurePeers()
  205. // fire periodically
  206. ticker := time.NewTicker(r.ensurePeersPeriod)
  207. for {
  208. select {
  209. case <-ticker.C:
  210. r.ensurePeers()
  211. case <-r.Quit:
  212. ticker.Stop()
  213. return
  214. }
  215. }
  216. }
  217. // ensurePeers ensures that sufficient peers are connected. (once)
  218. //
  219. // Old bucket / New bucket are arbitrary categories to denote whether an
  220. // address is vetted or not, and this needs to be determined over time via a
  221. // heuristic that we haven't perfected yet, or, perhaps is manually edited by
  222. // the node operator. It should not be used to compute what addresses are
  223. // already connected or not.
  224. //
  225. // TODO Basically, we need to work harder on our good-peer/bad-peer marking.
  226. // What we're currently doing in terms of marking good/bad peers is just a
  227. // placeholder. It should not be the case that an address becomes old/vetted
  228. // upon a single successful connection.
  229. func (r *PEXReactor) ensurePeers() {
  230. numOutPeers, numInPeers, numDialing := r.Switch.NumPeers()
  231. numToDial := minNumOutboundPeers - (numOutPeers + numDialing)
  232. r.Logger.Info("Ensure peers", "numOutPeers", numOutPeers, "numDialing", numDialing, "numToDial", numToDial)
  233. if numToDial <= 0 {
  234. return
  235. }
  236. // bias to prefer more vetted peers when we have fewer connections.
  237. // not perfect, but somewhate ensures that we prioritize connecting to more-vetted
  238. // NOTE: range here is [10, 90]. Too high ?
  239. newBias := cmn.MinInt(numOutPeers, 8)*10 + 10
  240. toDial := make(map[ID]*NetAddress)
  241. // Try maxAttempts times to pick numToDial addresses to dial
  242. maxAttempts := numToDial * 3
  243. for i := 0; i < maxAttempts && len(toDial) < numToDial; i++ {
  244. try := r.book.PickAddress(newBias)
  245. if try == nil {
  246. continue
  247. }
  248. if _, selected := toDial[try.ID]; selected {
  249. continue
  250. }
  251. if dialling := r.Switch.IsDialing(try.ID); dialling {
  252. continue
  253. }
  254. if connected := r.Switch.Peers().Has(try.ID); connected {
  255. continue
  256. }
  257. r.Logger.Info("Will dial address", "addr", try)
  258. toDial[try.ID] = try
  259. }
  260. // Dial picked addresses
  261. for _, item := range toDial {
  262. go func(picked *NetAddress) {
  263. _, err := r.Switch.DialPeerWithAddress(picked, false)
  264. if err != nil {
  265. r.book.MarkAttempt(picked)
  266. }
  267. }(item)
  268. }
  269. // If we need more addresses, pick a random peer and ask for more.
  270. if r.book.NeedMoreAddrs() {
  271. peers := r.Switch.Peers().List()
  272. peersCount := len(peers)
  273. if peersCount > 0 {
  274. peer := peers[rand.Int()%peersCount] // nolint: gas
  275. r.Logger.Info("We need more addresses. Sending pexRequest to random peer", "peer", peer)
  276. r.RequestPEX(peer)
  277. }
  278. }
  279. // If we are not connected to nor dialing anybody, fallback to dialing a seed.
  280. if numOutPeers+numInPeers+numDialing+len(toDial) == 0 {
  281. r.Logger.Info("No addresses to dial nor connected peers. Falling back to seeds")
  282. r.dialSeed()
  283. }
  284. }
  285. // check seed addresses are well formed
  286. func (r *PEXReactor) checkSeeds() error {
  287. lSeeds := len(r.config.Seeds)
  288. if lSeeds == 0 {
  289. return nil
  290. }
  291. _, errs := NewNetAddressStrings(r.config.Seeds)
  292. for _, err := range errs {
  293. if err != nil {
  294. return err
  295. }
  296. }
  297. return nil
  298. }
  299. // Explores the network searching for more peers. (continuous)
  300. // Seed/Crawler Mode causes this node to quickly disconnect
  301. // from peers, except other seed nodes.
  302. func (r *PEXReactor) seedCrawlerMode() {
  303. // Do an initial crawl
  304. r.crawlPeers()
  305. // Fire periodically
  306. ticker := time.NewTicker(defaultSeedModePeriod)
  307. for {
  308. select {
  309. case <-ticker.C:
  310. r.attemptDisconnects()
  311. r.crawlPeers()
  312. case <-r.Quit:
  313. return
  314. }
  315. }
  316. }
  317. // crawlStatus handles temporary data needed for the
  318. // network crawling performed during seed/crawler mode.
  319. type crawlStatus struct {
  320. // The remote address of a potential peer we learned about
  321. Addr *NetAddress
  322. // Not empty if we are connected to the address
  323. PeerID string
  324. // The last time we attempt to reach this address
  325. LastAttempt time.Time
  326. // The last time we successfully reached this address
  327. LastSuccess time.Time
  328. }
  329. // oldestFirst implements sort.Interface for []crawlStatus
  330. // based on the LastAttempt field.
  331. type oldestFirst []crawlStatus
  332. func (of oldestFirst) Len() int { return len(of) }
  333. func (of oldestFirst) Swap(i, j int) { of[i], of[j] = of[j], of[i] }
  334. func (of oldestFirst) Less(i, j int) bool { return of[i].LastAttempt.Before(of[j].LastAttempt) }
  335. // getCrawlStatus returns addresses of potential peers that we wish to validate.
  336. // NOTE: The status information is ordered as described above.
  337. func (r *PEXReactor) getCrawlStatus() []crawlStatus {
  338. var of oldestFirst
  339. addrs := r.book.ListOfKnownAddresses()
  340. // Go through all the addresses in the AddressBook
  341. for _, addr := range addrs {
  342. var peerID string
  343. // Check if a peer is already connected from this addr
  344. if p := r.Switch.peers.GetByRemoteAddr(addr.Addr); p != nil {
  345. peerID = p.Key()
  346. }
  347. of = append(of, crawlStatus{
  348. Addr: addr.Addr,
  349. PeerID: peerID,
  350. LastAttempt: addr.LastAttempt,
  351. LastSuccess: addr.LastSuccess,
  352. })
  353. }
  354. sort.Sort(of)
  355. return of
  356. }
  357. // crawlPeers will crawl the network looking for new peer addresses. (once)
  358. //
  359. // TODO Basically, we need to work harder on our good-peer/bad-peer marking.
  360. // What we're currently doing in terms of marking good/bad peers is just a
  361. // placeholder. It should not be the case that an address becomes old/vetted
  362. // upon a single successful connection.
  363. func (r *PEXReactor) crawlPeers() {
  364. crawlerStatus := r.getCrawlStatus()
  365. now := time.Now()
  366. // Use addresses we know of to reach additional peers
  367. for _, cs := range crawlerStatus {
  368. // Do not dial peers that are already connected
  369. if cs.PeerID != "" {
  370. continue
  371. }
  372. // Do not attempt to connect with peers we recently dialed
  373. if now.Sub(cs.LastAttempt) < defaultCrawlPeerInterval {
  374. continue
  375. }
  376. // Otherwise, attempt to connect with the known address
  377. p, err := r.Switch.DialPeerWithAddress(cs.Addr, false)
  378. if err != nil {
  379. r.book.MarkAttempt(cs.Addr)
  380. continue
  381. }
  382. // Enter the peer ID into our crawl status information
  383. cs.PeerID = p.Key()
  384. r.book.MarkGood(cs.Addr)
  385. }
  386. // Crawl the connected peers asking for more addresses
  387. for _, cs := range crawlerStatus {
  388. if cs.PeerID == "" {
  389. continue
  390. }
  391. // We will wait a minimum period of time before crawling peers again
  392. if now.Sub(cs.LastAttempt) >= defaultCrawlPeerInterval {
  393. p := r.Switch.Peers().Get(cs.PeerID)
  394. if p != nil {
  395. r.RequestPEX(p)
  396. r.book.MarkAttempt(cs.Addr)
  397. }
  398. }
  399. }
  400. }
  401. // attemptDisconnects checks the crawlStatus info for Peers to disconnect from. (once)
  402. func (r *PEXReactor) attemptDisconnects() {
  403. crawlerStatus := r.getCrawlStatus()
  404. now := time.Now()
  405. // Go through each peer we have connected with
  406. // looking for opportunities to disconnect
  407. for _, cs := range crawlerStatus {
  408. if cs.PeerID == "" {
  409. continue
  410. }
  411. // Remain connected to each peer for a minimum period of time
  412. if now.Sub(cs.LastSuccess) < defaultSeedDisconnectWaitPeriod {
  413. continue
  414. }
  415. // Fetch the Peer using the saved ID
  416. p := r.Switch.Peers().Get(cs.PeerID)
  417. if p == nil {
  418. continue
  419. }
  420. // Do not disconnect from persistent peers.
  421. // Specifically, we need to remain connected to other seeds
  422. if p.IsPersistent() {
  423. continue
  424. }
  425. // Otherwise, disconnect from the peer
  426. r.Switch.StopPeerGracefully(p)
  427. }
  428. }
  429. // crawlStatus handles temporary data needed for the
  430. // network crawling performed during seed/crawler mode.
  431. type crawlStatus struct {
  432. // The remote address of a potential peer we learned about
  433. Addr *NetAddress
  434. // Not empty if we are connected to the address
  435. PeerID string
  436. // The last time we attempt to reach this address
  437. LastAttempt time.Time
  438. // The last time we successfully reached this address
  439. LastSuccess time.Time
  440. }
  441. // oldestAttempt implements sort.Interface for []crawlStatus
  442. // based on the LastAttempt field.
  443. type oldestAttempt []crawlStatus
  444. func (oa oldestAttempt) Len() int { return len(oa) }
  445. func (oa oldestAttempt) Swap(i, j int) { oa[i], oa[j] = oa[j], oa[i] }
  446. func (oa oldestAttempt) Less(i, j int) bool { return oa[i].LastAttempt.Before(oa[j].LastAttempt) }
  447. // getCrawlStatus returns addresses of potential peers that we wish to validate.
  448. // NOTE: The status information is ordered as described above.
  449. func (r *PEXReactor) getCrawlStatus() []crawlStatus {
  450. var oa oldestAttempt
  451. addrs := r.book.ListOfKnownAddresses()
  452. // Go through all the addresses in the AddressBook
  453. for _, addr := range addrs {
  454. p := r.Switch.peers.GetByRemoteAddr(addr.Addr)
  455. oa = append(oa, crawlStatus{
  456. Addr: addr.Addr,
  457. PeerID: p.Key(),
  458. LastAttempt: addr.LastAttempt,
  459. LastSuccess: addr.LastSuccess,
  460. })
  461. }
  462. sort.Sort(oa)
  463. return oa
  464. }
  465. // crawlPeers will crawl the network looking for new peer addresses. (once)
  466. //
  467. // TODO Basically, we need to work harder on our good-peer/bad-peer marking.
  468. // What we're currently doing in terms of marking good/bad peers is just a
  469. // placeholder. It should not be the case that an address becomes old/vetted
  470. // upon a single successful connection.
  471. func (r *PEXReactor) crawlPeers() {
  472. crawlerStatus := r.getCrawlStatus()
  473. now := time.Now()
  474. // Use addresses we know of to reach additional peers
  475. for _, cs := range crawlerStatus {
  476. // Do not dial peers that are already connected
  477. if cs.PeerID != "" {
  478. continue
  479. }
  480. // Do not attempt to connect with peers we recently dialed
  481. if now.Sub(cs.LastAttempt) < defaultCrawlPeerInterval {
  482. continue
  483. }
  484. // Otherwise, attempt to connect with the known address
  485. p, err := r.Switch.DialPeerWithAddress(cs.Addr, false)
  486. if err != nil {
  487. r.book.MarkAttempt(cs.Addr)
  488. continue
  489. }
  490. // Enter the peer ID into our crawl status information
  491. cs.PeerID = p.Key()
  492. r.book.MarkGood(cs.Addr)
  493. }
  494. // Crawl the connected peers asking for more addresses
  495. for _, cs := range crawlerStatus {
  496. if cs.PeerID == "" {
  497. continue
  498. }
  499. // We will wait a minimum period of time before crawling peers again
  500. if now.Sub(cs.LastAttempt) >= defaultCrawlPeerInterval {
  501. p := r.Switch.peers.Get(cs.PeerID)
  502. if p != nil {
  503. r.RequestPEX(p)
  504. }
  505. }
  506. }
  507. }
  508. // attemptDisconnects checks the crawlStatus info for Peers to disconnect from. (once)
  509. func (r *PEXReactor) attemptDisconnects() {
  510. crawlerStatus := r.getCrawlStatus()
  511. now := time.Now()
  512. // Go through each peer we have connected with
  513. // looking for opportunities to disconnect
  514. for _, cs := range crawlerStatus {
  515. if cs.PeerID == "" {
  516. continue
  517. }
  518. // Remain connected to each peer for a minimum period of time
  519. if now.Sub(cs.LastSuccess) < defaultSeedDisconnectWaitPeriod {
  520. continue
  521. }
  522. // Fetch the Peer using the saved ID
  523. p := r.Switch.peers.Get(cs.PeerID)
  524. if p == nil {
  525. continue
  526. }
  527. // Do not disconnect from persistent peers.
  528. // Specifically, we need to remain connected to other seeds
  529. if p.IsPersistent() {
  530. continue
  531. }
  532. // Otherwise, disconnect from the peer
  533. r.Switch.StopPeerGracefully(p)
  534. }
  535. }
  536. // randomly dial seeds until we connect to one or exhaust them
  537. func (r *PEXReactor) dialSeed() {
  538. lSeeds := len(r.config.Seeds)
  539. if lSeeds == 0 {
  540. return
  541. }
  542. seedAddrs, _ := NewNetAddressStrings(r.config.Seeds)
  543. perm := r.Switch.rng.Perm(lSeeds)
  544. for _, i := range perm {
  545. // dial a random seed
  546. seedAddr := seedAddrs[i]
  547. peer, err := r.Switch.DialPeerWithAddress(seedAddr, false)
  548. if err != nil {
  549. r.Switch.Logger.Error("Error dialing seed", "err", err, "seed", seedAddr)
  550. } else {
  551. r.Switch.Logger.Info("Connected to seed", "peer", peer)
  552. return
  553. }
  554. }
  555. r.Switch.Logger.Error("Couldn't connect to any seeds")
  556. }
  557. //-----------------------------------------------------------------------------
  558. // Messages
  559. const (
  560. msgTypeRequest = byte(0x01)
  561. msgTypeAddrs = byte(0x02)
  562. )
  563. // PexMessage is a primary type for PEX messages. Underneath, it could contain
  564. // either pexRequestMessage, or pexAddrsMessage messages.
  565. type PexMessage interface{}
  566. var _ = wire.RegisterInterface(
  567. struct{ PexMessage }{},
  568. wire.ConcreteType{&pexRequestMessage{}, msgTypeRequest},
  569. wire.ConcreteType{&pexAddrsMessage{}, msgTypeAddrs},
  570. )
  571. // DecodeMessage implements interface registered above.
  572. func DecodeMessage(bz []byte) (msgType byte, msg PexMessage, err error) {
  573. msgType = bz[0]
  574. n := new(int)
  575. r := bytes.NewReader(bz)
  576. msg = wire.ReadBinary(struct{ PexMessage }{}, r, maxPexMessageSize, n, &err).(struct{ PexMessage }).PexMessage
  577. return
  578. }
  579. /*
  580. A pexRequestMessage requests additional peer addresses.
  581. */
  582. type pexRequestMessage struct {
  583. }
  584. func (m *pexRequestMessage) String() string {
  585. return "[pexRequest]"
  586. }
  587. /*
  588. A message with announced peer addresses.
  589. */
  590. type pexAddrsMessage struct {
  591. Addrs []*NetAddress
  592. }
  593. func (m *pexAddrsMessage) String() string {
  594. return fmt.Sprintf("[pexAddrs %v]", m.Addrs)
  595. }