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  }