github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/p2p/pex/pex_reactor.go (about)

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