github.com/Finschia/ostracon@v1.1.5/p2p/pex/pex_reactor.go (about)

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