github.com/pokt-network/tendermint@v0.32.11-0.20230426215212-59310158d3e9/p2p/peer.go (about)

     1  package p2p
     2  
     3  import (
     4  	"fmt"
     5  	"net"
     6  	"time"
     7  
     8  	"github.com/tendermint/tendermint/libs/cmap"
     9  	"github.com/tendermint/tendermint/libs/log"
    10  	"github.com/tendermint/tendermint/libs/service"
    11  
    12  	tmconn "github.com/tendermint/tendermint/p2p/conn"
    13  )
    14  
    15  const metricsTickerDuration = 10 * time.Second
    16  
    17  // Peer is an interface representing a peer connected on a reactor.
    18  type Peer interface {
    19  	service.Service
    20  	FlushStop()
    21  
    22  	ID() ID               // peer's cryptographic ID
    23  	RemoteIP() net.IP     // remote IP of the connection
    24  	RemoteAddr() net.Addr // remote address of the connection
    25  
    26  	IsOutbound() bool   // did we dial the peer
    27  	IsPersistent() bool // do we redial this peer when we disconnect
    28  
    29  	CloseConn() error // close original connection
    30  
    31  	NodeInfo() NodeInfo // peer's info
    32  	Status() tmconn.ConnectionStatus
    33  	SocketAddr() *NetAddress // actual address of the socket
    34  
    35  	Send(byte, []byte) bool
    36  	TrySend(byte, []byte) bool
    37  
    38  	Set(string, interface{})
    39  	Get(string) interface{}
    40  
    41  	SetRemovalFailed()
    42  	GetRemovalFailed() bool
    43  }
    44  
    45  //----------------------------------------------------------
    46  
    47  // peerConn contains the raw connection and its config.
    48  type peerConn struct {
    49  	outbound   bool
    50  	persistent bool
    51  	conn       net.Conn // source connection
    52  
    53  	socketAddr *NetAddress
    54  
    55  	// cached RemoteIP()
    56  	ip net.IP
    57  }
    58  
    59  func newPeerConn(
    60  	outbound, persistent bool,
    61  	conn net.Conn,
    62  	socketAddr *NetAddress,
    63  ) peerConn {
    64  
    65  	return peerConn{
    66  		outbound:   outbound,
    67  		persistent: persistent,
    68  		conn:       conn,
    69  		socketAddr: socketAddr,
    70  	}
    71  }
    72  
    73  // ID only exists for SecretConnection.
    74  // NOTE: Will panic if conn is not *SecretConnection.
    75  func (pc peerConn) ID() ID {
    76  	return PubKeyToID(pc.conn.(*tmconn.SecretConnection).RemotePubKey())
    77  }
    78  
    79  // Return the IP from the connection RemoteAddr
    80  func (pc peerConn) RemoteIP() net.IP {
    81  	if pc.ip != nil {
    82  		return pc.ip
    83  	}
    84  
    85  	host, _, err := net.SplitHostPort(pc.conn.RemoteAddr().String())
    86  	if err != nil {
    87  		panic(err)
    88  	}
    89  
    90  	ips, err := net.LookupIP(host)
    91  	if err != nil {
    92  		panic(err)
    93  	}
    94  
    95  	pc.ip = ips[0]
    96  
    97  	return pc.ip
    98  }
    99  
   100  // peer implements Peer.
   101  //
   102  // Before using a peer, you will need to perform a handshake on connection.
   103  type peer struct {
   104  	service.BaseService
   105  
   106  	// raw peerConn and the multiplex connection
   107  	peerConn
   108  	mconn *tmconn.MConnection
   109  
   110  	// peer's node info and the channel it knows about
   111  	// channels = nodeInfo.Channels
   112  	// cached to avoid copying nodeInfo in hasChannel
   113  	nodeInfo NodeInfo
   114  	channels []byte
   115  
   116  	// User data
   117  	Data *cmap.CMap
   118  
   119  	metrics       *Metrics
   120  	metricsTicker *time.Ticker
   121  
   122  	// When removal of a peer fails, we set this flag
   123  	removalAttemptFailed bool
   124  }
   125  
   126  type PeerOption func(*peer)
   127  
   128  func (p *peer) SetRemovalFailed() {
   129  	p.removalAttemptFailed = true
   130  }
   131  
   132  func (p *peer) GetRemovalFailed() bool {
   133  	return p.removalAttemptFailed
   134  }
   135  
   136  func newPeer(
   137  	pc peerConn,
   138  	mConfig tmconn.MConnConfig,
   139  	nodeInfo NodeInfo,
   140  	reactorsByCh map[byte]Reactor,
   141  	chDescs []*tmconn.ChannelDescriptor,
   142  	onPeerError func(Peer, interface{}),
   143  	options ...PeerOption,
   144  ) *peer {
   145  	p := &peer{
   146  		peerConn:      pc,
   147  		nodeInfo:      nodeInfo,
   148  		channels:      nodeInfo.(DefaultNodeInfo).Channels, // TODO
   149  		Data:          cmap.NewCMap(),
   150  		metricsTicker: time.NewTicker(metricsTickerDuration),
   151  		metrics:       NopMetrics(),
   152  	}
   153  
   154  	p.mconn = createMConnection(
   155  		pc.conn,
   156  		p,
   157  		reactorsByCh,
   158  		chDescs,
   159  		onPeerError,
   160  		mConfig,
   161  	)
   162  	p.BaseService = *service.NewBaseService(nil, "Peer", p)
   163  	for _, option := range options {
   164  		option(p)
   165  	}
   166  
   167  	return p
   168  }
   169  
   170  // String representation.
   171  func (p *peer) String() string {
   172  	if p.outbound {
   173  		return fmt.Sprintf("Peer{%v %v out}", p.mconn, p.ID())
   174  	}
   175  
   176  	return fmt.Sprintf("Peer{%v %v in}", p.mconn, p.ID())
   177  }
   178  
   179  //---------------------------------------------------
   180  // Implements service.Service
   181  
   182  // SetLogger implements BaseService.
   183  func (p *peer) SetLogger(l log.Logger) {
   184  	p.Logger = l
   185  	p.mconn.SetLogger(l)
   186  }
   187  
   188  // OnStart implements BaseService.
   189  func (p *peer) OnStart() error {
   190  	if err := p.BaseService.OnStart(); err != nil {
   191  		return err
   192  	}
   193  
   194  	if err := p.mconn.Start(); err != nil {
   195  		return err
   196  	}
   197  
   198  	go p.metricsReporter()
   199  	return nil
   200  }
   201  
   202  // FlushStop mimics OnStop but additionally ensures that all successful
   203  // .Send() calls will get flushed before closing the connection.
   204  // NOTE: it is not safe to call this method more than once.
   205  func (p *peer) FlushStop() {
   206  	p.metricsTicker.Stop()
   207  	p.BaseService.OnStop()
   208  	p.mconn.FlushStop() // stop everything and close the conn
   209  }
   210  
   211  // OnStop implements BaseService.
   212  func (p *peer) OnStop() {
   213  	p.metricsTicker.Stop()
   214  	p.BaseService.OnStop()
   215  	p.mconn.Stop() // stop everything and close the conn
   216  }
   217  
   218  //---------------------------------------------------
   219  // Implements Peer
   220  
   221  // ID returns the peer's ID - the hex encoded hash of its pubkey.
   222  func (p *peer) ID() ID {
   223  	return p.nodeInfo.ID()
   224  }
   225  
   226  // IsOutbound returns true if the connection is outbound, false otherwise.
   227  func (p *peer) IsOutbound() bool {
   228  	return p.peerConn.outbound
   229  }
   230  
   231  // IsPersistent returns true if the peer is persitent, false otherwise.
   232  func (p *peer) IsPersistent() bool {
   233  	return p.peerConn.persistent
   234  }
   235  
   236  // NodeInfo returns a copy of the peer's NodeInfo.
   237  func (p *peer) NodeInfo() NodeInfo {
   238  	return p.nodeInfo
   239  }
   240  
   241  // SocketAddr returns the address of the socket.
   242  // For outbound peers, it's the address dialed (after DNS resolution).
   243  // For inbound peers, it's the address returned by the underlying connection
   244  // (not what's reported in the peer's NodeInfo).
   245  func (p *peer) SocketAddr() *NetAddress {
   246  	return p.peerConn.socketAddr
   247  }
   248  
   249  // Status returns the peer's ConnectionStatus.
   250  func (p *peer) Status() tmconn.ConnectionStatus {
   251  	return p.mconn.Status()
   252  }
   253  
   254  // Send msg bytes to the channel identified by chID byte. Returns false if the
   255  // send queue is full after timeout, specified by MConnection.
   256  func (p *peer) Send(chID byte, msgBytes []byte) bool {
   257  	if !p.IsRunning() {
   258  		// see Switch#Broadcast, where we fetch the list of peers and loop over
   259  		// them - while we're looping, one peer may be removed and stopped.
   260  		return false
   261  	} else if !p.hasChannel(chID) {
   262  		return false
   263  	}
   264  	res := p.mconn.Send(chID, msgBytes)
   265  	if res {
   266  		labels := []string{
   267  			"peer_id", string(p.ID()),
   268  			"chID", fmt.Sprintf("%#x", chID),
   269  		}
   270  		p.metrics.PeerSendBytesTotal.With(labels...).Add(float64(len(msgBytes)))
   271  	}
   272  	return res
   273  }
   274  
   275  // TrySend msg bytes to the channel identified by chID byte. Immediately returns
   276  // false if the send queue is full.
   277  func (p *peer) TrySend(chID byte, msgBytes []byte) bool {
   278  	if !p.IsRunning() {
   279  		return false
   280  	} else if !p.hasChannel(chID) {
   281  		return false
   282  	}
   283  	res := p.mconn.TrySend(chID, msgBytes)
   284  	if res {
   285  		labels := []string{
   286  			"peer_id", string(p.ID()),
   287  			"chID", fmt.Sprintf("%#x", chID),
   288  		}
   289  		p.metrics.PeerSendBytesTotal.With(labels...).Add(float64(len(msgBytes)))
   290  	}
   291  	return res
   292  }
   293  
   294  // Get the data for a given key.
   295  func (p *peer) Get(key string) interface{} {
   296  	return p.Data.Get(key)
   297  }
   298  
   299  // Set sets the data for the given key.
   300  func (p *peer) Set(key string, data interface{}) {
   301  	p.Data.Set(key, data)
   302  }
   303  
   304  // hasChannel returns true if the peer reported
   305  // knowing about the given chID.
   306  func (p *peer) hasChannel(chID byte) bool {
   307  	for _, ch := range p.channels {
   308  		if ch == chID {
   309  			return true
   310  		}
   311  	}
   312  	// NOTE: probably will want to remove this
   313  	// but could be helpful while the feature is new
   314  	p.Logger.Debug(
   315  		"Unknown channel for peer",
   316  		"channel",
   317  		chID,
   318  		"channels",
   319  		p.channels,
   320  	)
   321  	return false
   322  }
   323  
   324  // CloseConn closes original connection. Used for cleaning up in cases where the peer had not been started at all.
   325  func (p *peer) CloseConn() error {
   326  	return p.peerConn.conn.Close()
   327  }
   328  
   329  //---------------------------------------------------
   330  // methods only used for testing
   331  // TODO: can we remove these?
   332  
   333  // CloseConn closes the underlying connection
   334  func (pc *peerConn) CloseConn() {
   335  	pc.conn.Close() // nolint: errcheck
   336  }
   337  
   338  // RemoteAddr returns peer's remote network address.
   339  func (p *peer) RemoteAddr() net.Addr {
   340  	return p.peerConn.conn.RemoteAddr()
   341  }
   342  
   343  // CanSend returns true if the send queue is not full, false otherwise.
   344  func (p *peer) CanSend(chID byte) bool {
   345  	if !p.IsRunning() {
   346  		return false
   347  	}
   348  	return p.mconn.CanSend(chID)
   349  }
   350  
   351  //---------------------------------------------------
   352  
   353  func PeerMetrics(metrics *Metrics) PeerOption {
   354  	return func(p *peer) {
   355  		p.metrics = metrics
   356  	}
   357  }
   358  
   359  func (p *peer) metricsReporter() {
   360  	for {
   361  		select {
   362  		case <-p.metricsTicker.C:
   363  			status := p.mconn.Status()
   364  			var sendQueueSize float64
   365  			for _, chStatus := range status.Channels {
   366  				sendQueueSize += float64(chStatus.SendQueueSize)
   367  			}
   368  
   369  			p.metrics.PeerPendingSendBytes.With("peer_id", string(p.ID())).Set(sendQueueSize)
   370  		case <-p.Quit():
   371  			return
   372  		}
   373  	}
   374  }
   375  
   376  //------------------------------------------------------------------
   377  // helper funcs
   378  
   379  func createMConnection(
   380  	conn net.Conn,
   381  	p *peer,
   382  	reactorsByCh map[byte]Reactor,
   383  	chDescs []*tmconn.ChannelDescriptor,
   384  	onPeerError func(Peer, interface{}),
   385  	config tmconn.MConnConfig,
   386  ) *tmconn.MConnection {
   387  
   388  	onReceive := func(chID byte, msgBytes []byte) {
   389  		reactor := reactorsByCh[chID]
   390  		if reactor == nil {
   391  			// Note that its ok to panic here as it's caught in the conn._recover,
   392  			// which does onPeerError.
   393  			panic(fmt.Sprintf("Unknown channel %X", chID))
   394  		}
   395  		labels := []string{
   396  			"peer_id", string(p.ID()),
   397  			"chID", fmt.Sprintf("%#x", chID),
   398  		}
   399  		p.metrics.PeerReceiveBytesTotal.With(labels...).Add(float64(len(msgBytes)))
   400  		reactor.Receive(chID, p, msgBytes)
   401  	}
   402  
   403  	onError := func(r interface{}) {
   404  		onPeerError(p, r)
   405  	}
   406  
   407  	return tmconn.NewMConnectionWithConfig(
   408  		conn,
   409  		chDescs,
   410  		onReceive,
   411  		onError,
   412  		config,
   413  	)
   414  }