github.com/number571/tendermint@v0.34.11-gost/internal/p2p/pex/reactor.go (about)

     1  package pex
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"runtime/debug"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/number571/tendermint/internal/p2p"
    11  	"github.com/number571/tendermint/internal/p2p/conn"
    12  	"github.com/number571/tendermint/libs/log"
    13  	tmmath "github.com/number571/tendermint/libs/math"
    14  	"github.com/number571/tendermint/libs/service"
    15  	protop2p "github.com/number571/tendermint/proto/tendermint/p2p"
    16  	"github.com/number571/tendermint/types"
    17  )
    18  
    19  var (
    20  	_ service.Service = (*ReactorV2)(nil)
    21  	_ p2p.Wrapper     = (*protop2p.PexMessage)(nil)
    22  )
    23  
    24  // TODO: Consolidate with params file.
    25  // See https://github.com/number571/tendermint/issues/6371
    26  const (
    27  	// the minimum time one peer can send another request to the same peer
    28  	minReceiveRequestInterval = 100 * time.Millisecond
    29  
    30  	// the maximum amount of addresses that can be included in a response
    31  	maxAddresses uint16 = 100
    32  
    33  	// allocated time to resolve a node address into a set of endpoints
    34  	resolveTimeout = 3 * time.Second
    35  
    36  	// How long to wait when there are no peers available before trying again
    37  	noAvailablePeersWaitPeriod = 1 * time.Second
    38  
    39  	// indicates the ping rate of the pex reactor when the peer store is full.
    40  	// The reactor should still look to add new peers in order to flush out low
    41  	// scoring peers that are still in the peer store
    42  	fullCapacityInterval = 10 * time.Minute
    43  )
    44  
    45  // TODO: We should decide whether we want channel descriptors to be housed
    46  // within each reactor (as they are now) or, considering that the reactor doesn't
    47  // really need to care about the channel descriptors, if they should be housed
    48  // in the node module.
    49  func ChannelDescriptor() conn.ChannelDescriptor {
    50  	return conn.ChannelDescriptor{
    51  		ID:                  PexChannel,
    52  		Priority:            1,
    53  		SendQueueCapacity:   10,
    54  		RecvMessageCapacity: maxMsgSize,
    55  		RecvBufferCapacity:  32,
    56  		MaxSendBytes:        200,
    57  	}
    58  }
    59  
    60  // ReactorV2 is a PEX reactor for the new P2P stack. The legacy reactor
    61  // is Reactor.
    62  //
    63  // FIXME: Rename this when Reactor is removed, and consider moving to p2p/.
    64  //
    65  // The peer exchange or PEX reactor supports the peer manager by sending
    66  // requests to other peers for addresses that can be given to the peer manager
    67  // and at the same time advertises addresses to peers that need more.
    68  //
    69  // The reactor is able to tweak the intensity of it's search by decreasing or
    70  // increasing the interval between each request. It tracks connected peers via
    71  // a linked list, sending a request to the node at the front of the list and
    72  // adding it to the back of the list once a response is received.
    73  type ReactorV2 struct {
    74  	service.BaseService
    75  
    76  	peerManager *p2p.PeerManager
    77  	pexCh       *p2p.Channel
    78  	peerUpdates *p2p.PeerUpdates
    79  	closeCh     chan struct{}
    80  
    81  	// list of available peers to loop through and send peer requests to
    82  	availablePeers map[types.NodeID]struct{}
    83  
    84  	mtx sync.RWMutex
    85  
    86  	// requestsSent keeps track of which peers the PEX reactor has sent requests
    87  	// to. This prevents the sending of spurious responses.
    88  	// NOTE: If a node never responds, they will remain in this map until a
    89  	// peer down status update is sent
    90  	requestsSent map[types.NodeID]struct{}
    91  
    92  	// lastReceivedRequests keeps track of when peers send a request to prevent
    93  	// peers from sending requests too often (as defined by
    94  	// minReceiveRequestInterval).
    95  	lastReceivedRequests map[types.NodeID]time.Time
    96  
    97  	// the time when another request will be sent
    98  	nextRequestTime time.Time
    99  
   100  	// keep track of how many new peers to existing peers we have received to
   101  	// extrapolate the size of the network
   102  	newPeers   uint32
   103  	totalPeers uint32
   104  
   105  	// discoveryRatio is the inverse ratio of new peers to old peers squared.
   106  	// This is multiplied by the minimum duration to calculate how long to wait
   107  	// between each request.
   108  	discoveryRatio float32
   109  }
   110  
   111  // NewReactor returns a reference to a new reactor.
   112  func NewReactorV2(
   113  	logger log.Logger,
   114  	peerManager *p2p.PeerManager,
   115  	pexCh *p2p.Channel,
   116  	peerUpdates *p2p.PeerUpdates,
   117  ) *ReactorV2 {
   118  
   119  	r := &ReactorV2{
   120  		peerManager:          peerManager,
   121  		pexCh:                pexCh,
   122  		peerUpdates:          peerUpdates,
   123  		closeCh:              make(chan struct{}),
   124  		availablePeers:       make(map[types.NodeID]struct{}),
   125  		requestsSent:         make(map[types.NodeID]struct{}),
   126  		lastReceivedRequests: make(map[types.NodeID]time.Time),
   127  	}
   128  
   129  	r.BaseService = *service.NewBaseService(logger, "PEX", r)
   130  	return r
   131  }
   132  
   133  // OnStart starts separate go routines for each p2p Channel and listens for
   134  // envelopes on each. In addition, it also listens for peer updates and handles
   135  // messages on that p2p channel accordingly. The caller must be sure to execute
   136  // OnStop to ensure the outbound p2p Channels are closed.
   137  func (r *ReactorV2) OnStart() error {
   138  	go r.processPexCh()
   139  	go r.processPeerUpdates()
   140  	return nil
   141  }
   142  
   143  // OnStop stops the reactor by signaling to all spawned goroutines to exit and
   144  // blocking until they all exit.
   145  func (r *ReactorV2) OnStop() {
   146  	// Close closeCh to signal to all spawned goroutines to gracefully exit. All
   147  	// p2p Channels should execute Close().
   148  	close(r.closeCh)
   149  
   150  	// Wait for all p2p Channels to be closed before returning. This ensures we
   151  	// can easily reason about synchronization of all p2p Channels and ensure no
   152  	// panics will occur.
   153  	<-r.pexCh.Done()
   154  	<-r.peerUpdates.Done()
   155  }
   156  
   157  // processPexCh implements a blocking event loop where we listen for p2p
   158  // Envelope messages from the pexCh.
   159  func (r *ReactorV2) processPexCh() {
   160  	defer r.pexCh.Close()
   161  
   162  	for {
   163  		select {
   164  		case <-r.closeCh:
   165  			r.Logger.Debug("stopped listening on PEX channel; closing...")
   166  			return
   167  
   168  		// outbound requests for new peers
   169  		case <-r.waitUntilNextRequest():
   170  			r.sendRequestForPeers()
   171  
   172  		// inbound requests for new peers or responses to requests sent by this
   173  		// reactor
   174  		case envelope := <-r.pexCh.In:
   175  			if err := r.handleMessage(r.pexCh.ID, envelope); err != nil {
   176  				r.Logger.Error("failed to process message", "ch_id", r.pexCh.ID, "envelope", envelope, "err", err)
   177  				r.pexCh.Error <- p2p.PeerError{
   178  					NodeID: envelope.From,
   179  					Err:    err,
   180  				}
   181  			}
   182  		}
   183  	}
   184  }
   185  
   186  // processPeerUpdates initiates a blocking process where we listen for and handle
   187  // PeerUpdate messages. When the reactor is stopped, we will catch the signal and
   188  // close the p2p PeerUpdatesCh gracefully.
   189  func (r *ReactorV2) processPeerUpdates() {
   190  	defer r.peerUpdates.Close()
   191  
   192  	for {
   193  		select {
   194  		case peerUpdate := <-r.peerUpdates.Updates():
   195  			r.processPeerUpdate(peerUpdate)
   196  
   197  		case <-r.closeCh:
   198  			r.Logger.Debug("stopped listening on peer updates channel; closing...")
   199  			return
   200  		}
   201  	}
   202  }
   203  
   204  // handlePexMessage handles envelopes sent from peers on the PexChannel.
   205  func (r *ReactorV2) handlePexMessage(envelope p2p.Envelope) error {
   206  	logger := r.Logger.With("peer", envelope.From)
   207  
   208  	switch msg := envelope.Message.(type) {
   209  
   210  	case *protop2p.PexRequest:
   211  		// Check if the peer hasn't sent a prior request too close to this one
   212  		// in time.
   213  		if err := r.markPeerRequest(envelope.From); err != nil {
   214  			return err
   215  		}
   216  
   217  		// parse and send the legacy PEX addresses
   218  		pexAddresses := r.resolve(r.peerManager.Advertise(envelope.From, maxAddresses))
   219  		r.pexCh.Out <- p2p.Envelope{
   220  			To:      envelope.From,
   221  			Message: &protop2p.PexResponse{Addresses: pexAddresses},
   222  		}
   223  
   224  	case *protop2p.PexResponse:
   225  		// check if the response matches a request that was made to that peer
   226  		if err := r.markPeerResponse(envelope.From); err != nil {
   227  			return err
   228  		}
   229  
   230  		// check the size of the response
   231  		if len(msg.Addresses) > int(maxAddresses) {
   232  			return fmt.Errorf("peer sent too many addresses (max: %d, got: %d)",
   233  				maxAddresses,
   234  				len(msg.Addresses),
   235  			)
   236  		}
   237  
   238  		for _, pexAddress := range msg.Addresses {
   239  			// no protocol is prefixed so we assume the default (mconn)
   240  			peerAddress, err := p2p.ParseNodeAddress(
   241  				fmt.Sprintf("%s@%s:%d", pexAddress.ID, pexAddress.IP, pexAddress.Port))
   242  			if err != nil {
   243  				continue
   244  			}
   245  			added, err := r.peerManager.Add(peerAddress)
   246  			if err != nil {
   247  				logger.Error("failed to add PEX address", "address", peerAddress, "err", err)
   248  			}
   249  			if added {
   250  				r.newPeers++
   251  				logger.Debug("added PEX address", "address", peerAddress)
   252  			}
   253  			r.totalPeers++
   254  		}
   255  
   256  	// V2 PEX MESSAGES
   257  	case *protop2p.PexRequestV2:
   258  		// check if the peer hasn't sent a prior request too close to this one
   259  		// in time
   260  		if err := r.markPeerRequest(envelope.From); err != nil {
   261  			return err
   262  		}
   263  
   264  		// request peers from the peer manager and parse the NodeAddresses into
   265  		// URL strings
   266  		nodeAddresses := r.peerManager.Advertise(envelope.From, maxAddresses)
   267  		pexAddressesV2 := make([]protop2p.PexAddressV2, len(nodeAddresses))
   268  		for idx, addr := range nodeAddresses {
   269  			pexAddressesV2[idx] = protop2p.PexAddressV2{
   270  				URL: addr.String(),
   271  			}
   272  		}
   273  		r.pexCh.Out <- p2p.Envelope{
   274  			To:      envelope.From,
   275  			Message: &protop2p.PexResponseV2{Addresses: pexAddressesV2},
   276  		}
   277  
   278  	case *protop2p.PexResponseV2:
   279  		// check if the response matches a request that was made to that peer
   280  		if err := r.markPeerResponse(envelope.From); err != nil {
   281  			return err
   282  		}
   283  
   284  		// check the size of the response
   285  		if len(msg.Addresses) > int(maxAddresses) {
   286  			return fmt.Errorf("peer sent too many addresses (max: %d, got: %d)",
   287  				maxAddresses,
   288  				len(msg.Addresses),
   289  			)
   290  		}
   291  
   292  		for _, pexAddress := range msg.Addresses {
   293  			peerAddress, err := p2p.ParseNodeAddress(pexAddress.URL)
   294  			if err != nil {
   295  				continue
   296  			}
   297  			added, err := r.peerManager.Add(peerAddress)
   298  			if err != nil {
   299  				logger.Error("failed to add V2 PEX address", "address", peerAddress, "err", err)
   300  			}
   301  			if added {
   302  				r.newPeers++
   303  				logger.Debug("added V2 PEX address", "address", peerAddress)
   304  			}
   305  			r.totalPeers++
   306  		}
   307  
   308  	default:
   309  		return fmt.Errorf("received unknown message: %T", msg)
   310  	}
   311  
   312  	return nil
   313  }
   314  
   315  // resolve resolves a set of peer addresses into PEX addresses.
   316  //
   317  // FIXME: This is necessary because the current PEX protocol only supports
   318  // IP/port pairs, while the P2P stack uses NodeAddress URLs. The PEX protocol
   319  // should really use URLs too, to exchange DNS names instead of IPs and allow
   320  // different transport protocols (e.g. QUIC and MemoryTransport).
   321  //
   322  // FIXME: We may want to cache and parallelize this, but for now we'll just rely
   323  // on the operating system to cache it for us.
   324  func (r *ReactorV2) resolve(addresses []p2p.NodeAddress) []protop2p.PexAddress {
   325  	limit := len(addresses)
   326  	pexAddresses := make([]protop2p.PexAddress, 0, limit)
   327  
   328  	for _, address := range addresses {
   329  		ctx, cancel := context.WithTimeout(context.Background(), resolveTimeout)
   330  		endpoints, err := address.Resolve(ctx)
   331  		r.Logger.Debug("resolved node address", "endpoints", endpoints)
   332  		cancel()
   333  
   334  		if err != nil {
   335  			r.Logger.Debug("failed to resolve address", "address", address, "err", err)
   336  			continue
   337  		}
   338  
   339  		for _, endpoint := range endpoints {
   340  			r.Logger.Debug("checking endpint", "IP", endpoint.IP, "Port", endpoint.Port)
   341  			if len(pexAddresses) >= limit {
   342  				return pexAddresses
   343  
   344  			} else if endpoint.IP != nil {
   345  				r.Logger.Debug("appending pex address")
   346  				// PEX currently only supports IP-networked transports (as
   347  				// opposed to e.g. p2p.MemoryTransport).
   348  				//
   349  				// FIXME: as the PEX address contains no information about the
   350  				// protocol, we jam this into the ID. We won't need to this once
   351  				// we support URLs
   352  				pexAddresses = append(pexAddresses, protop2p.PexAddress{
   353  					ID:   string(address.NodeID),
   354  					IP:   endpoint.IP.String(),
   355  					Port: uint32(endpoint.Port),
   356  				})
   357  			}
   358  		}
   359  	}
   360  
   361  	return pexAddresses
   362  }
   363  
   364  // handleMessage handles an Envelope sent from a peer on a specific p2p Channel.
   365  // It will handle errors and any possible panics gracefully. A caller can handle
   366  // any error returned by sending a PeerError on the respective channel.
   367  func (r *ReactorV2) handleMessage(chID p2p.ChannelID, envelope p2p.Envelope) (err error) {
   368  	defer func() {
   369  		if e := recover(); e != nil {
   370  			err = fmt.Errorf("panic in processing message: %v", e)
   371  			r.Logger.Error(
   372  				"recovering from processing message panic",
   373  				"err", err,
   374  				"stack", string(debug.Stack()),
   375  			)
   376  		}
   377  	}()
   378  
   379  	r.Logger.Debug("received PEX message", "peer", envelope.From)
   380  
   381  	switch chID {
   382  	case p2p.ChannelID(PexChannel):
   383  		err = r.handlePexMessage(envelope)
   384  
   385  	default:
   386  		err = fmt.Errorf("unknown channel ID (%d) for envelope (%v)", chID, envelope)
   387  	}
   388  
   389  	return err
   390  }
   391  
   392  // processPeerUpdate processes a PeerUpdate. For added peers, PeerStatusUp, we
   393  // send a request for addresses.
   394  func (r *ReactorV2) processPeerUpdate(peerUpdate p2p.PeerUpdate) {
   395  	r.Logger.Debug("received PEX peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status)
   396  
   397  	r.mtx.Lock()
   398  	defer r.mtx.Unlock()
   399  
   400  	switch peerUpdate.Status {
   401  	case p2p.PeerStatusUp:
   402  		r.availablePeers[peerUpdate.NodeID] = struct{}{}
   403  	case p2p.PeerStatusDown:
   404  		delete(r.availablePeers, peerUpdate.NodeID)
   405  		delete(r.requestsSent, peerUpdate.NodeID)
   406  		delete(r.lastReceivedRequests, peerUpdate.NodeID)
   407  	default:
   408  	}
   409  }
   410  
   411  func (r *ReactorV2) waitUntilNextRequest() <-chan time.Time {
   412  	return time.After(time.Until(r.nextRequestTime))
   413  }
   414  
   415  // sendRequestForPeers pops the first peerID off the list and sends the
   416  // peer a request for more peer addresses. The function then moves the
   417  // peer into the requestsSent bucket and calculates when the next request
   418  // time should be
   419  func (r *ReactorV2) sendRequestForPeers() {
   420  	r.mtx.Lock()
   421  	defer r.mtx.Unlock()
   422  	if len(r.availablePeers) == 0 {
   423  		// no peers are available
   424  		r.Logger.Debug("no available peers to send request to, waiting...")
   425  		r.nextRequestTime = time.Now().Add(noAvailablePeersWaitPeriod)
   426  
   427  		return
   428  	}
   429  	var peerID types.NodeID
   430  
   431  	// use range to get a random peer.
   432  	for peerID = range r.availablePeers {
   433  		break
   434  	}
   435  
   436  	// The node accommodates for both pex systems
   437  	if r.isLegacyPeer(peerID) {
   438  		r.pexCh.Out <- p2p.Envelope{
   439  			To:      peerID,
   440  			Message: &protop2p.PexRequest{},
   441  		}
   442  	} else {
   443  		r.pexCh.Out <- p2p.Envelope{
   444  			To:      peerID,
   445  			Message: &protop2p.PexRequestV2{},
   446  		}
   447  	}
   448  
   449  	// remove the peer from the abvailable peers list and mark it in the requestsSent map
   450  	delete(r.availablePeers, peerID)
   451  	r.requestsSent[peerID] = struct{}{}
   452  
   453  	r.calculateNextRequestTime()
   454  	r.Logger.Debug("peer request sent", "next_request_time", r.nextRequestTime)
   455  }
   456  
   457  // calculateNextRequestTime implements something of a proportional controller
   458  // to estimate how often the reactor should be requesting new peer addresses.
   459  // The dependent variable in this calculation is the ratio of new peers to
   460  // all peers that the reactor receives. The interval is thus calculated as the
   461  // inverse squared. In the beginning, all peers should be new peers.
   462  // We  expect this ratio to be near 1 and thus the interval to be as short
   463  // as possible. As the node becomes more familiar with the network the ratio of
   464  // new nodes will plummet to a very small number, meaning the interval expands
   465  // to its upper bound.
   466  // CONTRACT: Must use a write lock as nextRequestTime is updated
   467  func (r *ReactorV2) calculateNextRequestTime() {
   468  	// check if the peer store is full. If so then there is no need
   469  	// to send peer requests too often
   470  	if ratio := r.peerManager.PeerRatio(); ratio >= 0.95 {
   471  		r.Logger.Debug("peer manager near full ratio, sleeping...",
   472  			"sleep_period", fullCapacityInterval, "ratio", ratio)
   473  		r.nextRequestTime = time.Now().Add(fullCapacityInterval)
   474  		return
   475  	}
   476  
   477  	// baseTime represents the shortest interval that we can send peer requests
   478  	// in. For example if we have 10 peers and we can't send a message to the
   479  	// same peer every 500ms, then we can send a request every 50ms. In practice
   480  	// we use a safety margin of 2, ergo 100ms
   481  	peers := tmmath.MinInt(len(r.availablePeers), 50)
   482  	baseTime := minReceiveRequestInterval
   483  	if peers > 0 {
   484  		baseTime = minReceiveRequestInterval * 2 / time.Duration(peers)
   485  	}
   486  
   487  	if r.totalPeers > 0 || r.discoveryRatio == 0 {
   488  		// find the ratio of new peers. NOTE: We add 1 to both sides to avoid
   489  		// divide by zero problems
   490  		ratio := float32(r.totalPeers+1) / float32(r.newPeers+1)
   491  		// square the ratio in order to get non linear time intervals
   492  		// NOTE: The longest possible interval for a network with 100 or more peers
   493  		// where a node is connected to 50 of them is 2 minutes.
   494  		r.discoveryRatio = ratio * ratio
   495  		r.newPeers = 0
   496  		r.totalPeers = 0
   497  	}
   498  	// NOTE: As ratio is always >= 1, discovery ratio is >= 1. Therefore we don't need to worry
   499  	// about the next request time being less than the minimum time
   500  	r.nextRequestTime = time.Now().Add(baseTime * time.Duration(r.discoveryRatio))
   501  }
   502  
   503  func (r *ReactorV2) markPeerRequest(peer types.NodeID) error {
   504  	r.mtx.Lock()
   505  	defer r.mtx.Unlock()
   506  	if lastRequestTime, ok := r.lastReceivedRequests[peer]; ok {
   507  		if time.Now().Before(lastRequestTime.Add(minReceiveRequestInterval)) {
   508  			return fmt.Errorf("peer sent a request too close after a prior one. Minimum interval: %v",
   509  				minReceiveRequestInterval)
   510  		}
   511  	}
   512  	r.lastReceivedRequests[peer] = time.Now()
   513  	return nil
   514  }
   515  
   516  func (r *ReactorV2) markPeerResponse(peer types.NodeID) error {
   517  	r.mtx.Lock()
   518  	defer r.mtx.Unlock()
   519  	// check if a request to this peer was sent
   520  	if _, ok := r.requestsSent[peer]; !ok {
   521  		return fmt.Errorf("peer sent a PEX response when none was requested (%v)", peer)
   522  	}
   523  	delete(r.requestsSent, peer)
   524  	// attach to the back of the list so that the peer can be used again for
   525  	// future requests
   526  
   527  	r.availablePeers[peer] = struct{}{}
   528  	return nil
   529  }
   530  
   531  // all addresses must use a MCONN protocol for the peer to be considered part of the
   532  // legacy p2p pex system
   533  func (r *ReactorV2) isLegacyPeer(peer types.NodeID) bool {
   534  	for _, addr := range r.peerManager.Addresses(peer) {
   535  		if addr.Protocol != p2p.MConnProtocol {
   536  			return false
   537  		}
   538  	}
   539  	return true
   540  }